[HarekazeCTF2019]Sqlite Voting
我们在页面看见了投票的源码和数据库
我们在schema.sql里发现了flag表
1 | DROP TABLE IF EXISTS `vote`; |
在vote.php里给出了查询sql的语句
1 |
|
从源码来看,我们要post传一个id,但传递的id过滤了许多sql注入关键字,并且过滤了char和”和‘,所以我们无法使用 ASCII 码和字符进行判断
我们注意到源码给出了sql注入的语句,同时update的成功与失败分别对应不同的页面,因此我们可以考虑sql盲注
1 | $pdo = new PDO('sqlite:../db/vote.db'); |
由于源码进行了过滤,我们考虑用hex进行字符判断,将所有的字符串组合用有限的36个字符表示
先考虑对 flag 16 进制长度的判断,假设它的长度为 x
,y
表示 2 的 n 次方,那么 x&y
就能表现出 x
二进制为 1 的位置,将这些 y
再进行或运算就可以得到完整的 x
的二进制,也就得到了 flag 的长度,而 1<<n
恰可以表示 2 的 n 次方
那如何构造报错语句,在sqlite3中abs函数有一个整数溢出的报错,如果abs的参数是-9223372036854775808
就会报错,同样如果是正数也会报错
判断长度的 payload : abs(case(length(hex((select(flag)from(flag))))&{1<<n})when(0)then(0)else(0x8000000000000000)end)
脚本
1 | import requests |
爆出来flag长度为84
然后考虑逐字符进行判断,但是is_vaild()过滤了大部分截取字符的函数,而且也无法用ascii码进行判断
这一题对盲注语句的构造很巧妙,首先利用如下语句分别构造出 ABCDEF
,这样十六进制的所有字符都可以使用了,并且使用 trim(0,0)
来表示空字符
1 | # hex(b'zebra') = 7A65627261 |
然后逐字符进行爆破,已经知道 flag 格式为 flag{}
,hex(b'flag{')==666C61677B
,在其后面逐位添加十六进制字符,构成 paylaod
再利用 replace(length(replace(flag,payload,''))),84,'')
这个语句进行判断
如果 flag 不包含 payload ,那么得到的 length
必为 84 ,最外面的 replace
将返回 false
,通过 case when then else
构造 abs
参数为 0
,它不报错
如果 flag 包含 payload ,那么 replace(flag, payload, '')
将 flag 中的 payload 替换为空,得到的 length
必不为 84 ,最外面的 replace
将返回 true
,通过 case when then else
构造 abs
参数为 0x8000000000000000
令其报错
以上就可以根据报错爆破出 flag,最后附上出题人脚本
1 | # coding: utf-8 |
sqlite3 盲注 bypass ,利用 replace() 和 length 进行爆破,trim() 替换空字符,trim() 和 hex() 构造字符,& 特性获取长度等等,在 mysql 中也存在溢出的现象
参考
https://blog.csdn.net/SopRomeo/article/details/108954685
https://xz.aliyun.com/t/6628?time__1311=n4%2BxnD0Dg7%3DWqrxBqooGkDuiRfQ4D5DkiCUjeD#toc-4