news 2026/9/24 19:25:00

Ping Ping Ping 命令注入实战:从空格绕过到关键字过滤的 CTF 通关思路

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Ping Ping Ping 命令注入实战:从空格绕过到关键字过滤的 CTF 通关思路

BUUCTF 平台上挂着的一道 [GXYCTF2019]Ping Ping Ping,算是我见过最适合入门 Web 命令注入的题目之一。界面简单到不能再简单,就是给你一个输入框让你填 IP,填完以后页面会模拟 ping 命令把结果回显出来。可就是这么个“玩具题”,把命令注入里最常出现的两个过滤点——空格过滤和关键字过滤——全塞进去了,而且过滤方式干净利落,没有多余的花活。整道题不用扫描器、不用猜路径,纯手工就能打穿,非常适合刚接触 CTF Web 方向、想搞明白“命令注入到底是怎么绕过过滤”的同学练手。

我这篇 write up 不打算只贴 payload,而是把每一步的思考过程、为什么这么绕、绕的时候踩了哪些坑都展开聊。毕竟命令注入这类题,背十个 payload 不如真正理解 shell 的解析机制一次,理解了以后,遇到再怎么变种的过滤你都能现场推出来。

1. 题目概览与考点拆解

1.1 题目长什么样

打开靶机,页面只有一个输入框,名字叫“Ping Ping Ping”,提示你输入 IP 地址。你输入127.0.0.1,页面就会返回类似这样的内容:

PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data. 64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.023 ms 64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.043 ms 64 bytes from 127.0.0.1: icmp_seq=3 ttl=64 time=0.037 ms 64 bytes from 127.0.0.1: icmp_seq=4 ttl=64 time=0.045 ms

一看这个交互形式,几乎可以断定后端是把用户输入拼到了系统命令里,最常见的就是这种:

$cmd = "ping -c 4 " . $_GET['ip']; exec($cmd, $output);

关键问题在于:这个拼接有没有过滤、过滤了什么、怎么绕过去。整道题的考点也就从这里展开。

1.2 核心考点拆解

这道题表面上是“Ping”,实际上考的是 Linux 命令注入,而且考得非常经典,核心知识点可以拆成四块:

第一,命令拼接与注入点识别。你要能判断出参数是直接被拼接到 shell 命令后面的,并且知道用;|&&&、换行符这些符号来闭合前面的“ping -c 4”部分,把自己想要的命令接上去。

第二,空格过滤绕过。后端对输入里的空格做了过滤,直接写cat index.php会被拦。Linux 的 shell 里有很多东西可以替代空格,比如$IFS${IFS}、Tab、重定向符<,这些都属于 shell 本身的语法特性,过滤脚本可能没考虑到。

第三,关键字过滤绕过flag这个字符串被直接屏蔽了,所以你不能在输入里出现flag。绕过思路包括变量拼接、通配符匹配、命令替换、base64 编码管道执行等。

第四,源码审计意识。这道题真正的突破口不是盲猜,而是先想办法把index.php的源码读出来,看清楚过滤规则,再针对性构造 payload。很多新手上来就硬试flag,被拦了十几次还在试,这就是没有“先看规则再打”的意识。后面我会详细说。

2. 信息收集:从正常 ping 到摸清过滤规则

2.1 正常参数与第一次注入尝试

先用127.0.0.1测一下,确认回显正常。接着试注入,常见的闭合符号就是;,因为分号在 Linux shell 里表示“前一条命令执行完,接着执行后一条”,最直观。

尝试:

?ip=127.0.0.1;ls

返回结果里除了 ping 的输出,还多了当前目录的文件列表:

flag.php index.php

这一步很关键,说明命令注入是通的,ls成功执行了。同时也确认了目标文件就叫flag.php,那么下一步自然是想办法把它读出来。

