黑帽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()
self.wordlist = set(["password"])
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):
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)
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:
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()
self.wordlist = set(["password"])
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):
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)
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:
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
|