2019-05-08 13:47:52 +03:00
#!/usr/bin/env python
2010-03-23 01:57:57 +03:00
"""
2024-01-04 01:11:52 +03:00
Copyright ( c ) 2006 - 2024 sqlmap developers ( https : / / sqlmap . org / )
2017-10-11 15:50:46 +03:00
See the file ' LICENSE ' for copying permission
2010-03-23 01:57:57 +03:00
"""
import re
2011-01-28 19:36:09 +03:00
from lib . core . common import Backend
from lib . core . common import Format
2016-05-14 16:05:50 +03:00
from lib . core . common import hashDBRetrieve
from lib . core . common import hashDBWrite
2019-03-28 18:04:38 +03:00
from lib . core . compat import xrange
2019-05-06 01:54:21 +03:00
from lib . core . convert import getUnicode
2010-03-23 01:57:57 +03:00
from lib . core . data import conf
from lib . core . data import kb
from lib . core . data import logger
2010-11-08 12:20:02 +03:00
from lib . core . enums import DBMS
2020-01-21 01:11:37 +03:00
from lib . core . enums import FORK
2016-05-14 16:05:50 +03:00
from lib . core . enums import HASHDB_KEYS
2011-04-23 20:25:09 +04:00
from lib . core . enums import OS
2010-03-23 01:57:57 +03:00
from lib . core . session import setDbms
from lib . core . settings import MYSQL_ALIASES
from lib . request import inject
from plugins . generic . fingerprint import Fingerprint as GenericFingerprint
class Fingerprint ( GenericFingerprint ) :
def __init__ ( self ) :
2011-01-14 14:55:20 +03:00
GenericFingerprint . __init__ ( self , DBMS . MYSQL )
2010-03-23 01:57:57 +03:00
2012-12-06 17:14:19 +04:00
def _commentCheck ( self ) :
2011-01-20 02:06:15 +03:00
infoMsg = " executing %s comment injection fingerprint " % DBMS . MYSQL
2010-03-23 01:57:57 +03:00
logger . info ( infoMsg )
2013-02-04 18:12:03 +04:00
result = inject . checkBooleanExpression ( " [RANDNUM]=[RANDNUM]/* NoValue */ " )
2010-03-23 01:57:57 +03:00
if not result :
2011-01-20 02:06:15 +03:00
warnMsg = " unable to perform %s comment injection " % DBMS . MYSQL
2022-06-22 13:04:34 +03:00
logger . warning ( warnMsg )
2010-03-23 01:57:57 +03:00
return None
2016-06-03 03:31:31 +03:00
# Reference: https://downloads.mysql.com/archives/community/
2019-11-06 16:45:48 +03:00
# Reference: https://dev.mysql.com/doc/relnotes/mysql/<major>.<minor>/en/
2010-03-23 01:57:57 +03:00
versions = (
2024-06-24 19:22:56 +03:00
( 80300 , 80302 ) , # MySQL 8.3
2024-04-12 17:21:32 +03:00
( 80200 , 80202 ) , # MySQL 8.2
2023-10-27 16:17:47 +03:00
( 80100 , 80102 ) , # MySQL 8.1
2024-06-24 19:22:56 +03:00
( 80000 , 80037 ) , # MySQL 8.0
2020-01-26 01:37:57 +03:00
( 60000 , 60014 ) , # MySQL 6.0
2024-04-12 17:21:32 +03:00
( 50700 , 50745 ) , # MySQL 5.7
2021-05-19 19:20:39 +03:00
( 50600 , 50652 ) , # MySQL 5.6
2020-01-26 01:37:57 +03:00
( 50500 , 50563 ) , # MySQL 5.5
( 50400 , 50404 ) , # MySQL 5.4
2023-02-20 17:37:32 +03:00
( 50100 , 50174 ) , # MySQL 5.1
2020-01-26 01:37:57 +03:00
( 50000 , 50097 ) , # MySQL 5.0
( 40100 , 40131 ) , # MySQL 4.1
( 40000 , 40032 ) , # MySQL 4.0
( 32300 , 32359 ) , # MySQL 3.23
( 32200 , 32235 ) , # MySQL 3.22
2018-03-13 15:45:42 +03:00
)
2010-03-23 01:57:57 +03:00
2020-01-26 01:37:57 +03:00
found = False
for candidate in versions :
result = inject . checkBooleanExpression ( " [RANDNUM]=[RANDNUM]/*! %d AND [RANDNUM1]=[RANDNUM2]*/ " % candidate [ 0 ] )
2010-12-31 15:21:02 +03:00
2020-01-26 01:37:57 +03:00
if not result :
found = True
2010-12-31 15:21:02 +03:00
break
2010-03-23 01:57:57 +03:00
2020-01-26 01:37:57 +03:00
if found :
for version in xrange ( candidate [ 1 ] , candidate [ 0 ] - 1 , - 1 ) :
2010-06-02 16:45:40 +04:00
version = getUnicode ( version )
2013-02-04 18:12:03 +04:00
result = inject . checkBooleanExpression ( " [RANDNUM]=[RANDNUM]/*! %s AND [RANDNUM1]=[RANDNUM2]*/ " % version )
2010-03-23 01:57:57 +03:00
2020-01-26 01:37:57 +03:00
if not result :
2010-03-23 01:57:57 +03:00
if version [ 0 ] == " 3 " :
2020-01-26 01:37:57 +03:00
midVer = version [ 1 : 3 ]
2010-03-23 01:57:57 +03:00
else :
2020-01-26 01:37:57 +03:00
midVer = version [ 2 ]
2010-03-23 01:57:57 +03:00
2020-01-26 01:37:57 +03:00
trueVer = " %s . %s . %s " % ( version [ 0 ] , midVer , version [ 3 : ] )
2010-03-23 01:57:57 +03:00
return trueVer
return None
def getFingerprint ( self ) :
2020-01-22 01:19:11 +03:00
fork = hashDBRetrieve ( HASHDB_KEYS . DBMS_FORK )
if fork is None :
2020-01-24 01:27:33 +03:00
if inject . checkBooleanExpression ( " VERSION() LIKE ' % MariaDB % ' " ) :
fork = FORK . MARIADB
elif inject . checkBooleanExpression ( " VERSION() LIKE ' % TiDB % ' " ) :
fork = FORK . TIDB
2020-02-03 13:46:03 +03:00
elif inject . checkBooleanExpression ( " @@VERSION_COMMENT LIKE ' %d rizzle % ' " ) :
fork = FORK . DRIZZLE
2020-01-28 01:47:47 +03:00
elif inject . checkBooleanExpression ( " @@VERSION_COMMENT LIKE ' % Percona % ' " ) :
fork = FORK . PERCONA
2020-02-04 00:11:19 +03:00
elif inject . checkBooleanExpression ( " AURORA_VERSION() LIKE ' % ' " ) : # Reference: https://aws.amazon.com/premiumsupport/knowledge-center/aurora-version-number/
fork = FORK . AURORA
2020-01-24 01:27:33 +03:00
else :
fork = " "
2020-01-22 01:19:11 +03:00
hashDBWrite ( HASHDB_KEYS . DBMS_FORK , fork )
2011-04-30 17:20:05 +04:00
value = " "
2011-01-28 19:36:09 +03:00
wsOsFp = Format . getOs ( " web server " , kb . headersFp )
2010-03-23 01:57:57 +03:00
2017-04-10 20:21:22 +03:00
if wsOsFp and not conf . api :
2010-03-23 01:57:57 +03:00
value + = " %s \n " % wsOsFp
if kb . data . banner :
2011-01-28 19:36:09 +03:00
dbmsOsFp = Format . getOs ( " back-end DBMS " , kb . bannerFp )
2010-03-23 01:57:57 +03:00
2017-04-10 20:21:22 +03:00
if dbmsOsFp and not conf . api :
2010-03-23 01:57:57 +03:00
value + = " %s \n " % dbmsOsFp
2011-01-20 02:06:15 +03:00
value + = " back-end DBMS: "
2011-01-28 19:36:09 +03:00
actVer = Format . getDbms ( )
2010-03-23 01:57:57 +03:00
if not conf . extensiveFp :
value + = actVer
2020-01-22 01:19:11 +03:00
if fork :
value + = " ( %s fork) " % fork
2010-03-23 01:57:57 +03:00
return value
2012-12-06 17:14:19 +04:00
comVer = self . _commentCheck ( )
2011-04-30 17:20:05 +04:00
blank = " " * 15
2010-03-23 01:57:57 +03:00
value + = " active fingerprint: %s " % actVer
if comVer :
2011-01-28 19:36:09 +03:00
comVer = Format . getDbms ( [ comVer ] )
2010-03-23 01:57:57 +03:00
value + = " \n %s comment injection fingerprint: %s " % ( blank , comVer )
if kb . bannerFp :
2018-08-25 23:57:49 +03:00
banVer = kb . bannerFp . get ( " dbmsVersion " )
2010-03-23 01:57:57 +03:00
2019-05-22 10:43:10 +03:00
if banVer :
if banVer and re . search ( r " -log$ " , kb . data . banner or " " ) :
banVer + = " , logging enabled "
2010-03-23 01:57:57 +03:00
2019-05-22 10:43:10 +03:00
banVer = Format . getDbms ( [ banVer ] )
value + = " \n %s banner parsing fingerprint: %s " % ( blank , banVer )
2010-03-23 01:57:57 +03:00
2011-01-28 19:36:09 +03:00
htmlErrorFp = Format . getErrorParsedDBMSes ( )
2010-03-23 01:57:57 +03:00
if htmlErrorFp :
value + = " \n %s html error message fingerprint: %s " % ( blank , htmlErrorFp )
2020-01-22 01:19:11 +03:00
if fork :
value + = " \n %s fork fingerprint: %s " % ( blank , fork )
2010-03-23 01:57:57 +03:00
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 )
"""
2017-03-06 14:53:04 +03:00
if not conf . extensiveFp and Backend . isDbmsWithin ( MYSQL_ALIASES ) :
2011-01-28 19:36:09 +03:00
setDbms ( " %s %s " % ( DBMS . MYSQL , Backend . getVersion ( ) ) )
2011-01-20 02:06:15 +03:00
2022-03-15 13:34:09 +03:00
if Backend . isVersionGreaterOrEqualThan ( " 5 " ) or inject . checkBooleanExpression ( " DATABASE() LIKE SCHEMA() " ) :
2010-03-23 01:57:57 +03:00
kb . data . has_information_schema = True
self . getBanner ( )
2011-01-14 15:47:07 +03:00
return True
2010-03-23 01:57:57 +03:00
2011-01-20 02:06:15 +03:00
infoMsg = " testing %s " % DBMS . MYSQL
2010-03-31 14:50:47 +04:00
logger . info ( infoMsg )
2023-02-04 01:30:05 +03:00
result = inject . checkBooleanExpression ( " QUARTER(NULL XOR NULL) IS NULL " )
2010-03-23 01:57:57 +03:00
if result :
2011-01-20 02:06:15 +03:00
infoMsg = " confirming %s " % DBMS . MYSQL
2010-03-23 01:57:57 +03:00
logger . info ( infoMsg )
2015-10-10 15:19:47 +03:00
result = inject . checkBooleanExpression ( " SESSION_USER() LIKE USER() " )
2010-03-23 01:57:57 +03:00
2020-01-21 01:11:37 +03:00
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 )
2010-03-23 01:57:57 +03:00
if not result :
2011-01-20 02:06:15 +03:00
warnMsg = " the back-end DBMS is not %s " % DBMS . MYSQL
2022-06-22 13:04:34 +03:00
logger . warning ( warnMsg )
2010-03-23 01:57:57 +03:00
return False
2011-05-20 03:03:00 +04:00
# 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
2021-10-11 19:01:34 +03:00
kb . data . has_information_schema = True
2018-07-02 11:58:25 +03:00
# Determine if it is MySQL >= 8.0.0
if inject . checkBooleanExpression ( " ISNULL(JSON_STORAGE_FREE(NULL)) " ) :
Backend . setVersion ( " >= 8.0.0 " )
setDbms ( " %s 8 " % DBMS . MYSQL )
self . getBanner ( )
2011-05-20 03:28:27 +04:00
# Determine if it is MySQL >= 5.0.0
2018-07-02 11:58:25 +03:00
elif inject . checkBooleanExpression ( " ISNULL(TIMESTAMPADD(MINUTE,[RANDNUM],NULL)) " ) :
2011-01-28 19:36:09 +03:00
Backend . setVersion ( " >= 5.0.0 " )
2010-11-02 14:59:24 +03:00
setDbms ( " %s 5 " % DBMS . MYSQL )
2010-03-23 01:57:57 +03:00
self . getBanner ( )
if not conf . extensiveFp :
return True
2011-01-20 02:06:15 +03:00
infoMsg = " actively fingerprinting %s " % DBMS . MYSQL
logger . info ( infoMsg )
2018-07-02 11:58:25 +03:00
# 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 " )
2010-03-23 01:57:57 +03:00
# Check if it is MySQL >= 5.1.2 and < 5.5.0
2010-12-14 00:33:42 +03:00
elif inject . checkBooleanExpression ( " @@table_open_cache=@@table_open_cache " ) :
2013-02-04 18:12:03 +04:00
if inject . checkBooleanExpression ( " [RANDNUM]=(SELECT [RANDNUM] FROM information_schema.GLOBAL_STATUS LIMIT 0, 1) " ) :
2011-01-28 19:36:09 +03:00
Backend . setVersionList ( [ " >= 5.1.12 " , " < 5.5.0 " ] )
2013-02-04 18:12:03 +04:00
elif inject . checkBooleanExpression ( " [RANDNUM]=(SELECT [RANDNUM] FROM information_schema.PROCESSLIST LIMIT 0, 1) " ) :
2011-01-28 19:36:09 +03:00
Backend . setVersionList ( [ " >= 5.1.7 " , " < 5.1.12 " ] )
2013-02-04 18:12:03 +04:00
elif inject . checkBooleanExpression ( " [RANDNUM]=(SELECT [RANDNUM] FROM information_schema.PARTITIONS LIMIT 0, 1) " ) :
2011-01-28 19:36:09 +03:00
Backend . setVersion ( " = 5.1.6 " )
2013-02-04 18:12:03 +04:00
elif inject . checkBooleanExpression ( " [RANDNUM]=(SELECT [RANDNUM] FROM information_schema.PLUGINS LIMIT 0, 1) " ) :
2011-01-28 19:36:09 +03:00
Backend . setVersionList ( [ " >= 5.1.5 " , " < 5.1.6 " ] )
2010-03-23 01:57:57 +03:00
else :
2011-01-28 19:36:09 +03:00
Backend . setVersionList ( [ " >= 5.1.2 " , " < 5.1.5 " ] )
2010-03-23 01:57:57 +03:00
# Check if it is MySQL >= 5.0.0 and < 5.1.2
2010-12-14 00:33:42 +03:00
elif inject . checkBooleanExpression ( " @@hostname=@@hostname " ) :
2011-01-28 19:36:09 +03:00
Backend . setVersionList ( [ " >= 5.0.38 " , " < 5.1.2 " ] )
2011-01-01 22:38:51 +03:00
elif inject . checkBooleanExpression ( " @@character_set_filesystem=@@character_set_filesystem " ) :
2011-01-28 19:36:09 +03:00
Backend . setVersionList ( [ " >= 5.0.19 " , " < 5.0.38 " ] )
2013-02-04 18:12:03 +04:00
elif not inject . checkBooleanExpression ( " [RANDNUM]=(SELECT [RANDNUM] FROM DUAL WHERE [RANDNUM1]!=[RANDNUM2]) " ) :
2011-01-28 19:36:09 +03:00
Backend . setVersionList ( [ " >= 5.0.11 " , " < 5.0.19 " ] )
2011-01-01 22:38:51 +03:00
elif inject . checkBooleanExpression ( " @@div_precision_increment=@@div_precision_increment " ) :
2011-01-28 19:36:09 +03:00
Backend . setVersionList ( [ " >= 5.0.6 " , " < 5.0.11 " ] )
2011-01-01 22:38:51 +03:00
elif inject . checkBooleanExpression ( " @@automatic_sp_privileges=@@automatic_sp_privileges " ) :
2011-01-28 19:36:09 +03:00
Backend . setVersionList ( [ " >= 5.0.3 " , " < 5.0.6 " ] )
2010-03-23 01:57:57 +03:00
else :
2011-01-28 19:36:09 +03:00
Backend . setVersionList ( [ " >= 5.0.0 " , " < 5.0.3 " ] )
2010-03-23 01:57:57 +03:00
2010-12-18 16:08:37 +03:00
elif inject . checkBooleanExpression ( " DATABASE() LIKE SCHEMA() " ) :
2011-01-28 19:36:09 +03:00
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 " ) :
2011-01-28 19:36:09 +03:00
Backend . setVersion ( " < 5.0.0 " )
2010-11-02 14:59:24 +03:00
setDbms ( " %s 4 " % DBMS . MYSQL )
2010-03-23 01:57:57 +03:00
self . getBanner ( )
2021-10-11 19:01:34 +03:00
kb . data . has_information_schema = False
2010-03-23 01:57:57 +03:00
if not conf . extensiveFp :
return True
# Check which version of MySQL < 5.0.0 it is
2010-12-14 00:33:42 +03:00
if inject . checkBooleanExpression ( " 3=(SELECT COERCIBILITY(USER())) " ) :
2011-01-28 19:36:09 +03:00
Backend . setVersionList ( [ " >= 4.1.11 " , " < 5.0.0 " ] )
2010-12-14 00:33:42 +03:00
elif inject . checkBooleanExpression ( " 2=(SELECT COERCIBILITY(USER())) " ) :
2011-01-28 19:36:09 +03:00
Backend . setVersionList ( [ " >= 4.1.1 " , " < 4.1.11 " ] )
2010-12-14 00:33:42 +03:00
elif inject . checkBooleanExpression ( " CURRENT_USER()=CURRENT_USER() " ) :
2011-01-28 19:36:09 +03:00
Backend . setVersionList ( [ " >= 4.0.6 " , " < 4.1.1 " ] )
2010-03-23 01:57:57 +03:00
2010-12-14 00:33:42 +03:00
if inject . checkBooleanExpression ( " ' utf8 ' =(SELECT CHARSET(CURRENT_USER())) " ) :
2011-01-28 19:36:09 +03:00
Backend . setVersion ( " = 4.1.0 " )
2010-03-23 01:57:57 +03:00
else :
2011-01-28 19:36:09 +03:00
Backend . setVersionList ( [ " >= 4.0.6 " , " < 4.1.0 " ] )
2010-12-20 14:25:18 +03:00
else :
2011-01-28 19:36:09 +03:00
Backend . setVersionList ( [ " >= 4.0.0 " , " < 4.0.6 " ] )
2010-12-20 14:23:57 +03:00
else :
2011-01-28 19:36:09 +03:00
Backend . setVersion ( " < 4.0.0 " )
2010-12-20 14:23:57 +03:00
setDbms ( " %s 3 " % DBMS . MYSQL )
self . getBanner ( )
2010-03-23 01:57:57 +03:00
2021-10-11 19:01:34 +03:00
kb . data . has_information_schema = False
2010-03-23 01:57:57 +03:00
return True
else :
2011-01-20 02:06:15 +03:00
warnMsg = " the back-end DBMS is not %s " % DBMS . MYSQL
2022-06-22 13:04:34 +03:00
logger . warning ( warnMsg )
2010-03-23 01:57:57 +03:00
return False
2010-11-03 13:08:27 +03:00
2010-03-23 01:57:57 +03:00
def checkDbmsOs ( self , detailed = False ) :
2011-04-23 20:25:09 +04:00
if Backend . getOs ( ) :
2010-03-23 01:57:57 +03:00
return
infoMsg = " fingerprinting the back-end DBMS operating system "
logger . info ( infoMsg )
2011-04-21 21:33:07 +04:00
result = inject . checkBooleanExpression ( " ' W ' =UPPER(MID(@@version_compile_os,1,1)) " )
2011-01-20 02:06:15 +03:00
if result :
2011-04-23 20:25:09 +04:00
Backend . setOs ( OS . WINDOWS )
2011-04-21 21:34:26 +04:00
elif not result :
2011-04-23 20:25:09 +04:00
Backend . setOs ( OS . LINUX )
2010-03-23 01:57:57 +03:00
2011-04-23 20:25:09 +04:00
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 ( )
2010-03-23 01:57:57 +03:00
self . cleanup ( onlyFileTbl = True )