能够成功地将红队和渗透测试人员区分的一个特征是能否适应和了解不同的安全防护机制。分析底层汇编语言,编写shellcode,创建自定义命令和控制二进制文件,修改代码空间隐藏恶意软件,这些都是日常工作的一部分。我经常遇到不会编码的渗透测试人员,虽然编码不是必需的,但是确实会影响他们进一步的专业发展。因此,我想为没有真正使用过底层语言开发代码的读者,专门写一章内容,帮助他们开始编写代码。
键盘记录器是任何渗透测试人员/红队必备的工具,本节将指导您制作一个通用键盘记录器。有时我们只想持续监控某个用户或获取凭证。这可能是因为无法获得任何类型的横向渗透/权限提升,或者我们可能只想监视用户,为后续的行动做准备。在这样的情况下,我们喜欢在被攻击者系统上放置并运行键盘记录器,并将他们的键击记录发送出去。以下的例子只是一个原型系统,本实验的目的是让您了解原理和构建方法。使用C语言的原因是生成的二进制文件相对较小,并且由于是底层语言,可以更好地访问操作系统,并且可以规避杀毒软件检测。在本书第2版中,我们使用Python语言编写了键盘记录器,并使用py2exe编译成二进制文件,但是生成的文件容易被检测到。下面让我们来看一个稍微复杂的例子。
下面是使用C语言编写和编译,生成Windows二进制文件,并创建自定义键盘记录器所需的基本设置。
到目前为止,Windows API编程的推荐资源是微软公司的MSDN网站。MSDN是一个非常宝贵的资源网站,详细说明了系统调用、类型和结构定义,并包含了许多示例。虽然这个项目并不一定需要这些资源,但通过阅读微软公司出版的Windows Internals一书,可以更深入地了解Windows操作系统。对于C语言,则可以参考《C语言设计语言》一书,作者是Kernighan和Ritchie。另外,也可以阅读Beej的《网络编程指南》(有印刷版和在线版),这是C语言网络编程的一本很好的入门读物。
在下面这些实验中,将会有多个代码示例。本实验室将使用微软公司的Optimizing Compiler工具编译代码,该编译器随Visual Studio Community一起提供,并内置在Visual Studio Developer命令提示符中。在安装Visual Studio Community工具后,需确保安装通用Windows平台开发环境和工具,配置工具和C++桌面开发环境。如果编译示例,那么需打开开发人员命令提示符,然后导航到包含源文件的文件夹。最后,运行命令“cl sourcefile.c io.c”。这将生成一个与源文件同名的可执行文件。
编译器默认编译32位应用程序,但此代码也可以编译成64位应用程序。要编译成64位应用程序,需运行位于Visual Studio文件夹中的批处理脚本。在命令提示符中,导航到“C:\Program Files(x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build”,需要注意此路径可能会有所不同,具体取决于您的Visual Studio版本。然后,运行命令“vcvarsall.bat x86_amd64”,这将设置微软编译器编译64位可执行文件而不是32位可执行文件。现在,您可以通过运行“cl path/to/code.c”编译代码。
该项目的目标是创建一个使用C语言和底层Windows功能来监视击键的键盘记录器。该键盘记录器使用SetWindowsHookEx和LowLevelKeyboardProc函数。SetWindowsHookEx允许在本地和全局上下文中设置各种类型的钩子。在这种情况下,WH_KEYBOARD_LL参数用于获取底层键盘事件。SetWindowsHookEx的函数原型如下所示。
HHOOK WINAPI SetWindowsHookEx(
_In_ int idHook,
_In_ HOOKPROC lpfn,
_In_ HINSTANCE hMod,
_In_ DWORD dwThreadId
);
SetWindowsHookEx函数采用整数表示钩子ID、指向函数的指针、句柄模块和线程ID,前两个值很重要。钩子ID是安装的钩子类型的整数标识。Windows功能页面上列出可用ID。在我们的例子中,使用ID 13或WH_KEYBOARD_LL。HOOKPROC是一个指向回调函数的指针,每次挂了钩子的进程接收数据都会调用该函数。这意味着每次按下一个键,都会调用HOOKPROC。这个函数用于将键值写入文件。hMod是DLL的句柄,包含lpfn指向的函数。此值将设置为NULL,因为函数与SetWindowsHookEx在同一进程中使用。dwThreadId设置为0,将与桌面应用程序的所有线程回调相关联。最后,该函数返回一个整数,该整数将用于验证钩子是否设置正确,如果设置不正确则退出。
第二部分是回调函数。回调函数实现程序大量的功能。此函数接收处理按键信息,将其转换为ASCII字母以及所有文件操作。LowLevelKeyboardProc的原型如下所示。
LRESULT CALLBACK LowLevelKeyboardProc(
_In_ int nCode,
_In_ WPARAM wParam,
_In_ LPARAM lParam
);
让我们回顾一下LowLevelKeyboardProc参数的内容。该函数的第一个参数是一个整数,告诉Windows如何解释该消息。其中两个参数是,①wParam,消息的标识符;②lParam,它指向KBDLLHOOKSTRUCT结构的指针。wParam的值需在函数参数中指定。参数lParam指向KBDLLHOOKSTRUCT成员。lParam KBDLLHOOKSTRUCT的值是vkCode或虚拟键盘码。这是按下键的代码,而不是实际的字母,因为字母可能会根据键盘语言的不同而有所不同。vkCode需要随后转换为相应的字母。现在,不需要担心参数传递给键盘回调函数,因为钩子激活后,操作系统自动传递参数。
在查看框架代码时,需要注意的事项是,在回调函数中,包含pragma注释行、消息循环和返回CallNextHookEx行。pragma注释行是用于链接User32 DLL的编译器指令。此DLL包含程序所需的大多数函数调用,因此需要进行链接。它也可以与编译器选项相关联。接下来,如果需要使用LowLevelKeyboardProc函数,则必须使用消息循环。MSDN声明:“此钩子在安装它的线程的上下文中调用。通过向安装了钩子的线程发送消息来进行调用。因此,安装钩子的线程必须有一个消息循环。”
返回CallNextHookEx是因为MSDN的声明:“调用CallNextHookEx函数链接到下一个挂钩过程是可选的,但是强烈推荐使用;否则,已安装挂钩的其他应用程序将不会收到挂钩通知,因此可能会出现错误行为。您应该调用CallNextHookEx,除非您需要阻止其他应用程序看到通知。”
接下来,我们继续构建回调函数,从文件句柄开始。在示例代码中,它在Windows Temp目录(C:\Windows\Temp)创建名为“log.txt”的文件。该文件配置了append参数,因为键盘记录器需要不断地将按键记录输出到文件。如果temp中不存在该文件,则将创建一个文件。
回到KBDLLHOOKSTRUCT,代码声明了一个KBDLLHOOKSTRUCT指针,然后将其分配给lParam。这将允许访问每个按键的lParam内的参数。然后代码检查wParam是否返回“WM_KEYDOWN”,即检查按键是否被按下。这样做是因为钩子会在按下和释放按键时触发。如果代码没有检查WM_KEYDOWN事件,那么程序将每次写入两次按键操作。
发现按键操作后,需要一个switch语句,检查lParam的vkCode(虚拟键码),获取按键值。某些键需要以其他方式写入文件,例如return、control、Shift、Space和Tab键。对于默认情况,代码需要将按键的虚拟键码转换为实际的字母。执行此转换的简单方法是使用ToAscii函数。ToAscii输入参数vkCode、一个ScanCode、一个指向键盘状态数组指针、指向接收字母缓冲区的指针,以及uFlags的整数值。vkCode和ScanCode来自键结构,键盘状态是先前声明的字节数组,用于保存输出的缓冲区,uFlags参数设置为0。
必须检查是否释放了某些键,例如Shift键。这可以通过编写另一个“if语句”来检查“WM_KEYUP”,然后使用“switch语句”来检查所需的按键。最后,需要关闭该文件并返回CallNextHookEx。
此时,键盘记录器完全正常工作。但是,有两个问题。一个问题是运行程序会产生一个命令提示符,这表明程序正在运行,并且缺少输出的内容,容易让人产生怀疑。另一个问题是运行键盘记录器获得文件仅放在本地计算机上,意义不是很大。
命令提示问题的修复相对容易,具体做法是修改标准C“Main”函数入口点为Windows特定的WinMain函数入口。根据我的理解,这样做很有效的原因是WinMain是Windows上图形程序的入口点。虽然操作系统预期是创建程序窗口,但我们可以告诉操作系统不创建任何窗口,因为有这个控件。现在,该程序只是在后台生成一个进程,不创建任何窗口。
该程序网络方面的问题可以更加直接地进行解决。首先通过声明WSAData,启动Winsock,清除提示结构以及填充相关参数,初始化Windows套接字函数。举个例子,代码将AF_UNSPEC用于IPv4,SOC_STREAM用于TCP连接,使用getaddrinfo函数填充命令和控制数据结构。在填写所有必需的参数后,可以创建套接字。最后,使用socket_connect函数创建套接字。
连接之后,socket_sendfile函数将执行大部分操作。它使用Windows“CreateFile”函数打开日志文件的句柄,然后使用“GetFileSizeEx”函数获取文件大小。一旦获取了文件大小,代码将分配一个文件大小的缓冲区,加上一个用于填充的缓冲区,然后将文件读入该缓冲区。最后,我们通过套接字发送缓冲区的内容。
对于服务器端,在命令和控制服务器上启动socat监听3490端口(启动socat命令:socat - TCP4-LISTEN:3490,fork)。一旦监听器启动并且键盘记录器正常运行,您就会看到被攻击者主机的所有命令,并且每10 min被推送到命令和控制服务器。在编译version_1.c之前,确保将getaddrinfo修改为当前的命令和控制服务器的IP地址。编译代码:cl version_1.c io.c。
需要介绍的最后一个函数是thread_func函数。thread_func调用函数get_time,获取当前时间。然后检查该值是否可被5整除,因为该工具每5min发送一次文件。如果它可以被5整除,那么它会设置套接字并尝试连接命令和控制服务器。如果连接成功,那么它将发送文件并运行清理功能。然后,循环休眠59 s。需要休眠功能的原因是这一切都在一个稳定的循环中运行,这意味着该函数将在几秒钟内运行,建立连接,连接和发送文件。如果没有59s的休眠时间,那么该函数最终可能会在1 min的间隔内发送文件数10次。休眠函数允许循环等待足够长的时间,切换到下一分钟,因此仅每5 min发送一次文件。
有数百种不同的方法来执行混淆。虽然本章不能全部涉及,但我想为您介绍一些基本的技巧和思路来规避杀毒软件。
您可能已经知道,杀毒软件会查找特定的字符串。规避杀毒软件的一种简单方法是创建一个简单的转盘密码,移动字符串的字符。在下面的代码中,有一个基本的解密函数,可以将所有字符串移动6个字符(ROT6)。这会导致杀毒软件可能无法检测到乱码。在程序开始时,代码将调用解密函数,获取字符串数组,返回到常规格式。解密函数如下所示。
int decrypt(const char* string, char result[]){
int key = 6;
int len = strlen(string);
for(int n = 0; n < len; n++){
int symbol = string[n];
int e_symbol = symbol - key;
result[n] = e_symbol;
}
result[len] = '\0';
return 0;
}
另一种规避杀毒软件的方法是使用函数指针调用User32.dll中的函数,而不是直接调用函数。为此,首先编写函数定义,然后使用Windows GetProcAddress函数找到要调用的函数的地址,最后,将函数定义指针指定给从GetProcAddress接收的地址。可以在CitHub找到如何使用函数指针调用SetWindowsHookEx函数的示例。
该程序的第3个版本将前一个示例中的字符串加密与使用指针调用函数的方法相结合。有趣的是,如果您将已编译的二进制文件提交到VirusTotal,那么看不到User32.dll。在图7.1中,左侧图像显示的是版本1,右侧图像显示的是带有指针调用的版本3。
图7.1
为了查看您是否已成功规避杀毒软件,最好的选择是始终在实际运行的杀毒软件系统中进行测试。在实际的行动中,我不建议使用VirusTotal,因为您的样本可能会被发送给不同的安全厂商。但是VirusTotal网站非常适合测试/学习。
实验
您的最终目标是什么?想法是无限的!一点点修复可能是对log.txt内容进行混淆/加密,或者在程序启动后,启动加密套接字,然后将按键内容写入套接字。在接收方,服务器将重建流,写入文件。这将阻止日志数据以纯文本形式显示,就像当前一样,并且可以防止之前的内容写入硬盘。
另一个非常明显的改进是将可执行文件转换为DLL,然后将DLL注入正在运行的进程,使得进程不会显示在任务管理器中。有一些程序可以显示系统上所有当前加载的DLL,因此注入DLL会更加隐蔽。此外,有些程序可以反射性地从内存加载DLL而根本不写入磁盘,从而进一步降低了被取证的风险。
放置工具是红队工具包的重要组成部分,允许您在被攻击者计算机上植入程序。不在磁盘上放置植入程序是为了降低被发现的风险,并且可以使用多次。在本节中,我们将介绍一个黑客秘笈定制的放置工具,它可以植入shellcode或者是仅驻留在内存中的动态库。
在设计放置工具和相应的服务器时,您需要记住一些事项。放置工具是工具箱中阅后即焚(use-and-burn)的一个工具,这意味着您当前可以正常使用,但是在后续的行动中很可能被检测发现。
为了使后续行动更容易,您需要开发一个标准服务器,可以重复使用。在这个例子中,您将看到一个基本的网络实现框架,它允许为不同的消息注册新的处理程序。这个例子仅包含LOAD_BLOB消息类型的处理程序,您可以轻松添加新的处理程序,从而扩展功能。这就可以提供良好的基础,因为您的所有通信都实现了标准化。
编写放置工具程序或者快速找到目标并进行逆向工程,一个重要的步骤是过滤字符串。当您第一次构建软件时,调试消息非常有用,可以让您不必手动单步调试,查看出现问题的原因。但是,如果调试信息在最终版本中被意外地保留下来,那么软件分析人员将很容易逆向利用您的恶意软件。很多时候,杀毒软件将对独特的字符串或常量值进行签名。举个例子,我使用InfoLog()和ErrorLog()函数,预处理器将在发布版本中编译这些宏。使用这些宏,检查是否定义了_DEBUG,并指示是否包含相关的调用。
在下面的例子中,您可以让放置工具加载完整的DLL或者shellcode。通常有许多公开的植入工具,您可以生成一个完整的DLL,实现DLL下载并且执行。放置工具直接加载DLL,可以使您省略加载更多的API调用,从而保持隐蔽。由于头部信息被修改,因此某些植入工具可能无法正确加载。如果您的一个植入工具不能正常工作,但是包含生成shellcode的方法,那么这应该可以解决您的问题。这是因为定制的加载器,通常可以修复头部信息,并从该DLL加载头部信息。
网络中还有大量的shellcode,如shell-storm这样的网站,保存大量的shellcode,其中一些可能会在您后续的行动中派上用场。
构建服务器其实比较简单。在定制的黑客秘笈 kali镜像中,您需要运行以下命令。
第一次编译。
对于后续编译,您需要进行的步骤如下。
运行服务器,在编译完成后,您需输入如下内容。
表7.1为值所对应的当前适用的加载类型。
表7.1
0 |
shellcode |
发送原始shellcode字节到客户端 |
---|---|---|
1 |
DLL |
发送正常DLL文件,客户端反射注入DLL |
虽然静荷(shellcode/DLL)可以来自任何类型的命令和控制工具(Metasploit/Meterpreter、Cobalt Strike等),但我们将在示例中使用Meterpreter静荷。生成静荷的步骤如下。
客户端与服务器的运行方式是相似的,其中客户端为每种消息类型注册处理程序。在启动时,客户端尝试回连服务器,如果无法连接或连接断开了,则重试n次,并发送消息要求加载模块。服务器使用BLOB_PACKET进行响应,客户端通过head->msg字段识别并分发该数据包。所有数据包必须在开始时定义HEAD_PACKET字段,否则网络处理程序将无法识别,从而丢弃数据包。使用BuildPacketAndSend()函数正确设置数据包头部,从而允许另一方解码数据包。
要构建客户端,您需要Visual Studio和Git工具。首先将Git存储库(https://github.com/cheetz/ thpDropper.git)复制到一个文件夹中,然后在Visual Studio中打开thpDropper.sln。确保放置设备的代码设置了正确的体系结构,如果您不需要任何调试消息,那么可设置为发布版本。完成此操作后,按F7键,Visual Studio将为用户生成可执行文件。
大多数客户端的配置都可以在globals.cpp文件中找到,您需要更改的3个主要配置是主机名、端口和数据包持续时间。每个配置选项旁边都有注释,说明配置项的内容。您不需要更改数据包签名,如果更改数据包签名,那么发送的每个数据包的前两个字节将被修改,用于标识这是服务器上的有效连接。如果您希望对IP地址和端口进行模糊处理,则可以编写代码,在访问IP地址和端口时,对数据进行解密,在二进制文件中仅存储加密版本。
在服务器端的main.cpp文件中,您可以修改服务器监听的端口。此配置是main函数中StartupNetworking()的唯一参数。如果您修改客户端中的数据包签名,则需要修改服务器对应该数据包。这意味着在include/lib/networking.h文件中,PACKET_SIGNATURE值需要与客户端中的全局值匹配。
网络代码库允许您轻松添加新功能。为此,您需要使用客户端的void name()原型函数或服务器上的void name(int conn)原型函数创建一个回调函数。对于各种消息类型,注册一系列的处理程序,在验证数据包头部时,这些处理程序将被调用。在这些函数中,您需要实现从recv缓冲区中读取数据包和数据。您需要调用recv()指针,处理数据包结构和大小。这将获取recv缓冲区相关信息。在这个例子中,您将看到我们在处理程序中读取BLOB_PACKET,存储在packet.payloadLen中的值,表明读取的字节数。同样的方法适用于获取其他数据类型。如果将包含文件路径的字符串发送到被攻击者计算机上的某个文件,数据包中包含一个字段用于描述字符串的长度,该字段随数据包一同发送。
上面的代码提供开发的基础,您可以通过多种方式自行改进。在传输层上添加加密层非常简单和方便。您可能希望创建自己的send和recv管理器,在调用send和recv函数之前解密/加密。一种非常简单的方法是使用多字节XOR密钥,虽然不是很安全,但是由于改变了您的消息内容,所以不容易被识别。另一个练习可能是扩展LoadBlobHandler()函数,添加新的LOAD_TYPE,如果客户端以管理员身份运行,则会加载已签名的驱动程序。这可以通过使用CreateService()和StartService()windows api调用实现。但是需要记住,加载驱动程序需要文件存储在磁盘上,这将触发文件系统-过滤器驱动程序,捕获该操作。
络检测 这个话题有些复杂,您很可能在编译过程中遇到一些问题。有很多值得推荐的工具,如Metasploit/Meterpreter,每个杀毒软件和网络入侵检测(NID)工具都对这些工具进行签名。我们可以尝试使用Shikata Ga Nai软件对静荷进行混淆,并通过HTTPS进行混淆,这是目前常用的方式。任何类型的混淆通常都会有一个根签名,可以被发现和检测,杀毒软件在内存特定位置查看特定的字符串,网络设备实施中间人策略对HTTPS通信内容进行检查。那么如何才能持续使用我们选择的工具,同时绕过所有常见的安全防护机制呢?以Metasploit/Meterpreter为例,介绍一下如何绕过所有这些障碍。我们的目标是绕过二进制文件的杀毒软件签名、内存中的杀毒软件签名和网络流量签名。
为了规避所有的检测方法,我们需要做一些事情。首先,修改Meterpreter静荷,确保在网络流量和内存数据中,无法基于签名检测静荷。然后,修改metsvc持久性模块,防止被杀毒软件标识。接着,我们使用Clang编译部分metsrv(实际的Meterpreter静荷),同样防止被杀毒软件标识。最后,编写自己的stage0静荷,下载执行Meterpreter,规避所有的杀毒软件。
使用Clang编译metsrv(Meterpreter的网络服务包装器),删除metsrv/metsvc-server引用。
修改静荷,去掉类似于Mimikatz的字符串。
修改反射性DLL注入字符串,删除类似于ReflectiveLoader的字符串。
当Meterpreter在网络传输时,许多网络产品检测Meterpreter的0/1/2级加载模块。除了混淆静荷,我们还可以对实际的shellcode进行混淆。一个例子是遍历所有Ruby文件,获取不同的静荷类型,并添加随机nop字符,规避检测。
自定义Stage0静荷。
实验
在本实验中,我们将修改Metasploit/Meterpreter代码,重新编译它,确保可以规避基本的杀毒软件检测。
在开始前,先查看Metasploit的编译环境。
Windows的环境设置如下所示。
首先获取所有cyberspacekitten的存储库。作为原型系统,这些文件在实验室已经做了较大的修改。首先,我们需要下载框架和所有的静荷。
在存储库中修改字符串,采用Clang编译器进行编译,添加静荷nops,务必查看存储库之间的Metasploit差异,确切了解更改的内容。
编译Metasploit / Meterpreter
我们要做的第一件事,使用更新内容重新编译metsvc和metsvc-server。在Visual Studio 2013中运行其命令提示符,如下所示。
将新创建的二进制文件移动到meterpreter文件夹。
接下来,修改Meterpreter静荷,使用提供的.bat文件进行编译。
编译所有内容后,生成两个文件夹(x86和x64)。将所有已编译的DLL复制到meterpreter文件夹。
这就是服务器版本的meterpreter。我们现在可以将整个metasploit-framework文件夹移动到Kali系统,启动反向HTTPS处理程序(windows/x64/meterpreter/reverse_https)。
我们需要做的最后一件事是创建一个Stage 0静荷,让最开始的可执行文件绕过所有杀毒软件检测。您可能不是很了解,Meterpreter中的Stage 0是任何漏洞利用或静荷的第一阶段。这是一段代码,完成一件很简单的事情:以我们想要的方式(reverse_https、reverse_ tcp和bind_tcp等)回连或者监听,然后接收metsrv.dll文件。然后,加载这个文件并执行。从本质上来讲,任何Stage 0静荷只是一个美化的“下载并执行”静荷。这是Metasploit的所有功能的基础,在许多杀毒软件解决方案中都有针对Metasploit特定行为的高级签名技术和启发式检测方法,甚至修改shellcode并添加垃圾代码,仍然由于启发式检测而被标记。为了解决这个问题,我们编写了自己的Stage 0,执行同样的功能(在内存中下载和执行):复制Meterpreter的reverse_https静荷的下载代码,从服务器获取metsrv.dll,然后在内存中存储并执行。
此处提供的具体静荷例子,具有一些更复杂的功能。这些静荷实现了位置无关,无须导入函数。这个代码是基于thealpiste的代码进行开发的(https://github.com/thealpiste/ C_ReverseHTTPS_Shellcode)。
提供的示例执行以下操作。
此功能实现的过程与msfvenom构建静荷过程是相同的。但是,msfvenom将这个过程添加到生成可执行文件模板中,采用的是可预测和检测的方式,但是不可配置。因此,大多数杀毒软件能够识别这些可执行文件。相反,通过一些编码技术,您可以重新设计静荷的功能,因为静荷很小,并且可以绕过当前存在的杀毒软件检测。在撰写本书时,静荷可以规避所有杀毒软件,包括Windows Defender。
创建静荷过程如下所示。
您现在拥有一个深度混淆的Meterpreter二进制文件,传输层也进行混淆,绕过所有默认的保护机制。现在,这只是一个入门的原型系统。本书发行后,其中一些技术会被检测生成签名。您还可以采取更多的措施,规避检测工具。例如,您可以进行如下操作。
作为红队,较耗时的工作之一是创建静荷,规避下一代杀毒软件和沙箱。我们一直在寻找新的方法,创建初始入口。一个名为SharpShooter的工具采用了许多反沙箱技术,James Forshaw编写的DotNetToJScript可用来执行Windows脚本格式的shellcode(CACTUSTORCH工具见GitHub相关网页)。
MDSec网站介绍了SharpShooter:“SharpShooter支持分阶段和无阶段静荷执行。分阶段执行可以采用HTTP(S)、DNS或两者进行传输。当执行分阶段静荷时,它尝试检索C Sharp压缩的源代码文件,然后使用所选择的传递技术,进行base64编码。下载C Sharp项目源代码,在主机上使用.NET CodeDom编译器进行编译。然后,使用反射方法从源代码执行所需的方法。”
下面我们看一个简单的例子。
配置和开发恶意软件后,将其移至Web目录(malware.hta、malware.html、malware. payload),启动Apache 2服务,然后启动Meterpreter处理程序。现在采用社会工程学方法,引诱被攻击者访问恶意网站!上面给出的示例是SharpShooter的SharePoint在线模板。当被攻击者使用IE/Edge访问您的恶意页面时,HTA会自动下载并提示运行。弹出窗口后,如果选择运行,将运行静荷,下载第二静荷(如果规避沙箱监控),并在内存中执行Meterpreter静荷,如图7.2所示。
图7.2
我们已经讨论了在不运行PowerShell代码的情况下触发PowerShell的不同方法,但如果您无法在Windows系统上运行自定义二进制文件,该怎么办?应用程序规避的原理是找到默认的Windows二进制文件,执行静荷。我们登录类似域控制器设备,但是系统被锁定,代码执行受到限制。我们可以使用不同的Windows文件来绕过这些限制,让我们来看看其中的几个文件。
一个经常被讨论的Windows二进制文件是MSBuild.exe,实现绕过应用程序白名单。什么是MSBuild.exe,它有什么作用?MSBuild是.NET Framework中的默认应用程序,使用XML格式的项目文件,构建.NET应用程序。我们可以利用这个功能,使用名为GreatSCT的工具,创建自己的恶意XML项目文件,执行Meterpreter会话。
GreatSCT(见GitHub中的GreatSCT网页)包括各种应用程序白名单绕过方法,这里我们只介绍MSBuild。在这个例子中,我们创建恶意的XML文件,该文件承载一个reverse_http Meterpreter会话。这将要求我们在被攻击系统中写入XML文件,使用MSBuild执行XML文件,如图7.3所示。
图7.3
在Kali实例中,我们使用GreatSCT创建shellcode.xml文件,该文件包含构建信息和Meterpreter反向HTTP Shell。需要将此文件移动到被攻击系统,并使用MSBuild进行调用,如图7.4所示。
图7.4
注意:我确实看到GreatSCT正在“开发”分支(见GitHub中GreatSCT页面的tree/develop子页面)上积极构建,其中包括HTTPS Meterpreter和其他白名单绕过机制。我猜测在本书出版之后,它将被转移到“主版本”。
一旦在被攻击的计算机的Windows上具备执行权限,使用命令“C:\Windows\Microsoft. NET\Framework\v4.0.30319\MSBuild.exe shellcode.xml”,.NET将开始构建shellcode.xml文件。在这个过程中,被攻击的计算机将生成反向HTTP Meterpreter会话,绕过任何应用白名单机制,如图7.5所示。您可能希望编辑shellcode.xml文件,放入混淆的静荷,因为默认的Meterpreter很可能会触发杀毒软件。
有许多不同的方法可以绕过应用程序白名单机制,这些方程足够编写为一本书。
图7.5
与任何红队活动一样,我们一直在寻找富有创造性的方式,在环境中横向移动或长期控制。通常情况下,如果掌握凭证,我们就会尝试使用WMI或PsExec在远程系统上执行静荷。有时,我们需要采用富有创造性的方式,在一个环境中移动而不被轻易跟踪。
作为红队,在行动中被发现,可能并不是最糟糕的事情。最糟糕的事情是被发现,并且蓝队发现行动中的域名、IP地址和突破的主机。蓝队通过查看WMI/PsExec连接,识别横向移动,因为这些流量看起来不是正常的流量。那么,我们可以做些什么来隐藏横向移动呢?
这是我们发挥创造力的地方,并且没有正确的答案(如果有效,那么对我来说已经足够了)。我最喜欢做的事情就是在环境中发现共享目录和主动共享/执行的文件。我们可以尝试在Office文件中添加宏,但这可能太明显了。将定制恶意软件嵌入可执行二进制文件,这种攻击方式被发现的概率较低,成功率高。这可以是类似PuTTY的共享二进制文件,一个常见的内部胖客户端,甚至是数据库工具。
执行这些攻击的一个简单工具是Backdoor Factory,虽然它不再维护。Backdoor Factory在真实程序中查找代码洞或空块,攻击者可以在其中注入自己的恶意shellcode。本书第2版介绍了这项技术。
PowerShell 脚本现在的问题是,如果您将脚本放到磁盘上,那么很多杀毒软件都会查杀脚本。即使您将脚本导入内存,杀毒软件通过查看内存,也可能发出警报。
无论如何,如果您从Cobalt Strike、Meterpreter或PowerShell Empire将脚本导入内存,那么需确保不会被杀毒软件发现。如果我们将脚本导入内存,那么至少应急响应/取证团队应该很难逆向分析我们的攻击静荷。
让我们来查看PowerShell的命令,如下所示。
这是我们看到的最基本的字符串组合,可以绕过执行策略,隐藏运行/非交互,以及下载和执行PowerShell静荷。对于蓝队,我们已经看到有很多日志,记录这些特定的参数,例如“-Exec Bypass”。因此,我们通过一些常见的PowerShell语法混淆这些参数。
更疯狂的是,我相信Daniel Bohannon识别出了这个,您根本不需要完整的字符串来完成上述操作。例如,对于-ExecutionPolicy Bypass,以下的这些例子都将能正常工作。
这些技术同样适用于WindowStyle甚至EncodedCommand参数。当然,这些技巧目前是可以使用的,我们需要创建更多混淆变换的方法。首先,我们提供一个非常简单的示例,使用PowerShell管理命令行,执行我们的远程PowerShell脚本(在本例中是mimikatz),实现转储散列的功能。
使用(Invoke-Obfuscation),输入字符串,使用几种不同的技术对字符串进行深度混淆。
图7.6
如果查看混淆的字符串,会发现有一个随机生成的密钥和加密的安全字符串。执行管理员权限PowerShell,我们仍然可以执行完整的静荷,如图7.7所示。
图7.7
回到主屏幕,创建混淆的加载器,如图7.8所示。
图7.8
launcher。
CLIP++。
更好的是,我们可以查看Windows PowerShell日志,但它非常隐蔽,对于规避杀毒软件和安全信息工具警报非常有帮助,如图7.9所示。
图7.9
除Invoke-Obfuscation工具外,Daniel还研制了一个Invoke-CradleCrafter工具,实现远程下载的功能。Invoke-CradleCrafter工具为蓝队和红队开展研究、生成和混淆PowerShell远程下载提供了支持。此外,这个工具有助于帮助蓝队测试Invoke-Obfuscation输出结果的有效性。Invoke-CradleCrafter的缺陷是不包含任何字符串连接、编码、标识和类型转换等功能。
您最终在一个设备上获得远程代码执行权限,但您发现无法运行PowerShell.exe或者公司正在监视PowerShell.exe命令。您有什么办法让PowerShell静荷或者命令和控制代理在主机系统上运行?
我喜欢NoPowerShell(NPS)的概念。NPS是一个Windows二进制文件,它通过.NET执行PowerShell,而不是直接调用PowerShell.exe。虽然目前杀毒软件通常会对操作行为进行标记,但是我们可以使用相同的思路创建二进制文件,直接执行PowerShell恶意软件,无须运行PowerShell.exe。由于Ben0xA提供了源代码,因此可以尝试对二进制文件进行混淆处理,从而规避杀毒软件。
另外一个使用NPS原理的是TrustedSec工具,它利用了通过MSBuild.exe执行代码的优势。此工具将PowerShell静荷生成到msbuild_nps.xml文件中,该文件在调用时执行。XML文件可以通过以下方式调用。
SharpPick是PowerPick的一个组件,它是一个值得推荐的工具,允许调用PowerShell功能,而无须使用PowerShell.exe二进制文件。在SharpPick中,RunPS函数使用System.Management.Automation函数在PowerShell运行空间中执行脚本,无须启动PowerShell进程。
下载SharpPick后,您可以使用PowerShell Empire静荷,创建二进制文件。
有时可能无法在主机系统上放置二进制文件。在这些情况下,可以创建一个类库(DLL文件),我们可以将DLL文件放到系统,并使用“rundll32.exe runmalicious.dll,EntryPoint”执行。
当然,可以使用Meterpreter或Cobalt Strike自动创建这些DLL文件,从而可以灵活地运行特定的PowerShell静荷,而无须调用PowerShell.exe。
几年前,我制作了一个工具HideMyPS,取得了非常好的效果。它始终只是一个POC工具,但即使经过这么多年,它仍然可以工作。我遇到的问题是,现在PowerShell脚本都会被杀毒软件查杀。例如,如果我们在运行Windows Defender的Windows系统中,删除正常的Invoke-Mimikatz.ps1,它将立即查杀PowerShell脚本,并在相应位置标记红色。这是传统杀毒软件的一个主要缺点,杀毒软件通常在恶意软件中寻找特定的字符串。因此,我整理了一个小的Python脚本,该脚本采用PowerShell脚本对所有字符串进行混淆处理(由于仅仅使用少量的脚本进行测试,因此它远达不到生产代码标准)。
HideMyPS将查找所有函数,并使用ROT对其进行混淆处理,从PowerShell脚本中删除所有注释,剪切字符串规避杀毒软件的静态签名查杀。下面的例子中,我们将使用Invoke_ Mimikatz.ps1,混淆PowerShell文件,如图7.10所示。
图7.10
现在,查看原始文件和创建的新文件之间的区别。首先,您可以看到函数名称全部混淆,变量已经更改,字符串被分成两半,并且所有注释都删除了,如图7.11所示。
图7.11
您必须记住的一件事是我们更改了PowerShell脚本中的所有函数名称。因此,为了调用这些函数,需要重新查看混淆后的文件,看一看我们是如何替换“function Invoke-Mimikatz”的。在这种情况下,Invoke-Mimikatz更改为Vaibxr-Zvzvxngm。以下示例是在打了完整补丁的Windows 10中运行的,其中Defender已更新至较新的病毒库,如图7.12所示。
图7.12
作为红队或者渗透测试人员,总是需要与主机和网络检测工具进行“猫捉老鼠”的游戏。这就是为什么需要理解防护系统底层的工作原理、编写底层代码来直接与Windows API进行交互而不是使用Shell命令、跳出设备本身进行思考并发挥创造性等非常重要。如果您仅仅专注于使用常规的工具,那么在企业环境中被检测发现的可能性非常高。如果这些工具是公开的,那么在该工具出现后,安全厂商可能会逆向分析这些工具,并生成工具的签名。作为红队,在实际的攻击中,您需要利用系统漏洞,定制开发工具,防止工具被安全厂商识别。