:2026-02-11 18:06 点击:7
在CTF(Capture The Flag)的征途中,Web方向往往是参赛者挑战逻辑思维、编码能力和安全知识的重要战场,BugkuCTF作为国内知名的CTF平台,其Web题目设计兼具趣味性和挑战性,本文将详细解析BugkuCTF中的一道经典Web题目——Web3,通过这道题目,我们将一同探索PHP语言的某些特殊特性,并学习如何进行基础的代码审计,最终成功获取flag。 初探
我们打开BugkuCTF平台的Web3题目,这类题目会提供一个URL,访问后我们能看到一个简单的页面,或者是一段源代码,对于Web3,我们假设访问后直接展示了一段PHP代码(如果题目环境不同,可能需要先进行信息收集,如查看源码、扫描目录等,但核心思路类似)。
假设我们看到的代码如下(这是一个常见的Web3题目类型,可能略有差异,但核心逻辑一致):
<?php
$flag = 'flag{xxxxxxxxxxxxxxxx}';
if (isset($_GET['v1']) && isset($_GET['v2'])) {
if ($_GET['v1'] !== $_GET['v2'] && md5($_GET['v1']) === md5($_GET['v2'])) {
echo $flag;
} else {
echo "NO!";
}
} else {
echo "NO!";
}
?>
拿到代码后,我们的第一步是仔细阅读和理解代码的逻辑。
变量初始化与存在性检查:
$flag = 'flag{xxxxxxxxxxxxxxxx}';:定义了一个变量$flag,这是我们最终的目标。if (isset($_GET['v1']) && isset($_GET['v2'])):检查URL参数v1和v2是否都被设置,如果其中一个或两个都没有设置,则输出"NO!"。核心条件判断:
if ($_GET['v1'] !== $_GET['v2'] && md5($_GET['v1']) === md5($_GET['v2'])):这是整个题目的关键,包含两个条件,必须同时满足:$_GET['v1'] !== $_GET['v2']:要求v1和v2的值不完全相等(注意是,不仅比较值,还比较类型)。md5($_GET['v1']) === md5($_GET['v2']):要求v1和v2经过md5()哈希计算后的值完全相等(同样,严格比较)。我们的目标是构造v1和v2的值,使得上述两个条件同时成立。
v1和v2不能完全一样,这很容易满足,比如v1=a,v2=b。md5(v1)等于md5(v2),这就比较棘手了,因为MD5是单向哈希函数,通常不同的输入会产生不同的哈希值(碰撞概率极低)。是否存在不同的字符串,它们的MD5值是相同的呢?答案是肯定的,这涉及到MD5的碰撞问题,虽然MD5碰撞在实际攻击中很难构造,但在CTF题目中,出题人往往会利用一些已知的MD5碰撞字符串对,或者利用PHP语言本身的特性。
PHP MD5特性利用:
在PHP中,md5()函数处理字符串时,有一些特殊情况需要注意:
数组哈希:当md5()函数的参数是数组时,它会返回一个固定的字符串:NULL,这是因为PHP的md5()函数期望一个字符串参数,如果传入数组,它会返回NULL。
md5(array()) 返回 NULLmd5(array(1,2)) 也返回 NULL特定字符串的MD5值:存在一些不同的字符串,它们的MD5值都是以0e开头的数字字符串,在PHP中,0e开头的数字字符串会被科学计数法解析为0。
md5('240610708') = 0e462097431906509019562988736854md5('QNKCDZO') = 0e8304004519934940580242199033910e462097431906509019562988736854 和 0e830400451993494058024219903391 都被视为 0,因此它们相等。根据上述分析,我们可以构造两种主要的Payload:
利用数组哈希
v1为数组,v2也为数组,但内容不同。v1[]=1&v2[]=2isset($_GET['v1']) 和 isset($_GET['v2']) 都为真,因为参数存在。$_GET['v1'] !== $_GET['v2']:数组[1]和[2]显然不相等,满足条件一。md5($_GET['v1']) 和 md5($_GET['v2']):因为传入的是数组,所以都返回NULL,NULL === NULL为真,满足条件二。http://[题目URL]/index.php?v1[]=1&v2[]=2 即可得到flag。利用特定字符串的MD5碰撞(0e前缀)
0e开头。QNKCDZO 和 240610708(还有其他组合,如 s878926199a 和 s155964671a 等)。v1=QNKCDZO&v2=240610708isset($_GET['v1']) 和 isset($_GET['v2']) 都为真。$_GET['v1'] !== $_GET['v2']:'QNKCDZO' 和 '240610708' 是两个不同的字符串,满足条件一。md5('QNKCDZO') = 0e830400451993494058024219903391md5('240610708') = 0e4620974319065090195629887368540e830400451993494058024219903391 === 0e462097431906509019562988736854:在PHP中,这两个字符串都被解释为0,因此比较结果为真,满足条件二。http://[题目URL]/index.php?v1=QNKCDZO&v2=240610708 也可以得到flag。
将上述任一Payload输入到题目URL中,如果页面输出flag{xxxxxxxxxxxxxxxx},则说明我们的解法是正确的。
通过这道BugkuCTF Web3题目,我们学到了:
md5())时的特殊行为,这些特性往往成为CTF题目的突破口。CTF学习是一个不断积累和实践的过程,Web3这类题目虽然简单,但蕴含的基础知识非常重要,希望大家通过本文的解析,能够对PHP特性和代码审计有更深的理解,在CTF的道路上越走越远!
本文由用户投稿上传,若侵权请提供版权资料并联系删除!