关于特征码查杀的回望与分析

2023-01-19
3479

大家好!年关将近,辞岁迎新。偶然偷得浮生半日,想起师傅布置的作业。遂放下手柄,参考笔记,从新梳理、修正、扩展了,关于特征码对抗技术的发展史,进一步完善了我的免杀体系。如果大家对免杀还有惧学心理,个人建议大家观看《黑客免杀攻防》这本书,虽然里面的技术可能不那么流行,但是里面蕴含的思想真的值得我们慢慢感悟。好了,废话不说直入正题!

0X00前言

0X01基于敏感行为的查杀

即启发式查杀,这并不是这次文章的重点,所以简单介绍一下。我们对未知文件的识别方法是通过分析文件的执行过程的行为,去判读是否为恶意软件。这意味着我们不仅需要了解文件调用的函数,还要知晓其行为方式。例如:

  1. 当启发式病毒查杀程序发现一个潜在的可执行文件时,它可能会对其执行逆向工程以检查代码的确切作用(检查其操作是否恶意)。它还可能会尝试在安全环境中执行代码以查看结果。

  2. 当杀软发现允许用户输入的元素时,它会尝试通过发送意外数据来“欺骗目标”。然后它分析目标的响应以查看它是否成功。

  3. 内存查杀是杀毒软件的内存查杀是指在病毒运行的时候,病毒被载入计算机的内 存后,杀毒软件在内存中侦测到病毒的运行,并将病毒在内存中击杀。

启发式扫描有一些主要

优缺点启发式查杀静态查杀
准确率更加精确,并且可以提供证据由于非常快,准确率较低,且无法有效应对0day
查杀率不仅可以发现任何类型的威胁,还可以捕获0day通过匹配签名/特征码方式,效果较差
资源占用率涉及"行为",会占用较多资源静态分析,资源占用率较低
泛用性较差,时间和效率无法兼顾是大多数杀软的必备选项

0X02基于签名/特征码的查杀

这次我们重点介绍签名/特征码查杀的对抗历史,以及在攻防演练中如何应对这样的查杀。由于AI安全的兴起,在基于360QNM引擎为代表的机器学习引擎的查杀下,正常的通过微软签名+修改特征码的模式,已经无法适应于存在def、edr等设备的对抗了。但是,大家不要灰心在对付"火绒"这个杀软的时候,恰巧这种方式,更能节约时间和精力。

众所周知,静态查杀是需要庞大的数据库支撑的,这些数据库要么从杀毒软件的生产商商准备,要么从海量的公共数据库中获取样本。所以,针对病毒这种类型,杀软可以通过静态查杀对可疑文件中存在的特定字节链进行匹配;如果找到该字节链,则认为已找到恶意文件。而且病毒的制作几乎总是主要基于签名,一些高级杀软厂商一定会有基于行为的扫描,但它通常是可选的(具体原因是:这样的扫描需要消耗更多的时间和资源)。静态查杀也可以确定来自可疑服务器的特定响应,以识别服务器使用的软件的确切版本。它可能像软件实际响应版本信息一样简单,也可能更复杂,例如识别某些典型行为。

但是签名查杀,大家可能不是特别了解(我在以前的mimikatz免杀和LASS中有所提及);代码完整性是Microsoft在多年前首次引入的威胁保护功能。在基于x64的Windows版本上,内核模式驱动程序必须在每次加载到内存时进行数字签名和检查,也称为驱动程序签名强制执行 (DSE)。它的作用是检测未签名的驱动程序或系统文件是否正在加载到内核中,或者系统文件是否已被修改(可能被以管理权限运行的恶意软件修改)可提高操作系统的安全性。攻击者为了能够克服这些限制,就会通过使用有效的数字证书(无论是正常颁发的,还是被偷盗复制的),或者在目标电脑运行时禁用DSE,已达到攻击成功的目的。

补充:SCA查杀是通过在源代码、中间代码或二进制代码中查找某些代码元素,以识别软件正在使用或者导入的已知组件及其确切版本。

0X02.1基于签名的查杀

基于签名的查杀是非常简单是。最常见的杀软厂商解决方案就是通过建立具有大量带有文件哈希的签名且时实更新的数据库,厂商只需将外存上任何可疑文件的哈希与已知的恶意可执行软件哈希进行比较。eg:厂商数据库若包含Mimikatz的二进制文件打包的.exe文件的SHA1/MD5哈希,就可以通过哈希值效验来判断可疑文件是否是恶意文件。但是更改mimikatz.exe文件的哈希值非常简单,只需要修改其中的单个字节就可以达到目的,所以此检测并不是真正可靠,厂商一般会搭配其他方式交叉验证。

