三个WAF,不知道的以为我在注CIA。
本文首发于奇安信攻防社区~
. . . * . * ☄️. * . * . 🔆 .* . * . 🧶 * . * . . .
发现漏洞
经典的字符串型盲注,无报错回显。注入单引号1返回404或500(交替出现,无规律),注入单引号2正常显示:
由于可能存在混淆情况(前几天就遇到一个模糊搜索导致1-0、1-1回显不同让人以为是注入),用单引号3、单引号4等再次测试,确定这里存在一个注入点。
第一个WAF
判定WAF
开始注入,先使用百用百不腻的' and '1'='1。这个payload有两个作用,一是进一步确定注入是否存在,二是测试WAF是否存在,因为几乎没有WAF会不拦截它。
也是果不其然的拦截了:
垃圾参数绕过
由于返回的是空包,也不好确定是不是云WAF,先使用最懒的办法,也就是垃圾参数绕过:
一发入魂,四千九百多个参数绕过成功,但看返回包很明显还有WAF。
第二个WAF
垃圾参数绕过
逐个删除敏感关键词,看看这第二个WAF到底拦截了哪些词。
删除=,拦截;删除单引号,正常;删除and,正常。
看来是拦截了'+and,然后换用其它常见关键字测试,'+select拦截,'+union拦截,'+or拦截;而'+if,'+case都可以。试着使用注释/换行代替空格,无效。对于常见函数,substr、substring、sleep自然是全部拦截,但left和right并未拦截。
看来这个WAF主要拦截的是连接词和常用函数。使用||代替or,成功(当然,这里也有不用连接词的注法)。
第三个WAF
实际上,这个WAF(或者说拦截)我搞了半天才发现。
很显然这个系统过了很多个开发的手,不然我不觉得有点追求的开发会写这么多个过滤拦截在代码里,毕竟这一点都不优雅。被打这么多次就直接上ORM好吗?好的。
判定数据库类型
言归正传,一开始我认为所有WAF都被绕过了,但碍于这个关键字拦截不好直接上sqlmap跑,于是只能手动注入之。要手注最开始肯定要判断下数据库类型。
使用USER无报错,USER()报错,排除MYSQL。
使用db_name()报错,排除MSSQL。
使用exp(291)报错,exp(290)不报错,确定当前数据库为Oracle。
艰难的盲注之路
确定数据库之后选用payload,由于是只能使用or的盲注且原始包返回的结果集就是空的(这意味着我无法构造两个不同的结果集),所以我选择使用1/0也就是除零报错进行盲注。
构造payload为' || 1/(case when 1=1 then 1 else 1 end) || ',使用永真看一下语句是否报错,结果却报错了。
换用if(1=1,1,1)继续尝试,结果还是报错。
理论上用在大多数数据库上都生效的基础语句为什么会报错呢?这实在让人百思不得其解。
由于想了半天(真是打死我也不敢想开发会弄三个WAF来恶心我)想不出个所以然,所以我决定先放开这头,去测试字符串函数。毕竟手注中最能令人信服的证据莫过于注出当前用户名了。
substr、substring就别想了,会被第二个WAF拦截。使用left('a',1)代替字符串,居然还是报错?
一次报错是我写的不对,次次报错就真的有点问题了。这下我开始怀疑存在第三个无回显的WAF。
使用'|| 1=1 ||',居然也报错?
那么非常有可能是拦截了=等于号。换用不等于<>进行测试,未报错:
但是对case when套了<>进去,还是报错:
那很有可能是既对=拦截又对case when等条件语句拦截。这下咋办?只能重新找一个函数啦。
找到payload
几番搜索,找到一个函数INSTR,用于搜索子字符串的出现位置:
这个函数是可以利用的,只是过程稍微麻烦些。至于如何利用呢——我们做一道小小的数学题即可:
' || 1/(1-INSTR(USER,'a')) || '
由于INSTR会返回整个字符串中子串出现第一次的位置,所以我们只需要同时爆破被减数与子串,即可定位整个用户名字符串(如果字符多次出现,使用INSTR(USER,'SUBSTR',[FIRST INDEX],[LAST INDEX])扩展参数即可):
. . . * . * 🌟 * . * . . .
原文始发于微信公众号(重生之成为赛博女保安):渗透实战:怒绕三个WAF注入的小故事
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论