黑帽python第二版(Black Hat Python 2nd Edition)读书笔记 之 第六章 扩展Burp代理(3)使用目标站点内容构建密码字典

发布于:2022-10-14 ⋅ 阅读:(344) ⋅ 点赞:(0)

黑帽python第二版(Black Hat Python 2nd Edition)读书笔记 之 第六章 扩展Burp代理(3)使用目标站点内容构建密码字典



写在前面

很多时候,安全性归结为一件事——用户密码,这虽然有些郁闷,但却是真的。更糟糕的是,当涉及到web应用程序,尤其是自定义应用程序时,很容易发现在经过一定次数的失败身份验证尝试后,程序并没有锁定用户。还有些情况,不会强制使用强密码。这些情况下,像上一章中的在线密码猜测可能只是访问站点的开始。
在线密码猜测的诀窍是获得正确的单词列表。因为时间有限,我们可能无法测试1000万个密码,因此我们需要创建一个针对相关网站的单词列表,Kali Linux中也有一些脚本可以抓取网站并根据网站内容生成单词列表。但是,如果我们已经使用Burp扫描网站,为什么要发送更多流量来生成单词表呢?此外,这些脚本通常要记住大量命令行参数,并且工作中很难记住足够多的命令行参数,所以让Burp来做这项繁重的工作是比较理想的。

构建bhp_wordlist.py脚本

定义支撑类与基本类

创建并打开bhp_wordlist.py脚本,然后输入下面的代码。

from burp import IBurpExtender
from burp import IContextMenuFactory

from java.util import ArrayList
from javax.swing import JMenuItem

import re

class TagStripper(HTMLParser):
    def __init__(self):
        HTMLParser.__init__(self)
        self.page_text = []
    
    def handle_data(self, data):
        self.page_text.append(data)
    
    def handle_comment(self, data):
        self.page_text.append(data)
    
    def strip(self, html):
        self.feed(html)
        return " ".join(self.page_text)

class BurpExtender(IBurpExtender, IContextMenuFactory):
    def registerExtenderCallbacks(self, callbacks):
        self._callbacks = callbacks
        self._helpers = callbacks.getHelpers()
        self.context = None
        self.hosts = set()

        # start with something we know is common
        self.wordlist = set(["password"])

        # we set up our extension
        callbacks.setExtensionName("BHP Wordlist")
        callbacks.registerContextMenuFactory(self)

        return
    
    def createMenuItems(self, context_menu):
        self.context = context_menu
        menu_list = ArrayList()
        menu_list.add(JMenuItem(
            "Create Wordlist", actionPerformed=self.wordlist_menu))
        
        return menu_list

上述代码大家应该已经非常熟悉了。我们首先导入所需的模块。支撑类TagStripper将允许我们从稍后处理的HTTP响应中去掉HTML标记;其handle_data方法将页面文本存储在一个成员变量中;我们还定义了handle_comment方法,因为我们希望把开发人员注释中存储的单词添加到密码列表中,handle_comment方法只调用handle_data(防止我们想改变处理页面文本的方式)。
strip方法将HTML代码提供给基类HTMLParser,并返回结果页面的文本,这将在以后派上用场。其余部分几乎与前面刚刚完成的bhp_bing.py脚本的开始完全相同。同样,目标是在Burp UI中创建上下文菜单项。这里唯一的新功能是我们将单词表存储在一个集合中,这样可以确保我们不会在运行过程中引入重复的单词。我们用每个人最喜欢的密码“password”初始化这个集合,只是为了确保它最终会出现在我们的列表中。

定义上下文菜单执行函数并转换基本单词列表

现在,让我们添加逻辑以便从Burp获取选定的HTTP流量,并将其转换为基本单词列表:

    def wordlist_menu(self, event):
        # grab the details of what the user clicked
        http_traffic = self.context.getSelectedMessages()

        for traffic in http_traffic:
            http_service = traffic.getHttpService()
            host = http_service.getHost()
            self.hosts.add(host)
            http_response = traffic.getResponse()
            if http_response:
                self.get_words(http_response)
        self.display_wordlist()
        return
    
    def get_words(self, http_response):
        headers, body = http_response.tostring().split('\r\n\r\n', 1)

        # skip non-text responses
        if headers.lower().find('content-type: text') == -1:
            return
        
        tag_stripper = TagStripper()
        page_text = tag_stripper.strip(body)

        words = re.findall("[a-zA-Z]\w{2,}", page_text)
        for word in words:
            # filter out long strings
            if len(word) <= 12:
                self.wordlist.add(word.lower())
        return

我们的首要任务是定义wordlist_menu方法,它处理菜单点击,保存响应主机的名称以备稍后使用,然后检索HTTP响应并将其提供给get_words方法。get_words方法检查响应头,以确保我们只处理基于文本的响应。TagStripper类从页面文本的其余部分中去除HTML代码。我们使用正则表达式查找所有以字母字符开头的单词,以及用\w{2,}正则表达式指定的两个或更多“单词”字符。我们将匹配此模式的单词以小写形式保存到单词列表中。

修改单词列表并展示