有人可能会问:为什么ls没被过滤?因为ls这条命令本身就是纯字母,没有空格,不触发过滤规则。但如果你想执行cat index.php,命令里带了空格,就立刻会被拦住。所以下一步的关键是绕过空格。

2.2 读取 index.php 源码,拿到过滤规则

面对一个未知过滤规则的题目,我的习惯是先读源码,再决定怎么打。既然需要读index.php,而空格被过滤了,那这里先直接用最常见的空格替代品$IFS$9

?ip=127.0.0.1;cat$IFS$9index.php

返回结果里会出现一段 PHP 代码,核心部分大概是:

<?php if(isset($_GET['ip'])){ $ip = $_GET['ip']; if(preg_match("/flag|\ /", $ip)){ die("fxck your flag!"); } $cmd = "ping -c 4 " . $ip; exec($cmd, $out); foreach($out as $k=>$v){ echo $v."<br>"; } } ?>

到这里,过滤规则一目了然:正则/flag|\ /只过滤了两样东西——字符串flag和空格。注意这个正则没有过滤分号、没有过滤$、没有过滤反引号、没有过滤|、没有过滤?、也没有过滤*,这就给后面留了特别多的操作空间。

2.3 过滤规则分析:空间比你想象的大

很多人看到“过滤了 flag 和空格”就懵了,觉得啥都干不了。实际上仔细拆一下这个正则,能发现它非常宽松

空格被过滤,意味着你不能直接写cat flag.php,但可以用 Linux shell 的多种语法特性来替代空格。

flag字符串被过滤,意味着输入里不能直接出现这四个字母,但 shell 变量拼接、通配符展开、命令替换这些机制都能让“flag 字符”在执行阶段才出现,而正则检查的是原始输入字符串,根本管不到执行阶段发生的事情。

更关键的是,这个正则没有过滤$,所以$IFS$()$9全都能用。没有过滤|,所以管道符也可以随便用。没有过滤?*,所以通配符这扇大门也敞开着。可以说出题人故意留了一道口子,考察的就是你有没有吃透 shell 的解析顺序。

3. 空格绕过:$IFS 系全家桶

3.1 为什么一个空格就能拦住大多数人

空格在 shell 里的作用是分隔参数,比如cat index.php,空格告诉 shell 左边是命令,右边是参数。如果过滤脚本把输入里的空格全部杀掉,那你写的命令在 shell 眼里就变成了catindex.php,系统会去找一个叫catindex.php的命令,自然执行失败。

但问题是,shell 本身并不只有空格这一种方式来分隔命令和参数。Linux 的 shell 语法里,$IFS(Internal Field Separator,内部字段分隔符)就是用来决定“怎么把一行字符串拆成多个字段”的,默认值包含空格、Tab 和换行。所以只要用$IFS顶替空格,shell 在解析时就会把它当成字段分隔符处理,效果完全一样。

3.2 空格替代方案横向对比

实测下来,可用的空格替代方案有下面几个,我整理成表格方便对比:

方案写法原理实测结果
$IFS$9cat$IFS$9index.php$IFS是默认分隔符,$9是第 9 个位置参数,通常为空,用来把$IFS和后面的文件名隔开推荐,最通用
${IFS}cat${IFS}index.php花括号明确变量名边界,防止 shell 把IFSindex当成变量名可用,效果稳定
单个$IFScat$IFSindex.php理论可行,但 shell 会尝试解析$IFSindex这个变量名,可能解析失败不推荐,容易翻车
Tab 字符cat%09index.phpTab 本身也是分隔符,URL 编码为%09部分环境可用,不通用
输入重定向cat<index.php<表示把文件内容作为标准输入传给 cat,不需要空格可用,但部分命令/场景受限
花括号展开{cat,index.php}bash 的花括号展开会把逗号解释成空格依赖 bash,dash 下会失效

