利用 procfs 实现只读环境下的任意写到RCE
字数 1999 2025-08-23 18:31:09

利用 procfs 实现只读环境下的任意文件写入到 RCE 教学文档

1. 任意文件写入漏洞概述

任意文件写入漏洞是指攻击者能够控制写入文件的内容和位置的安全漏洞。这种漏洞通常出现在以下场景:

  • 用户上传文件时未对文件名和路径进行充分验证
  • 用户提交的内容被直接写入服务器文件系统
  • 使用用户提供的参数直接作为文件操作函数的参数

1.1 常见漏洞代码示例

PHP 示例

<?php
if (isset($_POST['filename']) && isset($_POST['content'])) {
    $filename = $_POST['filename'];
    $content = $_POST['content'];
    file_put_contents($filename, $content);
    echo "File written!";
} else {
    echo "No file data provided.";
}
?>

Python Flask 示例

@app.route('/upload', methods=['POST'])
def upload_file():
    filename = request.form.get('filename')
    content = request.form.get('content')
    with open(filename, 'w') as file:
        file.write(content)
    return 'File written successfully!'

Node.js 示例

app.post('/upload', (req, res) => {
    const { filename, content } = req.body;
    fs.writeFile(filename, content, (err) => {
        if (err) {
            return res.status(500).send('File write failed.');
        }
        res.send('File written successfully!');
    });
});

2. 可写环境下的 RCE 利用

