本帖最后由 梦幻的彼岸 于 2021-5-27 11:10 编辑
备注
原文地址:https://evasions.checkpoint.com/techniques/network.html
原文标题:Evasions: Network
更新日期:2021年5月27日
此文后期:根据自身所学进行内容扩充
因自身技术有限,只能尽自身所能翻译国外技术文章,供大家学习,若有不当或可完善的地方,希望可以指出,用于共同完善这篇文章。
目录
- 所使用的网络检测方法
- 1. 具体的网络属性
- 1.1. 检查MAC地址是否是特定的
- 1.2. 检查适配器名称是否是特定的
- 1.3. 检查网络共享的提供者的名称是否是特定的
- 2. 检查网络是否属于安全范围
- 3. 基于NetValidateName结果的反伪装技术
- 4. 基于Cuckoo ResultServer连接的反伪装技术
- 识别标志:
- 反制措施
- 归功于
网络检测方法
这一组的规避技术与网络有着某种联系。要么使用与网络有关的功能,要么检查网络参数--如果它们与通常的主机操作系统不同,那么虚拟环境很可能被检测到。
1. 具体的网络属性
不同虚拟环境的供应商为他们的产品硬编码了一些值(MAC地址)和名称(网络适配器)--由于这一事实,这种环境可以通过检查适当对象的属性来检测。
1.1. 检查MAC地址是否是特定的
使用的函数:
- GetAdaptersAddresses(AF_UNSPEC, ...)
- GetAdaptersInfo
代码样本(函数GetAdaptersAddresses):
[C++] 纯文本查看 复制代码 int pafish_check_mac_vendor(char * mac_vendor) {
unsigned long alist_size = 0, ret;
ret = GetAdaptersAddresses(AF_UNSPEC, 0, 0, 0, &alist_size);
if (ret == ERROR_BUFFER_OVERFLOW) {
IP_ADAPTER_ADDRESSES* palist = (IP_ADAPTER_ADDRESSES*)LocalAlloc(LMEM_ZEROINIT,alist_size);
void * palist_free = palist;
if (palist) {
GetAdaptersAddresses(AF_UNSPEC, 0, 0, palist, &alist_size);
char mac[6]={0};
while (palist){
if (palist->PhysicalAddressLength == 0x6) {
memcpy(mac, palist->PhysicalAddress, 0x6);
if (!memcmp(mac_vendor, mac, 3)) { /* First 3 bytes are the same */
LocalFree(palist_free);
return TRUE;
}
}
palist = palist->Next;
}
LocalFree(palist_free);
}
}
return FALSE;
}
此代码样本的作者:pafish项目
代码样本(函数etAdaptersInfo):
[C++] 纯文本查看 复制代码 BOOL check_mac_addr(TCHAR* szMac)
{
BOOL bResult = FALSE;
PIP_ADAPTER_INFO pAdapterInfo;
ULONG ulOutBufLen = sizeof (IP_ADAPTER_INFO);
pAdapterInfo = (PIP_ADAPTER_INFO) MALLOC(sizeof(IP_ADAPTER_INFO));
if (pAdapterInfo == NULL)
{
_tprintf(_T("Error allocating memory needed to call GetAdaptersinfo.\n"));
return -1;
}
// Make an initial call to GetAdaptersInfo to get the necessary size into the ulOutBufLen variable
if (GetAdaptersInfo(pAdapterInfo, &ulOutBufLen) == ERROR_BUFFER_OVERFLOW)
{
FREE(pAdapterInfo);
pAdapterInfo = (PIP_ADAPTER_INFO) MALLOC(ulOutBufLen);
if (pAdapterInfo == NULL) {
printf("Error allocating memory needed to call GetAdaptersinfo\n");
return 1;
}
}
// Now, we can call GetAdaptersInfo
if (GetAdaptersInfo(pAdapterInfo, &ulOutBufLen) == ERROR_SUCCESS)
{
// Convert the given mac address to an array of multibyte chars so we can compare.
CHAR szMacMultiBytes [4];
for (int i = 0; i < 4; i++) {
szMacMultiBytes[i] = (CHAR)szMac[i];
}
while(pAdapterInfo)
{
if (pAdapterInfo->AddressLength == 6 && !memcmp(szMacMultiBytes, pAdapterInfo->Address, 3))
{
bResult = TRUE;
break;
}
pAdapterInfo = pAdapterInfo->Next;
}
}
return bResult;
}
此代码样本的作者:al-khaser项目
检测表:
检查MAC地址是否从以下值开始: | 检测 | MAC地址的开头是 | 字节数 | Parallels | 00:1C:42 | \x00\x1C\x42 | VirtualBox | 08:00:27 | \x08\x00\x27 | VMware | 00:05:69 | \x00\x05\x69 | 00:0C:29 | \x00\x0C\x29 | 00:1C:14 | \x00\x1C\x14 | 00:50:56 | \x00\x50\x56 | Xen | 00:16:E3 | \x00\x16\xE3 |
1.2. 检查适配器名称是否是特定的
使用的函数:
- GetAdaptersAddresses(AF_UNSPEC, ...)
- GetAdaptersInfo
代码样本(函数GetAdaptersAddresses):
[C++] 纯文本查看 复制代码 int pafish_check_adapter_name(char * name) {
unsigned long alist_size = 0, ret;
wchar_t aux[1024];
mbstowcs(aux, name, sizeof(aux)-sizeof(aux[0]));
ret = GetAdaptersAddresses(AF_UNSPEC, 0, 0, 0, &alist_size);
if (ret == ERROR_BUFFER_OVERFLOW) {
IP_ADAPTER_ADDRESSES *palist = (IP_ADAPTER_ADDRESSES *)LocalAlloc(LMEM_ZEROINIT, alist_size);
void * palist_free = palist;
if (palist) {
if (GetAdaptersAddresses(AF_UNSPEC, 0, 0, palist, &alist_size) == ERROR_SUCCESS) {
while (palist) {
if (wcsstr(palist->Description, aux)) {
LocalFree(palist_free);
return TRUE;
}
palist = palist->Next;
}
}
LocalFree(palist_free);
}
}
return FALSE;
}
此代码样本的作者:pafish项目
代码样本(函数GetAdaptersInfo):
[C++] 纯文本查看 复制代码 BOOL check_adapter_name(TCHAR* szName)
{
BOOL bResult = FALSE;
PIP_ADAPTER_INFO pAdapterInfo;
ULONG ulOutBufLen = sizeof(IP_ADAPTER_INFO);
pAdapterInfo = (PIP_ADAPTER_INFO)MALLOC(sizeof(IP_ADAPTER_INFO));
if (pAdapterInfo == NULL)
{
_tprintf(_T("Error allocating memory needed to call GetAdaptersinfo.\n"));
return -1;
}
// Make an initial call to GetAdaptersInfo to get the necessary size into the ulOutBufLen variable
if (GetAdaptersInfo(pAdapterInfo, &ulOutBufLen) == ERROR_BUFFER_OVERFLOW)
{
FREE(pAdapterInfo);
pAdapterInfo = (PIP_ADAPTER_INFO)MALLOC(ulOutBufLen);
if (pAdapterInfo == NULL) {
printf("Error allocating memory needed to call GetAdaptersinfo\n");
return 1;
}
}
if (GetAdaptersInfo(pAdapterInfo, &ulOutBufLen) == ERROR_SUCCESS)
{
while (pAdapterInfo)
{
if (StrCmpI(ascii_to_wide_str(pAdapterInfo->Description), szName) == 0)
{
bResult = TRUE;
break;
}
pAdapterInfo = pAdapterInfo->Next;
}
}
return bResult;
}
此代码样本的作者:al-khaser项目
检测表:
检查适配器的名称为: | 检测 | 名称 | VMware | Vmware |
1.3. 检查网络共享的提供者的名称是否是特定的
使用的函数(见关于本地函数的注释):
- WNetGetProviderName(WNNC_NET_RDR2SAMPLE, ...)
代码样本:
[C++] 纯文本查看 复制代码
int vbox_network_share() {
unsigned long pnsize = 0x1000;
char provider[pnsize];
int retv = WNetGetProviderName(WNNC_NET_RDR2SAMPLE, provider, &pnsize);
if (retv == NO_ERROR) {
if (lstrcmpi(provider, "VirtualBox Shared Folders") == 0)
return TRUE;
else
return FALSE;
}
return FALSE;
}
此代码样本的作者:pafish项目
检测表:
检查网络共享的提供者名称如下: | 检测 | 名称 | VirtualBox | VirtualBox Shared Folders |
2. 检查网络是否属于安全范围
恶意软件向https[:]//www.maxmind.com/geoip/v2.1/city/me 发出请求,这通常需要某种认证或API密钥。为了绕过这一要求,恶意软件通过将HTTP Referrer设置为https[:]//www.maxmind.com/en/locate-my-ip-address,将User-Agent设置为Mozilla/5.0(兼容;MSIE 10.0;Windows NT 6.1;Trident/6.0),使请求看起来像是来自该网站本身。这个技巧允许样本检索它所运行的机器的IP地址信息。
响应以JSON格式返回,包含国家、城市的信息,最重要的是,与IP地址相关的组织。如果在响应中发现一些 "坏 "字符串,恶意软件就知道它是在某种安全边界/组织内启动的。
实例
恶意软件样本中的“包含恶意的字符串”(固定大小写):
- Amazon
- anonymous
- BitDefender
- BlackOakComputers
- Blue Coat
- BlueCoat
- Cisco
- cloud
- Data Center
- DataCenter
- DataCentre
- dedicated
- ESET, Spol
- FireEye
- ForcePoint
- Fortinet
- Hetzner
- hispeed.ch
- hosted
- Hosting
- Iron Port
- IronPort
- LeaseWeb
- MessageLabs
- Microsoft
- MimeCast
- NForce
- Ovh Sas
- Palo Alto
- ProofPoint
- Rackspace
- security
- Server
- Strong Technologies
- Trend Micro
- TrendMicro
- TrustWave
- VMVault
- Zscaler
复制代码
3. 基于NetValidateName结果的反伪装技术
最初,这种技术是为绕过AV检测而设计的。它本身并不是一种规避技术--相反,而是在调用函数后滥用有趣的副作用。
主要的想法是使用NetValidateName API函数调用的确定结果,将无效的参数作为服务器名称(例如 "123")来动态地计算跳转地址。这个跳转通常指向某些指令的中间,以绕过AV软件的启发式分析。但这种技术也有(至少)一个副作用。
如果在操作系统中设置了默认的NetBiOS设置(NetBIOS over TCP/IP被启用),返回代码总是等于ERROR_BAD_NETPATH(0x35)。
如果关闭了TCP/IP上的NetBIOS,那么返回代码是ERROR_NETWORK_UNREACHABLE(0x4CF)。
因此,跳转地址将被错误地计算,这将导致样本崩溃。因此,这个技术可以用来破解沙盒中的伪装,在沙盒中,TCP/IP上的NetBIOS被关闭,以防止操作系统产生垃圾流量。
注意:关闭TCP/IP上的NetBIOS是为了在通过DNS解析服务器IP时不产生额外的网络请求。关掉这个选项会取消本地网络的查询请求。
代码样本 (函数GetAdaptersAddresses):
[C++] 纯文本查看 复制代码 void EntryPoint(void)
{
HANDLE NetApi32 = LoadLibraryW(L"netapi32.dll");
TD_NetValidateName NetValidateName = (TD_NetValidateName)GetProcAddress(NetApi32, "NetValidateName");
DWORD Result = NetValidateName(L"123", L"", L"", L"", 1);
__asm
{
call dword ptr ds:[GetLastError]
add eax, offset TrueEntryPoint
sub eax, 0xCB // ERROR_ENVVAR_NOT_FOUND
call eax
}
}
4. 基于Cuckoo ResultServer连接的反伪装技术
这项技术可用于检测Cuckoo沙盒虚拟环境。恶意软件列举了所有已建立的出站TCP连接,并检查是否有连接到Cuckoo ResultServer使用的特定TCP端口(2042)。
识别标志
别标志对每种技术都是通用的:钩住使用的函数并跟踪它是否被调用。例如,很难说出应用程序为什么要获取适配器名称。这并不一定意味着应用规避技术。所以在这种情况下,最好的办法是拦截目标函数并跟踪其调用。
反制措施
- 与检查网络参数的对比:为虚拟环境更改;
- 与检查安全周长相比:以适当的方式模拟网络响应;
- 与NetValidateName基于结果的技术相比:通过TCP/IP打开NetBIOS;
- 与基于Cuckoo ResultServer连接的技术相比:在Cuckoo配置中改变ResultServer端口。
归功于
归功于开源项目,代码样本取自该项目
尽管Check Point工具InviZzzible已经实现了所有这些功能,但由于代码的模块化结构,需要更多的空间来展示这个工具的代码样本,以达到相同的目的。这就是为什么我们决定在整个百科全书中使用其他伟大的开源项目作为例子。 |