Ejemplo n.º 1
0
class DirBurster(object):
    """
    目录猜解,独立线程运行
    """
    def __init__(self, kb, thread_num, dic_paths, logger):
        """
        :kb:            Universal KnowledgeBase
        :thread_num:    线程数量
        :dic_paths:     字典路径列表
        :logger:        输出
        """
        super(DirBurster, self).__init__()

        # 禁掉https证书的错误信息
        requests.packages.urllib3.disable_warnings()

        self.kb = kb
        self.logger = logger

        # 读入目录字典
        self.dic = []
        for file_path in dic_paths:
            self.dic.extend(open(file_path, 'r').readlines())
        for i in xrange(0, len(self.dic)):
            self.dic[i] = self.dic[i][1:-1]

        self.exit_flag = False
        self.visited_mutex = threading.Lock()
        self.task_queue = []
        self.visited = {}
        self.seconds_wait = 2

        # 线程分发器
        self.dispather = ParallelDispatcher(thread_num=thread_num,
                                            data_source=self.task_queue,
                                            execute_func=self.burst_url,
                                            logger=self.logger,
                                            owner='DirBurster',
                                            start_index=0,
                                            seconds_wait=2)

    def engine_start(self):
        """
        从kb中取数据并进行目录猜解
        """
        self.log(['Engine started.'])

        url_count = 0
        while True:
            # 读取数据,读取完全返回None
            results = self.kb.read_data(URL, DIR_BURSTER, url_count)
            if results is None:
                break

            # 读取结果保存到任务列表中,由dispather分发
            if len(results) > 0:
                url_count += len(results)
                self.task_queue.extend([result[0] for result in results])
                self.dispather.dispath_scan_task()

            # 检测到退出标志置位,退出
            if self.exit_flag:
                break
            time.sleep(self.seconds_wait)

        # 等待所有线程结束
        self.dispather.suicide()
        self.log(['Engine stopped.'])

    def burst_url(self, url, thread_no):
        """
        线程执行函数,对url进行目录猜解,保存到数据库
        :url:       目标URL
        :thread_no: 当前线程号
        """
        path, domain = extract_path_domain(url)

        # 猜解目录,得到猜解成功地目录,保存到数据库
        urls = self.parse_path(path, domain)
        self.kb.save_data(URL, [(url, 0, 200, 0) for url in urls])

    def parse_path(self, path, domain):
        """
        递归猜解所有的路径
        :path:      当前路径
        :domain:    当前域名
        :return:    猜解成功的路径列表
        """
        if len(path) == 0:
            return []

        # 如果目录已存在就跳过,否则继续
        full_path = domain + path
        self.visited_mutex.acquire()
        if self.visited.get(full_path) is not None:
            self.visited_mutex.release()
            return []
        self.visited[full_path] = 0
        self.visited_mutex.release()

        # 去掉一层目录,返回检测到的地址
        upper = path[0:-1]
        i = upper.rfind('/')
        if i == -1:
            upper = ''
        else:
            upper = upper[0:i + 1]
        u_list = self.parse_path(upper, domain)
        u_list.extend(self.burst_single(full_path))

        return u_list

    def burst_single(self, url):
        """
        对单个路径进行猜解
        :url:       目标路径
        :return:    猜解成功地目录列表
        """
        u_list = []
        for x in self.dic:
            if self.exit_flag:
                break
            if (self.head_request(url + x)):
                u_list.append(url + x)
        return u_list

    def head_request(self, url):
        """
        发起head请求,目前只是判断了状态码
        """
        try:
            found = requests.head(url, verify=False).status_code == 200
        except:
            found = False
        return found

    def exit(self):
        self.dispather.exit()
        self.exit_flag = True

    def log(self, msgs):
        self.logger.debug('DirBurster', msgs)
Ejemplo n.º 2
0
class CODEExec(object):
    """
    代码执行扫描器,扫描数据库中的结果并保存到数据库中
    """
    def __init__(self, kb, logger, thread_num, cookie):
        """
        :kb:                    Universal KnowledgeBase
        :logger:                输出
        :thread_num:            扫描线程数
        :cookie:                cookie
        """
        super(CODEExec, self).__init__()

        self.kb = kb
        self.cookie = cookie
        self.logger = logger

        self.task_queue = []
        self.seconds_wait = 2
        self.exit_flag = False
        self.owner = 'CODEExec'

        # 线程分发器
        self.dispather = ParallelDispatcher(
            thread_num=thread_num,
            data_source=self.task_queue,
            execute_func=self.check_on_CODE_exec,
            logger=self.logger,
            owner=self.owner,
            start_index=0,
            seconds_wait=2)

        PRINT_REPEATS = 5

        PRINT_STRINGS = (
            # PHP http://php.net/eval
            "echo str_repeat('%%s',%s);" % PRINT_REPEATS,
            # Perl http://perldoc.perl.org/functions/eval.html
            "print '%%s'x%s" % PRINT_REPEATS,
            # Python
            # http://docs.python.org/reference/simple_stmts.html#the-exec-statement
            "print '%%s'*%s" % PRINT_REPEATS,
            # ASP
            "Response.Write(new String(\"%%s\",%s))" % PRINT_REPEATS,
        )

        self.payloads = PRINT_STRINGS

    def engine_start(self):
        """
        主函数,从kb中取数据并进行测试
        """
        self.log(['Engine started.'], DEBUG)

        url_count = 0
        while True:
            # 读取数据,读取完全返回None
            results = self.kb.read_data(URL, CODE_EXEC, url_count)
            if results is None:
                break

            # 添加到任务列表,只取2-POST和1-带参数的GET
            if len(results) > 0:
                url_count += len(results)
                self.task_queue.extend([
                    result for result in results
                    if (result[1] == 1 or result[1] == 2)
                ])
                self.dispather.dispath_scan_task()

            # 检测到退出标志置位,退出
            if self.exit_flag:
                break
            time.sleep(self.seconds_wait)

        # 等待线程关闭
        self.dispather.suicide()
        self.log(['Engine stopped.'], DEBUG)

    def check_on_CODE_exec(self, task, thread_no):
        """
        线程执行函数,检测单个URL上的代码执行漏洞
        :task:      (url, is_post, code, depth)的元组
        :thread_no: 当前线程号
        """
        url, query = extract_path_query(task[0])
        is_post = task[1]

        # 对payload中的所有payload挨个检查,一个成功则退出
        for payload in self.payloads:
            # 检测到退出标志置位,退出
            if self.exit_flag:
                self.log(['Thread killed, abort on %s' % task[0]], DEBUG)
                break

            # 发送payload,检查参数位置
            index = send_payload(url, is_post, query, payload,
                                 self.analyze_CODE_result, self.cookie)
            if index == -1:
                continue

            # 打印,并保存payload
            self.log([
                '[VULNERABLE] ' + task[0], '    [LOCATION] ' + query[index][0],
                '    [PAYLOAD] ' + payload
            ], not DEBUG)
            self.kb.save_data(CODE,
                              (task[0], query[index][0], payload, 'CODE'))
            break
        else:
            self.log(['[INVULNERABLE] ' + task[0]], DEBUG)

    def analyze_CODE_result(self, payload, src, ori_time):
        """
        检查结果中是否有payload,找到对应的返回结果的正则,匹配一下看看
        """

        return '%s' * 5 in src

    def exit(self):
        self.dispather.exit()
        self.exit_flag = True

    def log(self, msgs, debug):
        if debug:
            self.logger.debug(self.owner, msgs)
        else:
            self.logger.info(self.owner, msgs)