eg:这就是杀软厂商开始转向通过检测特定字节模式签名作为文件哈希的替代方式。下图是Mimikatz的字节哈希示例,在特定字节模式/十六进制值的标记,如下代码段所示:

730065006B00750072006C00730061005F006D0069006E006900640075006D0070
-->HEXvaluesfors.e.k.u.r.l.s.a._.m.i.n.i.d.u.m.p

在这个修改的过程中,不仅要为每个已知的恶意二进制文件或敏感字节做出标记,并且全部进行修改为普通的样子,这才有意义。Mimikatz对于新手来说是基于签名的检测的一个绕不过去的坎,因为通常软件开发商有着几十种针对Mimikatz二进制检测的哈希值。通过这样做,杀软可以确保检测到稍微修改过的版本。甚至有的杀软厂商通过使用yara规则构建更高级的检测。这些规则可以扫描文件或内存内容,并允许更复杂的条件和不同模式的组合。Mimikatz yara规则,如下代码段所示:

rule Windows_Hacktool_Mimikatz_1388212a {
meta:
author = "Elastic Security"
id = "1388212a-2146-4565-b93d-4555a110364f"
fingerprint = "dbbdc492c07e3b95d677044751ee4365ec39244e300db9047ac224029dfe6ab7"
creation_date = "2021-04-13"
last_modified = "2021-08-23"
threat_name = "Windows.Hacktool.Mimikatz"
reference_sample = "66b4a0681cae02c302a9b6f1d611ac2df8c519d6024abdb506b4b166b93f636a"
severity = 100
arch_context = "x86"
scan_context = "file, memory"
license = "Elastic License v2"
os = "windows"
strings:
$a1 = "   Password: %s" wide fullword
$a2 = "  * Session Key   : 0x%08x - %s" wide fullword
$a3 = "   * Injecting ticket : " wide fullword
$a4 = " ## / \\ ##  /*** Benjamin DELPY `gentilkiwi` ( benjamin@gentilkiwi.com )" wide fullword
$a5 = "Remove mimikatz driver (mimidrv)" wide fullword
$a6 = "mimikatz(commandline) # %s" wide fullword
$a7 = "  Password: %s" wide fullword
$a8 = " - SCardControl(FEATURE_CCID_ESC_COMMAND)" wide fullword
$a9 = " * to 0 will take all 'cmd' and 'mimikatz' process" wide fullword
$a10 = "** Pass The Ticket **" wide fullword
$a11 = "-> Ticket : %s" wide fullword
$a12 = "Busylight Lync model (with bootloader)" wide fullword
$a13 = "mimikatz.log" wide fullword
$a14 = "Log mimikatz input/output to file" wide fullword
$a15 = "ERROR kuhl_m_dpapi_masterkey ; kull_m_dpapi_unprotect_domainkey_with_key" wide fullword
$a16 = "ERROR kuhl_m_lsadump_dcshadow ; unable to start the server: %08x" wide fullword
$a17 = "ERROR kuhl_m_sekurlsa_pth ; GetTokenInformation (0x%08x)" wide fullword
$a18 = "ERROR mimikatz_doLocal ; \"%s\" module not found !" wide fullword
$a19 = "Install and/or start mimikatz driver (mimidrv)" wide fullword
$a20 = "Target: %hhu (0x%02x - %s)" wide fullword
$a21 = "mimikatz Ho, hey! I'm a DC :)" wide fullword
$a22 = "mimikatz service (mimikatzsvc)" wide fullword
$a23 = "[masterkey] with DPAPI_SYSTEM (machine, then user): " wide fullword
$a24 = "$http://blog.gentilkiwi.com/mimikatz 0" ascii fullword
$a25 = " * Username : %wZ" wide fullword
condition:
3 of ($a*)
}

