一道简单的SQL注入题
一道简单的
sqlite
数据库的注入题目。
初探
开局登录框,贴合实战(
抓包发现返回为0,跑了一下弱口令,无果。
继续测试SQL,在用户名测试发现 'or 1=1 --
返回数据包为 111,基本上断定存在注入,暂时来看应该是MYSQL
(一般是PHP+MYSQL)布尔注入。
注入第一回-数据库类型判断
接上回,在判断是布尔盲注后,开始构造payload进行注入,先手动测试数据库长度,测试payload是否可用
|
|
发现返回0,正常来说应该是返回真,比如
所以应该是返回111,开始思考为什么会出现返回0的情况。简单思考感觉原因可能是
database
等关键字被拦截- 数据库不是
MYSQL
(但是length函数可以)
关键字是否被拦截(×)
开始验证,首先是 database
关键字拦截。注意,这里只能通过构造永真表达式 length('xxx')
来判断关键字是否被拦截,因为用户名我们不知道,可能存在拦截后也返回0的情况
测试 database
是否被拦截。
|
|
返回为111,说明不存在该情况。对于其他关键字也可以一一测试。
数据库不是MYSQL(✓)
继续接上回,由于length函数可以执行,排除了一些数据库,可以在这个限定条件下寻找database函数不存在的数据库,查了一圈文档发现可能是SQLite,这里发现了一个小tips,SQLite的time函数可以不传递参数即可返回值。如下
其他数据库基本也需要传递参数,所以基本上判断为SQLite数据库。
注入第二回-再探注入类型
书接上文的布尔注入,是通过返回包的111返回判断为布尔盲注。下一步就是判断列,由于SQLite和MSYQL注入方法高度相似,可通过 order by
和联合查询来判断列数,这里分析一下两种方法的差异。
order by
是在SQL查询的排序方法,其判断列数时属于盲注判断,而 union
联合查询是可以有回显。
如下
可以发现 order by
判断时,无回显,联合查询是存在回显。
所以在注入类型判断时尽量使用 union
联合查询,很可能使盲注变回显。回到这道题,我们使用联合注入进行列数判断。在判断到第五列的时候,发现回显了5
|
|
所以现在盲注就变回显注入了。
注入第三回-WAF绕过
在可回显判断完之后,就应该开始一把梭了,首先探测版本
|
|
发现返回为3
但是通过length判断版本的长度应该为6。猜测页面应该过滤了非int型的输出,waf伪代码应该是
|
|
对于只输出 int
类型,首先想到的应该是进制转换,通过翻查SQLite文档,发现两种方法可以进行进制转换,分别是cast和printf,这里我们用 printf
来进行转换。一开始的思路是直接将查询的结果,利用hex函数进行16进制转换,然后利用printf转成10进制。如
|
|
但是这里有个小问题,当hex的结果存在非数字时,printf转换时会忽略结果,如上图。不过由于一般字母数字的16进制不包含字母,所以勉强能接受这个结果。但是忽略的话后续内容怎么办。
这时想到了截断,利用 substr
来对结果进行截断,这样忽略的内容也在可控范围。
构造查询数据库表创建语句
|
|
效果如下
通过脚本遍历截断查询相关内容,注意返回的6是因为字母16进制转换问题,对应的字母有j,k,l,m,n,o,z(大写+小写,这里可脚本简单遍历一下即可。
最后如下
|
|
需要注意的是密码后面的16进制转换问题。最后密码为AES密文,加密文件存在,这里不过多描述解密相关。
注入第四回-回显再研究
在赛后回忆了一下,总觉得办法太过笨拙(看了解题数量好像有点多。这里对16进制转换再研究,通过本地测试,发现 printf
在 %d
转换存在缺陷,干脆不用它转换16进制到10进制。
利用两层16进制编码,即可在回显的时候不存在字符。如
到最后发现其实走了一圈弯路,Orz。