Ejemplo n.º 3
0
class LFIScanner(object):
    """
    LFI扫描器,扫描数据库中的结果并保存到数据库中
    """

    def __init__(self, kb, logger, thread_num, cookie):
        """
        :kb:                    Universal KnowledgeBase
        :logger:                输出
        :thread_num:            扫描线程数
        :cookie:                cookie
        """
        super(LFIScanner, self).__init__()

        self.kb = kb
        self.cookie = cookie
        self.logger = logger

        self.task_queue = []
        self.seconds_wait = 2
        self.exit_flag = False
        self.owner = 'LFIScanner'

        # 线程分发器
        self.dispather = ParallelDispatcher(
            thread_num=thread_num,
            data_source=self.task_queue,
            execute_func=self.check_on_lfi,
            logger=self.logger,
            owner=self.owner,
            start_index=0,
            seconds_wait=2
            )

        heads = ['../' * 16]
        self.bodys = ['etc/passwd', 'windows/win.ini']
        tails = ['', '\0', '%00', '?', '/' + './' * 2048]
        self.payloads = [''.join([a, b, c]) for a in heads for b in self.bodys for c in tails]

        # 新加了两个xxe的payload,检测与之前一致
        temp = ['<?xml version="1.0" encoding="ISO-8859-1"?><!DOCTYPE foo[<!ELEMENT foo ANY><!ENTITY xxe SYSTEM "file:///etc/passwd">]><foo>&xxe;</foo>',
                '<?xml version="1.0" encoding="ISO-8859-1"?><!DOCTYPE foo[<!ELEMENT foo ANY><!ENTITY xxe SYSTEM "file://c:/windows/win.ini>]><foo>&xxe;</foo>']
        self.payloads.extend(temp)
        self.bodys.extend(temp)

        # 每一个body对应一个result,正则编译的结果
        results = [ r'.*root:[^\n]*:/bin/sh.*', r'.*\[extensions\].*',
                    r'.*root:[^\n]*:/bin/sh.*', r'.*\[extensions\].*']
        self.results = [re.compile(rgx, re.DOTALL | re.IGNORECASE) for rgx in results]

    def engine_start(self):
        """
        主函数,从kb中取数据并进行测试
        """
        self.log(['Engine started.'], DEBUG)

        url_count = 0
        while True:
            # 读取数据,读取完全返回None
            results = self.kb.read_data(URL, LFI_SCANNER, url_count)
            if results is None:
                break

            # 添加到任务列表,只取2-POST和1-带参数的GET
            if len(results) > 0:
                url_count += len(results)
                self.task_queue.extend([result for result in results if (result[1] == 1 or result[1] == 2)])
                self.dispather.dispath_scan_task()

            # 检测到退出标志置位,退出
            if self.exit_flag:
                break
            time.sleep(self.seconds_wait)

        # 等待线程关闭
        self.dispather.suicide()
        self.log(['Engine stopped.'], DEBUG)

    def check_on_lfi(self, task, thread_no):
        """
        线程执行函数,检测单个URL上的LFI漏洞
        :task:      (url, is_post, code, depth)的元组
        :thread_no: 当前线程号
        """
        url, query = extract_path_query(task[0])
        is_post = task[1]

        # 对payload中的所有payload挨个检查,一个成功则退出
        for payload in self.payloads:
            # 检测到退出标志置位,退出
            if self.exit_flag:
                self.log(['Thread killed, abort on %s' % task[0]], DEBUG)
                break

            # 发送payload,检查参数位置
            index = send_payload(url, is_post, query, payload, self.analyze_lfi_result, self.cookie)
            if index == -1:
                continue

            # 打印,并保存payload
            self.log(['[VULNERABLE] ' + task[0], '    [LOCATION] ' + query[index][0], '    [PAYLOAD] ' + payload], not DEBUG)
            self.kb.save_data(LFI, (task[0], query[index][0], payload, 'LFI'))
            break
        else:
            self.log(['[INVULNERABLE] ' + task[0]], DEBUG)

    def analyze_lfi_result(self, payload, src, _):
        """
        检查结果中是否有payload,找到对应的返回结果的正则,匹配一下看看
        """
        for i in xrange(len(self.bodys)):
            if self.bodys[i] in payload:
                if self.results[i].match(src):
                    return True
                return False
        return False

    def exit(self):
        self.dispather.exit()
        self.exit_flag = True

    def log(self, msgs, debug):
        if debug:
            self.logger.debug(self.owner, msgs)
        else:
            self.logger.info(self.owner, msgs)
Ejemplo n.º 4
0
class CMDExec(object):
    """
    命令执行扫描器,扫描数据库中的结果并保存到数据库中
    """
    def __init__(self, kb, logger, thread_num, cookie):
        """
        :kb:                    Universal KnowledgeBase
        :logger:                输出
        :thread_num:            扫描线程数
        :cookie:                cookie
        """
        super(CMDExec, self).__init__()

        self.kb = kb
        self.cookie = cookie
        self.logger = logger

        self.task_queue = []
        self.seconds_wait = 2
        self.exit_flag = False
        self.owner = 'CMDExec'

        # 线程分发器
        self.dispather = ParallelDispatcher(
            thread_num=thread_num,
            data_source=self.task_queue,
            execute_func=self.check_on_cmd_exec,
            logger=self.logger,
            owner=self.owner,
            start_index=0,
            seconds_wait=2)

        heads = ['', ';', '|']
        bodys = [
            'ping -c 20 127.0.0.1', 'ping -n 20 127.0.0.1',
            'echo 123456789098765432345678'
        ]
        self.results = ['123456789098765432345678']
        self.payloads = [''.join([a, b]) for a in heads for b in bodys]

    def engine_start(self):
        """
        主函数,从kb中取数据并进行测试
        """
        self.log(['Engine started.'], DEBUG)

        url_count = 0
        while True:
            # 读取数据,读取完全返回None
            results = self.kb.read_data(URL, CMD_EXEC, url_count)
            if results is None:
                break

            # 添加到任务列表,只取2-POST和1-带参数的GET
            if len(results) > 0:
                url_count += len(results)
                self.task_queue.extend([
                    result for result in results
                    if (result[1] == 1 or result[1] == 2)
                ])
                self.dispather.dispath_scan_task()

            # 检测到退出标志置位,退出
            if self.exit_flag:
                break
            time.sleep(self.seconds_wait)

        # 等待线程关闭
        self.dispather.suicide()
        self.log(['Engine stopped.'], DEBUG)

    def check_on_cmd_exec(self, task, thread_no):
        """
        线程执行函数,检测单个URL上的命令执行漏洞
        :task:      (url, is_post, code, depth)的元组
        :thread_no: 当前线程号
        """
        url, query = extract_path_query(task[0])
        is_post = task[1]

        # 对payload中的所有payload挨个检查,一个成功则退出
        for payload in self.payloads:
            # 检测到退出标志置位,退出
            if self.exit_flag:
                self.log(['Thread killed, abort on %s' % task[0]], DEBUG)
                break

            # 发送payload,检查参数位置
            index = send_payload(url, is_post, query, payload,
                                 self.analyze_cmd_result, self.cookie)
            if index == -1:
                continue

            # 打印,并保存payload
            self.log([
                '[VULNERABLE] ' + task[0], '    [LOCATION] ' + query[index][0],
                '    [PAYLOAD] ' + payload
            ], not DEBUG)
            self.kb.save_data(CMD, (task[0], query[index][0], payload, 'CMD'))
            break
        else:
            self.log(['[INVULNERABLE] ' + task[0]], DEBUG)

    def analyze_cmd_result(self, payload, src, ori_time):
        """
        检查结果中是否有payload,找到对应的返回结果的正则,匹配一下看看
        """
        if 'echo' in payload:
            return self.results[0] in src and not ('echo ' +
                                                   self.results[0]) in src
        #print time.time() - ori_time
        #通过延时来判读命令是否执行
        return (time.time() - ori_time) > 19

    def exit(self):
        self.dispather.exit()
        self.exit_flag = True

    def log(self, msgs, debug):
        if debug:
            self.logger.debug(self.owner, msgs)
        else:
            self.logger.info(self.owner, msgs)