rule Windows_Hacktool_Mimikatz_674fd079 {
meta:
author = "Elastic Security"
id = "674fd079-f7fe-4d89-87e7-ac11aa21c9ed"
fingerprint = "b8f71996180e5f03c10e39eb36b2084ecaff78d7af34bd3d0d75225d2cfad765"
creation_date = "2021-04-14"
last_modified = "2021-08-23"
description = "Detection for default mimikatz memssp module"
threat_name = "Windows.Hacktool.Mimikatz"
reference_sample = "66b4a0681cae02c302a9b6f1d611ac2df8c519d6024abdb506b4b166b93f636a"
severity = 99
arch_context = "x86"
scan_context = "file, memory"
license = "Elastic License v2"
os = "windows"
strings:
$a1 = { 44 30 00 38 00 }
$a2 = { 48 78 00 3A 00 }
$a3 = { 4C 25 00 30 00 }
$a4 = { 50 38 00 78 00 }
$a5 = { 54 5D 00 20 00 }
$a6 = { 58 25 00 77 00 }
$a7 = { 5C 5A 00 5C 00 }
$a8 = { 60 25 00 77 00 }
$a9 = { 64 5A 00 09 00 }
$a10 = { 6C 5A 00 0A 00 }
$a11 = { 68 25 00 77 00 }
$a12 = { 68 25 00 77 00 }
$a13 = { 6C 5A 00 0A 00 }
$b1 = { 6D 69 6D 69 C7 84 24 8C 00 00 00 6C 73 61 2E C7 84 24 90 00 00 00 6C 6F 67 }
condition:
all of ($a*) or $b1
}

rule Windows_Hacktool_Mimikatz_355d5d3a {
meta:
author = "Elastic Security"
id = "355d5d3a-e50e-4614-9a84-0da668c40852"
fingerprint = "9a23845ec9852d2490171af111612dc257a6b21ad7fdfd8bf22d343dc301d135"
creation_date = "2021-04-14"
last_modified = "2021-08-23"
description = "Detection for Invoke-Mimikatz"
threat_name = "Windows.Hacktool.Mimikatz"
reference_sample = "945245ca795e0a3575ee4fdc174df9d377a598476c2bf4bf0cdb0cde4286af96"
severity = 90
arch_context = "x86"
scan_context = "file, memory"
license = "Elastic License v2"
os = "windows"
strings:
$a1 = "$PEBytes32 = \"TVqQAAMAAAAEAAAA//8AALgAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAEAAA4fug4AtAnNIbgBTM0hVGhpcyBwc"
$a2 = "$PEBytes64 = \"TVqQAAMAAAAEAAAA//8AALgAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAEAAA4fug4AtAnNIbgBTM0hVGhpcyBwc"
$b1 = "Write-BytesToMemory -Bytes $Shellcode"
$b2 = "-MemoryAddress $GetCommandLineWAddrTemp"
$b3 = "-MemoryAddress $GetCommandLineAAddrTemp"
$c1 = "Invoke-Command -ScriptBlock $RemoteScriptBlock -ArgumentList @($PEBytes64, $PEBytes32, \"Void\", 0, \"\", $ExeArgs)" fullword
$c2 = "Invoke-Command -ScriptBlock $RemoteScriptBlock -ArgumentList @($PEBytes64, $PEBytes32, \"Void\", 0, \"\", $ExeArgs) -ComputerNam"
$c3 = "at: http://blog.gentilkiwi.com"
$c4 = "on the local computer to dump certificates."
$c5 = "Throw \"Unable to write shellcode to remote process memory.\"" fullword
$c6 = "-Command \"privilege::debug exit\" -ComputerName \"computer1\""
$c7 = "dump credentials without"
$c8 = "#The shellcode writes the DLL address to memory in the remote process at address $LoadLibraryARetMem, read this memory" fullword
$c9 = "two remote computers to dump credentials."
$c10 = "#If a remote process to inject in to is specified, get a handle to it" fullword
condition:
(1 of ($a*) or 2 of ($b*)) or 5 of ($c*)
}

rule Windows_Hacktool_Mimikatz_71fe23d9 {
meta:
author = "Elastic Security"
id = "71fe23d9-ee1a-47fb-a99f-2be2eb9ccb1a"
fingerprint = "22b1f36e82e604fc3a80bb5abf87aef59957b1ceeb050eea3c9e85fb0b937db1"
creation_date = "2022-04-07"
last_modified = "2022-04-07"
description = "Subject: Benjamin Delpy"
threat_name = "Windows.Hacktool.Mimikatz"
reference_sample = "856687718b208341e7caeea2d96da10f880f9b5a75736796a1158d4c8755f678"
severity = 100
arch_context = "x86"
scan_context = "file"
license = "Elastic License v2"
os = "windows"
strings:
$subject_name = { 06 03 55 04 03 [2] 42 65 6E 6A 61 6D 69 6E 20 44 65 6C 70 79 }
condition:
int16(uint32(0x3C) + 0x5c) == 0x0001 and $subject_name
}

