1. 从零理解Warmup题目的核心逻辑第一次看到这道题的时候我也被绕晕了。题目给出了一个PHP文件source.php里面有个emmm类做安全检查。简单来说它就像个严格的保安只允许你访问白名单里的文件source.php和hint.php。但我们的目标是读取ffffllllaaaagggg文件里的flag这明显不在白名单里。这里的关键在于理解代码中的三个检查关卡第一个if检查参数是否存在且是字符串第二个if检查是否直接匹配白名单第三个关卡最复杂用mb_substr和strpos组合截取问号前的部分再检查我刚开始尝试直接用../../ffffllllaaaagggg结果被拦住了。后来发现这个检查机制有个特点它会把你的输入从开头截取到第一个问号处只检查这部分是否在白名单中。这就给了我们可乘之机。2. 关键函数mb_substr和strpos的妙用mb_substr($page, 0, mb_strpos($page.?, ?))这行代码是绕过的核心。我拆解一下它的工作原理mb_strpos($page.?, ?)会先在你的输入后面加个问号然后找第一个问号的位置mb_substr再从开头截取到这个位置比如你输入source.php?../../ffffllllaaaagggg加上问号变成source.php?../../ffffllllaaaagggg?第一个问号在source.php后面所以截取到的就是source.php这个机制意味着只要我们的输入以白名单文件名开头后面加问号就能通过检查。而问号后面的内容会被原封不动地传给include这就是我们的突破口。3. 构造有效payload的完整思路经过多次测试我总结出payload的构造步骤以白名单中的文件名开头source.php或hint.php加上问号作为分隔符问号后使用目录遍历符号(../)尝试访问上级目录最后指向目标文件ffffllllaaaagggg具体操作中我发现需要多次尝试../的数量。在我的测试中payloadsource.php?../../../../../ffffllllaaaagggg成功了。这里的五个../是为了确保能回溯到根目录因为不确定目标文件具体在哪层。4. 为什么PHP伪协议行不通最初我也想过用php://filter来读取文件内容测试了这样的payloadsource.php?php://filter/readconvert.base64-encode/resource../../../ffffllllaaaagggg但发现不行因为include最终接收到的完整路径是source.php?php://filter/readconvert.base64-encode/resource../../../ffffllllaaaaggggPHP会把整个字符串当作文件名处理而不会正确解析伪协议。这个问题困扰了我很久后来才明白这种场景下伪协议需要直接作为参数值不能跟在问号后面。5. 实战中的常见误区与解决方案在解这道题的过程中我踩过几个坑一开始没注意代码中的urldecode处理尝试用双重编码绕过结果发现没必要测试时../数量不够导致找不到目标文件忽略了hint.php的提示浪费时间去扫描目录过早放弃目录遍历的思路转而去尝试其他复杂方法最有效的解决方案还是仔细阅读代码逻辑特别是白名单检查的每个细节。理解mb_substr和strpos的配合方式后构造payload就水到渠成了。6. 同类题目的通用解法这类白名单绕过题目有几个常见变种但核心思路相通先确定白名单检查的严格程度寻找字符串处理函数可能存在的截断点测试是否能用../等符号进行目录遍历尝试用各种分隔符问号、井号等在实际CTF比赛中类似的题目可能会用不同的函数组合但只要我们掌握了字符串截断和目录遍历的基本原理就能快速找到突破口。7. 安全防护的改进建议从防御角度来说这个案例告诉我们单纯依靠字符串截断做白名单检查不安全应该对最终包含的文件路径做完整校验可以考虑realpath函数解析完整路径限制包含文件所在的基目录是更好的做法开发者如果使用类似代码建议改用更严格的白名单实现方式比如完全匹配整个路径而不是部分截取检查。8. 从这道题学到的调试技巧解这道题的过程中我总结出几个实用的调试方法在本地搭建测试环境修改代码加入var_dump输出中间结果使用Burp Suite拦截请求方便快速修改和重放payload分阶段测试先确保能绕过白名单再尝试包含目标文件记录每次测试的payload和响应方便回溯分析这些技巧在解决其他CTF题目时也同样适用特别是涉及Web安全的题型。养成系统化的测试习惯能大大提高解题效率。