这里重点说下为什么我推荐$IFS$9。单独用$IFS的时候,它后面紧跟的字符会跟IFS连在一起被 shell 尝试解析成一个新的变量名,比如cat$IFSindex.php会被解析成$IFSindex,而这个变量不存在,于是展开为空,最终命令就变成了catindex.php,直接报错。加一个$9(第 9 个位置参数),在没传参的情况下它展开为空字符串,作用就是隔断变量名边界,让 shell 先解析$IFS,再解析$9,最后得到正确的空格分隔效果。这个细节不理解的话,光背$IFS$9是背不明白的。

3.3 嵌套命令里的空格怎么处理

index.php只是第一步,后面读flag.php的时候,payload 里往往还带嵌套命令,比如反引号ls$()命令替换,里面也可能出现空格。这时候要注意:嵌套部分的空格同样要用$IFS$9替代

举个例子,如果你想执行:

cat $(ls)

里面的$(ls)没有空格没问题,但如果你想执行:

cat $(echo flag.php)

echoflag.php之间的空格也得换成$IFS$9,否则整条命令在用户输入层面就触发了空格过滤。这种嵌套场景最容易踩坑,因为有些新手在内外层之间混用了真实空格和$IFS$9,导致明明看起来没问题却一直报错。

4. 关键字过滤绕过:花式读取 flag.php

4.1 直接硬闯 flag.php 的下场

在读完源码、确认过滤规则之后,我一开始还是不死心,直接试了:

?ip=127.0.0.1;cat$IFS$9flag.php

返回:

fxck your flag!

果不其然,flag字符串被正则逮住了。这里要注意一个细节:过滤脚本匹配的是原始输入字符串,你输入里只要有flag这四个连续字符,不管你后面有没有.php、不管它是不是变量名的一部分,都会被拦。所以flag不能直接在输入里出现,得让它在 shell 执行阶段“拼出来”。

4.2 变量拼接法:让 flag 在执行时诞生

思路很简单:既然flag四个字母不能出现,那我就把它拆成两半,用两个变量分别存,执行的时候让 shell 把它们拼起来。

?ip=127.0.0.1;x=fl;y=ag.php;cat$IFS$9$x$y

shell 在执行这一行时,会先把$x展开成fl,把$y展开成ag.php,于是cat$IFS$9$x$y在解析后变成:

cat flag.php

而正则检查的是原始输入x=fl;y=ag.php;cat$IFS$9$x$y,里面确实没有flag这个连续字符串,所以顺利绕过。

这里还有个进阶写法,直接用变量名本身来拼:

?ip=127.0.0.1;a=g;cat$IFS$9fla$a.php

原理一样,fla$a在执行时变成flag。我个人的习惯是拆成两个变量xy,逻辑上更清晰,不容易写乱。实测下来这个方案最稳,也是我最后拿 flag 用的主方案。

4.3 通配符法:让 shell 自己去补全文件名

既然正则不让输入里出现flag,那就干脆不写全,用通配符让 shell 帮你补全。Linux 的 shell 在处理*?时,会先对文件名进行匹配,再把匹配到的结果传给命令。这个展开过程发生在命令执行阶段,原始输入里没有flag字符串。

单字符通配:

?ip=127.0.0.1;cat$IFS$9fla?.php

?匹配任意单个字符,fla?匹配到了flag,于是 shell 把cat fla?.php展开成cat flag.php再执行。也可以用*

?ip=127.0.0.1;cat$IFS$9f*.php

f*.php匹配flag.php,原理相同。但用*的时候要小心:如果目录下有多个以 f 开头且以 .php 结尾的文件,cat会把它们全部作为参数,比如index.php不会被匹配到,但假如以后遇到更复杂的目录结构,可能一次会 cat 多个文件。单字符?的匹配范围更可控,在文件名长度确定时我更推荐它。

4.4 命令替换配合 cat:动态获取文件名

命令替换的反引号和$()也可以用来绕关键字过滤。比如先用ls列出文件名,再把输出作为cat的参数:

?ip=127.0.0.1;cat$IFS$9`ls`

这个 payload 里ls的输出是flag.php index.php,于是命令在执行阶段变成:

cat flag.php index.php

两个文件都会被 cat 出来,那 flag 自然也就暴露了。$()写法的原理完全一样:

?ip=127.0.0.1;cat$IFS$9$(ls)

两个都实测可用。不过要注意,这种写法会把当前目录下所有文件都作为参数传给 cat,如果目录里文件很多、存在二进制文件,输出可能会很乱,但刷题场景下问题不大,反而能快速摸清楚目录里都有哪些文件。

4.5 base64 编码管道执行:把命令藏进编码里

如果碰到过滤更严格的题目,变量拼接和通配符可能都会失效,那就上大杀器:base64 编码 + 管道解码 + shell 执行。这题的过滤根本没有删除|、没有删除echo、没有删除base64,所以这条路也通。

先把要执行的命令cat flag.php编码:

echo "cat flag.php" | base64

得到:

Y2F0IGZsYWcucGhw

然后构造 payload:

?ip=127.0.0.1;echo$IFS$9Y2F0IGZsYWcucGhw|base64$IFS$9-d|sh

这条命令在 shell 里执行的过程是:先echo输出那串 base64 字符串,然后用管道传给base64 -d解码成cat flag.php,再通过管道交给sh去执行。整条原始输入里既没有空格(全用$IFS$9代替),也没有flag字符串,所以正则检查直接通过。

这个方案最大的好处是通用性极强。不管过滤了哪些关键字,只要没把echobase64、管道符、sh这些基础组件删掉,你就能把任意命令打包进 base64 里执行。后面遇到更难的命令注入题,这个思路依然非常能打。

4.6 其他偏方:换行、编码命令与心机技巧

除了上面几个主流方案,还有一些偏方可以扩展一下思路。

换行符%0a在某些题目里可以用来绕过对同一行内容的检查,比如:

?ip=127.0.0.1%0acat$IFS$9flag.php

如果后端只对$_GET['ip']做了正则检查,那换行符后面的内容也在同一个字符串里,通常也会被检查,这题实测会被拦。但在某些只检查第一行的题目里,这个技巧是能用的。

命令本身也可以用十六进制或八进制编码写在 shell 命令里,比如$'\x63\x61\x74'表示cat,不过这种写法在 CTF 的常规命令注入里用得少,而且依赖 bash 的 ANSI-C Quoting,不是所有 shell 都支持,这里就不展开细讲了。

还有一个比较“绕”的思路:用grep直接搜文件内容,不读整个文件:

?ip=127.0.0.1;grep$IFS$9'f'$IFS$9fla?.php

这个复杂度有点高,而且不一定比直接 cat 优雅,我只是列出来说明思路可以很多样。

4.7 最终拿到 flag

用变量拼接法或者通配符法,执行成功后页面会输出flag.php的源码:

<?php echo "flag{xxxxxxxxxxxxxxxxxxxx}"; ?>

flag{...}就是本题的最终答案。这里要提醒一句:不同平台、不同时间的靶机环境 flag 值不一样,你打的时候以自己的回显为准。我写这篇 write up 的时候,flag 就是直接写在flag.php里的,没有套娃、没有二次解密,读出来就是答案。

5. 常见问题与排查记录

5.1 复制 payload 却不生效的原因

刷题群里经常有人问“我和 wp 里写的一模一样,为什么我的就不出 flag”,排在最前面的原因通常是本地 shell 和 URL 层的展开问题

如果你在终端里用 curl 发包,直接写双引号包住 URL,那你本地 shell 会先把$IFS$9$x这些变量展开掉,发到服务器上的内容就已经不是 payload 本身了。正确做法是用单引号包住整个 URL,或者给$加反斜杠转义:

curl 'http://target/?ip=127.0.0.1;x=fl;y=ag.php;cat$IFS$9$x$y'

在浏览器里直接输入时,也要注意浏览器可能会自动把某些字符做 URL 编码,比如空格变成%20#变成锚点等。payload 里尽量别用到#,真要用的话得编码成%23

另外还有一个很隐蔽的坑:有些 payload 末尾会加#注释符来截断后面的内容,但在 URL 里#本身就是锚点标识符,会被浏览器拦下,属于“本地解析吃掉了一部分 payload”的典型问题。

5.2 环境和终端差异

BUUCTF 的容器不同批次可能跑在不同的基础镜像上,有的是 Ubuntu,有的是 Debian,有的可能是精简版 Alpine。不同发行版的默认 shell 不一样,有的是 bash,有的是 dash,对语法特性的支持也不一样。

实测下来,$IFS$9${IFS}在这些环境里都稳定可用,但{cat,index.php}这种花括号展开写法,在 dash 环境下是无效的,因为它是 bash 特有的语法。所以我写 payload 的时候,会优先选那些跨 shell 通用的写法,这也算是个实战经验:不是所有题目环境都是 bash,能通用的姿势才是好姿势。

base64 -d这个参数在 GNU coreutils、busybox 里基本都支持,但极少数精简环境可能只有base64 --decode或者没有 base64 命令,真遇到这种情况就换变量拼接或者通配符方案,没必要死磕。

5.3 信息回显问题

这一题的页面会把所有输出拼在 HTML 里,导致有些输出被<br>标签或者其他页面元素遮挡。尤其是读index.php源码的时候,如果你用浏览器直接看渲染后的页面,可能只看到一部分,感觉少了东西。解决办法有两个:一个是右键“查看网页源代码”,另一个是直接用 curl 在终端里看原始响应,后者更干净。

还有种情况是 ping 的输出比较长,flag 被挤到后面了,需要往下翻。我在调试时习惯给 payload 加一个;ls先确认当前目录,再决定下一步读哪个文件,省得对着输出猜。

5.4 建议的调试工具

做这类题我强烈建议用 Burp Suite 的 Repeater 而不是纯浏览器。Repeater 可以手动控制每一个字符的编码,还能直接看到服务器返回的原始响应包,排查编码问题特别方便。如果你不习惯用 Burp,那 curl 单引号发包也够用,配合-i参数看响应头,也能定位很多问题。

我自己调试时的习惯是:先用 curl 发最简 payload 确认命令能执行,比如?ip=127.0.0.1;ls,再逐步追加复杂度。每次只改一个变量,报错了也容易定位是空格问题、关键字问题还是编码问题。

6. 这套思路能延伸到哪些地方

6.1 常见过滤变体

Ping Ping Ping 的过滤规则已经算很温柔了,实战里你会遇到更多变体。比如过滤cat关键字,可以用tacmorelessheadtailnl等替代命令;过滤/路径分隔符,可以用cd进入目录后用相对路径;过滤$,那就只能靠通配符和重定向;过滤|,那就用;$()来做命令分隔;过滤字母,那就要考虑base64sh的管道执行,或者把命令写进变量再拼接执行。

这题的过滤规则是“输入里出现 flag 就拦截”,但很多题目是“输出里包含 flag 就拦”,这种就变成命令注入加回显过滤的组合题,常见解法是外带数据:把命令执行结果通过 DNS 查询、HTTP 请求发到你自己的服务器上。还有一种更恶心的,没有任何回显,只能靠盲注,比如sleep做时间盲注,或者用curl外带做带外盲注。

6.2 继续刷题方向

刷完这一道,如果想趁热打铁,可以按这个路径走:先做同题型的其他命令注入题,比如 DVWA 的 Command Injection 模块,巩固;|&&的闭合方式;然后找一些过滤规则更复杂的靶场,比如 SQLi Labs 之外专门练命令注入的题目;接着尝试无回显场景,自己搭一个只返回“执行成功/失败”的靶机,练盲注和外带。最后可以看看真实的 CVE 案例里命令注入是怎么被利用的,很多老漏洞的 payload 写法都能在这道题里找到影子。