Ejemplo n.º 5
0
class CODEExec(object):
    """
    代码执行扫描器,扫描数据库中的结果并保存到数据库中
    """

    def __init__(self, kb, logger, thread_num, cookie):
        """
        :kb:                    Universal KnowledgeBase
        :logger:                输出
        :thread_num:            扫描线程数
        :cookie:                cookie
        """
        super(CODEExec, self).__init__()

        self.kb = kb
        self.cookie = cookie
        self.logger = logger

        self.task_queue = []
        self.seconds_wait = 2
        self.exit_flag = False
        self.owner = 'CODEExec'

        # 线程分发器
        self.dispather = ParallelDispatcher(
            thread_num=thread_num,
            data_source=self.task_queue,
            execute_func=self.check_on_CODE_exec,
            logger=self.logger,
            owner=self.owner,
            start_index=0,
            seconds_wait=2
        )

        PRINT_REPEATS = 5

        PRINT_STRINGS = (
            # PHP http://php.net/eval
            "echo str_repeat('%%s',%s);" % PRINT_REPEATS,
            # Perl http://perldoc.perl.org/functions/eval.html
            "print '%%s'x%s" % PRINT_REPEATS,
            # Python
            # http://docs.python.org/reference/simple_stmts.html#the-exec-statement
            "print '%%s'*%s" % PRINT_REPEATS,
            # ASP
            "Response.Write(new String(\"%%s\",%s))" % PRINT_REPEATS,
        )

        self.payloads = PRINT_STRINGS

    def engine_start(self):
        """
        主函数,从kb中取数据并进行测试
        """
        self.log(['Engine started.'], DEBUG)

        url_count = 0
        while True:
            # 读取数据,读取完全返回None
            results = self.kb.read_data(URL, CODE_EXEC, url_count)
            if results is None:
                break

            # 添加到任务列表,只取2-POST和1-带参数的GET
            if len(results) > 0:
                url_count += len(results)
                self.task_queue.extend([result for result in results if (result[1] == 1 or result[1] == 2)])
                self.dispather.dispath_scan_task()

            # 检测到退出标志置位,退出
            if self.exit_flag:
                break
            time.sleep(self.seconds_wait)

        # 等待线程关闭
        self.dispather.suicide()
        self.log(['Engine stopped.'], DEBUG)

    def check_on_CODE_exec(self, task, thread_no):
        """
        线程执行函数,检测单个URL上的代码执行漏洞
        :task:      (url, is_post, code, depth)的元组
        :thread_no: 当前线程号
        """
        url, query = extract_path_query(task[0])
        is_post = task[1]

        # 对payload中的所有payload挨个检查,一个成功则退出
        for payload in self.payloads:
            # 检测到退出标志置位,退出
            if self.exit_flag:
                self.log(['Thread killed, abort on %s' % task[0]], DEBUG)
                break

            # 发送payload,检查参数位置
            index = send_payload(url, is_post, query, payload, self.analyze_CODE_result, self.cookie)
            if index == -1:
                continue

            # 打印,并保存payload
            self.log(['[VULNERABLE] ' + task[0], '    [LOCATION] ' + query[index][0], '    [PAYLOAD] ' + payload],
                     not DEBUG)
            self.kb.save_data(CODE, (task[0], query[index][0], payload, 'CODE'))
            break
        else:
            self.log(['[INVULNERABLE] ' + task[0]], DEBUG)

    def analyze_CODE_result(self, payload, src, ori_time):
        """
        检查结果中是否有payload,找到对应的返回结果的正则,匹配一下看看
        """

        return '%s' * 5 in src

    def exit(self):
        self.dispather.exit()
        self.exit_flag = True

    def log(self, msgs, debug):
        if debug:
            self.logger.debug(self.owner, msgs)
        else:
            self.logger.info(self.owner, msgs)
Ejemplo n.º 6
0
class CMDExec(object):
    """
    命令执行扫描器,扫描数据库中的结果并保存到数据库中
    """

    def __init__(self, kb, logger, thread_num, cookie):
        """
        :kb:                    Universal KnowledgeBase
        :logger:                输出
        :thread_num:            扫描线程数
        :cookie:                cookie
        """
        super(CMDExec, self).__init__()

        self.kb = kb
        self.cookie = cookie
        self.logger = logger

        self.task_queue = []
        self.seconds_wait = 2
        self.exit_flag = False
        self.owner = 'CMDExec'

        # 线程分发器
        self.dispather = ParallelDispatcher(
            thread_num=thread_num,
            data_source=self.task_queue,
            execute_func=self.check_on_cmd_exec,
            logger=self.logger,
            owner=self.owner,
            start_index=0,
            seconds_wait=2
            )

        heads = ['', ';', '|']
        bodys = ['ping -c 20 127.0.0.1', 'ping -n 20 127.0.0.1', 'echo 123456789098765432345678']
        self.results = ['123456789098765432345678']
        self.payloads = [''.join([a, b]) for a in heads for b in bodys]

    def engine_start(self):
        """
        主函数,从kb中取数据并进行测试
        """
        self.log(['Engine started.'], DEBUG)

        url_count = 0
        while True:
            # 读取数据,读取完全返回None
            results = self.kb.read_data(URL, CMD_EXEC, url_count)
            if results is None:
                break

            # 添加到任务列表,只取2-POST和1-带参数的GET
            if len(results) > 0:
                url_count += len(results)
                self.task_queue.extend([result for result in results if (result[1] == 1 or result[1] == 2)])
                self.dispather.dispath_scan_task()

            # 检测到退出标志置位,退出
            if self.exit_flag:
                break
            time.sleep(self.seconds_wait)

        # 等待线程关闭
        self.dispather.suicide()
        self.log(['Engine stopped.'], DEBUG)

    def check_on_cmd_exec(self, task, thread_no):
        """
        线程执行函数,检测单个URL上的命令执行漏洞
        :task:      (url, is_post, code, depth)的元组
        :thread_no: 当前线程号
        """
        url, query = extract_path_query(task[0])
        is_post = task[1]

        # 对payload中的所有payload挨个检查,一个成功则退出
        for payload in self.payloads:
            # 检测到退出标志置位,退出
            if self.exit_flag:
                self.log(['Thread killed, abort on %s' % task[0]], DEBUG)
                break

            # 发送payload,检查参数位置
            index = send_payload(url, is_post, query, payload, self.analyze_cmd_result, self.cookie)
            if index == -1:
                continue

            # 打印,并保存payload
            self.log(['[VULNERABLE] ' + task[0], '    [LOCATION] ' + query[index][0], '    [PAYLOAD] ' + payload], not DEBUG)
            self.kb.save_data(CMD, (task[0], query[index][0], payload, 'CMD'))
            break
        else:
            self.log(['[INVULNERABLE] ' + task[0]], DEBUG)

    def analyze_cmd_result(self, payload, src, ori_time):
        """
        检查结果中是否有payload,找到对应的返回结果的正则,匹配一下看看
        """
        if 'echo' in payload:
            return self.results[0] in src and not ('echo ' + self.results[0]) in src
        #print time.time() - ori_time
        #通过延时来判读命令是否执行
        return (time.time() - ori_time) > 19

    def exit(self):
        self.dispather.exit()
        self.exit_flag = True

    def log(self, msgs, debug):
        if debug:
            self.logger.debug(self.owner, msgs)
        else:
            self.logger.info(self.owner, msgs)
