前言
虽然这道题被ai打烂了,但是复现的话还是认真分析一下整个流程
解法
首先看一下在哪里获取flag public\superadmin.php
1 2 3 4 5 6 7 8 9
| <?php require_once __DIR__ . '/../app/config/autoload.php'; Auth::init(); $user_types = config('user_types'); if (Auth::check() && Auth::type() < $user_types['admin']) { echo getenv('FLAG') ?: 'RCTF{test_flag}'; }else{ header('Location: /'); }
|
Auth::check() 是检查是否登录
Auth::type() 是查看用户的身份类型

配置文件中可以看到不同身份对应的数字
所以这里要求用户的身份类别对应的数字小于0,正常情况是2,所以我们继续分析系统是如何识别用户身份的
Auth::type()代码如下
1 2 3
| public static function type() { return self::$user['type']; }
|
那么user数组是怎么来的呢
app\middlewares\Auth.php存在如下代码
1 2 3 4 5 6 7 8 9 10
| public static function init() { if (session_status() === PHP_SESSION_NONE) { session_name(config('session.name')); session_start(); } if (isset($_SESSION['user_id'])) { self::$user = User::findById($_SESSION['user_id']); } }
|
他是调用User::findById函数,根据session中的user_id来的,继续跟进findById函数
1 2 3 4 5 6
| public static function findById($userId) { return DB::table('user') ->leftJoin('photo', 'user.background_photo_id', '=', 'photo.id') ->where('user.id', '=', $userId) ->first(); }
|
这里会返回id匹配的第一条记录,同时还返回了背景图的信息,问题就出在这里
https://github.com/php/php-src/issues/20300
我们继续跟进分析一下原理
first函数
1 2 3 4 5
| public function first() { $this->limit(1); $results = $this->get(); return !empty($results) ? $results[0] : null; }
|
是通过get方法来获取结果的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| public function get() { $sql = $this->buildSql(); $stmt = $this->db->prepare($sql); foreach ($this->bindings as $key => $value) { $type = is_int($value) ? SQLITE3_INTEGER : SQLITE3_TEXT; $stmt->bindValue($key, $value, $type); } $result = $stmt->execute(); $rows = []; while ($row = $result->fetchArray(SQLITE3_ASSOC)) { $rows[] = $row; } return $rows; }
|
问题出现在fetchArray(SQLITE3_ASSOC),根据php的官方文档

SQLITE3_ASSOC返回的数组是按列名索引的,加上这里的join的user表和photo表存在相同的type列,那么后者就会覆盖前者的数据,而我们是可以上传照片的,接下来分析一下photo表中的type是怎么获取的
直接从上传的文件的type中获取
那么我们的思路就是上传一张type为-1的图片,并设置为背景图,虽然这里的type”-1”是字符串类型,但是php的弱类型毕竟是满足“-1”< 0的,最后访问superadmin即可
总结
这题考察了SQLite3Result::fetchArray处理join两个存在相同列名的表时的数据覆盖问题