这套“先试注入点、再读源码、后针对性绕过”的流程,本质上跟真实渗透测试里的操作习惯是一致的:信息收集永远是第一步,规则没摸清之前,不要盲目打。

我个人刷了这么多命令注入题,最大的体会是:不要背 payload,而是理解 shell 的解析顺序。为什么$IFS$9有用?因为 shell 先做变量展开再做命令解析。为什么变量拼接能绕过?因为正则检查的是静态字符串,而 shell 执行的是动态解析后的结果。这些机制搞懂了,就算考题把过滤规则换成一百种不同的写法,你也能顺着 shell 的特性现场推出来。信息收集的耐心、对 shell 机制的敏感度,才是这类题真正的收获。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/24 19:24:57

Java Web智慧医疗平台源码拆解:SpringBoot+Vue前后端分离实战

1. 医疗信息化项目&#xff0c;为什么值得你关注这套源码医疗行业的信息化改造一直是Java后端开发者绕不开的业务场景。早年间我做过的医疗项目大多是SSH框架配合JSP页面&#xff0c;前后端耦合严重&#xff0c;改个字段要同时动三四个文件。这两年随着SpringBoot和Vue这类前后…

作者头像 李华
网站建设 2026/9/24 19:23:54

PyTorch MLP实战:Iris数据集分类与常见坑点详解

简介&#xff1a;这是一份面向机器学习初学者、高校学生及神经网络入门者的Python实践资源&#xff0c;围绕鸢尾花&#xff08;Iris&#xff09;花卉数据集&#xff0c;使用numpy从零搭建全连接神经网络&#xff08;MLP&#xff09;完成图像分类&#xff0c;全程不依赖高级深度…

作者头像 李华
网站建设 2026/9/24 19:23:54

video-caption-cnn:基于CNN的视频描述生成实战与部署指南

1. 从标题拆解 video-caption-cnn 到底在做什么第一次看到video-caption-cnn这个项目名&#xff0c;很多人会下意识觉得它就是个“给视频配字幕”的玩具。但真做过视频理解方向的人都知道&#xff0c;视频描述生成&#xff08;Video Captioning&#xff09;是计算机视觉与自然语…

作者头像 李华
网站建设 2026/9/24 19:22:31

fastDFS从零安装到Nginx集成:海量小文件存储实战指南

如果你是因为“图片越存越多、单机磁盘快扛不住”才搜到 fastDFS&#xff0c;那我猜你现在的心情和我当年差不多。我第一次被逼到来找 fastDFS&#xff0c;是因为内部系统每天产生几万张图片和短视频片段&#xff0c;NFS 挂载盘越来越慢&#xff0c;目录一多读写就开始卡&#…

作者头像 李华
网站建设 2026/9/24 19:20:11

防红系统源码部署与二开实战:域名调度、安全加固与常见坑

简介&#xff1a;梦幻防红cos系统&#xff08;后台版&#xff09;是一款面向网站运营者的DDoS防御辅助工具&#xff0c;无需深入理解复杂防护技术&#xff0c;即可在后台自定义防红接口&#xff0c;为流量较大、易遭受攻击的网站提供简洁的防护方案。资源包共82个文件&#xff…

作者头像 李华
网站建设 2026/9/24 19:18:58

Presto ANALYZE 语句详解:表与列统计信息收集指南

Presto ANALYZE 语句详解&#xff1a;表与列统计信息收集指南 【免费下载链接】presto The official home of the Presto distributed SQL query engine for big data 项目地址: https://gitcode.com/gh_mirrors/pre/presto ANALYZE 是 Presto 中用于收集表和列统计信息…

作者头像 李华