php无文件攻击(三) - php-fpm文件描述符泄露
字数 1213 2025-08-10 08:29:06

PHP-FPM 文件描述符泄露漏洞分析与利用

一、漏洞原理

1.1 文件描述符继承机制

在Linux系统中,进程使用文件描述符(FD)来管理打开的文件。当使用system()等函数执行外部程序时,PHP-FPM存在以下行为:

  1. PHP-FPM没有使用FD_CLOEXEC标志处理文件描述符
  2. 导致fork()出来的子进程会继承PHP-FPM进程的所有FD
  3. 继承的FD包括PHP-FPM监听的9000端口的socket

1.2 关键问题

子进程继承了父进程(php-fpm worker)的socket FD后,可以直接使用accept()函数从该socket接受连接,从而可能实现对PHP-FPM的控制。

二、漏洞验证

2.1 基础验证

  1. 创建测试PHP文件:
<?php 
system("sleep 60");
?>
  1. 检查sleep进程继承的FD:
ls -l /proc/<sleep_pid>/fd/

可以看到sleep进程继承了php-fpm的socket FD(可能是5号FD,具体值可能变化)

2.2 利用验证

  1. 创建测试PHP文件(index.php):
<?php
system("/tmp/test");
?>
  1. 创建C程序(/tmp/test.c):
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>

int main(int argc, char *argv[]){
    int sockfd, newsockfd, clilen;
    struct sockaddr_in cli_addr;
    clilen = sizeof(cli_addr);
    
    // 直接使用继承的FD作为socket句柄
    sockfd = 5; // 可能需要调整
    
    // accept会阻塞,直到接收到连接
    newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);
    system("/bin/touch /tmp/lol");
    return 0;
}
  1. 编译并测试:
gcc -o /tmp/test /tmp/test.c

访问index.php后,程序会被阻塞。此时访问任意PHP-FPM处理的文件,test进程会接收到socket连接并执行system命令。

三、完整利用思路

3.1 利用步骤

  1. PHP脚本运行后先删除自身
  2. PHP脚本创建一个socket,并获取FD号
  3. PHP脚本调用system()建立一个子进程
  4. 子进程attach到父进程(php-fpm worker)
  5. 向父进程中注入复制FD的shellcode(使用dup2命令)
  6. 子进程恢复worker进程状态后detach并退出
  7. PHP代码中的socket即可操作php-fpm的socket

3.2 关键代码实现

  1. PHP部分(t3.php):
<?php
sleep(10);
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
sleep(10);
  1. 观察FD变化:
  • 原本worker只有0、1、2、5四个FD
  • 新建socket后会多出一个FD(通常是3号)

四、Webshell实现

  1. 解析FastCGI请求
  2. 如果请求包含特定指令,拦截并执行
  3. 否则正常转发到9000端口让正常worker处理

五、漏洞限制

  1. 环境限制:

    • 仅限Linux系统
    • PHP版本限制:
      • 5.x < 5.6.35
      • 7.0.x < 7.0.29
      • 7.1.x < 7.1.16
      • 7.2.x < 7.2.4
  2. 攻击限制:

    • PHP-FPM worker进程众多,请求被污染worker接收的概率低
    • PHP-FPM socket FD号不固定(需要遍历)

六、防御建议

  1. 升级PHP到安全版本
  2. 对PHP-FPM配置进行安全加固
  3. 限制PHP执行系统命令的能力
  4. 使用FD_CLOEXEC标志处理敏感文件描述符

七、扩展思考

  1. 更优雅的shellcode注入方式
  2. 自动化FD号探测
  3. 提高攻击成功率的worker选择策略

八、参考资源

  1. 原始漏洞分析文章
  2. PHP官方安全公告
  3. 相关GitHub PoC代码(完善后)
PHP-FPM 文件描述符泄露漏洞分析与利用 一、漏洞原理 1.1 文件描述符继承机制 在Linux系统中,进程使用文件描述符(FD)来管理打开的文件。当使用 system() 等函数执行外部程序时,PHP-FPM存在以下行为: PHP-FPM没有使用 FD_CLOEXEC 标志处理文件描述符 导致 fork() 出来的子进程会继承PHP-FPM进程的所有FD 继承的FD包括PHP-FPM监听的9000端口的socket 1.2 关键问题 子进程继承了父进程(php-fpm worker)的socket FD后,可以直接使用 accept() 函数从该socket接受连接,从而可能实现对PHP-FPM的控制。 二、漏洞验证 2.1 基础验证 创建测试PHP文件: 检查sleep进程继承的FD: 可以看到sleep进程继承了php-fpm的socket FD(可能是5号FD,具体值可能变化) 2.2 利用验证 创建测试PHP文件(index.php): 创建C程序(/tmp/test.c): 编译并测试: 访问index.php后,程序会被阻塞。此时访问任意PHP-FPM处理的文件,test进程会接收到socket连接并执行system命令。 三、完整利用思路 3.1 利用步骤 PHP脚本运行后先删除自身 PHP脚本创建一个socket,并获取FD号 PHP脚本调用system()建立一个子进程 子进程attach到父进程(php-fpm worker) 向父进程中注入复制FD的shellcode(使用dup2命令) 子进程恢复worker进程状态后detach并退出 PHP代码中的socket即可操作php-fpm的socket 3.2 关键代码实现 PHP部分(t3.php): 观察FD变化: 原本worker只有0、1、2、5四个FD 新建socket后会多出一个FD(通常是3号) 四、Webshell实现 解析FastCGI请求 如果请求包含特定指令,拦截并执行 否则正常转发到9000端口让正常worker处理 五、漏洞限制 环境限制: 仅限Linux系统 PHP版本限制: 5.x < 5.6.35 7.0.x < 7.0.29 7.1.x < 7.1.16 7.2.x < 7.2.4 攻击限制: PHP-FPM worker进程众多,请求被污染worker接收的概率低 PHP-FPM socket FD号不固定(需要遍历) 六、防御建议 升级PHP到安全版本 对PHP-FPM配置进行安全加固 限制PHP执行系统命令的能力 使用 FD_CLOEXEC 标志处理敏感文件描述符 七、扩展思考 更优雅的shellcode注入方式 自动化FD号探测 提高攻击成功率的worker选择策略 八、参考资源 原始漏洞分析文章 PHP官方安全公告 相关GitHub PoC代码(完善后)