黑帽python第二版(Black Hat Python 2nd Edition)读书笔记 之 第六章 扩展Burp代理(1)配置&Burp模糊测试
文章目录
写在前面
单个web服务器提供多个web应用程序服务并不罕见,只是其中一些应用程序我们可能不知道。如果我们正在渗透web服务器,应该尽最大努力发现这上面其它的hostname,因为这些hostname可能会为我们提供一种更简单的获取shell的方法。在同一台目标服务器上找到不安全的web应用程序以及开发资源并不罕见。Microsoft的Bing搜索引擎具备的搜索功能允许我们使用“IP”关键字查询Bing在单个IP地址上找到的所有网站。如果我们使用“domain”搜索关键字,Bing还会告诉我们该域名下的所有子域。
现在,我们可以编写一个scraper将这些查询提交给Bing,然后在结果中获取HTML,但这是不礼貌的(也违反了大多数搜索引擎的使用条款)。为了避免麻烦,我们将改用Bing API以编程方式提交这些查询并自己解析结果。除了上下文菜单,我们不会用这个扩展实现任何花哨的Burp GUI;每次运行查询时,我们只需将结果输出到Burp中,任何检测到的指向Burp目标范围的URL都会自动添加。
编写bhp_bing.py脚本
定义Burp扩展类BurpExtender
因为我们已经介绍了如何阅读Burp API文档并将其转换为Python,所以让我们直接写代码。创建并打开bhp_bing.py文件,并敲入以下内容:
from burp import IBurpExtender
from burp import IContextMenuFactory
from java.net import URL
from java.util import ArrayList
from javax.swing import JMenuItem
from thread import start_new_thread
import json
import socket
import urllib
API_KEY = 'YOURKEY'
API_HOST = 'api.cognitive.microsoft.com'
class BurpExtender(IBurpExtender, IContextMenuFactory):
def registerExtenderCallbacks(self, callbacks):
self._callbacks = callbacks
self._helpers = callbacks.getHelpers()
self.context = None
# we set up our extension
callbacks.setExtensionName('BHP Bing')
callbacks.registerContextMenuFactory(self)
return
def createMenuItems(self, context_menu):
self.context = context_menu
menu_list = ArrayList()
menu_list.add(JMenuItem(
"Send to Bing", actionPerformed=self.bing_menu))
return menu_list
这是我们Bing扩展的第一部分代码,需要说明的是我们需要确保将Bing API密钥设置到位。Bing API允许每月进行1000次免费搜索。先定义一个BurpExtender类,该类实现标准IBurpExtender接口和IContextMenuFactory,它允许我们在用户右键单击Burp中的请求时提供上下文菜单,本示例中的菜单将显示“Send to Bing”选项。我们注册了一个菜单处理程序,该处理程序确定用户点击了哪个网站,以使得我们可构建Bing查询。接下来,程序设置了一个createMenuItem方法,该方法将接收一个IContextMenuInvocation对象,并使用这个对象来确定用户选择了哪个HTTP请求。最后一步是呈现菜单项并使用bing_menu方法处理单击事件。
创建Bing查询执行函数
现在,让我们执行Bing查询,输出结果,并将任何发现的虚拟主机添加到Burp的目标范围:
def bing_menu(self, event):
# grab the details of what the user clicked
http_traffic = self.context.getSelectedMessages()
print("%d requests highlighted" % len(http_traffic))
for traffic in http_traffic:
http_service = traffic.getHttpService()
host = http_service.getHost()
print("User selected host: %s" % host)
self.bing_serch(host)
return
def bing_search(self, host):
# check if we have an IP or hostname
try:
is_ip = bool(socket.inet_aton(host))
except socket.error:
is_ip = False
if is_ip:
ip_address = host
domain = False
else:
ip_address = socket.gethostbyname(host)
domain = True
start_new_thread(self.bing_query, ("ip:%s" % ip_address,))
if domain:
start_new_thread(self.bing_query, ("domain:%s" % host,))
当用户单击我们定义的上下文菜单项时,bing_menu方法被触发。我们检索突出显示的HTTP请求,并检索每个请求的host部分,然后将其发送到bing_search方法进行进一步处理。bing_search方法首先确定获取到的host部分是IP地址还是主机名,然后查询Bing中与host具有相同IP地址的所有虚拟主机。如果扩展收到的是一个domain,则会对Bing可能索引的任何子目录进行二次搜索。
定义Bing API的请求与解析函数bing_query
现在,我们将安装所需的管道,以便将请求发送到Bing并使用Burp的HTTP API解析返回结果。在BurpExtender类中添加以下代码:
def bing_query(self, bing_query_string):
print('Performing Bing search: %s' % bing_query_string)
http_request = 'Get https://%s/bing/v7.0/search?' % API_HOST
# encode our query
http_request += 'q=%s HTTP/1.1\r\n' % urllib.quote(bing_query_string)
http_request += 'HOST: %s\r\n' % API_HOST
http_request += 'Connection:close\r\n'
http_request += 'Ocp-Apim-Subscription-Key: %s\r\n' % API_KEY
http_request += 'User-Agent: Black Hat Python\r\n\r\n'
json_body = self._callbacks.makeHttpRequest(
API_HOST, 443, True, http_request).tostring()
json_body = json_body.split('\r\n\r\n', 1)[1]
try:
response = json.loads(json_body)
except (TypeError, ValueError) as err:
print('No results from Bing: %s' % err)
else:
sites = list()
if response.get('webPages'):
sites = response['webPages']['value']
if len(sites):
for site in sites:
print('*'*100)
print('Name: %s ' % site['name'])
print('URL: %s ' % site['url'])
print('Description: %r ' % site['snippet'])
print('*'*100)
java_url = URL(site['url'])
if not self._callbacks.isInScope(java_url):
print('Adding %s to Burp scope' % site['url'])
self._callbacks.includeInScope(java_url)
else:
print('Empty response from Bing.: %s' % bing_query_string)
return
小结
Burp的HTTP API要求在发送之前将整个HTTP请求构建为字符串,另外我们还需要添加Bing API Key来进行API调用。然后我们将HTTP请求发送到Microsoft的服务器。当响应返回时,我们分离http头,然后将http头传递给JSON解析器。对于每组结果,我们都将输出一些关于我们发现的站点的信息。如果发现的站点不在Burp的目标搜索范围内,我们会自动添加之。
为此,我们在Burp扩展中混合了Jython API和纯Python,这将有助于我们在攻击特定目标时进行额外的侦察工作。接下来我们试一下。
小试牛刀
要使我们的Bing搜索extension能够正常工作,我们需要使用与前一章节的模糊测试extension相同的过程。加载扩展之后,浏览至http://testphp.vulnweb.com/,然后右键单击刚才发出的GET请求。如果扩展加载正确,我们应该会看到显示菜单项“Send to Bing”,下图所示。
当我们选择Send to Bing菜单时,我们应该会看到来自Bing的结果,我们得到的结果类型取决于我们加载扩展时选择的输出。但是我在执行的时候,得到的结果跟预期不一致,还有错误,如下图所示。
根据以往经验,可能应该还是敲错了代码,先根据错误提示检查一下吧。果真如此,在bing_menu函数中调用bing_search函数的时候,写成了bing_serch,可惜导入extension的时候burp还是木有检查出来,看来以后不能太相信burp协助检查错误了。重新运行,结果正常,不过现在这个站点不会再搜出内容,如下图。
如果单击Burp中的Target选项卡并选择Scope,我们应该会看到新项目自动添加到目标范围,如下图所示。目标范围的作用是仅将攻击、爬行和扫描等活动限制到定义的主机。
说明:不知道是不是因为我在前一步中bing搜索出来为空,我这里的目标范围始终是空的,等有了可测试的站点我再补充。
可执行代码
附上完整的代码。
from burp import IBurpExtender
from burp import IContextMenuFactory
from java.net import URL
from java.util import ArrayList
from javax.swing import JMenuItem
from thread import start_new_thread
import json
import socket
import urllib
API_KEY = '02b3f79965ee4e269ffff43bb03ccb2a'
API_HOST = 'api.cognitive.microsoft.com'
class BurpExtender(IBurpExtender, IContextMenuFactory):
def registerExtenderCallbacks(self, callbacks):
self._callbacks = callbacks
self._helpers = callbacks.getHelpers()
self.context = None
# we set up our extension
callbacks.setExtensionName('BHP Bing')
callbacks.registerContextMenuFactory(self)
return
def createMenuItems(self, context_menu):
self.context = context_menu
menu_list = ArrayList()
menu_list.add(JMenuItem(
"Send to Bing", actionPerformed=self.bing_menu))
return menu_list
def bing_menu(self, event):
# grab the details of what the user clicked
http_traffic = self.context.getSelectedMessages()
print("%d requests highlighted" % len(http_traffic))
for traffic in http_traffic:
http_service = traffic.getHttpService()
host = http_service.getHost()
print("User selected host: %s" % host)
self.bing_search(host)
return
def bing_search(self, host):
# check if we have an IP or hostname
try:
is_ip = bool(socket.inet_aton(host))
except socket.error:
is_ip = False
if is_ip:
ip_address = host
domain = False
else:
ip_address = socket.gethostbyname(host)
domain = True
start_new_thread(self.bing_query, ("ip:%s" % ip_address,))
if domain:
start_new_thread(self.bing_query, ("domain:%s" % host,))
def bing_query(self, bing_query_string):
print('Performing Bing search: %s' % bing_query_string)
http_request = 'GET https://%s/bing/v7.0/search?' % API_HOST
# encode our query
http_request += 'q=%s HTTP/1.1\r\n' % urllib.quote(bing_query_string)
http_request += 'Host: %s' % API_HOST
http_request += 'Connection:close\r\n'
http_request += 'Ocp-Apim-Subscription-Key: %s\r\n' % API_KEY
http_request += 'User-Agent: Black Hat Python\r\n\r\n'
json_body = self._callbacks.makeHttpRequest(
API_HOST, 443, True, http_request).tostring()
json_body = json_body.split('\r\n\r\n', 1)[1]
try:
response = json.loads(json_body)
except (TypeError, ValueError) as err:
print('No results from Bing: %s' % err)
else:
sites = list()
if response.get('webPages'):
sites = response['webPages']['value']
if len(sites):
for site in sites:
print('*'*100)
print('Name: %s ' % site['name'])
print('URL: %s ' % site['url'])
print('Description: %r ' % site['snippet'])
print('*'*100)
java_url = URL(site['url'])
if not self._callbacks.isInScope(java_url):
print('Adding %s to Burp scope' % site['url'])
self._callbacks.includeInScope(java_url)
else:
print('Empty response from Bing.: %s' % bing_query_string)
return