在可写环境下,攻击者可以通过以下方式将任意文件写入转化为 RCE:

  • 写入 webshell 或恶意脚本
  • 覆盖 web 服务模板文件
  • 修改关键系统文件(如 /etc/passwd
  • 添加或修改定时任务(cronjob)
  • 写入 SSH 密钥文件(.ssh/authorized_keys

3. 只读环境下的挑战与解决方案

3.1 只读环境的限制

在启用系统级强化安全或严格控制权限的环境下:

  • 文件系统通常是只读的
  • 可写目录有限(如指定的 uploads 目录)
  • 可写目录下的文件通常不被解析执行

3.2 procfs 的特性

Linux 的 procfs(进程文件系统)是一个虚拟文件系统,挂载在 /proc 目录下:

  • 不依赖于磁盘存储,提供系统和进程的实时信息
  • /proc/[pid]/fd 目录包含进程打开的文件描述符
  • 这些文件描述符代表内存中的数据结构(如匿名管道、网络套接字)
  • 即使文件系统是只读的,这些文件描述符仍可进行写操作

4. 利用 libuv 实现 RCE

4.1 libuv 简介

libuv 是一个跨平台的异步 I/O 库,特点包括:

  • 提供高效的事件循环和非阻塞 I/O 操作
  • 被 Node.js、Python 的 pyuv 库、Julia 语言等广泛使用
  • 用于构建高并发网络服务、系统工具等

4.2 原理分析

libuv 的信号处理机制:

  1. uv__signal_event 函数从管道中读取 uv__signal_msg_t 结构体消息
  2. 结构体定义:
    typedef struct {
        uv_signal_t* handle;
        int signum;
    } uv__signal_msg_t;
    
  3. uv_signal_t 结构体包含回调函数指针:
    struct uv_signal_s {
        UV_HANDLE_FIELDS
        uv_signal_cb signal_cb;
        int signum;
        UV_SIGNAL_PRIVATE_FIELDS
    };
    
  4. 当事件循环读取消息时,如果 signum 匹配,则调用 handle->signal_cb

4.3 利用方法

通过以下步骤实现 RCE:

  1. 在内存中寻找可被解析为 uv_signal_s 结构体的数据段
  2. 确保该结构体中的 signal_cb 指向可控地址
  3. 构造 uv__signal_msg_t 消息:
    • handle 指向伪造的 uv_signal_s 结构体
    • signum 与结构体中的 signum 匹配
  4. 通过任意文件写入漏洞将构造的消息写入 procfs 暴露的管道文件描述符
  5. libuv 的事件处理器会调用 signal_cb 指向的地址

5. 利用条件

要实现这种攻击,需要满足以下条件:

  1. 可获得目标程序的 ELF 可执行文件
  2. 在目标程序中可以通过构造 ROP 链等方法实现 RCE
  3. 目标程序未开启 PIE 保护(地址固定)
  4. 存在可被解析为 uv_signal_s 结构体的数据段
  5. 任意文件写入漏洞可以写入任意二进制数据或能绕过格式限制

6. 详细实现步骤

6.1 寻找 RCE 入口地址

  1. 分析目标程序 ELF 文件
  2. 构造 ROP 链或其他利用方法
  3. 确定实现 RCE 的入口地址 entry_address

6.2 扫描内存寻找可利用数据段

  1. 扫描所有可读内存段(不限于可执行段)
  2. 寻找可被解析为 uv_signal_s 结构体的数据
  3. 确保其中的 signal_cb 值可被控制或指向 entry_address
  4. 记录该数据段的固定地址(因未开启 PIE)

6.3 构造恶意消息

  1. 创建 uv__signal_msg_t 结构:
    • handle 设置为找到的 uv_signal_s 地址
    • signum 设置为与结构体中 signum 匹配的值
  2. 确保消息格式符合预期

6.4 触发漏洞

  1. 通过任意文件写入漏洞将构造的消息写入:
    • /proc/[pid]/fd/[x](特定管道文件描述符)
  2. libuv 事件循环会处理该消息
  3. 调用伪造的 signal_cb 实现 RCE

7. 更广泛的攻击面

即使目标环境不使用 libuv,这种技术仍有应用场景:

  • 任何使用管道作为通信机制的应用程序
  • 通过 procfs 公开的管道文件描述符
  • 只要能控制写入管道的数据并影响程序行为

8. 防御措施

为防止此类攻击,建议采取以下措施:

  1. 对所有文件写入操作进行严格的输入验证
  2. 启用 PIE(位置无关可执行文件)保护
  3. 使用最小权限原则运行服务
  4. 限制对 /proc 文件系统的访问
  5. 对关键文件描述符进行权限控制
  6. 使用现代内存保护机制(如 ASLR)
利用 procfs 实现只读环境下的任意文件写入到 RCE 教学文档 1. 任意文件写入漏洞概述 任意文件写入漏洞是指攻击者能够控制写入文件的内容和位置的安全漏洞。这种漏洞通常出现在以下场景: 用户上传文件时未对文件名和路径进行充分验证 用户提交的内容被直接写入服务器文件系统 使用用户提供的参数直接作为文件操作函数的参数 1.1 常见漏洞代码示例 PHP 示例 Python Flask 示例 Node.js 示例 2. 可写环境下的 RCE 利用 在可写环境下,攻击者可以通过以下方式将任意文件写入转化为 RCE: 写入 webshell 或恶意脚本 覆盖 web 服务模板文件 修改关键系统文件(如 /etc/passwd ) 添加或修改定时任务(cronjob) 写入 SSH 密钥文件( .ssh/authorized_keys ) 3. 只读环境下的挑战与解决方案 3.1 只读环境的限制 在启用系统级强化安全或严格控制权限的环境下: 文件系统通常是只读的 可写目录有限(如指定的 uploads 目录) 可写目录下的文件通常不被解析执行 3.2 procfs 的特性 Linux 的 procfs(进程文件系统)是一个虚拟文件系统,挂载在 /proc 目录下: 不依赖于磁盘存储,提供系统和进程的实时信息 /proc/[pid]/fd 目录包含进程打开的文件描述符 这些文件描述符代表内存中的数据结构(如匿名管道、网络套接字) 即使文件系统是只读的,这些文件描述符仍可进行写操作 4. 利用 libuv 实现 RCE 4.1 libuv 简介 libuv 是一个跨平台的异步 I/O 库,特点包括: 提供高效的事件循环和非阻塞 I/O 操作 被 Node.js、Python 的 pyuv 库、Julia 语言等广泛使用 用于构建高并发网络服务、系统工具等 4.2 原理分析 libuv 的信号处理机制: uv__signal_event 函数从管道中读取 uv__signal_msg_t 结构体消息 结构体定义: uv_signal_t 结构体包含回调函数指针: 当事件循环读取消息时,如果 signum 匹配,则调用 handle->signal_cb 4.3 利用方法 通过以下步骤实现 RCE: 在内存中寻找可被解析为 uv_signal_s 结构体的数据段 确保该结构体中的 signal_cb 指向可控地址 构造 uv__signal_msg_t 消息: handle 指向伪造的 uv_signal_s 结构体 signum 与结构体中的 signum 匹配 通过任意文件写入漏洞将构造的消息写入 procfs 暴露的管道文件描述符 libuv 的事件处理器会调用 signal_cb 指向的地址 5. 利用条件 要实现这种攻击,需要满足以下条件: 可获得目标程序的 ELF 可执行文件 在目标程序中可以通过构造 ROP 链等方法实现 RCE 目标程序未开启 PIE 保护(地址固定) 存在可被解析为 uv_signal_s 结构体的数据段 任意文件写入漏洞可以写入任意二进制数据或能绕过格式限制 6. 详细实现步骤 6.1 寻找 RCE 入口地址 分析目标程序 ELF 文件 构造 ROP 链或其他利用方法 确定实现 RCE 的入口地址 entry_address 6.2 扫描内存寻找可利用数据段 扫描所有可读内存段(不限于可执行段) 寻找可被解析为 uv_signal_s 结构体的数据 确保其中的 signal_cb 值可被控制或指向 entry_address 记录该数据段的固定地址(因未开启 PIE) 6.3 构造恶意消息 创建 uv__signal_msg_t 结构: handle 设置为找到的 uv_signal_s 地址 signum 设置为与结构体中 signum 匹配的值 确保消息格式符合预期 6.4 触发漏洞 通过任意文件写入漏洞将构造的消息写入: /proc/[pid]/fd/[x] (特定管道文件描述符) libuv 事件循环会处理该消息 调用伪造的 signal_cb 实现 RCE 7. 更广泛的攻击面 即使目标环境不使用 libuv,这种技术仍有应用场景: 任何使用管道作为通信机制的应用程序 通过 procfs 公开的管道文件描述符 只要能控制写入管道的数据并影响程序行为 8. 防御措施 为防止此类攻击,建议采取以下措施: 对所有文件写入操作进行严格的输入验证 启用 PIE(位置无关可执行文件)保护 使用最小权限原则运行服务 限制对 /proc 文件系统的访问 对关键文件描述符进行权限控制 使用现代内存保护机制(如 ASLR)