rule Windows_Hacktool_Mimikatz_b393864f {
meta:
author = "Elastic Security"
id = "b393864f-a9b0-47e7-aea4-0fc5a4a22a82"
fingerprint = "bfd497290db97b7578d59e8d43a28ee736a3d7d23072eb67d28ada85cac08bd3"
creation_date = "2022-04-07"
last_modified = "2022-04-07"
description = "Subject: Open Source Developer, Benjamin Delpy"
threat_name = "Windows.Hacktool.Mimikatz"
reference_sample = "8206ce9c42582ac980ff5d64f8e3e310bc2baa42d1a206dd831c6ab397fbd8fe"
severity = 100
arch_context = "x86"
scan_context = "file"
license = "Elastic License v2"
os = "windows"
strings:
$subject_name = { 06 03 55 04 03 [2] 4F 70 65 6E 20 53 6F 75 72 63 65 20 44 65 76 65 6C 6F 70 65 72 2C 20 42 65 6E 6A 61 6D 69 6E 20 44 65 6C 70 79 }
condition:
int16(uint32(0x3C) + 0x5c) == 0x0001 and $subject_name
}

因此,在这种情况下,如果在文件或内存中发现多个规则中提到的字符串,则将触发此响应,并且通过AV/EDR 软件可以执行警报或终止进程等操作,阻止恶意软件运行。

0X02.1.1文本替换

我们还可以通过签名的检测涉及寻找与已知错误代码匹配的静态签名。基于签名的检测示例包括将文件哈希值与已知恶意软件进行匹配,以及将潜在恶意软件中的字符串进行匹配。众所周知,许多 AV 供应商会将有效负载标记为恶意软件,这仅仅是因为作者的网名出现在文件中之中,造成的威胁。在本文中,我们将通过修改Mimikatz源代码来规避基于签名的检测,从而规避Windows Defender的查杀。但是基于签名的检测很脆弱,因为它依赖于匹配被扫描对象内的特定签名——通常是文本字符串。因此,如果我们修改我们的有效负载以便不再找到相关签名,我们就可以逃避基于签名的检测。大家众所周知的例子是将 Mimikatz更改为mimidogz。

现在我们知道了什么是基于签名的检测,但是我们如何着手识别哪些特定签名导致Windows Defender将我们的有效负载识别为恶意呢?通过文本信息,它可以帮助准确识别有效载荷中的哪些字节导致 Defender 将有效载荷标记为恶意。我下载了Mimikatz源码,用微软的VScode2020编译。在开始编译之前,我们需要对Mimikatz的源码进行一些修改。

  1. 首先,在Solutions Explorer中,右键单击 mimikatz,然后单击 Properties。

  2. 接着,我们需要更改默认的平台工具集选项。在撰写本文时,我将我的设置为Visual Studio 2020(v142)

  3. 最后,对mimilib解决方案也重复此过程。

我没有毕要花时间深入研究为什么需要这样做,只需要知道我们需要修改一些敏感的文本信息防止被杀软发现,我可以任意的取名地并进行构建。编译源代码完成后,我使用Defender查看二进制文件是否被检测为恶意的。毫不奇怪,当HackTool:Win64/Mikatz!dha.DefenderCheck返回导致Defender对负载发出警报的字节的hexdump时,它不幸被检测到。在下面的例图中(该图片由redsiege师傅提供),我们可以看到检测发生在二进制文件中包含的错误消息字符串中。


我有根据地猜测是mimikatz字符串中存在敏感文本导致被检测到,因此我执行了搜索和替换,以mimidogz替换掉所有的Mimikatz并重新编译二进制文件后,再次查杀这种类型的问题就不再出现了!

0X02.1.2DLL名称

接着,我通过Defender运行新的二进制文件并发现了一个新问题。这次违规签名似乎wdigest.dll如下图所示(该图片由redsiege师傅提供):

我有根据地猜测是DLL中存在敏感文件名被检测到,因此我执行了替换敏感的DLL文件名后,再次查杀这种类型的问题就不再出现了!

0X02.1.3函数名称

