黑帽python第二版(Black Hat Python 2nd Edition)读书笔记 之 第五章 WEB黑客(3)暴力破解目录与文件位置

黑帽python第二版(Black Hat Python 2nd Edition)读书笔记 之 第五章 WEB黑客(3)暴力破解目录与文件位置



写在前面

前面的示例假设我们已经对目标web应用的目标有很多了解。但是,当我们攻击自行开发的web应用程序或大型电子商务系统时,通常不会意识到web服务器上可访问的所有文件。这时候我们通常会部署一个爬虫(如Burp Suite中包含的爬虫)来抓取目标网站,以便尽可能多地发现web应用程序。但在很多情况下,我们会希望获得配置文件、遗弃的开发文件、调试脚本和其他可以提供敏感信息或软件开发人员不希望看到的功能的安全痕迹。发现此内容的唯一方法是使用暴力工具搜索常见文件名和目录。
接下来我们将构建一个简单的工具,接收来自常见暴力工具的单词列表,例如gobuster和SVNDigger,并尝试发现目标web服务器上可以访问的目录和文件。我们可以在互联网上找到许多可用的单词列表,并且我们的Kali发行版中已经有了很多单词列表(参见/usr/share/wordlists)。对于本章节,我们将使用SVNDigger中的列表。原书中的SVNDigger下载链接已经失效,大家可以直接百度SVNDigger,然后去Git上下载,然后解压,在目录下可以看到一个名为all.txt的文件。
在这里插入图片描述

bruter.py脚本

如前所述,我们将创建一个线程池来尝试发现内容。先从创建一些功能开始,从单词列表文件创建队列。创建一个名为bruter.py的脚本文件,然后输入以下代码:

import queue
import requests
import threading
import sys

AGENT = 'Mozilla/5.0 (X11; Linux x86_64; rv:19.0) Gecko/20100101 Firefox/19.0'
EXTENSIONS = ['.php', '.bak', '.orig', '.inc']
TARGET = 'http://testphp.vulnweb.com'
THREADS = 50
WORDLIST = '/home/kali/Downloads/SVNDigger-master/SVNDigger/all.txt'

def get_words(resume=None):
    def extend_words(word):
        if '.' in word:
            words.put(f'/{word}')
        else:
            words.put(f'/{word}/')
        
        for extension in EXTENSIONS:
            words.put(f'/{word}{extension}')

    with open(WORDLIST) as f:
        raw_words = f.read()
    
    found_resume = False
    words = queue.Queue()
    for word in raw_words.split():
        if resume is not None:
            if found_resume:
                extend_words(word)
            elif word == resume:
                found_resume = True
                print(f'Resuming wordlist from : {resume}')
        else:
            print(word)
            extend_words(word)
    return words

get_words支持函数返回我们将在目标上测试的单词队列。我们读入单词列表文件,然后开始遍历文件中的每一行。然后,我们将resume变量设置为暴力工具尝试的最后一条路径。如果网络连接中断或目标站点宕机,函数允许我们恢复暴力工具的会话。解析完整个文件后,我们返回一个Queue,其中包含要在实际的暴力工具函数中使用的单词。
请注意,get_words函数中有一个名为extend_words的内部函数。内部函数是在另一个函数中定义的函数。我们本来也可以在get_words之外编写它,但因为extend_words函数总是在get_words函数的上下文中运行,所以我们将它放在内部,以便保持命名空间的整洁,使代码更容易理解。
内部函数extend_words的目的是在发出请求时应用扩展列表进行测试。在某些情况下,我们不仅要尝试/admin扩展,例如,还要尝试admin.php,admin.inc和admin.html。在常规编程语言扩展的基础上,集思广益开发人员可能会使用但后来忘记删除的常见扩展,如.orig和.bak,是很有用的。extend_words内部函数使用以下规则提供了这类功能:如果单词包含一个点(.),我们将其附加到URL(例如,/test.php);否则,我们将其视为目录名(例如/admin/)。
无论哪种情况,我们都会将每个可能的扩展添加到结果中。例如,如果我们有两个单词,test.php和admin,则将把以下附加单词放入单词队列:

/test.php.bak, /test.php.inc, /test.php.orig, /test.php.php
/admin/admin.bak, /admin/admin.inc, /admin/admin.orig, /admin/admin.php

编写主要功能

接下来我们编写暴力工具的主要功能:

def dir_bruter(words):
    headers = {'User-Agent': AGENT}
    while not words.empty():
        url = f'{TARGET}{words.get()}'
        try:
            r = requests.get(url, headers=headers)
        except requests.exceptions.ConnectionError:
            sys.stderr.write('x')
            sys.stderr.flush()
            continue

        if r.status_code == 200:
            print(f'\nSuccess ({r.status_code}: {url})')
        elif r.status_code == 404:
            sys.stderr.write('.')
            sys.stderr.flush()
        else:
            print(f'{r.status_code} => {url}')

if __name__ == '__main__':
    words = get_words()
    print('Press return to continue.')
    sys.stdin.readline()
    for _ in range(THREADS):
        t = threading.Thread(target=dir_bruter, args=(words,))
        t.start()

dir_bruter函数接受一个Queue对象,该对象由get_words函数中准备的单词填充。我们在脚本的开头定义了一个用于HTTP请求的User-Agent字符串,以使得我们的请求看起来像来自好人的普通请求。我们将User-Agent添加到headers变量中,然后循环遍历单词队列。对于每个迭代,我们创建一个URL,用于向目标应用程序发起get请求。
dir_bruter函数将一些输出直接打印到控制台,并将一些输出打印到stderr,后面我们将用此技术以灵活的方式输出。
如果能及时知道程序遇到的任何连接错误,那就太好了;发生这种情况时,将x打印到stderr。如果成功(状态为200),将完整的URL打印到控制台。另外,我们还可以创建一个队列并将返回结果放在里面。如果返回404,我们将在stderr中打印一个点(.),然后继续循环。如果我们得到除此之外的其他响应代码(如301等),我们也会打印其URL,因为这可能表明远程web服务器上有一些有趣的内容。时刻关注工具的输出很有用,因为根据远程web服务器的配置,我们可能需要过滤掉其他的HTTP错误代码,这样更加方便梳理结果。
在__main__代码块中,我们得到单词列表,然后启动一系列线程来执行暴力请求。

小试牛刀

OWASP有一个在线和离线的易受攻击的web应用程序列表,例如虚拟机和磁盘映像,我们可以用这些来针对性地测试我们的工具。在这种情况下,源代码中引用的URL指向Acunetix托管的故意有错误的web应用程序,攻击这些应用程序会向我们展示暴力工具的有效性。
我们建议将THREADS变量设置为正常值,例如5,然后运行脚本。值过低将需要运行很长时间,而值过高会使服务器过载。
运行脚本,我们会看到如下的结果,相对比较完美:
在这里插入图片描述
如果只想看到成功的内容,因为前面使用了sys.stderr以写入“x”和“.”字符,我们可以将stderr重定向到/dev/null,以便仅在控制台上显示找到的文件:
在这里插入图片描述
这里需要注意,我们目前正在从远程网站获取一些有趣的结果,比如我们可能会发现超负荷工作的web开发人员留下的备份文件或代码片段。有了这些信息,我们就可以删除那些容易轻松破坏应用程序的文件。

完整代码

最后附上完整代码。

import queue
import re
import requests
import threading
import sys

AGENT = 'Mozilla/5.0 (X11; Linux x86_64; rv:19.0) Gecko/20100101 Firefox/19.0'
EXTENSIONS = ['.php', '.bak', '.orig', '.inc']
TARGET = 'http://testphp.vulnweb.com'
THREADS = 50
WORDLIST = '/home/kali/Downloads/SVNDigger-master/SVNDigger/all.txt'

def get_words(resume=None):
    def extend_words(word):
        if '.' in word:
            words.put(f'/{word}')
        else:
            words.put(f'/{word}/')
        
        for extension in EXTENSIONS:
            words.put(f'/{word}{extension}')

    with open(WORDLIST) as f:
        raw_words = f.read()
    
    found_resume = False
    words = queue.Queue()
    for word in raw_words.split():
        if resume is not None:
            if found_resume:
                extend_words(word)
            elif word == resume:
                found_resume = True
                print(f'Resuming wordlist from : {resume}')
        else:
            print(word)
            extend_words(word)
    return words

def dir_bruter(words):
    headers = {'User-Agent': AGENT}
    while not words.empty():
        url = f'{TARGET}{words.get()}'
        try:
            r = requests.get(url, headers=headers)
        except requests.exceptions.ConnectionError:
            sys.stderr.write('x')
            sys.stderr.flush()
            continue

        if r.status_code == 200:
            print(f'\nSuccess ({r.status_code}: {url})')
        elif r.status_code == 404:
            sys.stderr.write('.')
            sys.stderr.flush()
        else:
            print(f'{r.status_code} => {url}')

if __name__ == '__main__':
    words = get_words()
    print('Press return to continue.')
    sys.stdin.readline()
    for _ in range(THREADS):
        t = threading.Thread(target=dir_bruter, args=(words,))
        t.start()
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值