Ejemplo n.º 7
0
class SrcDownloader(object):
    """
    下载器模块,下载源代码,并且保存到kb
    """

    def __init__(self, kb, phantomjs_path, evaljs_path, logger, thread_num, filetype_whitelist, depth_limit,
                 temp_dir_path):
        """
        :kb:                Universal KnowledgeBase
        :phantomjs_path:    phantomjs路径
        :evaljs_path:       js脚本路径
        :logger:            输出
        :thread_num:        最大线程数
        :filetype_whitelist:文件类型白名单
        :depth_limit:       爬行深度限制
        :temp_dir_path:     临时文件夹
        """
        super(SrcDownloader, self).__init__()

        self.kb = kb
        self.logger = logger
        self.executable = ' '.join([phantomjs_path, evaljs_path, ''])
        self.filetype_whitelist = filetype_whitelist
        self.depth_limit = depth_limit
        self.temp_dir_path = temp_dir_path

        self.exit_flag = False
        self.task_queue = []
        self.seconds_wait = 1  # 检测新加进来的URL

        # 线程分发器
        self.dispather = ParallelDispatcher(
            thread_num=thread_num,
            data_source=self.task_queue,
            execute_func=self.download_page,
            logger=self.logger,
            owner='Downloader',
            start_index=0,
            seconds_wait=2
        )

    def engine_start(self):
        """
        下载器主函数,下载源代码,并且保存到kb
        """
        self.log(['Engine started.'])

        url_count = 0
        while True:
            # 读取URL,读取完全返回None
            results = self.kb.read_data(URL, SRC_DOWNLOADER, url_count)
            if results is None:
                break

            # 添加到任务队列,由分发器分发
            if len(results) > 0:
                url_count += len(results)
                self.task_queue.extend(results)
                self.dispather.dispath_scan_task()

            # 检测到退出标志置位,退出
            if self.exit_flag:
                break
            time.sleep(self.seconds_wait)

        # 等待线程结束
        self.dispather.suicide()
        self.log(['Engine stopped.'])

    def download_page(self, task, thread_no):
        """
        线程执行函数,用phantomjs下载页面并且处理JS,结果保存到数据库
        :task:          (url, is_post,status_code, depth)
        :thread_no:     线程号
        """

        # 深度超过限制,文件类型不合法,或者是logout(这个也许有别的方法?)
        if (self.depth_limit < task[3] or
                not self.is_valid_filetype(task[0]) or
                re.compile(r'.*logout.*', re.IGNORECASE | re.DOTALL).search(task[0])):
            return
        self.log(['%d %s' % (task[3], task[0])])
        # shell命令,phantomjs,在参数前面增加P/G代表POST或者GET
        # batcmd = self.executable + ('"P' if task[1] == 2 else '"G') + task[0].replace('"',
        #                                                                               '""') + '" "' + os.getcwd() + '/EagleX/extra/temp"'
        batcmd = '{phantomjs}"{method}{url}" "{tmpdir}/EagleX/extra/temp"'.format(phantomjs=self.executable,
                                                                                  method=('P' if task[1] == 2 else 'G'),
                                                                                  url=task[0].replace('"', '""'),
                                                                                  tmpdir=os.getcwd())

        try:

            result = subprocess.check_output(batcmd, stderr=subprocess.STDOUT, shell=True, universal_newlines=True)
            # self.log([result])
        except subprocess.CalledProcessError as exc:
            self.exc = exc
            self.log(['[ERROR] phantomjs crashed, stderr output saved to CRASH.txt'])

            format_time = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time()))
            f = open(self.temp_dir_path + 'CRASH.txt', 'a')

            msg = '''======================={time}========================
                  ***********************************************************
                  {task}
                  ***********************************************************
                  ===========================================================
                  '''.format(time=format_time, task=task[0])
            f.write(msg)
            f.close()
            return

        # 数据库
        self.kb.save_data(SRC, (task[0], result, task[3]))

    def is_valid_filetype(self, url):
        """
        检查文件后缀是否合法
        :url:       目标URL
        :return:    True or False
        """
        extension = os.path.splitext(urlparse(url).path)[1].lstrip('.')
        # print extension
        if not extension:
            return True  # 如果没有匹配到后缀放行
        return self.filetype_whitelist.get(extension) is not None

    def exit(self):
        self.dispather.exit()
        self.exit_flag = True

    def log(self, msgs):
        self.logger.debug('Downloader', msgs)
Ejemplo n.º 8
0
class DomXss(object):
    """
    检测DOM XSS,检查页面中可控字段,无需发送payload
    """

    def __init__(self, kb, logger, thread_num, cookie):
        """
        :kb:            Universal KnowledgeBase
        :logger:        输出
        :thread_num:    线程数
        :cookie:        cookie
        """
        super(DomXss, self).__init__()

        self.kb = kb
        self.logger = logger
        self.cookie = cookie
        self.owner = 'DomXss'

        self.task_queue = []
        self.visited = {}
        self.seconds_wait = 2
        self.exit_flag = False

        # 线程分发器
        self.dispather = ParallelDispatcher(
            thread_num=thread_num,
            data_source=self.task_queue,
            execute_func=self.check_on_dom_xss,
            logger=self.logger,
            owner=self.owner,
            start_index=0,
            seconds_wait=2
        )

    def engine_start(self):
        """
        检测DOM XSS主函数
        """
        url_count = 0
        while True:
            # 读取数据,读取完全返回None
            results = self.kb.read_data(URL, XSS_SCANNER, url_count)
            if results is None:
                break

            # 所有的URL都添加到任务列表
            if len(results) > 0:
                url_count += len(results)
                self.task_queue.extend(results)
                self.dispather.dispath_scan_task()

            # 检测到退出标志置位,退出
            if self.exit_flag:
                break
            time.sleep(self.seconds_wait)

        # 等待线程关闭
        self.dispather.suicide()

    def check_on_dom_xss(self, task, thread_no):
        """
        线程执行函数,检查可能的DOM输出点,包括其他包含的JS
        :task:      (url, is_post, code, depth)的元组
        :thread_no: 当前线程号
        """
        # 下载原页面,找到所有js src,访问并保存
        src = {task[0]: send_common_request(task[0], task[1], self.cookie)}
        for js in _script_src_re.findall(src[task[0]]):
            # 检测到退出标志置位,退出
            if self.exit_flag:
                self.log(['Thread killed, abort on %s' % task[0]], DEBUG)
                return

            # 访问js并且保存代码,重复不再爬取
            # 这里有个问题是,如果是某个js中有输出点,那第一个被检查出来之后后面的就不管了
            # 理论上不太可能是某个js中,就算是,至少也会爆出来一个。还算可以接受
            link = url_process(js, task[0])
            if self.visited.get(link) is not None:
                continue
            self.visited[link] = 0
            src[js] = send_common_request(link, 0, '')

        # 检查所有的src中的输出点,存在则保存
        for url in src.keys():
            res = self.analyze_dom_result(src[url])
            if res is not None:
                # 打印,并保存payload
                self.log(['[VULNERABLE] ' + task[0], '[FILE] ' + url, '[KEYWORD] ' + res], not DEBUG)
                self.kb.save_data(XSS, (task[0], url, res, 'DOM'))
                break
        else:
            self.log(['[INVULNERABLE] ' + task[0]], DEBUG)

    def analyze_dom_result(self, response):
        """
        检查源代码中是否有DOM的输出点
        :response:      源代码
        :return:        不存在则None,否则可控点的字符串
        """
        res = ''
        for function_re in JS_FUNCTION_CALLS:
            parameters = function_re.search(response)
            if parameters:
                for user_controlled in DOM_USER_CONTROLLED:
                    if user_controlled in parameters.groups()[0]:
                        res = '|'.join([res, user_controlled])
        if len(res) > 0:
            return res[1:]
        else:
            return None

    def exit(self):
        self.dispather.exit()
        self.exit_flag = True

    def log(self, msgs, debug):
        if debug:
            self.logger.debug(self.owner, msgs)
        else:
            self.logger.info(self.owner, msgs)