众所周知,攻击者为了让二进制文件绕过最新版本的Defender进行攻击,需要对大量的函数名称进行修改。这就包括以下需要修改的敏感字符串。eg:kull, kuhl, kiwi, sekurlsa, logonpasswords, credman。还有其他免杀中常用的函数名:I_NetServerAuthenticate2I_NetServerReqChallengeI_NetServerTrustPasswordsGet等。这些函数和netapi32.dll库的都是包含在Mimikatz/lib目录中的。我们可以参考网上的一些视频,针对这些函数名称规避。首先,我需要创建一个.def文件,用于构建一个新的库模块,该模块将包含在Mimikatz构建过程中。该文件的内容如下所示。这是正在发生的事情:一个库 (DLL) 可能会导出一个或多个可供其他程序使用的函数。这些函数通常按名称调用即可。同时,也可以按名称调用函数,代指函数。

I_NetServerAuthenticate2

I_NetServerReqChallenge

I_NetServerTrustPasswordsGet

我们需要使用VScode控制台通过lib /DEF:netapi32.def /OUT:netapi32.min.lib命令将此文件编译成模块。接着将文件放在libx64 目录中并替换初始文件。重建后,Mimikatz 不再包含来自netapi32.dll.。使得Defender的最终检查显示该文件不再被检测为恶意文件。

0X02.2基于特征码的查杀

特征码查杀也是十分形象的,例如,我们熟知的关于Mimikatz抓哈希,作者留下的工具logo,还有掉注释的信息和GO语言打包留下来的特征值对我们来说头至关重要。比如,我们通过分析shellcode中的开头/xfc/x48的字符,就可以推断出这是分离免杀的shellcode。同样我们也可以通过"字符串"确定该恶意文件是"XX家族"或者“XXAPT组织”。为了更好的免杀我们需要注意这些"免杀点",并且完善自己的免杀框架,锻炼思维,形成体系。

手动定位特征

CCL--->逐块填充的方式

Myccl--->逐块暴露的方式

MultiCCL--->混合定位,二分逐步查找的方式

自动化定位特征(eg:VirTest....)

eg:code-data-import-export-资源文件....

众所周知,火绒(静态查杀)是针对字符串的,这个时候我们可以通过Myccl进行破局,大家可参考玉玉师傅的文章:https://cloud.tencent.com/developer/article/2011190(详细步骤是:先分快后生成再杀软,二次处理杀软,最后查看特征区间)

至于自动化工具VirTest(下载链接:http://www.uzzf.com/soft/25659.html)就是相当于可以自动运行的Myccl。具体原理是我们可以这样假设报毒过程,如果检测文件是PE,如果在CODE位置存在标志A,在DATA位置存在标志B,在资源位置存在标志C,同时满足这个3个条件,那么杀软就会报毒,VIRTEST工作原理就是要找到引起报毒最后一个标志, 也就是假设中的标志C。因此VIRTEST采用2分排除法,测试标志C所在文件中的位置,由于被杀的文件可能存在多个类似于ABC这样的连锁条件,所以我们必须要通过一种排除机制,先要找最靠近文件前部的连锁条件,排除掉文件尾部数据,当找到第一个连锁条件后,抹掉引标志C,再恢复尾部数据,然后继续测试另外的连锁条件,直到找到最后一个连锁条件,抹掉后,整个文件免杀了,则说明特征代码被定为完毕了,所以VIRTEST绝对可以精确的定位出所有的复合特征。这比文件分块定位法先进得多,更为科学。

0X03结尾

一直以来,我以为加载器的代码越长越好,语言越生僻越好,这样免杀效果才好,其实不然。听了鬼屋师傅的讲解才知道,下面这个loder现在也还是当打之年,所以希望大家不要走弯路。

#include 
#include

unsignedcharbuff[] ="shellcode"
intmain()
{
((void(*)(void)) &buf)();
}

由于最近较为懒散,难免拖拖拉拉。这段时间又是准备考cisp又是学免杀学到了不少,特征码对抗免杀就是其中之一。本文特别是针对几种手段做了一些科普吧!期间查阅了很多博客,结合自己的实践,收获颇丰。若有不足,希望大家在评论区指出。咱们下次再见!!

参考链接:

https://blackducksoftware.github.io/synopsys-detect/6.0.0/properties/Configuration/signature%20scanner/

https://github.com/Yara-Rules/rules

https://github.com/gentilkiwi/mimikatz

https://cloud.tencent.com/developer/article/2011190

https://www.fortinet.com/blog/threat-research/driver-signature-enforcement-tampering

 

 

转载时必须以链接形式注明原始出处及本声明

扫描关注公众号