Path: blob/master/plugins/dbms/mysql/fingerprint.py
2992 views
#!/usr/bin/env python12"""3Copyright (c) 2006-2025 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(90200, 90202), # MySQL 9.248(90100, 90102), # MySQL 9.149(90000, 90002), # MySQL 9.050(80400, 80405), # MySQL 8.451(80300, 80302), # MySQL 8.352(80200, 80202), # MySQL 8.253(80100, 80102), # MySQL 8.154(80000, 80041), # MySQL 8.055(60000, 60014), # MySQL 6.056(50700, 50745), # MySQL 5.757(50600, 50652), # MySQL 5.658(50500, 50563), # MySQL 5.559(50400, 50404), # MySQL 5.460(50100, 50174), # MySQL 5.161(50000, 50097), # MySQL 5.062(40100, 40131), # MySQL 4.163(40000, 40032), # MySQL 4.064(32300, 32359), # MySQL 3.2365(32200, 32235), # MySQL 3.2266)6768found = False69for candidate in versions:70result = inject.checkBooleanExpression("[RANDNUM]=[RANDNUM]/*!%d AND [RANDNUM1]=[RANDNUM2]*/" % candidate[0])7172if not result:73found = True74break7576if found:77for version in xrange(candidate[1], candidate[0] - 1, -1):78version = getUnicode(version)79result = inject.checkBooleanExpression("[RANDNUM]=[RANDNUM]/*!%s AND [RANDNUM1]=[RANDNUM2]*/" % version)8081if not result:82if version[0] == "3":83midVer = version[1:3]84else:85midVer = version[2]8687trueVer = "%s.%s.%s" % (version[0], midVer, version[3:])8889return trueVer9091return None9293def getFingerprint(self):94fork = hashDBRetrieve(HASHDB_KEYS.DBMS_FORK)9596if fork is None:97if inject.checkBooleanExpression("VERSION() LIKE '%MariaDB%'"):98fork = FORK.MARIADB99elif inject.checkBooleanExpression("VERSION() LIKE '%TiDB%'"):100fork = FORK.TIDB101elif inject.checkBooleanExpression("@@VERSION_COMMENT LIKE '%drizzle%'"):102fork = FORK.DRIZZLE103elif inject.checkBooleanExpression("@@VERSION_COMMENT LIKE '%Percona%'"):104fork = FORK.PERCONA105elif inject.checkBooleanExpression("@@VERSION_COMMENT LIKE '%Doris%'"):106fork = FORK.DORIS107elif inject.checkBooleanExpression("@@VERSION_COMMENT LIKE '%StarRocks%'"):108fork = FORK.STARROCKS109elif inject.checkBooleanExpression("AURORA_VERSION() LIKE '%'"): # Reference: https://aws.amazon.com/premiumsupport/knowledge-center/aurora-version-number/110fork = FORK.AURORA111else:112fork = ""113114hashDBWrite(HASHDB_KEYS.DBMS_FORK, fork)115116value = ""117wsOsFp = Format.getOs("web server", kb.headersFp)118119if wsOsFp and not conf.api:120value += "%s\n" % wsOsFp121122if kb.data.banner:123dbmsOsFp = Format.getOs("back-end DBMS", kb.bannerFp)124125if dbmsOsFp and not conf.api:126value += "%s\n" % dbmsOsFp127128value += "back-end DBMS: "129actVer = Format.getDbms()130131if not conf.extensiveFp:132value += actVer133if fork:134value += " (%s fork)" % fork135return value136137comVer = self._commentCheck()138blank = " " * 15139value += "active fingerprint: %s" % actVer140141if comVer:142comVer = Format.getDbms([comVer])143value += "\n%scomment injection fingerprint: %s" % (blank, comVer)144145if kb.bannerFp:146banVer = kb.bannerFp.get("dbmsVersion")147148if banVer:149if banVer and re.search(r"-log$", kb.data.banner or ""):150banVer += ", logging enabled"151152banVer = Format.getDbms([banVer])153value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer)154155htmlErrorFp = Format.getErrorParsedDBMSes()156157if htmlErrorFp:158value += "\n%shtml error message fingerprint: %s" % (blank, htmlErrorFp)159160if fork:161value += "\n%sfork fingerprint: %s" % (blank, fork)162163return value164165def checkDbms(self):166"""167References for fingerprint:168169* http://dev.mysql.com/doc/refman/5.0/en/news-5-0-x.html (up to 5.0.89)170* http://dev.mysql.com/doc/refman/5.1/en/news-5-1-x.html (up to 5.1.42)171* http://dev.mysql.com/doc/refman/5.4/en/news-5-4-x.html (up to 5.4.4)172* http://dev.mysql.com/doc/refman/5.5/en/news-5-5-x.html (up to 5.5.0)173* http://dev.mysql.com/doc/refman/6.0/en/news-6-0-x.html (manual has been withdrawn)174"""175176if not conf.extensiveFp and Backend.isDbmsWithin(MYSQL_ALIASES):177setDbms("%s %s" % (DBMS.MYSQL, Backend.getVersion()))178179if Backend.isVersionGreaterOrEqualThan("5") or inject.checkBooleanExpression("DATABASE() LIKE SCHEMA()"):180kb.data.has_information_schema = True181self.getBanner()182183return True184185infoMsg = "testing %s" % DBMS.MYSQL186logger.info(infoMsg)187188result = inject.checkBooleanExpression("QUARTER(NULL XOR NULL) IS NULL")189190if result:191infoMsg = "confirming %s" % DBMS.MYSQL192logger.info(infoMsg)193194result = inject.checkBooleanExpression("COALESCE(SESSION_USER(),USER()) IS NOT NULL")195196if not result:197# Note: MemSQL doesn't support SESSION_USER()198result = inject.checkBooleanExpression("GEOGRAPHY_AREA(NULL) IS NULL")199200if result:201hashDBWrite(HASHDB_KEYS.DBMS_FORK, FORK.MEMSQL)202203if not result:204warnMsg = "the back-end DBMS is not %s" % DBMS.MYSQL205logger.warning(warnMsg)206207return False208209# reading information_schema on some platforms is causing annoying timeout exits210# Reference: http://bugs.mysql.com/bug.php?id=15855211212kb.data.has_information_schema = True213214# Determine if it is MySQL >= 9.0.0215if inject.checkBooleanExpression("ISNULL(VECTOR_DIM(NULL))"):216Backend.setVersion(">= 9.0.0")217setDbms("%s 9" % DBMS.MYSQL)218self.getBanner()219220# Determine if it is MySQL >= 8.0.0221elif inject.checkBooleanExpression("ISNULL(JSON_STORAGE_FREE(NULL))"):222Backend.setVersion(">= 8.0.0")223setDbms("%s 8" % DBMS.MYSQL)224self.getBanner()225226# Determine if it is MySQL >= 5.0.0227elif inject.checkBooleanExpression("ISNULL(TIMESTAMPADD(MINUTE,[RANDNUM],NULL))"):228Backend.setVersion(">= 5.0.0")229setDbms("%s 5" % DBMS.MYSQL)230self.getBanner()231232if not conf.extensiveFp:233return True234235infoMsg = "actively fingerprinting %s" % DBMS.MYSQL236logger.info(infoMsg)237238# Check if it is MySQL >= 5.7239if inject.checkBooleanExpression("ISNULL(JSON_QUOTE(NULL))"):240Backend.setVersion(">= 5.7")241242# Check if it is MySQL >= 5.6243elif inject.checkBooleanExpression("ISNULL(VALIDATE_PASSWORD_STRENGTH(NULL))"):244Backend.setVersion(">= 5.6")245246# Check if it is MySQL >= 5.5247elif inject.checkBooleanExpression("TO_SECONDS(950501)>0"):248Backend.setVersion(">= 5.5")249250# Check if it is MySQL >= 5.1.2 and < 5.5.0251elif inject.checkBooleanExpression("@@table_open_cache=@@table_open_cache"):252if inject.checkBooleanExpression("[RANDNUM]=(SELECT [RANDNUM] FROM information_schema.GLOBAL_STATUS LIMIT 0, 1)"):253Backend.setVersionList([">= 5.1.12", "< 5.5.0"])254elif inject.checkBooleanExpression("[RANDNUM]=(SELECT [RANDNUM] FROM information_schema.PROCESSLIST LIMIT 0, 1)"):255Backend.setVersionList([">= 5.1.7", "< 5.1.12"])256elif inject.checkBooleanExpression("[RANDNUM]=(SELECT [RANDNUM] FROM information_schema.PARTITIONS LIMIT 0, 1)"):257Backend.setVersion("= 5.1.6")258elif inject.checkBooleanExpression("[RANDNUM]=(SELECT [RANDNUM] FROM information_schema.PLUGINS LIMIT 0, 1)"):259Backend.setVersionList([">= 5.1.5", "< 5.1.6"])260else:261Backend.setVersionList([">= 5.1.2", "< 5.1.5"])262263# Check if it is MySQL >= 5.0.0 and < 5.1.2264elif inject.checkBooleanExpression("@@hostname=@@hostname"):265Backend.setVersionList([">= 5.0.38", "< 5.1.2"])266elif inject.checkBooleanExpression("@@character_set_filesystem=@@character_set_filesystem"):267Backend.setVersionList([">= 5.0.19", "< 5.0.38"])268elif not inject.checkBooleanExpression("[RANDNUM]=(SELECT [RANDNUM] FROM DUAL WHERE [RANDNUM1]!=[RANDNUM2])"):269Backend.setVersionList([">= 5.0.11", "< 5.0.19"])270elif inject.checkBooleanExpression("@@div_precision_increment=@@div_precision_increment"):271Backend.setVersionList([">= 5.0.6", "< 5.0.11"])272elif inject.checkBooleanExpression("@@automatic_sp_privileges=@@automatic_sp_privileges"):273Backend.setVersionList([">= 5.0.3", "< 5.0.6"])274else:275Backend.setVersionList([">= 5.0.0", "< 5.0.3"])276277elif inject.checkBooleanExpression("DATABASE() LIKE SCHEMA()"):278Backend.setVersion(">= 5.0.2")279setDbms("%s 5" % DBMS.MYSQL)280self.getBanner()281282elif inject.checkBooleanExpression("STRCMP(LOWER(CURRENT_USER()), UPPER(CURRENT_USER()))=0"):283Backend.setVersion("< 5.0.0")284setDbms("%s 4" % DBMS.MYSQL)285self.getBanner()286287kb.data.has_information_schema = False288289if not conf.extensiveFp:290return True291292# Check which version of MySQL < 5.0.0 it is293if inject.checkBooleanExpression("3=(SELECT COERCIBILITY(USER()))"):294Backend.setVersionList([">= 4.1.11", "< 5.0.0"])295elif inject.checkBooleanExpression("2=(SELECT COERCIBILITY(USER()))"):296Backend.setVersionList([">= 4.1.1", "< 4.1.11"])297elif inject.checkBooleanExpression("CURRENT_USER()=CURRENT_USER()"):298Backend.setVersionList([">= 4.0.6", "< 4.1.1"])299300if inject.checkBooleanExpression("'utf8'=(SELECT CHARSET(CURRENT_USER()))"):301Backend.setVersion("= 4.1.0")302else:303Backend.setVersionList([">= 4.0.6", "< 4.1.0"])304else:305Backend.setVersionList([">= 4.0.0", "< 4.0.6"])306else:307Backend.setVersion("< 4.0.0")308setDbms("%s 3" % DBMS.MYSQL)309self.getBanner()310311kb.data.has_information_schema = False312313return True314else:315warnMsg = "the back-end DBMS is not %s" % DBMS.MYSQL316logger.warning(warnMsg)317318return False319320def checkDbmsOs(self, detailed=False):321if Backend.getOs():322return323324infoMsg = "fingerprinting the back-end DBMS operating system"325logger.info(infoMsg)326327result = inject.checkBooleanExpression("'W'=UPPER(MID(@@version_compile_os,1,1))")328329if result:330Backend.setOs(OS.WINDOWS)331elif not result:332Backend.setOs(OS.LINUX)333334if Backend.getOs():335infoMsg = "the back-end DBMS operating system is %s" % Backend.getOs()336logger.info(infoMsg)337else:338self.userChooseDbmsOs()339340self.cleanup(onlyFileTbl=True)341342343