Ejemplo n.º 9
0
class DirBurster(object):
    """
    目录猜解,独立线程运行
    """

    def __init__(self, kb, thread_num, dic_paths, logger):
        """
        :kb:            Universal KnowledgeBase
        :thread_num:    线程数量
        :dic_paths:     字典路径列表
        :logger:        输出
        """
        super(DirBurster, self).__init__()

        # 禁掉https证书的错误信息
        requests.packages.urllib3.disable_warnings()

        self.kb = kb
        self.logger = logger

        # 读入目录字典
        self.dic = []
        for file_path in dic_paths:
            self.dic.extend(open(file_path, 'r').readlines())
        for i in xrange(0, len(self.dic)):
            self.dic[i] = self.dic[i][1:-1]

        self.exit_flag = False
        self.visited_mutex = threading.Lock()
        self.task_queue = []
        self.visited = {}
        self.seconds_wait = 2

        # 线程分发器
        self.dispather = ParallelDispatcher(
            thread_num=thread_num,
            data_source=self.task_queue,
            execute_func=self.burst_url,
            logger=self.logger,
            owner='DirBurster',
            start_index=0,
            seconds_wait=2
        )

    def engine_start(self):
        """
        从kb中取数据并进行目录猜解
        """
        self.log(['Engine started.'])

        url_count = 0
        while True:
            # 读取数据,读取完全返回None
            results = self.kb.read_data(URL, DIR_BURSTER, url_count)
            if results is None:
                break

            # 读取结果保存到任务列表中,由dispather分发
            if len(results) > 0:
                url_count += len(results)
                self.task_queue.extend([result[0] for result in results])
                self.dispather.dispath_scan_task()

            # 检测到退出标志置位,退出
            if self.exit_flag:
                break
            time.sleep(self.seconds_wait)

        # 等待所有线程结束
        self.dispather.suicide()
        self.log(['Engine stopped.'])

    def burst_url(self, url, thread_no):
        """
        线程执行函数,对url进行目录猜解,保存到数据库
        :url:       目标URL
        :thread_no: 当前线程号
        """
        path, domain = extract_path_domain(url)

        # 猜解目录,得到猜解成功地目录,保存到数据库
        urls = self.parse_path(path, domain)
        self.kb.save_data(URL, [(url, 0, 200, 0) for url in urls])

    def parse_path(self, path, domain):
        """
        递归猜解所有的路径
        :path:      当前路径
        :domain:    当前域名
        :return:    猜解成功的路径列表
        """
        if len(path) == 0:
            return []

        # 如果目录已存在就跳过,否则继续
        full_path = domain + path
        self.visited_mutex.acquire()
        if self.visited.get(full_path) is not None:
            self.visited_mutex.release()
            return []
        self.visited[full_path] = 0
        self.visited_mutex.release()

        # 去掉一层目录,返回检测到的地址
        upper = path[0:-1]
        i = upper.rfind('/')
        if i == -1:
            upper = ''
        else:
            upper = upper[0:i + 1]
        u_list = self.parse_path(upper, domain)
        u_list.extend(self.burst_single(full_path))

        return u_list

    def burst_single(self, url):
        """
        对单个路径进行猜解
        :url:       目标路径
        :return:    猜解成功地目录列表
        """
        u_list = []
        for x in self.dic:
            if self.exit_flag:
                break
            if (self.head_request(url + x)):
                u_list.append(url + x)
        return u_list

    def head_request(self, url):
        """
        发起head请求,目前只是判断了状态码
        """
        try:
            found = requests.head(url, verify=False).status_code == 200
        except:
            found = False
        return found

    def exit(self):
        self.dispather.exit()
        self.exit_flag = True

    def log(self, msgs):
        self.logger.debug('DirBurster', msgs)
Ejemplo n.º 10
0
class ReflectedXss(object):
    """
    反射型或者存储型XSS的检测,payload直接echo在返回的页面中
    """

    def __init__(self, kb, logger, thread_num, cookie):
        """
        :kb:            Universal KnowledgeBase
        :logger:        输出
        :thread_num:    线程数
        :cookie:        cookie
        """
        super(ReflectedXss, self).__init__()

        self.kb = kb
        self.logger = logger
        self.cookie = cookie
        self.owner = 'ReflectedXss'

        self.task_queue = []
        self.seconds_wait = 2
        self.exit_flag = False

        # 替换后的payload们
        self.payloads = [replace_randomize(i) for i in PAYLOADS]

        # 线程分发器
        self.dispather = ParallelDispatcher(
            thread_num=thread_num,
            data_source=self.task_queue,
            execute_func=self.check_on_reflected_xss,
            logger=self.logger,
            owner=self.owner,
            start_index=0,
            seconds_wait=2
        )

    def engine_start(self):
        """
        反射型Xss扫描器主函数,从kb中取数据并进行测试
        """
        url_count = 0
        while True:
            # 读取数据,读取完全返回None
            results = self.kb.read_data(URL, XSS_SCANNER, url_count)
            if results is None:
                break

            # 添加到任务列表,只取2-POST和1-带参数的GET
            if len(results) > 0:
                url_count += len(results)
                self.task_queue.extend([result for result in results if (result[1] == 1 or result[1] == 2)])
                self.dispather.dispath_scan_task()

            # 检测到退出标志置位,退出
            if self.exit_flag:
                break
            time.sleep(self.seconds_wait)

        # 等待线程关闭
        self.dispather.suicide()

    def check_on_reflected_xss(self, task, thread_no):
        """
        线程执行函数,检测单个URL上的XSS漏洞
        :task:      (url, is_post, code, depth)的元组
        :thread_no: 当前线程号
        """
        url, query = extract_path_query(task[0])
        is_post = task[1]

        # 对payload中的所有payload挨个检查,一个成功则退出
        for payload in self.payloads:
            # 检测到退出标志置位,退出
            if self.exit_flag:
                self.log(['Thread killed, abort on %s' % task[0]], DEBUG)
                break

            # 发送payload,检查参数位置
            index = send_payload(url, is_post, query, payload, self.analyze_reflected_result, self.cookie)
            if index == -1:
                continue

            # 打印,并保存payload
            self.log(['[VULNERABLE] ' + task[0], '[LOCATION] ' + query[index][0], '[PAYLOAD] ' + payload], not DEBUG)
            self.kb.save_data(XSS, (task[0], query[index][0], payload, 'XSS'))
            break
        else:
            self.log(['[INVULNERABLE] ' + task[0]], DEBUG)

    def analyze_reflected_result(self, payload, src, _):
        """
        检查结果中是否有payload,现在是检查payload原文是否在返回来的源代码中
        """
        return payload in src

    def exit(self):
        self.dispather.exit()
        self.exit_flag = True

    def log(self, msgs, debug):
        if debug:
            self.logger.debug(self.owner, msgs)
        else:
            self.logger.info(self.owner, msgs)
Ejemplo n.º 11
0
class INFOLeakage(object):
    """
    信息泄露扫描器,扫描数据库中的结果并保存到数据库中
    """

    def __init__(self, kb, logger, thread_num, cookie):
        """
        :kb:                    Universal KnowledgeBase
        :logger:                输出
        :thread_num:            扫描线程数
        :cookie:                cookie
        """
        super(INFOLeakage, self).__init__()

        self.kb = kb
        self.cookie = cookie
        self.logger = logger

        self.task_queue = []
        self.seconds_wait = 2
        self.exit_flag = False
        self.owner = 'INFOLeakage'

        # 线程分发器
        self.dispather = ParallelDispatcher(
            thread_num=thread_num,
            data_source=self.task_queue,
            execute_func=self.check_on_INFO_LEAKAGE,
            logger=self.logger,
            owner=self.owner,
            start_index=0,
            seconds_wait=2
        )

        self.payloads = pathlist

    def engine_start(self):
        """
        主函数,从kb中取数据并进行测试
        """
        self.log(['Engine started.'], DEBUG)

        url_count = 0
        while True:
            # 读取数据,读取完全返回None
            results = self.kb.read_data(URL, INFO_Leakage, url_count)
            if results is None:
                break

            # 添加到任务列表,只取2-POST和1-带参数的GET
            if len(results) > 0:
                url_count += len(results)
                self.task_queue.extend([result for result in results if (result[1] == 1 or result[1] == 2)])
                self.dispather.dispath_scan_task()

            # 检测到退出标志置位,退出
            if self.exit_flag:
                break
            time.sleep(self.seconds_wait)

        # 等待线程关闭
        self.dispather.suicide()
        self.log(['Engine stopped.'], DEBUG)

    def check_on_INFO_LEAKAGE(self, task, thread_no):
        """
        线程执行函数,检测单个URL上的信息泄露
        :task:      (url, is_post, code, depth)的元组
        :thread_no: 当前线程号
        """
        url, query = extract_path_query(task[0])
        is_post = task[1]

        # 对payload中的所有payload挨个检查,一个成功则退出
        for payload in self.payloads.keys():
            # 检测到退出标志置位,退出
            if self.exit_flag:
                self.log(['Thread killed, abort on %s' % task[0]], DEBUG)
                break

            # 发送payload,检查参数位置
            index = send_payload(url, is_post, query, payload, self.analyze_INFO_result, self.cookie)
            if index == -1:
                continue

            # 打印,并保存payload
            self.log(['[VULNERABLE] ' + task[0], '    [LOCATION] ' + query[index][0], '    [PAYLOAD] ' + payload],
                     not DEBUG)
            self.kb.save_data(INFO, (task[0], query[index][0], payload, 'INFO'))
            break
        else:
            self.log(['[INVULNERABLE] ' + task[0]], DEBUG)

    def analyze_INFO_result(self, payload, src, ori_time):
        """
        检查结果中是否有payload,找到对应的返回结果的正则,匹配一下看看
        """
        return self.payloads[payload] in src

    def exit(self):
        self.dispather.exit()
        self.exit_flag = True

    def log(self, msgs, debug):
        if debug:
            self.logger.debug(self.owner, msgs)
        else:
            self.logger.info(self.owner, msgs)
