之前国外的一个水准比较高的比赛,当时没空做,有时间就来复现学习一波,题目提供了源码,也算是一个比较有趣的逻辑注入了。
题目背景
题目代码实现了一个简单的交易系统,每个用户在注册以后,就有 100000000MGC,用户之前可以互相交易。
题目要求
在泄露的数据库文件中,我们发现了有关flag1的sql语句: 
代码架构分析
代码使用了编写路由来写逻辑的形式,很像是python开发中的flask框架,这种方式实现起来会比较清晰,对开发者的逻辑处理有比较好的帮助。
代码入口文件
整个代码的入口文件是初始化环境加上所有路由的前置路由实现:

基本的登陆注册逻辑
这里我们可以只看post的部分,因为get的部分都是渲染前端模板的,post的部分是代码具体逻辑实现:
交易的逻辑实现
经过对代码的理解,这个系统其实是做了一个类似于当前流行的区块链的钱包,每个用户在第一次使用的时候,是可以生产一个唯一的钱包地址,如果别人要跟你进行交易,就需要知道你的钱包地址,否则是不能实现的,可以通过下面的图片来理解:
底层分析—发现漏洞
可以看到具体的数据库操作都封装到了$app->db中,并且使用了绑定变量的预处理方法执行sql语句,那我们就具体去看一下具体的实现:
可以看到这里并没有直接把预处理交给数据库,而是自己实现了,根据ctf中的经验,这里一定有问题:
foreach ($param as $key => $value) {
       $search[] = $key;
       $replace[] = sprintf("'%s'", mysqli_real_escape_string($this->link, $value));
        }
       $sql = str_replace($search, $replace, $sql);
乍一看,貌似没有什么问题,但是自己想一下,这里确实是存在问题的:我们来写个demo测试一下:
但是如果是这样呢?
找到利用点
既然我们知道了这个函数是可以造成注入的,那我们就要梳理一下这个漏洞的利用需要什么?
- 我们需要一个绑定两个参数的sql语句
- 我们需要两个参数都可控
经过简单的浏览,我们发现了可以利用的位置:
我们继续将代码抽象出来单独构造:
first: ,1000000,100000000,(select(flag1)from(flag1)));#:notes
second: ,1000000,100000000,(select(flag1)from(flag1)));#
这样经过处理我们的sql语句就变成了:
INSERT INTO account (user_id, debit, credit, notes) VALUES (',1000000,100000000,(select(flag1)from(flag1)));#',1000000,100000000,(select(flag1)from(flag1)));#
获取flag
先分别注册用户:


后记
整个流程略显复杂,设计到交易代码的具体实现,可能刚开始看会觉得很乱,但是自己梳理,流程还是非常清晰的。这题主要考了快速发现代码异常,以及构造poc,以及找到合适的利用点几个方面的能力,还是很有意思的,和大家分享一下。