现在,让我们对脚本进行改进,使其能够修改和显示捕获的单词列表:

    def mangle(self, word):
        year = datetime.now().year
        suffixes = ["", "1", "!", year]
        mangled = []

        for password in (word, word.capitalize()):
            for suffix in suffixes:
                mangled.append("%s%s" % (password, suffix))
        
        return mangled
    
    def display_wordlist(self):
        print("#!comment: BHP Wordlist for site(s) %s" % ", ".join(self.hosts))
        for word in sorted(self.wordlist):
            for password in self.mangle(word):
                print(password)
        return

说明:这段代码在原书中的缩进有问题,后面“小试牛刀”章节再重点描述,也可以参照本文最后附上的源代码。
上面的mangle方法使用一个基本单词,并根据一些常见的密码创建策略将其转换为多个密码猜测。我们首先在方法中创建了一个后缀列表,用于附加在基本单词的末尾,包括当前年份。接下来,我们循环遍历每个后缀,并将其添加到基本单词中,以创建唯一的密码尝试。为了进一步提升效果,我们使用基本单词的大写版本进行另一轮循环。在display_wordlist方法中,我们打印一条注释来提醒我们使用哪些站点生成的这个单词列表。然后,我们处理每个基本单词并打印结果。接下来我们运行一下。

小试牛刀

单击Burp中的Extender选项卡,单击Add按钮,然后使用与以前相同的过程来加载Wordlist扩展。在Dashboard选项卡中,选择New live task。当对话框出现时,在顶部选中Live passive crawl,然后选择Add all links observated in traffic…,如下图所示,然后点击OK。
在这里插入图片描述
配置完成后,我们在浏览器(跟之前一样,设置好了proxy到burp)中访问http://testphp.vulnweb.com/。这个时候Burp记录并访问了目标站点上的所有链接,我们选择target选项卡右上角窗格中的所有请求,然后右键单击它们以打开上下文菜单,然后选择Create Wordlist,如下图所示。
在这里插入图片描述
现在检查扩展的Output选项卡,我们会发现burp打印出了所有的单词的列表(实际操作中我们会将单词列表保存到一个文件中),如下图所示。我们也可以将此列表反馈给Burp Intruder,以执行实际的密码猜测攻击。
在这里插入图片描述
到目前为止,我们通过构建我们自己的攻击payload与跟Burp UI交互的extension展示了Burp API的一小部分。在渗透测试过程中,我们可能经常会遇到特殊的问题或自动化需求;Burp Extender API提供了优异的接口,我们可以通过API编写代码走出困境,或者至少可以让我们避免将捕获的数据从Burp连续复制和粘贴到另一个工具。

源代码

附上可执行的代码。

from burp import IBurpExtender
from burp import IContextMenuFactory

from java.util import ArrayList
from javax.swing import JMenuItem

from datetime import datetime
from HTMLParser import HTMLParser

import re

class TagStripper(HTMLParser):
    def __init__(self):
        HTMLParser.__init__(self)
        self.page_text = []
    
    def handle_data(self, data):
        self.page_text.append(data)
    
    def handle_comment(self, data):
        self.page_text.append(data)
    
    def strip(self, html):
        self.feed(html)
        return " ".join(self.page_text)

class BurpExtender(IBurpExtender, IContextMenuFactory):
    def registerExtenderCallbacks(self, callbacks):
        self._callbacks = callbacks
        self._helpers = callbacks.getHelpers()
        self.context = None
        self.hosts = set()

        # start with something we know is common
        self.wordlist = set(["password"])

        # we set up our extension
        callbacks.setExtensionName("BHP Wordlist")
        callbacks.registerContextMenuFactory(self)

        return
    
    def createMenuItems(self, context_menu):
        self.context = context_menu
        menu_list = ArrayList()
        menu_list.add(JMenuItem(
            "Create Wordlist", actionPerformed=self.wordlist_menu))
        
        return menu_list
    
    def wordlist_menu(self, event):
        # grab the details of what the user clicked
        http_traffic = self.context.getSelectedMessages()

        for traffic in http_traffic:
            http_service = traffic.getHttpService()
            host = http_service.getHost()
            self.hosts.add(host)
            http_response = traffic.getResponse()
            if http_response:
                self.get_words(http_response)
        self.display_wordlist()
        return
    
    def get_words(self, http_response):
        headers, body = http_response.tostring().split('\r\n\r\n', 1)

        # skip non-text responses
        if headers.lower().find('content-type: text') == -1:
            return
        
        tag_stripper = TagStripper()
        page_text = tag_stripper.strip(body)

        words = re.findall(r"[a-zA-Z]\w{2,}", page_text)
        for word in words:
            # filter out long strings
            if len(word) <= 12:
                self.wordlist.add(word.lower())
        return
    
    def mangle(self, word):
        year = datetime.now().year
        suffixes = ["", "1", "!", year]
        mangled = []

        for password in (word, word.capitalize()):
            for suffix in suffixes:
                mangled.append("%s%s" % (password, suffix))
        
        return mangled
    
    def display_wordlist(self):
        print("#!comment: BHP Wordlist for site(%s)" % ", ".join(self.hosts))
        for word in sorted(self.wordlist):
            for password in self.mangle(word):
                print(password)
        return
本文含有隐藏内容,请 开通VIP 后查看