Ejemplo n.º 12
0
class DomXss(object):
    """
    检测DOM XSS,检查页面中可控字段,无需发送payload
    """
    def __init__(self, kb, logger, thread_num, cookie):
        """
        :kb:            Universal KnowledgeBase
        :logger:        输出
        :thread_num:    线程数
        :cookie:        cookie
        """
        super(DomXss, self).__init__()

        self.kb = kb
        self.logger = logger
        self.cookie = cookie
        self.owner = 'DomXss'

        self.task_queue = []
        self.visited = {}
        self.seconds_wait = 2
        self.exit_flag = False

        # 线程分发器
        self.dispather = ParallelDispatcher(thread_num=thread_num,
                                            data_source=self.task_queue,
                                            execute_func=self.check_on_dom_xss,
                                            logger=self.logger,
                                            owner=self.owner,
                                            start_index=0,
                                            seconds_wait=2)

    def engine_start(self):
        """
        检测DOM XSS主函数
        """
        url_count = 0
        while True:
            # 读取数据,读取完全返回None
            results = self.kb.read_data(URL, XSS_SCANNER, url_count)
            if results is None:
                break

            # 所有的URL都添加到任务列表
            if len(results) > 0:
                url_count += len(results)
                self.task_queue.extend(results)
                self.dispather.dispath_scan_task()

            # 检测到退出标志置位,退出
            if self.exit_flag:
                break
            time.sleep(self.seconds_wait)

        # 等待线程关闭
        self.dispather.suicide()

    def check_on_dom_xss(self, task, thread_no):
        """
        线程执行函数,检查可能的DOM输出点,包括其他包含的JS
        :task:      (url, is_post, code, depth)的元组
        :thread_no: 当前线程号
        """
        # 下载原页面,找到所有js src,访问并保存
        src = {task[0]: send_common_request(task[0], task[1], self.cookie)}
        for js in _script_src_re.findall(src[task[0]]):
            # 检测到退出标志置位,退出
            if self.exit_flag:
                self.log(['Thread killed, abort on %s' % task[0]], DEBUG)
                return

            # 访问js并且保存代码,重复不再爬取
            # 这里有个问题是,如果是某个js中有输出点,那第一个被检查出来之后后面的就不管了
            # 理论上不太可能是某个js中,就算是,至少也会爆出来一个。还算可以接受
            link = url_process(js, task[0])
            if self.visited.get(link) is not None:
                continue
            self.visited[link] = 0
            src[js] = send_common_request(link, 0, '')

        # 检查所有的src中的输出点,存在则保存
        for url in src.keys():
            res = self.analyze_dom_result(src[url])
            if res is not None:
                # 打印,并保存payload
                self.log([
                    '[VULNERABLE] ' + task[0], '[FILE] ' + url,
                    '[KEYWORD] ' + res
                ], not DEBUG)
                self.kb.save_data(XSS, (task[0], url, res, 'DOM'))
                break
        else:
            self.log(['[INVULNERABLE] ' + task[0]], DEBUG)

    def analyze_dom_result(self, response):
        """
        检查源代码中是否有DOM的输出点
        :response:      源代码
        :return:        不存在则None,否则可控点的字符串
        """
        res = ''
        for function_re in JS_FUNCTION_CALLS:
            parameters = function_re.search(response)
            if parameters:
                for user_controlled in DOM_USER_CONTROLLED:
                    if user_controlled in parameters.groups()[0]:
                        res = '|'.join([res, user_controlled])
        if len(res) > 0:
            return res[1:]
        else:
            return None

    def exit(self):
        self.dispather.exit()
        self.exit_flag = True

    def log(self, msgs, debug):
        if debug:
            self.logger.debug(self.owner, msgs)
        else:
            self.logger.info(self.owner, msgs)
Ejemplo n.º 13
0
class UrlRedirect(object):
    """
    URL跳转的检测,通过是否跳转到指定页面判断
    """
    def __init__(self, kb, logger, thread_num, cookie):
        """
        :kb:            Universal KnowledgeBase
        :logger:        输出
        :thread_num:    线程数
        :cookie:        cookie
        """
        super(UrlRedirect, self).__init__()

        self.kb = kb
        self.logger = logger
        self.cookie = cookie
        self.owner = 'UrlRedirect'

        self.task_queue = []
        self.seconds_wait = 2
        self.exit_flag = False
        test_urls = ['http://www.baidu.com/', '//baidu.com']

        self.payloads = test_urls

        # 线程分发器
        self.dispather = ParallelDispatcher(
            thread_num=thread_num,
            data_source=self.task_queue,
            execute_func=self.check_on_url_redirect,
            logger=self.logger,
            owner=self.owner,
            start_index=0,
            seconds_wait=2)

    def engine_start(self):
        """
        URL跳转扫描器主函数,从kb中取数据并进行测试
        """
        self.log(['Engine started.'], DEBUG)
        url_count = 0
        while True:
            # 读取数据,读取完全返回None
            results = self.kb.read_data(URL, URL_REDIRECT, url_count)
            if results is None:
                break

            # 添加到任务列表,只取2-POST和1-带参数的GET
            if len(results) > 0:
                url_count += len(results)
                self.task_queue.extend([
                    result for result in results
                    if (result[1] == 1 or result[1] == 2)
                ])
                self.dispather.dispath_scan_task()

            # 检测到退出标志置位,退出
            if self.exit_flag:
                break
            time.sleep(self.seconds_wait)

        # 等待线程关闭
        self.dispather.suicide()
        self.log(['Engine stopped.'], DEBUG)

    def check_on_url_redirect(self, task, thread_no):
        """
        线程执行函数,检测单个URL上的URL跳转漏洞
        :task:      (url, is_post, code, depth)的元组
        :thread_no: 当前线程号
        """
        url, query = extract_path_query(task[0])
        is_post = task[1]

        # 对payload中的所有payload挨个检查,一个成功则退出
        for payload in self.payloads:
            # 检测到退出标志置位,退出
            if self.exit_flag:
                self.log(['Thread killed, abort on %s' % task[0]], DEBUG)
                break

            # 发送payload,检查参数位置
            index = send_payload(url, is_post, query, payload,
                                 self.analyze_urlredirect_result, self.cookie)
            if index == -1:
                continue

            # 打印,并保存payload
            self.log([
                '[VULNERABLE] ' + task[0], '[LOCATION] ' + query[index][0],
                '[PAYLOAD] ' + payload
            ], not DEBUG)
            self.kb.save_data(
                URL_REDIRECT,
                (task[0], query[index][0], payload, 'URL_REDIRECT'))
            break
        else:
            self.log(['[INVULNERABLE] ' + task[0]], DEBUG)

    def analyze_urlredirect_result(self, payload, src, _):
        """
        检测是否跳转到指定页面(百度)
        """
        # print payload
        if u'<title>百度一下,你就知道</title>' in src or u'跳转中' in src:
            return 1
        return 0

    def exit(self):
        self.dispather.exit()
        self.exit_flag = True

    def log(self, msgs, debug):
        if debug:
            self.logger.debug(self.owner, msgs)
        else:
            self.logger.info(self.owner, msgs)
