某天,当我参与某个漏洞众测项目中,偶尔发现之前一个从未见过的子域名网站,因此我决定深入测试一下。该网站主界面是一个登录页面,开放了用户注册功能,所以我就注册了一个账户,最后试出了一个时间盲注来。

在查看该网站过程中,我发现其中还有一个搜索功能,我尝试进行了XSS,但是无效。当时,我根本没想着去测试SQL注入漏洞,因为我觉得开发人员应该部署了完善的过滤措施,不可能存在注入漏洞。但是,其中的点击按钮引起了我的注意,即当你点击其中的引用标题(如引用ID、类型等)后,会出现一些调用异常。

漏洞发现

比如,点击之后,用户当前的URL链接会被跳转到?order=type&ordering=ASC&search=路径下,这里的ASC参数看着好眼熟,其后台数据库的查询语句可能会是这样的:SELECT * FROM referrals ORDER BY type ASC”,我立马用单引号'测试了一下SQL注入问题,即把其中的order=type变成order=type’,之后让我惊讶的是,服务端响应回来了500的报错,看来离SQL注入漏洞接近了!

然后,我想到了基于union的sql注入一次性转储(dump in one shot for union based SQL Injection),但在我尝试了好一会儿也实现不了联合式查询union select 1,2,3,4,5…18--+-,只能实现order by 18的查询,这虽然能证明SQL注入漏洞的存在,但威胁性还远远不够。得想办法从后台数据库中获取点东西出来。

反复尝试不同Payload之后,我发现在ORDER BY 语句后的UNION SELECT查询是不可行的,因为其后台数据库的查询语句估计是这样的-SELECT * FROM referrals ORDER BY type ASC”,所以union based SQL 类型的注入是不行的。那就试试报错型或基于时间的盲注吧。先是报错型,服务端总是返回一个带500状态的空白页面,没有额外信息,因此排除。最后只剩下基于时间的盲注Time-Based Sql注入了。

在用相关的时间盲注Payload尝试了MySQL, MSSQL, 和PostgreSQL之后都无果,尽管我在我本地虚拟机上测试是有效的,但是在该网站中却连以下最基本的Payload都无效:

sleep(10)--
benchmark(1000000000,md5(1))--
pg_sleep(10)--
; WAITFOR DELAY ‘00:00:10’;--

构造Payload

我想,难道是后面有WAF,之后我又尝试了一遍WAF绕过技巧,无望。难道是其数据库禁用了类似sleep()或benchmark() 的方法函数?能否用其它方式来测试Time-Based Sql注入?

然后我尝试了boolean based blind的Payload-if(1=1,1,(select 1 union select 2)),因为若出现SQL错误,其网站服务端就会返回500报错页面;这里,我猜想,如果这里的1=1则其服务端应该会返回200状态;如果用if(1>2,1,(select 1 union select 2)),则其服务端应该会返回500,这完全是基于查询语句“SELECT * FROM referrals ORDER BY (select 1 union select 2) ASC”的猜想。但结果是=,< 和 >貌似都被拦截了。

我都快要放弃了,此时我又想到了能否从Payload中入手获取一下数据库版本呢?我好像记得,在类似SQL/*!50000 Comments*/这样的范式中,若SQL版本至少为5.00.00时,SQL就会执行其中的Payload。也就是说,如果后台数据库版本至少为5.0.0,那么按照这种方式构造/*!50000someInvalidSQLSyntax*/范式,则其应该返回500的报错;如果其后台数据库版本小于5.0.0,则会返回一个正常页面。

就用Burp Intruder来枚举一下吧,我构造了以下版本精确到小数字的数字Payload:

/*!50731someInvalidSQLSyntax*/ returns an error 500
/*!50732someInvalidSQLSyntax*/ returns a success 200

从上述测试结果可知,其后台数据库版本应该是5.7.31,且是一个MySQL数据库。那么接下来,我就可以围绕MySQL 5.7.31来构造sleep() 和 benchmark() Payload尝试一下了。

之后,我反复尝试,最终构造了Payload-(select*from(select(sleep(10)))a),并成功让服务端执行了sleep(10):

在修改Payload为-(select*from(select(sleep(30)))a)后,服务端也成功执行了sleep(30):

最终,我尽可能完整详细地写了一篇漏洞报告,进行了提交。半个月后,收获了$3500的漏洞奖励。

漏洞上报和处理进程

2020.11.23  漏洞上报
2020.11.25  漏洞修复
2020.12.12  漏洞分级为P1至P2中间,并奖励$3500
2020.12.17  收获赏金奖励

参考来源:medium,编译整理:clouds,转载请注明来自freebuf.com

本文作者:clouds, 转自FreeBuf