PHP代码审计-某0Day分析
字数 1438 2025-08-23 18:31:24

PHP代码审计:某0Day漏洞分析与利用教学文档

漏洞概述

本教学文档详细分析了一个PHP系统中的0Day漏洞,该漏洞存在于后台模块市场的文件上传功能中,通过精心构造的恶意模块包可以实现目录穿越和任意文件上传,最终导致Getshell。

漏洞环境

  • 漏洞位置:后台模块市场的上传功能
  • 受影响文件:app/admin/controller/Module.php
  • 关键类:app/admin/library/module中的相关方法
  • 漏洞类型:文件上传漏洞(目录穿越)

漏洞定位与分析

1. 漏洞入口点

漏洞位于后台的模块市场上传功能,具体在Module.php控制器的upload方法中:

public function upload(): void {
    AdminLog::setTitle(__('Upload install module'));
    $file = $this->request->get("file/s", '');
    $token = $this->request->get("token/s", '');
    
    if (!$file) $this->error(__('Parameter error'));
    if (!$token) $this->error(__('Please login to the official website account first'));
    
    $info = [];
    try {
        $info = Manage::instance()->upload($token, $file);
    } catch (Exception $e) {
        $this->error(__($e->getMessage()), $e->getData(), $e->getCode());
    } catch (Throwable $e) {
        $this->error(__($e->getMessage()));
    }
    $this->success('', ['info' => $info]);
}

2. 关键调用链

上传流程的调用链如下:

  1. Module.php中的upload()方法
  2. 调用Manage::instance()->upload($token, $file)
  3. 进入app/admin/library/module中的upload方法

3. 核心漏洞代码分析

app/admin/library/module中的upload方法存在以下关键处理逻辑:

public function upload(string $token, string $file): array {
    $file = Filesystem::fsFit(root_path() . 'public' . $file);
    if (!is_file($file)) {
        throw new Exception('Zip file not found');
    }
    
    // 文件移动
    $copyTo = $this->installDir . 'uploadTemp' . date('YmdHis') . '.zip';
    copy($file, $copyTo);
    
    // 解压
    $copyToDir = Filesystem::unzip($copyTo);
    $copyToDir .= DIRECTORY_SEPARATOR;
    
    // 删除zip
    @unlink($file);
    @unlink($copyTo);
    
    // 读取ini
    $info = Server::getIni($copyToDir);
    if (empty($info['uid'])) {
        Filesystem::delDir($copyToDir);
        throw new Exception('Basic configuration of the Module is incomplete');
    }
    
    // ... 省略部分检查代码 ...
    
    // 获取ini中的uid
    $this->uid = $info['uid'];
    // 把原本解压的文件目录改名为modules/ + info.ini中的uid/
    $this->modulesDir = $this->installDir . $info['uid'] . DIRECTORY_SEPARATOR;
    
    // ... 省略部分代码 ...
    
    // 放置新模块
    rename($copyToDir, $this->modulesDir);
    
    // 检查新包是否完整
    $this->checkPackage();
    
    // 设置为待安装状态
    $this->setInfo($newInfo);
    return $info;
}

4. 漏洞触发点

漏洞的核心在于:

  1. info.ini文件中读取uid字段
  2. 直接将uid值与$this->installDir拼接形成目标目录
  3. 使用rename()函数将解压的临时目录移动到拼接后的目录

由于没有对uid值进行严格的过滤和校验,攻击者可以通过构造恶意的uid值实现目录穿越。

漏洞利用步骤