Ejemplo n.º 14
0
class UrlRedirect(object):
    """
    URL跳转的检测,通过是否跳转到指定页面判断
    """

    def __init__(self, kb, logger, thread_num, cookie):
        """
        :kb:            Universal KnowledgeBase
        :logger:        输出
        :thread_num:    线程数
        :cookie:        cookie
        """
        super(UrlRedirect, self).__init__()

        self.kb = kb
        self.logger = logger
        self.cookie = cookie
        self.owner = 'UrlRedirect'

        self.task_queue = []
        self.seconds_wait = 2
        self.exit_flag = False
        test_urls = [
            'http://www.baidu.com/',
            '//baidu.com'
        ]

        self.payloads = test_urls

        # 线程分发器
        self.dispather = ParallelDispatcher(
            thread_num=thread_num,
            data_source=self.task_queue,
            execute_func=self.check_on_url_redirect,
            logger=self.logger,
            owner=self.owner,
            start_index=0,
            seconds_wait=2
        )

    def engine_start(self):
        """
        URL跳转扫描器主函数,从kb中取数据并进行测试
        """
        self.log(['Engine started.'], DEBUG)
        url_count = 0
        while True:
            # 读取数据,读取完全返回None
            results = self.kb.read_data(URL, URL_REDIRECT, url_count)
            if results is None:
                break

            # 添加到任务列表,只取2-POST和1-带参数的GET
            if len(results) > 0:
                url_count += len(results)
                self.task_queue.extend([result for result in results if (result[1] == 1 or result[1] == 2)])
                self.dispather.dispath_scan_task()

            # 检测到退出标志置位,退出
            if self.exit_flag:
                break
            time.sleep(self.seconds_wait)

        # 等待线程关闭
        self.dispather.suicide()
        self.log(['Engine stopped.'], DEBUG)

    def check_on_url_redirect(self, task, thread_no):
        """
        线程执行函数,检测单个URL上的URL跳转漏洞
        :task:      (url, is_post, code, depth)的元组
        :thread_no: 当前线程号
        """
        url, query = extract_path_query(task[0])
        is_post = task[1]

        # 对payload中的所有payload挨个检查,一个成功则退出
        for payload in self.payloads:
            # 检测到退出标志置位,退出
            if self.exit_flag:
                self.log(['Thread killed, abort on %s' % task[0]], DEBUG)
                break

            # 发送payload,检查参数位置
            index = send_payload(url, is_post, query, payload, self.analyze_urlredirect_result, self.cookie)
            if index == -1:
                continue

            # 打印,并保存payload
            self.log(['[VULNERABLE] ' + task[0], '[LOCATION] ' + query[index][0], '[PAYLOAD] ' + payload], not DEBUG)
            self.kb.save_data(URL_REDIRECT, (task[0], query[index][0], payload, 'URL_REDIRECT'))
            break
        else:
            self.log(['[INVULNERABLE] ' + task[0]], DEBUG)

    def analyze_urlredirect_result(self, payload, src, _):
        """
        检测是否跳转到指定页面(百度)
        """
        # print payload
        if u'<title>百度一下,你就知道</title>' in src or u'跳转中' in src:
            return 1
        return 0

    def exit(self):
        self.dispather.exit()
        self.exit_flag = True

    def log(self, msgs, debug):
        if debug:
            self.logger.debug(self.owner, msgs)
        else:
            self.logger.info(self.owner, msgs)
Ejemplo n.º 15
0
class INFOLeakage(object):
    """
    信息泄露扫描器,扫描数据库中的结果并保存到数据库中
    """
    def __init__(self, kb, logger, thread_num, cookie):
        """
        :kb:                    Universal KnowledgeBase
        :logger:                输出
        :thread_num:            扫描线程数
        :cookie:                cookie
        """
        super(INFOLeakage, self).__init__()

        self.kb = kb
        self.cookie = cookie
        self.logger = logger

        self.task_queue = []
        self.seconds_wait = 2
        self.exit_flag = False
        self.owner = 'INFOLeakage'

        # 线程分发器
        self.dispather = ParallelDispatcher(
            thread_num=thread_num,
            data_source=self.task_queue,
            execute_func=self.check_on_INFO_LEAKAGE,
            logger=self.logger,
            owner=self.owner,
            start_index=0,
            seconds_wait=2)

        self.payloads = pathlist

    def engine_start(self):
        """
        主函数,从kb中取数据并进行测试
        """
        self.log(['Engine started.'], DEBUG)

        url_count = 0
        while True:
            # 读取数据,读取完全返回None
            results = self.kb.read_data(URL, INFO_Leakage, url_count)
            if results is None:
                break

            # 添加到任务列表,只取2-POST和1-带参数的GET
            if len(results) > 0:
                url_count += len(results)
                self.task_queue.extend([
                    result for result in results
                    if (result[1] == 1 or result[1] == 2)
                ])
                self.dispather.dispath_scan_task()

            # 检测到退出标志置位,退出
            if self.exit_flag:
                break
            time.sleep(self.seconds_wait)

        # 等待线程关闭
        self.dispather.suicide()
        self.log(['Engine stopped.'], DEBUG)

    def check_on_INFO_LEAKAGE(self, task, thread_no):
        """
        线程执行函数,检测单个URL上的信息泄露
        :task:      (url, is_post, code, depth)的元组
        :thread_no: 当前线程号
        """
        url, query = extract_path_query(task[0])
        is_post = task[1]

        # 对payload中的所有payload挨个检查,一个成功则退出
        for payload in self.payloads.keys():
            # 检测到退出标志置位,退出
            if self.exit_flag:
                self.log(['Thread killed, abort on %s' % task[0]], DEBUG)
                break

            # 发送payload,检查参数位置
            index = send_payload(url, is_post, query, payload,
                                 self.analyze_INFO_result, self.cookie)
            if index == -1:
                continue

            # 打印,并保存payload
            self.log([
                '[VULNERABLE] ' + task[0], '    [LOCATION] ' + query[index][0],
                '    [PAYLOAD] ' + payload
            ], not DEBUG)
            self.kb.save_data(INFO,
                              (task[0], query[index][0], payload, 'INFO'))
            break
        else:
            self.log(['[INVULNERABLE] ' + task[0]], DEBUG)

    def analyze_INFO_result(self, payload, src, ori_time):
        """
        检查结果中是否有payload,找到对应的返回结果的正则,匹配一下看看
        """
        return self.payloads[payload] in src

    def exit(self):
        self.dispather.exit()
        self.exit_flag = True

    def log(self, msgs, debug):
        if debug:
            self.logger.debug(self.owner, msgs)
        else:
            self.logger.info(self.owner, msgs)
Ejemplo n.º 16
0
class LFIScanner(object):
    """
    LFI扫描器,扫描数据库中的结果并保存到数据库中
    """
    def __init__(self, kb, logger, thread_num, cookie):
        """
        :kb:                    Universal KnowledgeBase
        :logger:                输出
        :thread_num:            扫描线程数
        :cookie:                cookie
        """
        super(LFIScanner, self).__init__()

        self.kb = kb
        self.cookie = cookie
        self.logger = logger

        self.task_queue = []
        self.seconds_wait = 2
        self.exit_flag = False
        self.owner = 'LFIScanner'

        # 线程分发器
        self.dispather = ParallelDispatcher(thread_num=thread_num,
                                            data_source=self.task_queue,
                                            execute_func=self.check_on_lfi,
                                            logger=self.logger,
                                            owner=self.owner,
                                            start_index=0,
                                            seconds_wait=2)

        heads = ['../' * 16]
        self.bodys = ['etc/passwd', 'windows/win.ini']
        tails = ['', '\0', '%00', '?', '/' + './' * 2048]
        self.payloads = [
            ''.join([a, b, c]) for a in heads for b in self.bodys
            for c in tails
        ]

        # 新加了两个xxe的payload,检测与之前一致
        temp = [
            '<?xml version="1.0" encoding="ISO-8859-1"?><!DOCTYPE foo[<!ELEMENT foo ANY><!ENTITY xxe SYSTEM "file:///etc/passwd">]><foo>&xxe;</foo>',
            '<?xml version="1.0" encoding="ISO-8859-1"?><!DOCTYPE foo[<!ELEMENT foo ANY><!ENTITY xxe SYSTEM "file://c:/windows/win.ini>]><foo>&xxe;</foo>'
        ]
        self.payloads.extend(temp)
        self.bodys.extend(temp)

        # 每一个body对应一个result,正则编译的结果
        results = [
            r'.*root:[^\n]*:/bin/sh.*', r'.*\[extensions\].*',
            r'.*root:[^\n]*:/bin/sh.*', r'.*\[extensions\].*'
        ]
        self.results = [
            re.compile(rgx, re.DOTALL | re.IGNORECASE) for rgx in results
        ]

    def engine_start(self):
        """
        主函数,从kb中取数据并进行测试
        """
        self.log(['Engine started.'], DEBUG)

        url_count = 0
        while True:
            # 读取数据,读取完全返回None
            results = self.kb.read_data(URL, LFI_SCANNER, url_count)
            if results is None:
                break

            # 添加到任务列表,只取2-POST和1-带参数的GET
            if len(results) > 0:
                url_count += len(results)
                self.task_queue.extend([
                    result for result in results
                    if (result[1] == 1 or result[1] == 2)
                ])
                self.dispather.dispath_scan_task()

            # 检测到退出标志置位,退出
            if self.exit_flag:
                break
            time.sleep(self.seconds_wait)

        # 等待线程关闭
        self.dispather.suicide()
        self.log(['Engine stopped.'], DEBUG)

    def check_on_lfi(self, task, thread_no):
        """
        线程执行函数,检测单个URL上的LFI漏洞
        :task:      (url, is_post, code, depth)的元组
        :thread_no: 当前线程号
        """
        url, query = extract_path_query(task[0])
        is_post = task[1]

        # 对payload中的所有payload挨个检查,一个成功则退出
        for payload in self.payloads:
            # 检测到退出标志置位,退出
            if self.exit_flag:
                self.log(['Thread killed, abort on %s' % task[0]], DEBUG)
                break

            # 发送payload,检查参数位置
            index = send_payload(url, is_post, query, payload,
                                 self.analyze_lfi_result, self.cookie)
            if index == -1:
                continue

            # 打印,并保存payload
            self.log([
                '[VULNERABLE] ' + task[0], '    [LOCATION] ' + query[index][0],
                '    [PAYLOAD] ' + payload
            ], not DEBUG)
            self.kb.save_data(LFI, (task[0], query[index][0], payload, 'LFI'))
            break
        else:
            self.log(['[INVULNERABLE] ' + task[0]], DEBUG)

    def analyze_lfi_result(self, payload, src, _):
        """
        检查结果中是否有payload,找到对应的返回结果的正则,匹配一下看看
        """
        for i in xrange(len(self.bodys)):
            if self.bodys[i] in payload:
                if self.results[i].match(src):
                    return True
                return False
        return False

    def exit(self):
        self.dispather.exit()
        self.exit_flag = True

    def log(self, msgs, debug):
        if debug:
            self.logger.debug(self.owner, msgs)
        else:
            self.logger.info(self.owner, msgs)
