codeblue2018 MortAl mage aGEnts题目分析

之前国外的一个水准比较高的比赛,当时没空做,有时间就来复现学习一波,题目提供了源码,也算是一个比较有趣的逻辑注入了。

 

题目背景

题目代码实现了一个简单的交易系统,每个用户在注册以后,就有 100000000MGC,用户之前可以互相交易。

 

题目要求

在泄露的数据库文件中,我们发现了有关flag1的sql语句: 因此这个题目第一步就是要能够注入出flag。

 

代码架构分析

代码使用了编写路由来写逻辑的形式,很像是python开发中的flask框架,这种方式实现起来会比较清晰,对开发者的逻辑处理有比较好的帮助。

代码入口文件

整个代码的入口文件是初始化环境加上所有路由的前置路由实现:
可以看到,默认全局是使用了google的图片验证码,由于是在国内调试,所以我们注释了这部分。
然后我们看一下基本的逻辑:

基本的登陆注册逻辑

这里我们可以只看post的部分,因为get的部分都是渲染前端模板的,post的部分是代码具体逻辑实现:

可以看到注册和登陆逻辑没有什么问题,都实现的比较严谨,如果底层没有出现问题,是可以保证稳定运行的。因为是sql注入,所以我们要尤其关注sql的部分,下面我们跟进底层去看下。

交易的逻辑实现

经过对代码的理解,这个系统其实是做了一个类似于当前流行的区块链的钱包,每个用户在第一次使用的时候,是可以生产一个唯一的钱包地址,如果别人要跟你进行交易,就需要知道你的钱包地址,否则是不能实现的,可以通过下面的图片来理解:

底层分析—发现漏洞

可以看到具体的数据库操作都封装到了$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测试一下:
可以看到如果是正常的绑定变量,确实可以完成功能。
但是如果是这样呢?
可以很明显的看到,这里是可以造成二次绑定变量的,所以按理来说,是可以造成注入的,构造如下:

找到利用点

既然我们知道了这个函数是可以造成注入的,那我们就要梳理一下这个漏洞的利用需要什么?

  1. 我们需要一个绑定两个参数的sql语句
  2. 我们需要两个参数都可控

经过简单的浏览,我们发现了可以利用的位置:
这个位置是在插入交易信息的位置,就是说我们只要能注册两个恶意用户名的用户,互相交易,就可以注入出flag。

我们继续将代码抽象出来单独构造:
经过一段时间的测试,发现可以构造如下:

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

先分别注册用户:

然后使用第一个账号,生成交易token,然后第二个账号验证token,
然后第二个账号向第一个账号发起转账:
然后我们就可以在交易记录中发现flag:

 

后记

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

(完)