Hi All, Doing some experiments with the sql_injection.nasl yesterday showed that it failed to detect a trivially injectable CGI we set up. Looking back through the history of the plugin it appears that the problem was introduced in revision 1.25 when support for blind injection was added. I've attached a version that correctly detects the injection vulnerability (and also added a generic signature for oracle error messages). The problem sections are below: # This breaks detecting a trivially injectable CGI # if (egrep(string:bres, pattern:"^HTTP/1\..*200 OK")) # { # exit(0); # } Many CGIs will always give a 200 response, so this test will always call the script to exit rather than properly testing the CGI. # This breaks detecting a trivially injectable CGI # for ( i = 0; posreply[i]; i ++ ) # { # if ( posreply[i] >< res ) { # exit(0); # } # } This check follows sending a big number as the query string and terminates unless the script gives you an error. This is often not true for injectable CGIs - they may simply ignore the input and return (for example) a form for you to fill in. Neither of these checks were in revision 1.24 and it seems likely that their addition will lead to many new false negatives. Cheers Rich. -- Richard Moore, Principal Software Engineer, Westpoint Ltd, Albion Wharf, 19 Albion Street, Manchester, M1 5LN, England Tel: +44 161 237 1028 Fax: +44 161 237 1031 # # This script was written by John Lampe ... j_lampe@private # # Initial version of script was based (loosely) on wpoison by M.Meadele mm@private # See http://wpoison.sourceforge.net # # See the Nessus Scripts License for details # # # re-worked Aug 20, 2004 : jwlampe -at- tenablesecurity.com adds POST checks # June/July 2005 : jwlampe -at- tenablesecurity.com adds Blind SQL Injection checks if(description) { script_id(11139); #script_cve_id("CVE-MAP-NOMATCH"); script_version ("$Revision: 1.26 $"); name["english"] = "wpoison (nasl version)"; script_name(english:name["english"]); desc["english"] = " This script attempts to use SQL injection techniques on CGI scripts More info at : http://www.securitydocs.com/library/2651 Solution : Modify the relevant CGIs so that they properly escape arguments. Risk factor : High"; script_description(english:desc["english"]); summary["english"] = "Some common SQL injection techniques"; script_summary(english:summary["english"]); script_category(ACT_GATHER_INFO); script_copyright(english:"This script is Copyright (C) 2002 John Lampe...j_lampe@private"); family["english"] = "CGI abuses"; family["francais"] = "Abus de CGI"; script_family(english:family["english"], francais:family["francais"]); script_dependencie("find_service.nes", "webmirror.nasl"); script_require_ports("Services/www", 80); exit(0); } # # The script code starts here # include("http_func.inc"); include("http_keepalive.inc"); single_quote = raw_string(0x27); poison[0] = single_quote + "UNION" + single_quote; poison[1] = single_quote; poison[2] = single_quote + "%22"; poison[3] = "9%2c+9%2c+9"; poison[4] = single_quote + "bad_bad_value"; poison[5] = "bad_bad_value" + single_quote; poison[6] = single_quote + "+OR+" + single_quote; poison[7] = single_quote + "WHERE"; poison[8] = "%3B"; # semicolon poison[9] = single_quote + "OR"; # methods below from http://www.securiteam.com/securityreviews/5DP0N1P76E.html poison[10] = single_quote + " or 1=1--"; poison[11] = " or 1=1--"; poison[12] = single_quote + " or " + single_quote + "a" + single_quote + "=" + single_quote + "a"; poison[13] = single_quote + ") or (" + single_quote + "a" + single_quote + "=" + single_quote + "a"; # blind sql injection methods that we will pass # if they are putting the user-supplied variable within single quotes, then we trick them with this blinder[0] = single_quote + "+AND+" + single_quote + "a" + single_quote + ">" + single_quote + "b"; # otherwise, this will work most of the time blinder[1] = "+AND+1=1"; posreply[0] = "Can't find record in"; posreply[1] = "Column count doesn't match value count at row"; posreply[2] = "error " + single_quote; posreply[3] = "Incorrect column name"; posreply[4] = "Incorrect column specifier for column"; posreply[5] = "Invalid parameter type"; posreply[6] = "Microsoft OLE DB Provider for ODBC Drivers error"; posreply[7] = "ODBC Microsoft Access Driver"; posreply[8] = "ODBC SQL Server Driver"; posreply[9] = "supplied argument is not a valid MySQL result"; posreply[10] = "mysql_query()"; posreply[11] = "Unknown table"; posreply[12] = "You have an error in your SQL syntax"; posreply[13] = "Error Occurred While Processing Request"; posreply[14] = "Syntax"; posreply[15] = "not a valid MySQL result resource"; posreply[16] = "unexpected end of SQL command"; posreply[17] = "mySQL error with query"; posreply[18] = "ORA-00936: missing expression"; posreply[19] = "ORA-00933: SQL command not properly ended"; posreply[20] = "Unclosed quotation mark before the character string"; posreply[21] = "Incorrect syntax near"; posreply[22] = "PostgreSQL query failed:"; posreply[23] = "not a valid PostgreSQL result"; posreply[24] = "An illegal character has been found in the statement"; posreply[25] = "[IBM][CLI Driver][DB2/6000]"; posreply[26] = "Unable to connect to PostgreSQL server:"; posreply[27] = "Can't connect to local"; posreply[28] = "ADODB.Recordset"; posreply[29] = "ORA-"; port = get_http_port(default:80); if(! get_port_state(port)) exit(0); unsafe_urls = ""; mywarningcount = blindwarningcount = 0; name = string("www/", port, "/cgis"); cgi = get_kb_item(name); if(! cgi) exit(0); # populate two arrays param[] and data[] everythingrray = split(cgi, sep:" ", keep:FALSE); if (everythingrray[0] =~ ".*/$") { isdir = 1; } else { isdir = 0; } if (! isdir) { vrequest = string(everythingrray[0],"?"); bogus_vrequest = string(everythingrray[0],"?",rand()); pseudocount = 0; foreach rrayval (everythingrray) { if (pseudocount >= 2) { if ("]" >< rrayval) { pseudocount--; tmpf = ereg_replace(pattern:"\[|\]", string:rrayval, replace:""); data[pseudocount] = tmpf; vrequest = string(vrequest,"=",tmpf); } else { param[pseudocount] = rrayval; if (pseudocount == 2) { vrequest = string(vrequest,rrayval); } else { vrequest = string(vrequest,"&",rrayval); } } } else { param[pseudocount] = rrayval; } pseudocount++; } } for (z=2; param[z]; z = z + 1) { blind = ''; url = vrequest; req = http_get(item:url, port:port); res = http_keepalive_send_recv(port:port, data:req); if ( ( res == NULL ) || (! egrep(string:res, pattern:"^HTTP/1\..*(200 OK|302)")) ) { exit(0); } res_saved = strstr(res,string("\r\n\r\n")); req = http_get(item:bogus_vrequest, port:port); bres = http_keepalive_send_recv(port:port, data:req); # This breaks detecting a trivially injectable CGI # if (egrep(string:bres, pattern:"^HTTP/1\..*200 OK")) # { # exit(0); # } # This breaks detecting a trivially injectable CGI # for ( i = 0; posreply[i]; i ++ ) # { # if ( posreply[i] >< res ) { # exit(0); # } # } for (poo=0; poison[poo]; poo = poo + 1) { doblind = 0; qa = ''; url = string(param[0],"?"); blind = string(param[0],"?"); for (i=2 ; param[i]; i = i + 1) { if (i == z) { if (blinder[poo]) { doblind++; qa = string(blind,param[i],"=",data[i],"'"); blind = string(blind,param[i],"=",data[i], blinder[poo]); } if (data[i]) { url = string(url,param[i],"=",poison[poo]); } else { url = string(url,param[i],"=",poison[poo]); } } else { if (blinder[poo]) { qa = string(qa,param[i],"=",data[i]); blind = string(blind,param[i],"=",data[i]); } if (data[i]) { url = string(url,param[i],"=",data[i]); } else { url = string(url,param[i],"="); } } if (param[i + 1]) { url = string(url,"&"); blind = string(blind,"&"); qa = string(qa,"&"); } } req = http_get(item:url, port:port); inbuff = http_keepalive_send_recv(port:port, data:req); if( inbuff == NULL ) exit(0); for (mu=0; posreply[mu]; mu = mu + 1) { if (posreply[mu] >< inbuff ) { unsafe_urls = string(unsafe_urls, url, "\n"); mywarningcount = mywarningcount + 1; } } if (doblind > 0) { req_blind = http_get(item:blind, port:port); inbuff = http_keepalive_send_recv(port:port, data:req_blind); if( inbuff == NULL ) exit(0); buff_body = strstr(inbuff,string("\r\n\r\n")); if (buff_body == res_saved) { req_qa = http_get(item:qa, port:port); inbuff = http_keepalive_send_recv(port:port, data:req_qa); qa_body = strstr(inbuff,string("\r\n\r\n")); if (qa_body != res_saved) { blind_urls = string(blind_urls, blind, "\n"); blindwarningcount = blindwarningcount + 1; } } } if ( safe_checks() == 0 ) { # create a POST req tmppost = split(url, sep:"?", keep:FALSE); mypostdata = tmppost[1]; postreq = http_post(item:param[0], port:port, data:mypostdata); # Test the POST req inbuff = http_keepalive_send_recv(port:port, data:postreq); if ( inbuff == NULL ) exit(0); for (mu=0; posreply[mu]; mu = mu + 1) { if (posreply[mu] >< inbuff ) { unsafe_urls = string(unsafe_urls, url, "\n"); mywarningcount = mywarningcount + 1; } } if (doblind > 0) { # create a blind POST req tmppost = split(blind, sep:"?", keep:FALSE); mypostdata = tmppost[1]; postreq = http_post(item:param[0], port:port, data:mypostdata); inbuff = http_keepalive_send_recv(port:port, data:postreq); if ( inbuff == NULL ) exit(0); buff_body = strstr(inbuff,string("\r\n\r\n")); if (buff_body == res_saved) { qapost = split(blind, sep:"?", keep:FALSE); qapostdata = tmppost[1]; qareq = http_post(item:param[0], port:port, data:qapostdata); qabuff = http_keepalive_send_recv(port:port, data:qareq); qa_body = strstr(qabuff,string("\r\n\r\n")); if (qa_body != res_saved) { blind_urls = string(blind_urls, blind, "\n"); blindwarningcount = blindwarningcount + 1; } } } } # end the non-safe check } } if (mywarningcount > 0) { report = string(" The following URLs seem to be vulnerable to various SQL injection techniques : \n\n", unsafe_urls, "\n\n An attacker may exploit this flaws to bypass authentication or to take the control of the remote database. Solution : Modify the relevant CGIs so that they properly escape arguments Risk factor : High See also : http://www.securiteam.com/securityreviews/5DP0N1P76E.html"); security_hole(port:port, data:report); } if (blindwarningcount > 0) { report = string(" The following URLs seem to be vulnerable to BLIND SQL injection techniques : \n\n", blind_urls, "\n\n An attacker may exploit this flaws to bypass authentication or to take the control of the remote database. Solution : Modify the relevant CGIs so that they properly escape arguments Risk factor : High See also : http://www.securitydocs.com/library/2651"); security_hole(port:port, data:report); } _______________________________________________ Plugins-writers mailing list Plugins-writers@private http://mail.nessus.org/mailman/listinfo/plugins-writers
This archive was generated by hypermail 2.1.3 : Fri Apr 28 2006 - 03:12:49 PDT