命令注入成因小谈
字数 1960 2025-08-25 22:58:29
命令注入成因深度解析与防御指南
1. 命令注入概述
命令注入是一种安全漏洞,攻击者能够通过应用程序执行非预期的操作系统命令。这种漏洞通常发生在应用程序将用户输入直接传递给系统shell执行时。
2. 各语言命令执行实现机制
2.1 PHP命令执行实现
PHP提供了多个命令执行函数,底层实现主要基于popen:
-
主要函数:
system()exec()passthru()proc_open()shell_exec()popen()pcntl_exec()
-
调用链:
system/exec/passthru→php_exec_ex→php_exec→VCWD_POPENproc_open→ 调用execle/execl执行/bin/sh -cpopen和shell_exec直接调用VCWD_POPENpcntl_exec→ 调用execve执行/bin/sh -c
-
VCWD_POPEN定义:
#define VCWD_POPEN(command, type) virtual_popen(command, type) // 或 #define VCWD_POPEN(command, type) popen(command, type)
2.2 Java命令执行实现
Java通过Runtime.getRuntime().exec()执行命令:
-
调用链:
Runtime.getRuntime().exec()→ProcessBuilder→ProcessImpl- 使用系统调用
vfork,参数直接传递至execve
-
与PHP关键区别:
- 不调用
popen - 不传入
sh -c - 直接传递参数至
execve
- 不调用
2.3 Python命令执行实现
Python常用命令执行方式:
os.system()→ 对应execve("/bin/sh", ["sh", "-c", "id"]subprocess.check_output()→ 对应execve("/usr/bin/id"
3. 底层系统调用机制
3.1 Linux平台下的popen
- 实现路径:
libio/iopopen.c中的_IO_new_popen函数 - 调用链:
_IO_new_popen → _IO_new_proc_open → spawn_process → __posix_spawn - 最终调用:
sh -c
3.2 Windows平台下的CreateProcess
- 普通情况下不会引入命令注入风险
- 特殊情况:
- 当第一个参数为
.bat或.cmd文件时,会调用cmd.exe - 形成类似
cmd.exe /c "test.bat & id"的调用
- 当第一个参数为
4. Shell特性差异
/bin/sh通常指向/bin/dash(约150KB),而非bash(约1000KB)- 功能差异:
dash不支持function关键字- 不支持here string
- 不支持数组
- 不支持
++操作符
5. 跨平台差异与注意事项
5.1 Windows参数处理特性
- 处理命令行参数时会将
"中的内容拷贝为下一个参数 - 对
\"的处理存在缺陷 - 示例:
system('dir "\"&whoami"');- Linux:报错
- Windows:执行
dir和whoami两条命令
5.2 Java参数处理注意事项
-
错误示例:
String[] cmd={"whoami"whoami"};不会正确执行,
&作为whoami的参数传递 -
正确示例:
String[] cmd={"sth.bat"whoami"whoami"};会执行两次
whoami
6. 防御措施
6.1 通用防御策略
-
避免直接执行用户输入:
- 使用白名单验证输入
- 避免将用户输入直接传递给命令执行函数
-
参数转义:
- 使用专门的转义函数
- 注意不同平台的转义规则差异
-
最小权限原则:
- 以最低必要权限运行应用程序
6.2 语言特定防御
PHP防御:
- 使用
escapeshellarg()或escapeshellcmd() - 示例:
$safe_input = escapeshellarg($user_input); system("command " . $safe_input);
Java防御:
- 避免拼接命令字符串
- 使用
ProcessBuilder并明确指定参数 - 示例:
ProcessBuilder pb = new ProcessBuilder("/bin/ls", "-l", "dir"); Process p = pb.start();
Python防御:
- 使用
subprocess.run()并传递参数列表 - 示例:
subprocess.run(['ls', '-l'], check=True)
6.3 平台特定注意事项
-
Linux:
- 注意
sh和bash的特性差异 - 确保正确处理shell元字符
- 注意
-
Windows:
- 特别注意
.bat和.cmd文件的处理 - 正确处理引号和转义字符
- 特别注意
7. 测试与验证方法
7.1 使用strace跟踪系统调用
-
PHP示例:
strace -f -e trace=execve php test.php -
Java示例:
strace -f -e vfork,execve java Main -
Python示例:
strace -f -e vfork,execve python test.py
7.2 测试用例设计
-
基本注入测试:
; command&& command|| command| command> file
-
引号测试:
"command"'command'\"command\"
-
变量扩展测试:
$(command)`command`
-
特殊字符测试:
- 换行符
- 制表符
- 空字符