1. 构造恶意模块包

  1. 创建一个包含恶意代码的文件(如shell.php
  2. 创建info.ini文件,内容如下:
uid=/../public/test
title=恶意模块
intro=这是一个测试模块
author=attacker
version=1.0.0
state=1
  1. 将这两个文件打包为ZIP压缩包

2. 利用条件

  • 需要拥有后台管理员权限(因为上传需要有效的token)
  • 需要注册官方账户获取有效token

3. 利用过程

  1. 登录后台系统
  2. 进入模块市场上传功能
  3. 上传构造的恶意ZIP包
  4. 系统处理流程:
    • 解压ZIP包到临时目录
    • 读取info.ini文件获取uid
    • 拼接目录路径:$this->installDir . '/../public/test'
    • 使用rename()将临时目录移动到public/test目录
  5. 访问上传的恶意文件:http://target.com/public/test/shell.php

漏洞修复建议

  1. 严格过滤info.ini中的uid值,禁止包含路径遍历字符
  2. 对最终拼接的路径进行规范化处理,使用realpath()等函数解析绝对路径
  3. 限制uid只能包含字母、数字和下划线等安全字符
  4. 在移动目录前检查目标路径是否在允许的范围内

教学总结

  1. 危险操作点:模块导入/上传功能通常是高危操作,需要特别关注
  2. 路径拼接风险:任何用户输入与系统路径拼接的操作都可能存在目录遍历风险
  3. 文件移动操作rename()等文件移动函数使用不当可能导致安全风险
  4. 防御思路
    • 严格校验所有用户输入
    • 限制文件/目录操作的范围
    • 对路径进行规范化处理和安全检查

扩展思考

  1. 如何在不修改代码的情况下通过服务器配置防御此类漏洞?
  2. 除了目录穿越,类似的路径拼接操作还可能引发哪些安全问题?
  3. 如何设计安全的模块上传和安装机制?

通过本案例的分析,安全开发人员应更加重视文件操作中的路径处理问题,特别是在涉及用户输入的场景下,必须实施严格的安全检查和过滤机制。

PHP代码审计:某0Day漏洞分析与利用教学文档 漏洞概述 本教学文档详细分析了一个PHP系统中的0Day漏洞,该漏洞存在于后台模块市场的文件上传功能中,通过精心构造的恶意模块包可以实现目录穿越和任意文件上传,最终导致Getshell。 漏洞环境 漏洞位置:后台模块市场的上传功能 受影响文件: app/admin/controller/Module.php 关键类: app/admin/library/module 中的相关方法 漏洞类型:文件上传漏洞(目录穿越) 漏洞定位与分析 1. 漏洞入口点 漏洞位于后台的模块市场上传功能,具体在 Module.php 控制器的 upload 方法中: 2. 关键调用链 上传流程的调用链如下: Module.php 中的 upload() 方法 调用 Manage::instance()->upload($token, $file) 进入 app/admin/library/module 中的 upload 方法 3. 核心漏洞代码分析 在 app/admin/library/module 中的 upload 方法存在以下关键处理逻辑: 4. 漏洞触发点 漏洞的核心在于: 从 info.ini 文件中读取 uid 字段 直接将 uid 值与 $this->installDir 拼接形成目标目录 使用 rename() 函数将解压的临时目录移动到拼接后的目录 由于没有对 uid 值进行严格的过滤和校验,攻击者可以通过构造恶意的 uid 值实现目录穿越。 漏洞利用步骤 1. 构造恶意模块包 创建一个包含恶意代码的文件(如 shell.php ) 创建 info.ini 文件,内容如下: 将这两个文件打包为ZIP压缩包 2. 利用条件 需要拥有后台管理员权限(因为上传需要有效的token) 需要注册官方账户获取有效token 3. 利用过程 登录后台系统 进入模块市场上传功能 上传构造的恶意ZIP包 系统处理流程: 解压ZIP包到临时目录 读取 info.ini 文件获取 uid 值 拼接目录路径: $this->installDir . '/../public/test' 使用 rename() 将临时目录移动到 public/test 目录 访问上传的恶意文件: http://target.com/public/test/shell.php 漏洞修复建议 严格过滤 info.ini 中的 uid 值,禁止包含路径遍历字符 对最终拼接的路径进行规范化处理,使用 realpath() 等函数解析绝对路径 限制 uid 只能包含字母、数字和下划线等安全字符 在移动目录前检查目标路径是否在允许的范围内 教学总结 危险操作点 :模块导入/上传功能通常是高危操作,需要特别关注 路径拼接风险 :任何用户输入与系统路径拼接的操作都可能存在目录遍历风险 文件移动操作 : rename() 等文件移动函数使用不当可能导致安全风险 防御思路 : 严格校验所有用户输入 限制文件/目录操作的范围 对路径进行规范化处理和安全检查 扩展思考 如何在不修改代码的情况下通过服务器配置防御此类漏洞? 除了目录穿越,类似的路径拼接操作还可能引发哪些安全问题? 如何设计安全的模块上传和安装机制? 通过本案例的分析,安全开发人员应更加重视文件操作中的路径处理问题,特别是在涉及用户输入的场景下,必须实施严格的安全检查和过滤机制。