Path: blob/master/plugins/dbms/mysql/fingerprint.py
3554 views
#!/usr/bin/env python12"""3Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org)4See the file 'LICENSE' for copying permission5"""67import re89from lib.core.common import Backend10from lib.core.common import Format11from lib.core.common import hashDBRetrieve12from lib.core.common import hashDBWrite13from lib.core.compat import xrange14from lib.core.convert import getUnicode15from lib.core.data import conf16from lib.core.data import kb17from lib.core.data import logger18from lib.core.enums import DBMS19from lib.core.enums import FORK20from lib.core.enums import HASHDB_KEYS21from lib.core.enums import OS22from lib.core.session import setDbms23from lib.core.settings import MYSQL_ALIASES24from lib.request import inject25from plugins.generic.fingerprint import Fingerprint as GenericFingerprint2627class Fingerprint(GenericFingerprint):28def __init__(self):29GenericFingerprint.__init__(self, DBMS.MYSQL)3031def _commentCheck(self):32infoMsg = "executing %s comment injection fingerprint" % DBMS.MYSQL33logger.info(infoMsg)3435result = inject.checkBooleanExpression("[RANDNUM]=[RANDNUM]/* NoValue */")3637if not result:38warnMsg = "unable to perform %s comment injection" % DBMS.MYSQL39logger.warning(warnMsg)4041return None4243# Reference: https://downloads.mysql.com/archives/community/44# Reference: https://dev.mysql.com/doc/relnotes/mysql/<major>.<minor>/en/4546versions = (47(90300, 90302), # MySQL 9.348(90200, 90202), # MySQL 9.249(90100, 90102), # MySQL 9.150(90000, 90002), # MySQL 9.051(80400, 80406), # MySQL 8.452(80300, 80302), # MySQL 8.353(80200, 80202), # MySQL 8.254(80100, 80102), # MySQL 8.155(80000, 80043), # MySQL 8.056(60000, 60014), # MySQL 6.057(50700, 50745), # MySQL 5.758(50600, 50652), # MySQL 5.659(50500, 50563), # MySQL 5.560(50400, 50404), # MySQL 5.461(50100, 50174), # MySQL 5.162(50000, 50097), # MySQL 5.063(40100, 40131), # MySQL 4.164(40000, 40032), # MySQL 4.065(32300, 32359), # MySQL 3.2366(32200, 32235), # MySQL 3.2267)6869found = False70for candidate in versions:71result = inject.checkBooleanExpression("[RANDNUM]=[RANDNUM]/*!%d AND [RANDNUM1]=[RANDNUM2]*/" % candidate[0])7273if not result:74found = True75break7677if found:78for version in xrange(candidate[1], candidate[0] - 1, -1):79version = getUnicode(version)80result = inject.checkBooleanExpression("[RANDNUM]=[RANDNUM]/*!%s AND [RANDNUM1]=[RANDNUM2]*/" % version)8182if not result:83if version[0] == "3":84midVer = version[1:3]85else:86midVer = version[2]8788trueVer = "%s.%s.%s" % (version[0], midVer, version[3:])8990return trueVer9192return None9394def getFingerprint(self):95fork = hashDBRetrieve(HASHDB_KEYS.DBMS_FORK)9697if fork is None:98if inject.checkBooleanExpression("VERSION() LIKE '%MariaDB%'"):99fork = FORK.MARIADB100elif inject.checkBooleanExpression("VERSION() LIKE '%TiDB%'"):101fork = FORK.TIDB102elif inject.checkBooleanExpression("@@VERSION_COMMENT LIKE '%drizzle%'"):103fork = FORK.DRIZZLE104elif inject.checkBooleanExpression("@@VERSION_COMMENT LIKE '%Percona%'"):105fork = FORK.PERCONA106elif inject.checkBooleanExpression("@@VERSION_COMMENT LIKE '%Doris%'"):107fork = FORK.DORIS108elif inject.checkBooleanExpression("@@VERSION_COMMENT LIKE '%StarRocks%'"):109fork = FORK.STARROCKS110elif inject.checkBooleanExpression("AURORA_VERSION() LIKE '%'"): # Reference: https://aws.amazon.com/premiumsupport/knowledge-center/aurora-version-number/111fork = FORK.AURORA112else:113fork = ""114115hashDBWrite(HASHDB_KEYS.DBMS_FORK, fork)116117value = ""118wsOsFp = Format.getOs("web server", kb.headersFp)119120if wsOsFp and not conf.api:121value += "%s\n" % wsOsFp122123if kb.data.banner:124dbmsOsFp = Format.getOs("back-end DBMS", kb.bannerFp)125126if dbmsOsFp and not conf.api:127value += "%s\n" % dbmsOsFp128129value += "back-end DBMS: "130actVer = Format.getDbms()131132if not conf.extensiveFp:133value += actVer134if fork:135value += " (%s fork)" % fork136return value137138comVer = self._commentCheck()139blank = " " * 15140value += "active fingerprint: %s" % actVer141142if comVer:143comVer = Format.getDbms([comVer])144value += "\n%scomment injection fingerprint: %s" % (blank, comVer)145146if kb.bannerFp:147banVer = kb.bannerFp.get("dbmsVersion")148149if banVer:150if banVer and re.search(r"-log$", kb.data.banner or ""):151banVer += ", logging enabled"152153banVer = Format.getDbms([banVer])154value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer)155156htmlErrorFp = Format.getErrorParsedDBMSes()157158if htmlErrorFp:159value += "\n%shtml error message fingerprint: %s" % (blank, htmlErrorFp)160161if fork:162value += "\n%sfork fingerprint: %s" % (blank, fork)163164return value165166def checkDbms(self):167"""168References for fingerprint:169170* http://dev.mysql.com/doc/refman/5.0/en/news-5-0-x.html (up to 5.0.89)171* http://dev.mysql.com/doc/refman/5.1/en/news-5-1-x.html (up to 5.1.42)172* http://dev.mysql.com/doc/refman/5.4/en/news-5-4-x.html (up to 5.4.4)173* http://dev.mysql.com/doc/refman/5.5/en/news-5-5-x.html (up to 5.5.0)174* http://dev.mysql.com/doc/refman/6.0/en/news-6-0-x.html (manual has been withdrawn)175"""176177if not conf.extensiveFp and Backend.isDbmsWithin(MYSQL_ALIASES):178setDbms("%s %s" % (DBMS.MYSQL, Backend.getVersion()))179180if Backend.isVersionGreaterOrEqualThan("5") or inject.checkBooleanExpression("DATABASE() LIKE SCHEMA()"):181kb.data.has_information_schema = True182self.getBanner()183184return True185186infoMsg = "testing %s" % DBMS.MYSQL187logger.info(infoMsg)188189result = inject.checkBooleanExpression("IFNULL(QUARTER(NULL),NULL XOR NULL) IS NULL")190191if result:192infoMsg = "confirming %s" % DBMS.MYSQL193logger.info(infoMsg)194195result = inject.checkBooleanExpression("COALESCE(SESSION_USER(),USER()) IS NOT NULL")196197if not result:198# Note: MemSQL doesn't support SESSION_USER()199result = inject.checkBooleanExpression("GEOGRAPHY_AREA(NULL) IS NULL")200201if result:202hashDBWrite(HASHDB_KEYS.DBMS_FORK, FORK.MEMSQL)203204if not result:205warnMsg = "the back-end DBMS is not %s" % DBMS.MYSQL206logger.warning(warnMsg)207208return False209210# reading information_schema on some platforms is causing annoying timeout exits211# Reference: http://bugs.mysql.com/bug.php?id=15855212213kb.data.has_information_schema = True214215# Determine if it is MySQL >= 9.0.0216if inject.checkBooleanExpression("ISNULL(VECTOR_DIM(NULL))"):217Backend.setVersion(">= 9.0.0")218setDbms("%s 9" % DBMS.MYSQL)219self.getBanner()220221# Determine if it is MySQL >= 8.0.0222elif inject.checkBooleanExpression("ISNULL(JSON_STORAGE_FREE(NULL))"):223Backend.setVersion(">= 8.0.0")224setDbms("%s 8" % DBMS.MYSQL)225self.getBanner()226227# Determine if it is MySQL >= 5.0.0228elif inject.checkBooleanExpression("ISNULL(TIMESTAMPADD(MINUTE,[RANDNUM],NULL))"):229Backend.setVersion(">= 5.0.0")230setDbms("%s 5" % DBMS.MYSQL)231self.getBanner()232233if not conf.extensiveFp:234return True235236infoMsg = "actively fingerprinting %s" % DBMS.MYSQL237logger.info(infoMsg)238239# Check if it is MySQL >= 5.7240if inject.checkBooleanExpression("ISNULL(JSON_QUOTE(NULL))"):241Backend.setVersion(">= 5.7")242243# Check if it is MySQL >= 5.6244elif inject.checkBooleanExpression("ISNULL(VALIDATE_PASSWORD_STRENGTH(NULL))"):245Backend.setVersion(">= 5.6")246247# Check if it is MySQL >= 5.5248elif inject.checkBooleanExpression("TO_SECONDS(950501)>0"):249Backend.setVersion(">= 5.5")250251# Check if it is MySQL >= 5.1.2 and < 5.5.0252elif inject.checkBooleanExpression("@@table_open_cache=@@table_open_cache"):253if inject.checkBooleanExpression("[RANDNUM]=(SELECT [RANDNUM] FROM information_schema.GLOBAL_STATUS LIMIT 0, 1)"):254Backend.setVersionList([">= 5.1.12", "< 5.5.0"])255elif inject.checkBooleanExpression("[RANDNUM]=(SELECT [RANDNUM] FROM information_schema.PROCESSLIST LIMIT 0, 1)"):256Backend.setVersionList([">= 5.1.7", "< 5.1.12"])257elif inject.checkBooleanExpression("[RANDNUM]=(SELECT [RANDNUM] FROM information_schema.PARTITIONS LIMIT 0, 1)"):258Backend.setVersion("= 5.1.6")259elif inject.checkBooleanExpression("[RANDNUM]=(SELECT [RANDNUM] FROM information_schema.PLUGINS LIMIT 0, 1)"):260Backend.setVersionList([">= 5.1.5", "< 5.1.6"])261else:262Backend.setVersionList([">= 5.1.2", "< 5.1.5"])263264# Check if it is MySQL >= 5.0.0 and < 5.1.2265elif inject.checkBooleanExpression("@@hostname=@@hostname"):266Backend.setVersionList([">= 5.0.38", "< 5.1.2"])267elif inject.checkBooleanExpression("@@character_set_filesystem=@@character_set_filesystem"):268Backend.setVersionList([">= 5.0.19", "< 5.0.38"])269elif not inject.checkBooleanExpression("[RANDNUM]=(SELECT [RANDNUM] FROM DUAL WHERE [RANDNUM1]!=[RANDNUM2])"):270Backend.setVersionList([">= 5.0.11", "< 5.0.19"])271elif inject.checkBooleanExpression("@@div_precision_increment=@@div_precision_increment"):272Backend.setVersionList([">= 5.0.6", "< 5.0.11"])273elif inject.checkBooleanExpression("@@automatic_sp_privileges=@@automatic_sp_privileges"):274Backend.setVersionList([">= 5.0.3", "< 5.0.6"])275else:276Backend.setVersionList([">= 5.0.0", "< 5.0.3"])277278elif inject.checkBooleanExpression("DATABASE() LIKE SCHEMA()"):279Backend.setVersion(">= 5.0.2")280setDbms("%s 5" % DBMS.MYSQL)281self.getBanner()282283elif inject.checkBooleanExpression("STRCMP(LOWER(CURRENT_USER()), UPPER(CURRENT_USER()))=0"):284Backend.setVersion("< 5.0.0")285setDbms("%s 4" % DBMS.MYSQL)286self.getBanner()287288kb.data.has_information_schema = False289290if not conf.extensiveFp:291return True292293# Check which version of MySQL < 5.0.0 it is294if inject.checkBooleanExpression("3=(SELECT COERCIBILITY(USER()))"):295Backend.setVersionList([">= 4.1.11", "< 5.0.0"])296elif inject.checkBooleanExpression("2=(SELECT COERCIBILITY(USER()))"):297Backend.setVersionList([">= 4.1.1", "< 4.1.11"])298elif inject.checkBooleanExpression("CURRENT_USER()=CURRENT_USER()"):299Backend.setVersionList([">= 4.0.6", "< 4.1.1"])300301if inject.checkBooleanExpression("'utf8'=(SELECT CHARSET(CURRENT_USER()))"):302Backend.setVersion("= 4.1.0")303else:304Backend.setVersionList([">= 4.0.6", "< 4.1.0"])305else:306Backend.setVersionList([">= 4.0.0", "< 4.0.6"])307else:308Backend.setVersion("< 4.0.0")309setDbms("%s 3" % DBMS.MYSQL)310self.getBanner()311312kb.data.has_information_schema = False313314return True315else:316warnMsg = "the back-end DBMS is not %s" % DBMS.MYSQL317logger.warning(warnMsg)318319return False320321def checkDbmsOs(self, detailed=False):322if Backend.getOs():323return324325infoMsg = "fingerprinting the back-end DBMS operating system"326logger.info(infoMsg)327328result = inject.checkBooleanExpression("'W'=UPPER(MID(@@version_compile_os,1,1))")329330if result:331Backend.setOs(OS.WINDOWS)332elif not result:333Backend.setOs(OS.LINUX)334335if Backend.getOs():336infoMsg = "the back-end DBMS operating system is %s" % Backend.getOs()337logger.info(infoMsg)338else:339self.userChooseDbmsOs()340341self.cleanup(onlyFileTbl=True)342343344