RCE基础入门
RCE基础入门
RCE的本质
RCE(Remote Code Execution,远程代码执行)是一种非常严重的安全漏洞,允许攻击者在目标服务器上执行任意代码,从而获取系统控制权、窃取敏感信息、横向移动甚至完全接管服务器。
RCE的本质是:攻击者通过构造恶意输入,使应用程序在服务器上执行了这段输入作为代码。
这通常意味着开发者把用户输入拼接进 eval、exec、system、popen、shell_exec 等危险函数中,或者调用命令行/解释器时未正确处理输入。
RCE常见触发点
PHP中的危险函数
代码执行函数
eval()函数
eval()函数将字符串作为PHP代码执行。这是最危险的PHP函数之一,因为它可以执行任意PHP代码。
函数原型:eval(string $code): mixed
参数说明:
- $code:要执行的PHP代码字符串,必须以分号结尾
利用示例:
1 |
|
攻击Payload:
1 | http://example.com/vuln.php?cmd=system('ls'); |
原理说明:eval函数会将传入的字符串当作PHP代码执行,攻击者可以传入system(‘ls’)来执行系统命令,或者传入include等函数来读取文件。
assert()函数
assert()函数用于断言检查,在PHP 8.0之前,如果传入的是字符串,该字符串会被当作PHP代码执行。
函数原型:assert(mixed $assertion, Throwable|string $description = null): bool
参数说明:
- $assertion:断言表达式,如果是字符串则在PHP 8.0前会被当作代码执行
- $description:可选的描述信息
利用示例:
1 |
|
攻击Payload:
1 | http://example.com/vuln.php?code=system('ls') |
原理说明:在PHP 8.0之前,assert函数会将字符串参数当作PHP代码执行,类似于eval。但assert只能执行一个表达式,不能执行多条语句。
preg_replace()函数/e模式
preg_replace()函数执行正则表达式的搜索和替换,在PHP 7.3之前支持/e修饰符,该修饰符会将替换字符串作为PHP代码执行。
函数原型:preg_replace(string|array $pattern, string|array $replacement, string|array $subject, int $limit = -1, int &$count = null): string|array|null
参数说明:
- $pattern:正则表达式模式
- $replacement:替换字符串,使用/e修饰符时会被当作PHP代码
- $subject:要搜索替换的字符串或数组
- $limit:最大替换次数
- $count:实际替换次数
利用示例:
1 |
|
攻击Payload:
1 | http://example.com/vuln.php?\S*={${phpinfo()}} |
原理说明:/e修饰符会将replacement字符串当作PHP代码执行,\1是后向引用,表示第一个匹配组。通过构造{${phpinfo()}},利用PHP的可变变量特性,先执行phpinfo()函数。
命令执行函数
system()函数
system()函数执行外部程序并显示输出结果。这是最常见的命令执行函数。
函数原型:system(string $command, int &$result_code = null): string|false
参数说明:
- $command:要执行的命令
- $result_code:可选参数,用于接收命令的返回状态码
利用示例:
1 |
|
攻击Payload:
1 | http://example.com/vuln.php?cmd=ls |
原理说明:system函数会执行传入的shell命令,并将输出直接显示在页面上。攻击者可以执行任意系统命令。
exec()函数
exec()函数执行外部程序,但默认不显示输出,需要通过第二个参数获取。
函数原型:exec(string $command, array &$output = null, int &$result_code = null): string|false
参数说明:
- $command:要执行的命令
- $output:可选参数,用于接收命令的输出(每行一个元素)
- $result_code:可选参数,用于接收命令的返回状态码
利用示例:
1 |
|
攻击Payload:
1 | http://example.com/vuln.php?cmd=ls |
原理说明:exec函数执行命令但不会直接输出结果,需要通过第二个参数获取输出数组。第三个参数可以获取命令的退出状态码。
passthru()函数
passthru()函数执行外部程序并显示原始输出,特别适合执行二进制数据。
函数原型:passthru(string $command, int &$result_code = null): void
参数说明:
- $command:要执行的命令
- $result_code:可选参数,用于接收命令的返回状态码
利用示例:
1 |
|
攻击Payload:
1 | http://example.com/vuln.php?cmd=ls |
原理说明:passthru函数会直接将命令的原始输出传递给浏览器,不会进行任何处理,适合输出二进制数据(如图片)。
shell_exec()函数
shell_exec()函数通过shell执行命令并将完整的输出以字符串方式返回。
函数原型:shell_exec(string $command): string|false|null
参数说明:
- $command:要执行的命令
利用示例:
1 |
|
攻击Payload:
1 | http://example.com/vuln.php?cmd=ls |
原理说明:shell_exec函数执行命令并返回所有输出作为字符串,但不会自动显示,需要手动echo输出。
反引号运算符
反引号运算符(`)是PHP的执行运算符,与shell_exec()函数功能完全相同。
利用示例:
1 |
|
攻击Payload:
1 | http://example.com/vuln.php?cmd=ls |
原理说明:反引号中的内容会被当作shell命令执行,执行结果会被返回。这是PHP的运算符,不是函数,所以不需要括号。
proc_open()函数
proc_open()函数执行命令并打开用于输入/输出的文件指针,提供更强大的控制能力。
函数原型:proc_open(array|string $command, array $descriptor_spec, array &$pipes, ?string $cwd = null, ?array $env_vars = null, ?array $options = null): resource|false
参数说明:
- $command:要执行的命令
- $descriptor_spec:描述符数组,定义如何处理标准输入、输出、错误
- $pipes:返回的管道数组
- $cwd:工作目录
- $env_vars:环境变量
- $options:其他选项
利用示例:
1 |
|
攻击Payload:
1 | http://example.com/vuln.php?cmd=ls |
原理说明:proc_open提供了比其他函数更强大的控制能力,可以分别控制标准输入、输出和错误流。descriptor_spec数组定义了如何处理这些流。
popen()函数
popen()函数打开进程文件指针,可以用于读取或写入命令的输入输出。
函数原型:popen(string $command, string $mode): resource|false
参数说明:
- $command:要执行的命令
- $mode:打开模式,’r’表示读取,’w’表示写入
利用示例:
1 |
|
攻击Payload:
1 | http://example.com/vuln.php?cmd=ls |
原理说明:popen函数会执行命令并返回一个文件指针,可以像操作文件一样读取命令的输出或写入命令的输入。
回调函数后门
call_user_func()函数
call_user_func()函数把第一个参数作为回调函数调用,这是最基础的回调后门。
函数原型:call_user_func(callable $callback, mixed ...$args): mixed
参数说明:
- $callback:要调用的回调函数
- $args:传递给回调函数的参数
利用示例:
1 |
|
攻击Payload:
1 | http://example.com/vuln.php?pass=system('ls') |
原理说明:call_user_func会将第一个参数’assert’作为函数名,第二个参数作为参数传递给assert函数。这样就可以通过pass参数执行任意PHP代码。
array_filter()函数
array_filter()函数使用回调函数过滤数组的元素,可以构造回调后门。
函数原型:array_filter(array $array, ?callable $callback = null, int $mode = 0): array
参数说明:
- $array:要过滤的数组
- $callback:回调函数,用于判断元素是否保留
- $mode:过滤模式
利用示例:
1 |
|
攻击Payload:
1 | POST /vuln.php |
原理说明:array_filter会遍历数组,将每个元素传递给回调函数。这里回调函数是base64_decode(‘YXNzZXJ0’)=’assert’,数组元素是system(‘ls’),最终执行assert(‘system(‘ls’)’)。
array_map()函数
array_map()函数为数组的每个元素应用回调函数,与array_filter类似。
函数原型:array_map(?callable $callback, array $array, array ...$arrays): array
参数说明:
- $callback:回调函数
- $array:要处理的数组
- $arrays:其他数组(可选)
利用示例:
1 |
|
攻击Payload:
1 | POST /vuln.php |
原理说明:array_map会将回调函数应用到数组的每个元素,与array_filter类似,但会返回处理后的数组。
uasort()函数
uasort()函数使用用户定义的比较函数对数组进行排序并保持索引关联,需要PHP 5.4.8+版本。
函数原型:uasort(array &$array, callable $callback): bool
参数说明:
- $array:要排序的数组(引用传递)
- $callback:比较函数,接受两个参数
利用示例:
1 |
|
攻击Payload:
1 | POST /vuln.php |
原理说明:uasort的比较函数接受两个参数,在PHP 5.4.8+中,assert函数支持两个参数(第二个参数是描述信息),所以可以作为比较函数使用。
uksort()函数
uksort()函数使用用户自定义的比较函数对数组中的键名进行排序,需要PHP 5.4.8+版本。
函数原型:uksort(array &$array, callable $callback): bool
参数说明:
- $array:要排序的数组(引用传递)
- $callback:比较函数,接受两个参数
利用示例:
1 |
|
攻击Payload:
1 | POST /vuln.php |
原理说明:uksort会对数组的键进行比较,回调函数assert会被调用,参数是两个键名。由于assert可以执行代码,所以可以达到RCE目的。
usort()函数
usort()函数使用用户自定义的比较函数对数组中的值进行排序,需要PHP 5.4.8+版本。
函数原型:usort(array &$array, callable $callback): bool
参数说明:
- $array:要排序的数组(引用传递)
- $callback:比较函数,接受两个参数
利用示例:
1 |
|
攻击Payload:
1 | POST /vuln.php |
原理说明:usort会对数组的值进行比较,回调函数assert会被调用。虽然assert不是真正的比较函数,但在PHP 5.4.8+中仍然可以执行。
array_walk()函数
array_walk()函数使用用户自定义函数对数组中的每个元素做回调处理,支持三个参数的回调。
函数原型:array_walk(array|object &$array, callable $callback, mixed $arg = null): bool
参数说明:
- $array:要处理的数组(引用传递)
- $callback:回调函数,接受两个或三个参数
- $arg:传递给回调函数的第三个参数
利用示例:
1 |
|
攻击Payload:
1 | POST /vuln.php |
原理说明:array_walk的回调函数接受三个参数(值、键、额外参数)。这里使用preg_replace作为回调,利用/e模式执行代码。|.*|e是正则表达式,匹配所有内容并执行。
Python中的危险函数
eval(user_input)- 执行Python代码exec(user_input)- 执行Python代码os.system(user_input)- 执行系统命令subprocess.Popen(user_input, shell=True)- 执行系统命令
Node.js中的危险函数
eval(req.query.input)- 执行JavaScript代码child_process.exec(req.query.cmd)- 执行系统命令- 模板注入(EJS、Handlebars等)
漏洞代码示例
基础eval漏洞
1 |
|
system命令执行
1 |
|
preg_replace e模式(已废弃)
1 |
|
回调后门
1 |
|
RCE防护建议
输入验证
- 对所有用户输入进行严格验证
- 使用白名单而非黑名单
- 验证输入类型、长度、格式
避免危险函数
- 尽量避免使用eval、assert等代码执行函数
- 避免直接拼接用户输入到命令执行函数
- 使用参数化查询代替字符串拼接
使用安全函数
- 使用
escapeshellarg()转义命令参数 - 使用
escapeshellcmd()转义整个命令 - 使用
htmlspecialchars()转义HTML输出
权限控制
- 使用最小权限原则运行应用
- 禁用危险函数(disable_functions)
- 使用open_basedir限制文件访问
WAF防护
- 部署Web应用防火墙
- 配置规则拦截恶意请求
- 定期更新WAF规则库

