sqlmap/plugins/dbms/mysql/fingerprint.py

293 lines
11 KiB
Python
Raw Normal View History

#!/usr/bin/env python
"""
2012-07-12 21:38:03 +04:00
Copyright (c) 2006-2012 sqlmap developers (http://sqlmap.org/)
2010-10-15 03:18:29 +04:00
See the file 'doc/COPYING' for copying permission
"""
import re
from lib.core.common import Backend
from lib.core.common import Format
2010-06-02 16:45:40 +04:00
from lib.core.common import getUnicode
from lib.core.common import randomInt
from lib.core.data import conf
from lib.core.data import kb
from lib.core.data import logger
from lib.core.enums import DBMS
from lib.core.enums import OS
from lib.core.session import setDbms
from lib.core.settings import MYSQL_ALIASES
from lib.core.settings import UNKNOWN_DBMS_VERSION
from lib.request import inject
from plugins.generic.fingerprint import Fingerprint as GenericFingerprint
class Fingerprint(GenericFingerprint):
def __init__(self):
GenericFingerprint.__init__(self, DBMS.MYSQL)
def _commentCheck(self):
infoMsg = "executing %s comment injection fingerprint" % DBMS.MYSQL
logger.info(infoMsg)
2010-12-31 15:58:27 +03:00
randInt = randomInt()
result = inject.checkBooleanExpression("%d=%d/* NoValue */" % (randInt, randInt))
if not result:
warnMsg = "unable to perform %s comment injection" % DBMS.MYSQL
logger.warn(warnMsg)
return None
2011-04-10 17:30:27 +04:00
# MySQL valid versions updated on 04/2011
versions = (
2010-12-31 15:42:12 +03:00
(32200, 32235), # MySQL 3.22
(32300, 32359), # MySQL 3.23
(40000, 40032), # MySQL 4.0
2010-12-31 15:42:12 +03:00
(40100, 40131), # MySQL 4.1
(50000, 50092), # MySQL 5.0
2011-04-11 03:28:24 +04:00
(50100, 50156), # MySQL 5.1
2010-12-31 15:42:12 +03:00
(50400, 50404), # MySQL 5.4
2012-03-19 15:07:03 +04:00
(50500, 50521), # MySQL 5.5
(50600, 50604), # MySQL 5.6
2010-12-31 15:42:12 +03:00
(60000, 60014), # MySQL 6.0
)
2010-12-31 15:27:57 +03:00
index = -1
for i in xrange(len(versions)):
element = versions[i]
version = element[0]
randInt = randomInt()
version = getUnicode(version)
result = inject.checkBooleanExpression("%d=%d/*!%s AND %d=%d*/" % (randInt, randInt, version, randInt, randInt + 1))
if result:
break
2010-12-31 15:27:57 +03:00
else:
index += 1
2010-12-31 15:27:57 +03:00
if index >= 0:
prevVer = None
for version in xrange(versions[index][0], versions[index][1] + 1):
randInt = randomInt()
2010-06-02 16:45:40 +04:00
version = getUnicode(version)
result = inject.checkBooleanExpression("%d=%d/*!%s AND %d=%d*/" % (randInt, randInt, version, randInt, randInt + 1))
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 getFingerprint(self):
2011-04-30 17:20:05 +04:00
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: "
actVer = Format.getDbms()
if not conf.extensiveFp:
value += actVer
return value
comVer = self._commentCheck()
2011-04-30 17:20:05 +04:00
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["dbmsVersion"] if 'dbmsVersion' in kb.bannerFp else None
if re.search("-log$", kb.data.banner):
banVer += ", logging enabled"
banVer = Format.getDbms([banVer] if banVer else None)
value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer)
htmlErrorFp = Format.getErrorParsedDBMSes()
if htmlErrorFp:
value += "\n%shtml error message fingerprint: %s" % (blank, htmlErrorFp)
return value
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)
2010-06-02 16:45:40 +04:00
randInt = getUnicode(randomInt(1))
2012-09-08 22:22:13 +04:00
result = inject.checkBooleanExpression("QUARTER(NULL) IS NULL")
if result:
infoMsg = "confirming %s" % DBMS.MYSQL
logger.info(infoMsg)
result = inject.checkBooleanExpression("USER()=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
2011-05-20 03:28:27 +04:00
# Reference: http://bugs.mysql.com/bug.php?id=15855
# Determine if it is MySQL >= 5.0.0
if inject.checkBooleanExpression("ISNULL(TIMESTAMPADD(MINUTE,%s,%s))" % (randInt, randInt)):
kb.data.has_information_schema = True
Backend.setVersion(">= 5.0.0")
2010-11-02 14:59:24 +03:00
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("%s=(SELECT %s FROM information_schema.GLOBAL_STATUS LIMIT 0, 1)" % (randInt, randInt)):
Backend.setVersionList([">= 5.1.12", "< 5.5.0"])
elif inject.checkBooleanExpression("%s=(SELECT %s FROM information_schema.PROCESSLIST LIMIT 0, 1)" % (randInt,randInt)):
Backend.setVersionList([">= 5.1.7", "< 5.1.12"])
elif inject.checkBooleanExpression("%s=(SELECT %s FROM information_schema.PARTITIONS LIMIT 0, 1)" % (randInt, randInt)):
Backend.setVersion("= 5.1.6")
elif inject.checkBooleanExpression("%s=(SELECT %s FROM information_schema.PLUGINS LIMIT 0, 1)" % (randInt, randInt)):
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"])
2010-12-22 03:27:21 +03:00
elif not inject.checkBooleanExpression("%s=(SELECT %s FROM DUAL WHERE %s!=%s)" % (randInt, randInt, randInt, randInt)):
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"])
2010-12-18 16:08:37 +03:00
# For cases when information_schema is missing
elif inject.checkBooleanExpression("DATABASE() LIKE SCHEMA()"):
Backend.setVersion(">= 5.0.2")
2010-12-18 16:08:37 +03:00
setDbms("%s 5" % DBMS.MYSQL)
self.getBanner()
2010-12-22 03:51:20 +03:00
elif inject.checkBooleanExpression("STRCMP(LOWER(CURRENT_USER()), UPPER(CURRENT_USER()))=0"):
Backend.setVersion("< 5.0.0")
2010-11-02 14:59:24 +03:00
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"])
2010-12-20 14:25:18 +03:00
else:
Backend.setVersionList([">= 4.0.0", "< 4.0.6"])
2010-12-20 14:23:57 +03:00
else:
Backend.setVersion("< 4.0.0")
2010-12-20 14:23:57 +03:00
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 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)
2011-04-21 21:34:26 +04:00
elif not result:
Backend.setOs(OS.LINUX)
if Backend.getOs():
infoMsg = "the back-end DBMS operating system is %s" % Backend.getOs()
2011-01-16 02:54:03 +03:00
logger.info(infoMsg)
else:
self.userChooseDbmsOs()
self.cleanup(onlyFileTbl=True)