diff --git a/lib/controller/controller.py b/lib/controller/controller.py index 41b389491..bb649298b 100644 --- a/lib/controller/controller.py +++ b/lib/controller/controller.py @@ -113,7 +113,7 @@ def _selectInjection(): if select.isdigit() and int(select) < len(kb.injections) and int(select) >= 0: index = int(select) - elif select[0] in ( "Q", "q" ): + elif select[0] in ("Q", "q"): raise SqlmapUserQuitException else: errMsg = "invalid choice" diff --git a/lib/core/agent.py b/lib/core/agent.py index 412039cf4..817c38f9d 100644 --- a/lib/core/agent.py +++ b/lib/core/agent.py @@ -855,13 +855,13 @@ class Agent(object): else: query = expression - if ( select and re.search("\A(COUNT|LTRIM)\(", query, re.I) ) or len(query) <= 1: + if (select and re.search("\A(COUNT|LTRIM)\(", query, re.I)) or len(query) <= 1: return query if selectDistinctExpr: lengthExpr = "SELECT %s FROM (%s)" % (lengthQuery % query, expression) - if Backend.getIdentifiedDbms() in ( DBMS.MYSQL, DBMS.PGSQL ): + if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL): lengthExpr += " AS %s" % randomStr(lowercase=True) elif select: lengthExpr = expression.replace(query, lengthQuery % query, 1) diff --git a/lib/core/option.py b/lib/core/option.py index 5746e04bb..77347d90b 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -14,6 +14,7 @@ import socket import string import sys import threading +import time import urllib2 import urlparse @@ -135,7 +136,7 @@ from lib.request.httpshandler import HTTPSHandler from lib.request.rangehandler import HTTPRangeHandler from lib.request.redirecthandler import SmartRedirectHandler from lib.request.templates import getPageTemplate -from lib.utils.crawler import Crawler +from lib.utils.crawler import crawl from lib.utils.deps import checkDependencies from lib.utils.google import Google from thirdparty.colorama.initialise import init as coloramainit @@ -307,7 +308,7 @@ def _feedTargetsDict(reqFile, addedTargetUrls): params = True # Avoid proxy and connection type related headers - elif key not in ( HTTPHEADER.PROXY_CONNECTION, HTTPHEADER.CONNECTION ): + elif key not in (HTTPHEADER.PROXY_CONNECTION, HTTPHEADER.CONNECTION): conf.httpHeaders.append((getUnicode(key), getUnicode(value))) if getPostReq and (params or cookie): @@ -462,8 +463,7 @@ def _setCrawler(): if not conf.crawlDepth: return - crawler = Crawler() - crawler.getTargetUrls() + crawl(conf.url) def _setGoogleDorking(): """ @@ -571,15 +571,29 @@ def _findPageForms(): if not conf.forms or conf.crawlDepth: return - if not checkConnection(): + if conf.url and not checkConnection(): return infoMsg = "searching for forms" logger.info(infoMsg) - page, _ = Request.queryPage(content=True) + if not conf.bulkFile: + page, _ = Request.queryPage(content=True) + findPageForms(page, conf.url, True, True) + else: + targets = getFileItems(conf.bulkFile) + for i in xrange(len(targets)): + try: + target = targets[i] + page, _, _= Request.getPage(url=target.strip(), crawling=True, raise404=False) + findPageForms(page, target, False, True) - findPageForms(page, conf.url, True, True) + if conf.verbose in (1, 2): + status = '%d/%d links visited (%d%%)' % (i + 1, len(targets), round(100.0 * (i + 1) / len(targets))) + dataToStdout("\r[%s] [INFO] %s" % (time.strftime("%X"), status), True) + except Exception, ex: + errMsg = "problem occured while searching for forms at '%s' ('%s')" % (target, ex) + logger.error(errMsg) def _setDBMSAuthentication(): """ @@ -1047,11 +1061,11 @@ def _setHTTPAuthentication(): aTypeLower = conf.aType.lower() - if aTypeLower not in ( "basic", "digest", "ntlm" ): + if aTypeLower not in ("basic", "digest", "ntlm"): errMsg = "HTTP authentication type value must be " errMsg += "Basic, Digest or NTLM" raise SqlmapSyntaxException(errMsg) - elif aTypeLower in ( "basic", "digest" ): + elif aTypeLower in ("basic", "digest"): regExp = "^(.*?):(.*?)$" errMsg = "HTTP %s authentication credentials " % aTypeLower errMsg += "value must be in format username:password" @@ -1712,8 +1726,8 @@ def _saveCmdline(): if value is None: if datatype == "boolean": value = "False" - elif datatype in ( "integer", "float" ): - if option in ( "threads", "verbose" ): + elif datatype in ("integer", "float"): + if option in ("threads", "verbose"): value = "1" elif option == "timeout": value = "10" @@ -1836,7 +1850,7 @@ def _setTorHttpProxySettings(): found = None - for port in (DEFAULT_TOR_HTTP_PORTS if not conf.torPort else (conf.torPort, )): + for port in (DEFAULT_TOR_HTTP_PORTS if not conf.torPort else (conf.torPort,)): try: s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect((LOCALHOST, port)) @@ -1965,8 +1979,8 @@ def _basicOptionValidation(): errMsg = "maximum number of used threads is %d avoiding possible connection issues" % MAX_NUMBER_OF_THREADS raise SqlmapSyntaxException(errMsg) - if conf.forms and not conf.url: - errMsg = "switch '--forms' requires usage of option '-u' (--url)" + if conf.forms and not any ((conf.url, conf.bulkFile)): + errMsg = "switch '--forms' requires usage of option '-u' (--url) or '-m'" raise SqlmapSyntaxException(errMsg) if conf.requestFile and conf.url: @@ -2009,8 +2023,8 @@ def _basicOptionValidation(): errMsg = "option '--proxy' is incompatible with switch '--ignore-proxy'" raise SqlmapSyntaxException(errMsg) - if conf.forms and any([conf.logFile, conf.bulkFile, conf.direct, conf.requestFile, conf.googleDork]): - errMsg = "switch '--forms' is compatible only with option '-u' (--url)" + if conf.forms and any([conf.logFile, conf.direct, conf.requestFile, conf.googleDork]): + errMsg = "switch '--forms' is compatible only with options '-u' (--url) and '-m'" raise SqlmapSyntaxException(errMsg) if conf.timeSec < 1: diff --git a/lib/core/readlineng.py b/lib/core/readlineng.py index c1e07822a..7951c6e6b 100644 --- a/lib/core/readlineng.py +++ b/lib/core/readlineng.py @@ -38,7 +38,7 @@ uses_libedit = False if PLATFORM == 'mac' and _readline: import commands - (status, result) = commands.getstatusoutput( "otool -L %s | grep libedit" % _readline.__file__ ) + (status, result) = commands.getstatusoutput("otool -L %s | grep libedit" % _readline.__file__) if status == 0 and len(result) > 0: # We are bound to libedit - new in Leopard diff --git a/lib/core/settings.py b/lib/core/settings.py index 2aaad4658..9859c9e76 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -131,43 +131,43 @@ PLATFORM = os.name PYVERSION = sys.version.split()[0] # Database management system specific variables -MSSQL_SYSTEM_DBS = ( "Northwind", "master", "model", "msdb", "pubs", "tempdb" ) -MYSQL_SYSTEM_DBS = ( "information_schema", "mysql" ) # Before MySQL 5.0 only "mysql" -PGSQL_SYSTEM_DBS = ( "information_schema", "pg_catalog", "pg_toast" ) -ORACLE_SYSTEM_DBS = ( "SYSTEM", "SYSAUX", "SYS" ) # These are TABLESPACE_NAME -SQLITE_SYSTEM_DBS = ( "sqlite_master", "sqlite_temp_master" ) -ACCESS_SYSTEM_DBS = ( "MSysAccessObjects", "MSysACEs", "MSysObjects", "MSysQueries", "MSysRelationships", "MSysAccessStorage",\ - "MSysAccessXML", "MSysModules", "MSysModules2" ) -FIREBIRD_SYSTEM_DBS = ( "RDB$BACKUP_HISTORY", "RDB$CHARACTER_SETS", "RDB$CHECK_CONSTRAINTS", "RDB$COLLATIONS", "RDB$DATABASE",\ +MSSQL_SYSTEM_DBS = ("Northwind", "master", "model", "msdb", "pubs", "tempdb") +MYSQL_SYSTEM_DBS = ("information_schema", "mysql") # Before MySQL 5.0 only "mysql" +PGSQL_SYSTEM_DBS = ("information_schema", "pg_catalog", "pg_toast") +ORACLE_SYSTEM_DBS = ("SYSTEM", "SYSAUX", "SYS") # These are TABLESPACE_NAME +SQLITE_SYSTEM_DBS = ("sqlite_master", "sqlite_temp_master") +ACCESS_SYSTEM_DBS = ("MSysAccessObjects", "MSysACEs", "MSysObjects", "MSysQueries", "MSysRelationships", "MSysAccessStorage",\ + "MSysAccessXML", "MSysModules", "MSysModules2") +FIREBIRD_SYSTEM_DBS = ("RDB$BACKUP_HISTORY", "RDB$CHARACTER_SETS", "RDB$CHECK_CONSTRAINTS", "RDB$COLLATIONS", "RDB$DATABASE",\ "RDB$DEPENDENCIES", "RDB$EXCEPTIONS", "RDB$FIELDS", "RDB$FIELD_DIMENSIONS", " RDB$FILES", "RDB$FILTERS",\ "RDB$FORMATS", "RDB$FUNCTIONS", "RDB$FUNCTION_ARGUMENTS", "RDB$GENERATORS", "RDB$INDEX_SEGMENTS", "RDB$INDICES",\ "RDB$LOG_FILES", "RDB$PAGES", "RDB$PROCEDURES", "RDB$PROCEDURE_PARAMETERS", "RDB$REF_CONSTRAINTS", "RDB$RELATIONS",\ "RDB$RELATION_CONSTRAINTS", "RDB$RELATION_FIELDS", "RDB$ROLES", "RDB$SECURITY_CLASSES", "RDB$TRANSACTIONS", "RDB$TRIGGERS",\ - "RDB$TRIGGER_MESSAGES", "RDB$TYPES", "RDB$USER_PRIVILEGES", "RDB$VIEW_RELATIONS" ) -MAXDB_SYSTEM_DBS = ( "SYSINFO", "DOMAIN" ) -SYBASE_SYSTEM_DBS = ( "master", "model", "sybsystemdb", "sybsystemprocs" ) -DB2_SYSTEM_DBS = ( "NULLID", "SQLJ", "SYSCAT", "SYSFUN", "SYSIBM", "SYSIBMADM", "SYSIBMINTERNAL", "SYSIBMTS",\ - "SYSPROC", "SYSPUBLIC", "SYSSTAT", "SYSTOOLS" ) + "RDB$TRIGGER_MESSAGES", "RDB$TYPES", "RDB$USER_PRIVILEGES", "RDB$VIEW_RELATIONS") +MAXDB_SYSTEM_DBS = ("SYSINFO", "DOMAIN") +SYBASE_SYSTEM_DBS = ("master", "model", "sybsystemdb", "sybsystemprocs") +DB2_SYSTEM_DBS = ("NULLID", "SQLJ", "SYSCAT", "SYSFUN", "SYSIBM", "SYSIBMADM", "SYSIBMINTERNAL", "SYSIBMTS",\ + "SYSPROC", "SYSPUBLIC", "SYSSTAT", "SYSTOOLS") -MSSQL_ALIASES = ( "microsoft sql server", "mssqlserver", "mssql", "ms" ) -MYSQL_ALIASES = ( "mysql", "my" ) -PGSQL_ALIASES = ( "postgresql", "postgres", "pgsql", "psql", "pg" ) -ORACLE_ALIASES = ( "oracle", "orcl", "ora", "or" ) -SQLITE_ALIASES = ( "sqlite", "sqlite3" ) -ACCESS_ALIASES = ( "msaccess", "access", "jet", "microsoft access" ) -FIREBIRD_ALIASES = ( "firebird", "mozilla firebird", "interbase", "ibase", "fb" ) -MAXDB_ALIASES = ( "maxdb", "sap maxdb", "sap db" ) -SYBASE_ALIASES = ( "sybase", "sybase sql server" ) -DB2_ALIASES = ( "db2", "ibm db2", "ibmdb2" ) +MSSQL_ALIASES = ("microsoft sql server", "mssqlserver", "mssql", "ms") +MYSQL_ALIASES = ("mysql", "my") +PGSQL_ALIASES = ("postgresql", "postgres", "pgsql", "psql", "pg") +ORACLE_ALIASES = ("oracle", "orcl", "ora", "or") +SQLITE_ALIASES = ("sqlite", "sqlite3") +ACCESS_ALIASES = ("msaccess", "access", "jet", "microsoft access") +FIREBIRD_ALIASES = ("firebird", "mozilla firebird", "interbase", "ibase", "fb") +MAXDB_ALIASES = ("maxdb", "sap maxdb", "sap db") +SYBASE_ALIASES = ("sybase", "sybase sql server") +DB2_ALIASES = ("db2", "ibm db2", "ibmdb2") DBMS_DIRECTORY_DICT = dict((getattr(DBMS, _), getattr(DBMS_DIRECTORY_NAME, _)) for _ in dir(DBMS) if not _.startswith("_")) SUPPORTED_DBMS = MSSQL_ALIASES + MYSQL_ALIASES + PGSQL_ALIASES + ORACLE_ALIASES + SQLITE_ALIASES + ACCESS_ALIASES + FIREBIRD_ALIASES + MAXDB_ALIASES + SYBASE_ALIASES + DB2_ALIASES -SUPPORTED_OS = ( "linux", "windows" ) +SUPPORTED_OS = ("linux", "windows") -USER_AGENT_ALIASES = ( "ua", "useragent", "user-agent" ) -REFERER_ALIASES = ( "ref", "referer", "referrer" ) -HOST_ALIASES = ( "host", ) +USER_AGENT_ALIASES = ("ua", "useragent", "user-agent") +REFERER_ALIASES = ("ref", "referer", "referrer") +HOST_ALIASES = ("host",) # Items displayed in basic help (-h) output BASIC_HELP_ITEMS = ( diff --git a/lib/core/testing.py b/lib/core/testing.py index 68c45693e..cf8971c50 100644 --- a/lib/core/testing.py +++ b/lib/core/testing.py @@ -73,7 +73,7 @@ def smokeTest(): retVal = False count += 1 - status = '%d/%d (%d%s) ' % (count, length, round(100.0*count/length), '%') + status = '%d/%d (%d%%) ' % (count, length, round(100.0 * count / length)) dataToStdout("\r[%s] [INFO] complete: %s" % (time.strftime("%X"), status)) clearConsoleLine() diff --git a/lib/parse/banner.py b/lib/parse/banner.py index 693bd7f34..a7805b6f9 100644 --- a/lib/parse/banner.py +++ b/lib/parse/banner.py @@ -39,7 +39,7 @@ class MSSQLBannerHandler(ContentHandler): def _feedInfo(self, key, value): value = sanitizeStr(value) - if value in ( None, "None" ): + if value in (None, "None"): return self._info[key] = value diff --git a/lib/parse/handler.py b/lib/parse/handler.py index 8d8fc6ece..28f792c34 100644 --- a/lib/parse/handler.py +++ b/lib/parse/handler.py @@ -29,7 +29,7 @@ class FingerprintHandler(ContentHandler): def _feedInfo(self, key, value): value = sanitizeStr(value) - if value in ( None, "None" ): + if value in (None, "None"): return if key == "dbmsVersion": diff --git a/lib/takeover/abstraction.py b/lib/takeover/abstraction.py index 0ce567909..6473a1bc6 100644 --- a/lib/takeover/abstraction.py +++ b/lib/takeover/abstraction.py @@ -41,7 +41,7 @@ class Abstraction(Web, UDF, Xp_cmdshell): if self.webBackdoorUrl and not isTechniqueAvailable(PAYLOAD.TECHNIQUE.STACKED): self.webBackdoorRunCmd(cmd) - elif Backend.getIdentifiedDbms() in ( DBMS.MYSQL, DBMS.PGSQL ): + elif Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL): self.udfExecCmd(cmd, silent=silent) elif Backend.isDbms(DBMS.MSSQL): @@ -57,7 +57,7 @@ class Abstraction(Web, UDF, Xp_cmdshell): if self.webBackdoorUrl and not isTechniqueAvailable(PAYLOAD.TECHNIQUE.STACKED): retVal = self.webBackdoorRunCmd(cmd) - elif Backend.getIdentifiedDbms() in ( DBMS.MYSQL, DBMS.PGSQL ): + elif Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL): retVal = self.udfEvalCmd(cmd, first, last) elif Backend.isDbms(DBMS.MSSQL): @@ -97,7 +97,7 @@ class Abstraction(Web, UDF, Xp_cmdshell): logger.info(infoMsg) else: - if Backend.getIdentifiedDbms() in ( DBMS.MYSQL, DBMS.PGSQL ): + if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL): infoMsg = "going to use injected sys_eval and sys_exec " infoMsg += "user-defined functions for operating system " infoMsg += "command execution" @@ -136,7 +136,7 @@ class Abstraction(Web, UDF, Xp_cmdshell): if not command: continue - if command.lower() in ( "x", "q", "exit", "quit" ): + if command.lower() in ("x", "q", "exit", "quit"): break self.runCmd(command) @@ -186,7 +186,7 @@ class Abstraction(Web, UDF, Xp_cmdshell): warnMsg = "functionality requested probably does not work because " warnMsg += "the curent session user is not a database administrator" - if not conf.dbmsCred and Backend.getIdentifiedDbms() in ( DBMS.MSSQL, DBMS.PGSQL ): + if not conf.dbmsCred and Backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.PGSQL): warnMsg += ". You can try to use option '--dbms-cred' " warnMsg += "to execute statements as a DBA user if you " warnMsg += "were able to extract and crack a DBA " @@ -194,7 +194,7 @@ class Abstraction(Web, UDF, Xp_cmdshell): logger.warn(warnMsg) - if Backend.getIdentifiedDbms() in ( DBMS.MYSQL, DBMS.PGSQL ): + if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL): self.udfInjectSys() elif Backend.isDbms(DBMS.MSSQL): if mandatory: diff --git a/lib/takeover/metasploit.py b/lib/takeover/metasploit.py index bb1ba64d2..5037ea075 100644 --- a/lib/takeover/metasploit.py +++ b/lib/takeover/metasploit.py @@ -70,52 +70,52 @@ class Metasploit: self._msfPayloadsList = { "windows": { - 1: ( "Meterpreter (default)", "windows/meterpreter" ), - 2: ( "Shell", "windows/shell" ), - 3: ( "VNC", "windows/vncinject" ), + 1: ("Meterpreter (default)", "windows/meterpreter"), + 2: ("Shell", "windows/shell"), + 3: ("VNC", "windows/vncinject"), }, "linux": { - 1: ( "Shell (default)", "linux/x86/shell" ), - 2: ( "Meterpreter (beta)", "linux/x86/meterpreter" ), + 1: ("Shell (default)", "linux/x86/shell"), + 2: ("Meterpreter (beta)", "linux/x86/meterpreter"), } } self._msfConnectionsList = { "windows": { - 1: ( "Reverse TCP: Connect back from the database host to this machine (default)", "reverse_tcp" ), - 2: ( "Reverse TCP: Try to connect back from the database host to this machine, on all ports between the specified and 65535", "reverse_tcp_allports" ), - 3: ( "Reverse HTTP: Connect back from the database host to this machine tunnelling traffic over HTTP", "reverse_http" ), - 4: ( "Reverse HTTPS: Connect back from the database host to this machine tunnelling traffic over HTTPS", "reverse_https" ), - 5: ( "Bind TCP: Listen on the database host for a connection", "bind_tcp" ) + 1: ("Reverse TCP: Connect back from the database host to this machine (default)", "reverse_tcp"), + 2: ("Reverse TCP: Try to connect back from the database host to this machine, on all ports between the specified and 65535", "reverse_tcp_allports"), + 3: ("Reverse HTTP: Connect back from the database host to this machine tunnelling traffic over HTTP", "reverse_http"), + 4: ("Reverse HTTPS: Connect back from the database host to this machine tunnelling traffic over HTTPS", "reverse_https"), + 5: ("Bind TCP: Listen on the database host for a connection", "bind_tcp") }, "linux": { - 1: ( "Reverse TCP: Connect back from the database host to this machine (default)", "reverse_tcp" ), - 2: ( "Bind TCP: Listen on the database host for a connection", "bind_tcp" ), + 1: ("Reverse TCP: Connect back from the database host to this machine (default)", "reverse_tcp"), + 2: ("Bind TCP: Listen on the database host for a connection", "bind_tcp"), } } self._msfEncodersList = { "windows": { - 1: ( "No Encoder", "generic/none" ), - 2: ( "Alpha2 Alphanumeric Mixedcase Encoder", "x86/alpha_mixed" ), - 3: ( "Alpha2 Alphanumeric Uppercase Encoder", "x86/alpha_upper" ), - 4: ( "Avoid UTF8/tolower", "x86/avoid_utf8_tolower" ), - 5: ( "Call+4 Dword XOR Encoder", "x86/call4_dword_xor" ), - 6: ( "Single-byte XOR Countdown Encoder", "x86/countdown" ), - 7: ( "Variable-length Fnstenv/mov Dword XOR Encoder", "x86/fnstenv_mov" ), - 8: ( "Polymorphic Jump/Call XOR Additive Feedback Encoder", "x86/jmp_call_additive" ), - 9: ( "Non-Alpha Encoder", "x86/nonalpha" ), - 10: ( "Non-Upper Encoder", "x86/nonupper" ), - 11: ( "Polymorphic XOR Additive Feedback Encoder (default)", "x86/shikata_ga_nai" ), - 12: ( "Alpha2 Alphanumeric Unicode Mixedcase Encoder", "x86/unicode_mixed" ), - 13: ( "Alpha2 Alphanumeric Unicode Uppercase Encoder", "x86/unicode_upper" ), + 1: ("No Encoder", "generic/none"), + 2: ("Alpha2 Alphanumeric Mixedcase Encoder", "x86/alpha_mixed"), + 3: ("Alpha2 Alphanumeric Uppercase Encoder", "x86/alpha_upper"), + 4: ("Avoid UTF8/tolower", "x86/avoid_utf8_tolower"), + 5: ("Call+4 Dword XOR Encoder", "x86/call4_dword_xor"), + 6: ("Single-byte XOR Countdown Encoder", "x86/countdown"), + 7: ("Variable-length Fnstenv/mov Dword XOR Encoder", "x86/fnstenv_mov"), + 8: ("Polymorphic Jump/Call XOR Additive Feedback Encoder", "x86/jmp_call_additive"), + 9: ("Non-Alpha Encoder", "x86/nonalpha"), + 10: ("Non-Upper Encoder", "x86/nonupper"), + 11: ("Polymorphic XOR Additive Feedback Encoder (default)", "x86/shikata_ga_nai"), + 12: ("Alpha2 Alphanumeric Unicode Mixedcase Encoder", "x86/unicode_mixed"), + 13: ("Alpha2 Alphanumeric Unicode Uppercase Encoder", "x86/unicode_upper"), } } self._msfSMBPortsList = { "windows": { - 1: ( "139/TCP", "139" ), - 2: ( "445/TCP (default)", "445" ), + 1: ("139/TCP", "139"), + 2: ("445/TCP (default)", "445"), } } @@ -608,7 +608,7 @@ class Metasploit: self._runMsfCliSmbrelay() - if Backend.getIdentifiedDbms() in ( DBMS.MYSQL, DBMS.PGSQL ): + if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL): self.uncPath = "\\\\\\\\%s\\\\%s" % (self.lhostStr, self._randFile) else: self.uncPath = "\\\\%s\\%s" % (self.lhostStr, self._randFile) diff --git a/lib/takeover/udf.py b/lib/takeover/udf.py index 03ac3f278..234274c1d 100644 --- a/lib/takeover/udf.py +++ b/lib/takeover/udf.py @@ -112,7 +112,7 @@ class UDF: return output def udfCheckNeeded(self): - if ( not conf.rFile or ( conf.rFile and not Backend.isDbms(DBMS.PGSQL) ) ) and "sys_fileread" in self.sysUdfs: + if (not conf.rFile or (conf.rFile and not Backend.isDbms(DBMS.PGSQL))) and "sys_fileread" in self.sysUdfs: self.sysUdfs.pop("sys_fileread") if not conf.osPwn: @@ -164,7 +164,7 @@ class UDF: self.udfInjectCore(self.sysUdfs) def udfInjectCustom(self): - if Backend.getIdentifiedDbms() not in ( DBMS.MYSQL, DBMS.PGSQL ): + if Backend.getIdentifiedDbms() not in (DBMS.MYSQL, DBMS.PGSQL): errMsg = "UDF injection feature is not yet implemented on %s" % Backend.getIdentifiedDbms() raise SqlmapUnsupportedFeatureException(errMsg) @@ -300,10 +300,10 @@ class UDF: msg += "functions now? [Y/n/q] " choice = readInput(msg, default="Y") - if choice[0] in ( "n", "N" ): + if choice[0] in ("n", "N"): self.cleanup(udfDict=self.udfs) return - elif choice[0] in ( "q", "Q" ): + elif choice[0] in ("q", "Q"): self.cleanup(udfDict=self.udfs) raise SqlmapUserQuitException @@ -320,7 +320,7 @@ class UDF: while True: choice = readInput(msg) - if choice and choice[0] in ( "q", "Q" ): + if choice and choice[0] in ("q", "Q"): break elif isinstance(choice, basestring) and choice.isdigit() and int(choice) > 0 and int(choice) <= len(udfList): choice = int(choice) diff --git a/lib/techniques/blind/inference.py b/lib/techniques/blind/inference.py index 8f6305e68..e39cbeac1 100644 --- a/lib/techniques/blind/inference.py +++ b/lib/techniques/blind/inference.py @@ -86,20 +86,20 @@ def bisection(payload, expression, length=None, charsetType=None, firstChar=None firstChar = len(partialValue) elif "LENGTH(" in expression.upper() or "LEN(" in expression.upper(): firstChar = 0 - elif dump and conf.firstChar is not None and ( isinstance(conf.firstChar, int) or ( isinstance(conf.firstChar, basestring) and conf.firstChar.isdigit() ) ): + elif dump and conf.firstChar is not None and (isinstance(conf.firstChar, int) or (isinstance(conf.firstChar, basestring) and conf.firstChar.isdigit())): firstChar = int(conf.firstChar) - 1 elif firstChar is None: firstChar = 0 - elif ( isinstance(firstChar, basestring) and firstChar.isdigit() ) or isinstance(firstChar, int): + elif (isinstance(firstChar, basestring) and firstChar.isdigit()) or isinstance(firstChar, int): firstChar = int(firstChar) - 1 if "LENGTH(" in expression.upper() or "LEN(" in expression.upper(): lastChar = 0 - elif dump and conf.lastChar is not None and ( isinstance(conf.lastChar, int) or ( isinstance(conf.lastChar, basestring) and conf.lastChar.isdigit() ) ): + elif dump and conf.lastChar is not None and (isinstance(conf.lastChar, int) or (isinstance(conf.lastChar, basestring) and conf.lastChar.isdigit())): lastChar = int(conf.lastChar) - elif lastChar in ( None, "0" ): + elif lastChar in (None, "0"): lastChar = 0 - elif ( isinstance(lastChar, basestring) and lastChar.isdigit() ) or isinstance(lastChar, int): + elif (isinstance(lastChar, basestring) and lastChar.isdigit()) or isinstance(lastChar, int): lastChar = int(lastChar) if Backend.getDbms(): @@ -332,7 +332,7 @@ def bisection(payload, expression, length=None, charsetType=None, firstChar=None return None def etaProgressUpdate(charTime, index): - if len(progressTime) <= ( (length * 3) / 100 ): + if len(progressTime) <= ((length * 3) / 100): eta = 0 else: midTime = sum(progressTime) / len(progressTime) @@ -412,7 +412,7 @@ def bisection(payload, expression, length=None, charsetType=None, firstChar=None if conf.verbose in (1, 2) and not showEta: _ = count - firstChar output += '_' * (min(length, conf.progressWidth) - len(output)) - status = ' %d/%d (%d%s)' % (_, length, round(100.0 * _ / length), '%') + status = ' %d/%d (%d%%)' % (_, length, round(100.0 * _ / length)) output += status if _ != length else " " * len(status) dataToStdout("\r[%s] [INFO] retrieved: %s" % (time.strftime("%X"), filterControlChars(output))) @@ -507,7 +507,7 @@ def bisection(payload, expression, length=None, charsetType=None, firstChar=None else: val = getChar(index, asciiTbl) - if val is None or ( lastChar > 0 and index > lastChar ): + if val is None or (lastChar > 0 and index > lastChar): finalValue = partialValue break @@ -545,7 +545,7 @@ def bisection(payload, expression, length=None, charsetType=None, firstChar=None if conf.verbose in (1, 2) or showEta: dataToStdout("\n") - if ( conf.verbose in ( 1, 2 ) and showEta ) or conf.verbose >= 3: + if (conf.verbose in (1, 2) and showEta) or conf.verbose >= 3: infoMsg = "retrieved: %s" % filterControlChars(finalValue) logger.info(infoMsg) diff --git a/lib/techniques/brute/use.py b/lib/techniques/brute/use.py index 47a086738..6c450e78d 100644 --- a/lib/techniques/brute/use.py +++ b/lib/techniques/brute/use.py @@ -101,7 +101,7 @@ def tableExists(tableFile, regex=None): dataToStdout(infoMsg, True) if conf.verbose in (1, 2): - status = '%d/%d items (%d%s)' % (threadData.shared.count, threadData.shared.limit, round(100.0*threadData.shared.count/threadData.shared.limit), '%') + status = '%d/%d items (%d%%)' % (threadData.shared.count, threadData.shared.limit, round(100.0 * threadData.shared.count / threadData.shared.limit)) dataToStdout("\r[%s] [INFO] tried %s" % (time.strftime("%X"), status), True) kb.locks.io.release() @@ -192,7 +192,7 @@ def columnExists(columnFile, regex=None): dataToStdout(infoMsg, True) if conf.verbose in (1, 2): - status = '%d/%d items (%d%s)' % (threadData.shared.count, threadData.shared.limit, round(100.0*threadData.shared.count/threadData.shared.limit), '%') + status = '%d/%d items (%d%%)' % (threadData.shared.count, threadData.shared.limit, round(100.0 * threadData.shared.count / threadData.shared.limit)) dataToStdout("\r[%s] [INFO] tried %s" % (time.strftime("%X"), status), True) kb.locks.io.release() diff --git a/lib/techniques/error/use.py b/lib/techniques/error/use.py index c1d22794d..9bec5ec65 100644 --- a/lib/techniques/error/use.py +++ b/lib/techniques/error/use.py @@ -99,7 +99,7 @@ def _oneShotErrorUse(expression, field=None): # Parse the returned page to get the exact error-based # SQL injection output - output = reduce(lambda x, y: x if x is not None else y, ( \ + output = reduce(lambda x, y: x if x is not None else y, (\ extractRegexResult(check, page, re.DOTALL | re.IGNORECASE), \ extractRegexResult(check, listToStrValue(headers.headers \ if headers else None), re.DOTALL | re.IGNORECASE), \ diff --git a/lib/techniques/union/use.py b/lib/techniques/union/use.py index c50164b65..2325b9daf 100644 --- a/lib/techniques/union/use.py +++ b/lib/techniques/union/use.py @@ -76,7 +76,7 @@ def _oneShotUnionUse(expression, unpack=True, limited=False): # Parse the returned page to get the exact union-based # SQL injection output def _(regex): - return reduce(lambda x, y: x if x is not None else y, ( \ + return reduce(lambda x, y: x if x is not None else y, (\ extractRegexResult(regex, removeReflectiveValues(page, payload), re.DOTALL | re.IGNORECASE), \ extractRegexResult(regex, removeReflectiveValues(listToStrValue(headers.headers \ if headers else None), payload, True), re.DOTALL | re.IGNORECASE)), \ diff --git a/lib/utils/crawler.py b/lib/utils/crawler.py index f19b9fc88..3ddfaa982 100644 --- a/lib/utils/crawler.py +++ b/lib/utils/crawler.py @@ -25,116 +25,110 @@ from lib.request.connect import Connect as Request from thirdparty.beautifulsoup.beautifulsoup import BeautifulSoup from thirdparty.oset.pyoset import oset -class Crawler(object): - """ - This class defines methods used to perform crawling (command - line option '--crawl' - """ +def crawl(target): + try: + threadData = getCurrentThreadData() + threadData.shared.value = oset() - def getTargetUrls(self): - try: + def crawlThread(): threadData = getCurrentThreadData() - threadData.shared.value = oset() - def crawlThread(): - threadData = getCurrentThreadData() - - while kb.threadContinue: - with kb.locks.limit: - if threadData.shared.unprocessed: - current = threadData.shared.unprocessed.pop() - else: - break - - content = None - try: - if current: - content = Request.getPage(url=current, crawling=True, raise404=False)[0] - except SqlmapConnectionException, e: - errMsg = "connection exception detected (%s). skipping " % e - errMsg += "url '%s'" % current - logger.critical(errMsg) - except httplib.InvalidURL, e: - errMsg = "invalid url detected (%s). skipping " % e - errMsg += "url '%s'" % current - logger.critical(errMsg) - - if not kb.threadContinue: + while kb.threadContinue: + with kb.locks.limit: + if threadData.shared.unprocessed: + current = threadData.shared.unprocessed.pop() + else: break - if isinstance(content, unicode): - try: - match = re.search(r"(?si)]*>(.+)", content) - if match: - content = "%s" % match.group(1) + content = None + try: + if current: + content = Request.getPage(url=current, crawling=True, raise404=False)[0] + except SqlmapConnectionException, e: + errMsg = "connection exception detected (%s). skipping " % e + errMsg += "url '%s'" % current + logger.critical(errMsg) + except httplib.InvalidURL, e: + errMsg = "invalid url detected (%s). skipping " % e + errMsg += "url '%s'" % current + logger.critical(errMsg) - soup = BeautifulSoup(content) - tags = soup('a') - - if not tags: - tags = re.finditer(r'(?si)]+href="(?P[^>"]+)"', content) - - for tag in tags: - href = tag.get("href") if hasattr(tag, "get") else tag.group("href") - - if href: - url = urlparse.urljoin(conf.url, href) - - # flag to know if we are dealing with the same target host - _ = reduce(lambda x, y: x == y, map(lambda x: urlparse.urlparse(x).netloc.split(':')[0], (url, conf.url))) - - if conf.scope: - if not re.search(conf.scope, url, re.I): - continue - elif not _: - continue - - if url.split('.')[-1].lower() not in CRAWL_EXCLUDE_EXTENSIONS: - with kb.locks.value: - threadData.shared.deeper.add(url) - if re.search(r"(.*?)\?(.+)", url): - threadData.shared.value.add(url) - except UnicodeEncodeError: # for non-HTML files - pass - finally: - if conf.forms: - findPageForms(content, current, False, True) - - if conf.verbose in (1, 2): - threadData.shared.count += 1 - status = '%d/%d links visited (%d%s)' % (threadData.shared.count, threadData.shared.length, round(100.0*threadData.shared.count/threadData.shared.length), '%') - dataToStdout("\r[%s] [INFO] %s" % (time.strftime("%X"), status), True) - - threadData.shared.deeper = set() - threadData.shared.unprocessed = set([conf.url]) - - logger.info("starting crawler") - - for i in xrange(conf.crawlDepth): - if i > 0 and conf.threads == 1: - singleTimeWarnMessage("running in a single-thread mode. This could take a while.") - threadData.shared.count = 0 - threadData.shared.length = len(threadData.shared.unprocessed) - numThreads = min(conf.threads, len(threadData.shared.unprocessed)) - logger.info("searching for links with depth %d" % (i + 1)) - runThreads(numThreads, crawlThread) - clearConsoleLine(True) - if threadData.shared.deeper: - threadData.shared.unprocessed = set(threadData.shared.deeper) - else: + if not kb.threadContinue: break - except KeyboardInterrupt: - warnMsg = "user aborted during crawling. sqlmap " - warnMsg += "will use partial list" - logger.warn(warnMsg) + if isinstance(content, unicode): + try: + match = re.search(r"(?si)]*>(.+)", content) + if match: + content = "%s" % match.group(1) - finally: + soup = BeautifulSoup(content) + tags = soup('a') + + if not tags: + tags = re.finditer(r'(?si)]+href="(?P[^>"]+)"', content) + + for tag in tags: + href = tag.get("href") if hasattr(tag, "get") else tag.group("href") + + if href: + url = urlparse.urljoin(target, href) + + # flag to know if we are dealing with the same target host + _ = reduce(lambda x, y: x == y, map(lambda x: urlparse.urlparse(x).netloc.split(':')[0], (url, target))) + + if conf.scope: + if not re.search(conf.scope, url, re.I): + continue + elif not _: + continue + + if url.split('.')[-1].lower() not in CRAWL_EXCLUDE_EXTENSIONS: + with kb.locks.value: + threadData.shared.deeper.add(url) + if re.search(r"(.*?)\?(.+)", url): + threadData.shared.value.add(url) + except UnicodeEncodeError: # for non-HTML files + pass + finally: + if conf.forms: + findPageForms(content, current, False, True) + + if conf.verbose in (1, 2): + threadData.shared.count += 1 + status = '%d/%d links visited (%d%%)' % (threadData.shared.count, threadData.shared.length, round(100.0 * threadData.shared.count / threadData.shared.length)) + dataToStdout("\r[%s] [INFO] %s" % (time.strftime("%X"), status), True) + + threadData.shared.deeper = set() + threadData.shared.unprocessed = set([target]) + + logger.info("starting crawler") + + for i in xrange(conf.crawlDepth): + if i > 0 and conf.threads == 1: + singleTimeWarnMessage("running in a single-thread mode. This could take a while.") + threadData.shared.count = 0 + threadData.shared.length = len(threadData.shared.unprocessed) + numThreads = min(conf.threads, len(threadData.shared.unprocessed)) + logger.info("searching for links with depth %d" % (i + 1)) + runThreads(numThreads, crawlThread) clearConsoleLine(True) - - if not threadData.shared.value: - warnMsg = "no usable links found (with GET parameters)" - logger.warn(warnMsg) + if threadData.shared.deeper: + threadData.shared.unprocessed = set(threadData.shared.deeper) else: - for url in threadData.shared.value: - kb.targets.add(( url, None, None, None )) + break + + except KeyboardInterrupt: + warnMsg = "user aborted during crawling. sqlmap " + warnMsg += "will use partial list" + logger.warn(warnMsg) + + finally: + clearConsoleLine(True) + + if not threadData.shared.value: + warnMsg = "no usable links found (with GET parameters)" + logger.warn(warnMsg) + else: + for url in threadData.shared.value: + kb.targets.add((url, None, None, None)) diff --git a/plugins/dbms/mssqlserver/enumeration.py b/plugins/dbms/mssqlserver/enumeration.py index bc88c2e6c..e1d990b5e 100644 --- a/plugins/dbms/mssqlserver/enumeration.py +++ b/plugins/dbms/mssqlserver/enumeration.py @@ -59,7 +59,7 @@ class Enumeration(GenericEnumeration): kb.data.cachedUsersPrivileges[user] = None - return ( kb.data.cachedUsersPrivileges, areAdmins ) + return (kb.data.cachedUsersPrivileges, areAdmins) def getTables(self): if len(kb.data.cachedTables) > 0: diff --git a/plugins/dbms/mssqlserver/filesystem.py b/plugins/dbms/mssqlserver/filesystem.py index 20cb3cdc1..a35f2aa79 100644 --- a/plugins/dbms/mssqlserver/filesystem.py +++ b/plugins/dbms/mssqlserver/filesystem.py @@ -72,7 +72,7 @@ class Filesystem(GenericFilesystem): logger.debug("generating chunk file %s\%s from debug script %s" % (tmpPath, chunkName, randScr)) - commands = ( "cd %s" % tmpPath, "debug < %s" % randScr, "del /F /Q %s" % randScr ) + commands = ("cd %s" % tmpPath, "debug < %s" % randScr, "del /F /Q %s" % randScr) complComm = " & ".join(command for command in commands) self.execCmd(complComm) @@ -183,9 +183,9 @@ class Filesystem(GenericFilesystem): logger.debug("converting the file utilizing PowerShell EncodedCommand") - commands = ( "cd %s" % tmpPath, + commands = ("cd %s" % tmpPath, "powershell -EncodedCommand %s" % psString, - "del /F /Q %s" % randFilePath ) + "del /F /Q %s" % randFilePath) complComm = " & ".join(command for command in commands) self.execCmd(complComm) @@ -319,9 +319,9 @@ class Filesystem(GenericFilesystem): self.xpCmdshellWriteFile(vbs, tmpPath, randVbs) - commands = ( "cd %s" % tmpPath, "cscript //nologo %s" % randVbs, + commands = ("cd %s" % tmpPath, "cscript //nologo %s" % randVbs, "del /F /Q %s" % randVbs, - "del /F /Q %s" % randFile ) + "del /F /Q %s" % randFile) complComm = " & ".join(command for command in commands) self.execCmd(complComm) diff --git a/plugins/dbms/mssqlserver/fingerprint.py b/plugins/dbms/mssqlserver/fingerprint.py index 182704f47..15d298847 100644 --- a/plugins/dbms/mssqlserver/fingerprint.py +++ b/plugins/dbms/mssqlserver/fingerprint.py @@ -92,9 +92,9 @@ class Fingerprint(GenericFingerprint): infoMsg = "confirming %s" % DBMS.MSSQL logger.info(infoMsg) - for version, check in ( ("2000", "HOST_NAME()=HOST_NAME()"), \ + for version, check in (("2000", "HOST_NAME()=HOST_NAME()"), \ ("2005", "XACT_STATE()=XACT_STATE()"), \ - ("2008", "SYSDATETIME()=SYSDATETIME()") ): + ("2008", "SYSDATETIME()=SYSDATETIME()")): result = inject.checkBooleanExpression(check) if result: diff --git a/plugins/dbms/postgresql/fingerprint.py b/plugins/dbms/postgresql/fingerprint.py index a32ebdca2..3fba2c3c0 100644 --- a/plugins/dbms/postgresql/fingerprint.py +++ b/plugins/dbms/postgresql/fingerprint.py @@ -155,7 +155,7 @@ class Fingerprint(GenericFingerprint): # Windows executables should always have ' Visual C++' or ' mingw' # patterns within the banner - osWindows = ( " Visual C++", "mingw" ) + osWindows = (" Visual C++", "mingw") for osPattern in osWindows: query = "(SELECT LENGTH(%s) FROM %s WHERE %s " % (self.tblField, self.fileTblName, self.tblField) diff --git a/plugins/dbms/sybase/enumeration.py b/plugins/dbms/sybase/enumeration.py index f7fb794b6..4620aeff4 100644 --- a/plugins/dbms/sybase/enumeration.py +++ b/plugins/dbms/sybase/enumeration.py @@ -77,7 +77,7 @@ class Enumeration(GenericEnumeration): kb.data.cachedUsersPrivileges[user] = None - return ( kb.data.cachedUsersPrivileges, areAdmins ) + return (kb.data.cachedUsersPrivileges, areAdmins) def getDbs(self): if len(kb.data.cachedDbs) > 0: diff --git a/plugins/generic/takeover.py b/plugins/generic/takeover.py index 24bc0e223..c5fb03598 100644 --- a/plugins/generic/takeover.py +++ b/plugins/generic/takeover.py @@ -94,7 +94,7 @@ class Takeover(Abstraction, Metasploit, ICMPsh, Registry, Miscellaneous): msg = "how do you want to establish the tunnel?" msg += "\n[1] TCP: Metasploit Framework (default)" msg += "\n[2] ICMP: icmpsh - ICMP tunneling" - valids = ( 1, 2 ) + valids = (1, 2) while True: tunnel = readInput(msg, default=1) @@ -150,7 +150,7 @@ class Takeover(Abstraction, Metasploit, ICMPsh, Registry, Miscellaneous): errMsg += "is unlikely to receive commands sent from you" logger.error(errMsg) - if Backend.getIdentifiedDbms() in ( DBMS.MYSQL, DBMS.PGSQL ): + if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL): self.sysUdfs.pop("sys_bineval") if isTechniqueAvailable(PAYLOAD.TECHNIQUE.STACKED) or conf.direct: @@ -160,7 +160,7 @@ class Takeover(Abstraction, Metasploit, ICMPsh, Registry, Miscellaneous): self.initEnv(web=web) if tunnel == 1: - if Backend.getIdentifiedDbms() in ( DBMS.MYSQL, DBMS.PGSQL ): + if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL): msg = "how do you want to execute the Metasploit shellcode " msg += "on the back-end database underlying operating system?" msg += "\n[1] Via UDF 'sys_bineval' (in-memory way, anti-forensics, default)" @@ -169,11 +169,11 @@ class Takeover(Abstraction, Metasploit, ICMPsh, Registry, Miscellaneous): while True: choice = readInput(msg, default=1) - if isinstance(choice, basestring) and choice.isdigit() and int(choice) in ( 1, 2 ): + if isinstance(choice, basestring) and choice.isdigit() and int(choice) in (1, 2): choice = int(choice) break - elif isinstance(choice, int) and choice in ( 1, 2 ): + elif isinstance(choice, int) and choice in (1, 2): break else: @@ -251,7 +251,7 @@ class Takeover(Abstraction, Metasploit, ICMPsh, Registry, Miscellaneous): raise SqlmapUnsupportedDBMSException(errMsg) if not isTechniqueAvailable(PAYLOAD.TECHNIQUE.STACKED) and not conf.direct: - if Backend.getIdentifiedDbms() in ( DBMS.PGSQL, DBMS.MSSQL ): + if Backend.getIdentifiedDbms() in (DBMS.PGSQL, DBMS.MSSQL): errMsg = "on this back-end DBMS it is only possible to " errMsg += "perform the SMB relay attack if stacked " errMsg += "queries are supported" @@ -438,7 +438,7 @@ class Takeover(Abstraction, Metasploit, ICMPsh, Registry, Miscellaneous): message += "registry path '%s\%s? [y/N] " % (regKey, regVal) output = readInput(message, default="N") - if output and output[0] not in ( "Y", "y" ): + if output and output[0] not in ("Y", "y"): return infoMsg = "deleting Windows registry path '%s\%s'. " % (regKey, regVal)