Ejemplo n.º 17
0
class SqlScanner(object):
    """
    SQL注入扫描的主引擎
    """
    def __init__(self, kb, sqlmapapi_path, sqlmapapi_port, logger, cookie,
                 sqlmapapi_addr, thread_num, temp_dir_path):
        """
        :kb:                Universal KnowledgeBase
        :sqlmapapi_path:    脚本的路径
        :sqlmapapi_port:    监听的端口
        :sqlmapapi_addr:    监听的地址,默认localhost
        :logger:            输出
        :cookie:            cookie,默认为空
        :thread_num:    最大的线程数
        :temp_dir_path:     临时文件夹
        """
        super(SqlScanner, self).__init__()

        self.kb = kb
        self.logger = logger
        self.cookie = cookie
        self.temp_dir_path = temp_dir_path

        self.task_queue = []
        self.seconds_wait = 2  # 检测新加进来的URL
        self.sqlmapapi_process = None
        self.exit_flag = False
        self.sqlmapapi_server = ''.join(
            ['http://', sqlmapapi_addr, ':',
             str(sqlmapapi_port)])

        # autosqli的对象列表
        self.autosqli_list = [None for i in xrange(thread_num)]
        self.autosqli_list_mutex = threading.Lock()

        # 线程分发器
        self.dispather = ParallelDispatcher(
            thread_num=thread_num,
            data_source=self.task_queue,
            execute_func=self.check_on_sql_injection,
            logger=self.logger,
            owner='SqlScanner',
            start_index=0,
            seconds_wait=1)

        # 开启sqlmapapi服务
        self.start_sqlmapapi_server(sqlmapapi_path, sqlmapapi_addr,
                                    sqlmapapi_port)

    def start_sqlmapapi_server(self, path, addr, port):
        """
        开启sqlmapapi服务,输出重定向到文件中
        :path:      可执行路径
        :addr:      服务器监听地址
        :port:      服务器监听端口
        """
        self.f_out = open(self.temp_dir_path + 'sqlmapapi.stdout.temp', 'w')
        self.f_err = open(self.temp_dir_path + 'sqlmapapi.stderr.temp', 'w')

        # 更改脚本当前路径,防止sqlmapapi后续调用路径错误
        # 不再修改路径,把sqlmap放到了根目录下面,然后修改了api.py

        self.sqlmapapi_process = subprocess.Popen(
            ['python', path, '-s', '-H', addr, '-p',
             str(port)],
            shell=False,
            stdout=self.f_out,
            stderr=self.f_err)

        self.log(['sqlmapapi server started at %s:%d.' % (addr, port)], DEBUG)

    def engine_start(self):
        """
        Sql扫描器主函数,从kb中取数据并进行测试
        """
        self.log(['Engine started.'], DEBUG)

        url_count = 0
        while True:
            # 读取URL数据,读取完全返回None
            results = self.kb.read_data(URL, SQL_SCANNER, url_count)
            if results is None:
                break

            # 添加到任务列表,只取2-POST和1-带参数的GET
            if len(results) > 0:
                url_count += len(results)
                self.task_queue.extend([
                    result for result in results if (result[1] >= 1)
                ])  # or result[1] == 2)])
                self.dispather.dispath_scan_task()

            # 检测到退出标志置位,退出
            if self.exit_flag:
                break
            time.sleep(self.seconds_wait)

        # 清理现场,退出
        self.clean_up_the_mess()
        self.log(['Engine stopped.'], DEBUG)

    def check_on_sql_injection(self, task, thread_no):
        """
        线程执行函数,检查单个URL的注入,结果保存到数据库
        :task:      (url, is_post, code, depth)的元组
        :thread_no: 当前线程号,用于保存sqli
        """
        url = task[0]
        para = ''

        # POST则分隔开参数
        if task[1] == 2:
            url, para = url.split('?', 1)

        # 启动mAutoSqli调用sqlmapapi进行检测,添加到对象列表中
        sqli = AutoSqli(server=self.sqlmapapi_server,
                        target=url,
                        logger=self.logger,
                        timeout=120,
                        data=para,
                        referer='',
                        cookie=self.cookie,
                        other_options={},
                        retries=3)
        self.autosqli_list_mutex.acquire()
        self.autosqli_list[thread_no] = sqli
        self.autosqli_list_mutex.release()

        # 测完了要组装回去
        url += '?' + para if len(para) > 0 else ''

        # 扫描结果处理
        result = sqli.scan()
        if result is None:
            self.log(['[INVULNERABLE] ' + url], DEBUG)
        elif len(result) > 0:
            # 输出dbms和payload信息,保存到数据库
            (dbms, payload) = self.get_info(result)
            self.log([
                '[VULNERABLE] ' + url, '[DBMS] ' + dbms, '[PAYLOAD] ' + payload
            ], not DEBUG)
            self.kb.save_data(SQL, (url, dbms, payload))

    def get_info(self, result):
        """
        从Json格式的数据中读取到dbms和payload
        :result:    JSON数据
        :return:    (dbms, payload)
        """
        payload = ''
        dbms = ''
        try:
            data = result[0]['value'][0]['data']
            dbms = result[0]['value'][0]['dbms']
            if dbms is None:
                dbms = ''
            if data is None:
                raise NameError('')

            # 多个payload,用\n连接成串
            data_list = list(data)
            for x in data_list:
                try:
                    payload += '' if data[x]['payload'] == None else (
                        data[x]['payload'] + '\n')
                except:
                    pass
            if (len(payload) > 0):
                payload = payload[0:-1]  # 删除最后的回车
        except:
            pass
        return (dbms, payload)

    def clean_up_the_mess(self):
        """
        杀死AutoSqli对象,停止线程
        """
        # 杀死测试线程
        self.autosqli_list_mutex.acquire()
        for x in self.autosqli_list:
            if x != None:
                x.exit()
        self.autosqli_list_mutex.release()

        # 等待线程结束
        self.dispather.suicide()

        # 杀死sqlmapapi
        if self.sqlmapapi_process:
            try:
                self.sqlmapapi_process.kill()
                self.log(['sqlmapapi server stopped.'], DEBUG)
                self.f_out.close()
                self.f_err.close()
            except:
                pass

    def exit(self):
        self.dispather.exit()
        self.exit_flag = True

    def log(self, msgs, debug):
        if debug:
            self.logger.debug('SqlScanner', msgs)
        else:
            self.logger.info('SqlScanner', msgs)