本文是对新检测到的 NOBELIUM 恶意软件的深入分析,NOBELIUM 采用多种策略来进行凭据盗窃,目的是获得对 Active Directory 联合身份验证服务 ( AD FS ) 服务器的管理员级别访问权限。一旦 NOBELIUM 获得凭据并成功入侵服务器,攻击者就会依赖该访问权限来保持持久性并使用复杂的恶意软件和工具加深其渗透。NOBELIUM 使用 FoggyWeb 远程渗透受感染 AD FS 服务器的配置数据库、解密的令牌签名证书和令牌解密证书,以及下载和执行其他组件。
0x01 FoggyWeb:针对 AD FS 的后门
FoggyWeb 是一种被动且针对性很强的后门,能够从受感染的 AD FS 服务器中远程窃取敏感信息。它还可以从命令和控制 (C2) 服务器接收其他恶意组件,并在受感染的服务器上执行它们。
在入侵 AD FS 服务器后,观察到 NOBELIUM 在系统上删除了以下两个文件(将这些文件写入下面列出的文件夹需要管理员权限):
◼%WinDir%\ADFS\version.dll
◼%WinDir%\SystemResources\Windows.Data.TimeZones\pris\Windows.Data.TimeZones.zh-PH.pri
FoggyWeb 存储在加密文件Windows.Data.TimeZones.zh-PH.pri 中,而恶意文件version.dll是其加载程序。AD FS 服务可执行文件Microsoft.IdentityServer.ServiceHost.exe通过DLL 搜索顺序劫持技术加载所述 DLL 文件,该技术涉及核心公共语言运行时 (CLR) DLL 文件。该加载器负责加载加密的 FoggyWeb 后门文件,并利用自定义轻量级加密算法 (LEA) 例程来解密内存中的后门。
在对后门进行反混淆处理后,加载程序继续在 AD FS 应用程序的执行上下文中加载 FoggyWeb。加载程序是一个非托管应用程序,它利用 CLR 托管接口和 API 将后门(一个托管 DLL)加载到执行合法 AD FS 托管代码的同一应用程序域中。这将授予对 AD FS 代码库和资源的后门访问权限,包括 AD FS 配置数据库(因为它继承了访问配置数据库所需的 AD FS 服务帐户权限)。
加载后,FoggyWeb 后门(最初由其开发人员命名为Microsoft.IdentityServer.WebExtension.dll)用作被动和持久后门,允许使用安全断言标记语言 (SAML) 令牌。后门为攻击者定义的 URI 配置 HTTP 监听器,这些 URI 模仿目标的 AD FS 部署使用的合法 URI 的结构。自定义监听器被动监视从 Intranet/Internet 发送到 AD FS 服务器的所有传入 HTTP GET 和 POST 请求,并拦截与攻击者定义的自定义 URI 模式匹配的 HTTP 请求。此版本的 FoggyWeb 为以下硬编码 URI 模式配置监听器:
◼HTTP GET URI模式:/adfs/portal/images/theme/light01/profile.webp/adfs/portal/images/theme/light01/background.webp/adfs/portal/images/theme/light01/logo.webp
◼HTTP POST URI 模式:/adfs/services/trust/2005/samlmixed/upload
上面的每个 HTTP GET/POST URI 模式对应一个 C2 命令:
◼当 AD FS 服务器收到包含 URI 模式/adfs/portal/images/theme/light01/profile.webp的 HTTP GET 请求时,后门会检索受感染 AD FS 服务器的令牌签名证书,然后对证书进行混淆并将证书返回给请求的发出者。
◼同样,当 AD FS 服务器收到包含 URI 模式/adfs/portal/images/theme/light01/background.webp的 HTTP GET 请求时,后门会检索受感染 AD FS 服务器的令牌解密证书,然后进行混淆并返回证书给请求的发布者。
◼当 AD FS 服务器收到包含 URI 模式/adfs/portal/images/theme/light01/logo.webp的 HTTP GET 请求时,后门会检索受感染服务器的 AD FS 配置数据,对数据进行混淆,并返回混淆后的数据发送给请求的发布者。
◼当 AD FS 服务器收到包含 URI 模式/adfs/services/trust/2005/samlmixed/upload的 HTTP POST 请求时,后门将混淆和压缩的 POST 数据视为 .NET 程序集或源代码。如果是程序集,则后门在 AD FS 进程的执行上下文中执行程序集。如果是源代码,则后门会动态编译源代码并继续在 AD FS 进程的执行上下文中执行生成的内存驻留程序集。
下图说明了攻击者使用的方法与位于受感染的面向 Internet 的 AD FS 服务器上的 FoggyWeb 后门进行通信。
由于 FoggyWeb 在主 AD FS 进程的上下文中运行,因此它继承了访问 AD FS 配置数据库所需的 AD FS 服务帐户权限。这与必须在 AD FS 服务帐户的用户上下文下执行的工具(例如ADFSDump)形成对比。此外,由于 FoggyWeb 与 AD FS 托管代码加载到同一应用程序域中,因此它可以获得对合法 AD FS 类、方法、属性、字段、对象和组件的编程访问权限,随后 FoggyWeb 会利用这些来促进其恶意操作。例如,这允许 FoggyWeb 访问 AD FS 配置数据,而无需连接到 WID 命名管道或手动运行 SQL 查询来检索配置信息(例如,获取EncryptedPfx blob 来自配置数据)。FoggyWeb 也与 AD FS 版本无关;它不需要跟踪旧版与现代配置表名称和架构、命名管道名称以及 AD FS 的其他版本相关属性。
1.FoggyWeb加载器
文件version.dll是一个恶意加载器,负责从文件系统加载加密后门文件,解密后门文件,并将其加载到内存中。此恶意 DLL 与位于*%WinDir%\System32*文件夹中的合法 Windows DLL 共享名称。
当 AD FS 服务 ( adfssrv ) 启动时,将执行服务可执行文件Microsoft.IdentityServer.ServiceHost.exe。作为基于 .NET 的托管应用程序,Microsoft.IdentityServer.ServiceHost.exe导入名为mscoree.dll的非托管 Windows DLL 。
文件mscoree.dll动态加载另一个名为mscoreei.dll 的非托管 Windows/CLR DLL 。如下图,mscoreei.dll有一个名为version.dll的延迟加载导入(Delay Import)。
系统或 AD FS 服务重新启动后,Microsoft.IdentityServer.ServiceHost.exe会加载mscoree.dll,而后者又会加载mscoreei.dll。如上所述,mscoreei.dll有一个名为version.dll的延迟加载导入。不是从%WinDir%\System32*文件夹加载合法的version.dll,而是加载攻击者在%WinDir%\ADFS*文件夹中植入的恶意version.dll*(简称DLL搜索顺序劫持),如下图调用栈所示。
恶意加载程序version.dll充当所有合法version.dll导出函数调用的代理。如下所示,它导出与version.dll的合法版本相同的 17 个函数名称。
恶意version.dll的导出函数都是调用单个TrampolineFunction函数的短存根(short stubs),如下面的截图所示。
下面是trampoline 函数的伪代码。
这个trampoline 函数负责以下内容:
◼调用函数(被分析者标记为LoadDecryptExecuteBackdoor())从文件系统加载后门文件,然后在内存中解密并执行该文件
◼将执行从version.dll的合法版本转移到最初调用的目标函数。
Trampoline 函数通过保存某些 CPU 寄存器的值来保留合法版本version.dll 中用于该函数的参数/寄存器的值。在调用上面的LoadDecryptExecuteBackdoor()函数之前,它首先将它们压入堆栈,然后在将执行从version.dll的合法版本转移到函数之前恢复它们。
调用时,LoadDecryptExecuteBackdoor()尝试创建一个名为{2783c149-77a7-5e51-0d83-ac0566daff96}的 Windows 事件,以确保只有一个加载程序副本在系统上主动运行。在一个新线程中,然后检查以下文件是否存在(硬编码路径字符串):
C:\Windows\SystemResources\Windows.Data.TimeZones\pris\Windows.Data.TimeZones.zh-PH.pri
Windows.Data.TimeZones.zh-PH.pri是一个加密的后门文件,放在上面的文件夹中,我们将这个后门文件称为 FoggyWeb。
Microsoft.IdentityServer.ServiceHost.exe本身是一个非托管的 Windows 可执行文件,它是在编译高级 AD FS 托管代码时生成的。执行时,Microsoft.IdentityServer.ServiceHost.exe 中的非托管代码利用公共语言运行时 (CLR) 在虚拟运行时环境中运行托管 AD FS 代码。该虚拟运行时环境由一个或多个应用程序域组成,这些应用程序域为运行时环境提供了一个隔离单元,并允许不同的应用程序在进程内的不同容器内运行。托管 AD FS 代码在虚拟运行时环境内的应用程序域内执行。
FoggyWeb 后门(也是一个托管 DLL)与合法的 AD FS 代码一起运行。这意味着要让 FoggyWeb 加载器与 AD FS 代码一起加载后门,它需要访问执行 AD FS 代码的同一个应用程序域。由于 FoggyWeb 加载程序version.dll是非托管应用程序,因此它无法直接访问在其中执行托管 AD FS 代码的虚拟运行时环境。加载器克服了这一限制,并通过利用 CLR 托管接口和 API 访问执行 AD FS 代码的虚拟运行时环境,将后门与 AD FS 代码一起加载。
加载器执行以下高级操作:
◼枚举 AD FS 进程中加载的所有 CLR Microsoft.IdentityServer.ServiceHost.exe
◼对于每个 CLR,枚举所有正在运行的应用程序域并为每个域执行以下操作:将以下加密的 FoggyWeb 后门文件的内容读入内存:C:\Windows\SystemResources\Windows.Data.TimeZones\pris\Windows.Data.TimeZones.zh-PH.pri使用轻量级加密算法 (LEA) 解密加密的 FoggyWeb 后门文件。LEA-128 密钥使用以下硬编码的主密钥来生成轮密钥:
解密每个 16 字节密码块后,加载程序使用以下 XOR 密钥来解码每个单独的解密/明文块:
这相当于首先 LEA 解密整个文件,然后对解密的数据进行异或解码(而不是对每个单独的 16 字节块进行解密和异或解码)。
◼创建一个 Safe Array 并将解密的 FoggyWeb 后门字节复制到该数组中。然后它调用当前应用程序域的Load()函数将 FoggyWeb DLL 加载到应用程序域中。在将 FoggyWeb DLL 加载到当前应用程序域后,加载器从 DLL 调用以下方法:Microsoft.IdentityServer.WebExtension.WebHost。
在执行周期的这一点上,FoggyWeb DLL 被加载到一个或多个运行合法 AD FS 代码的应用程序域中。这意味着后门代码与 AD FS 代码一起运行,具有与 AD FS 应用程序相同的访问权限和权限。由于后门与 AD FS 代码加载在同一应用程序域中,因此它可以获得对各种 AD FS 模块用于执行其合法功能的合法类、方法、属性、字段、对象和组件的编程访问。这种访问允许 FoggyWeb 后门直接与 AD FS 代码库(不是外部磁盘驻留工具)交互,并有选择地调用促进其恶意操作所需的本机 AD FS 方法。
2.FoggyWeb 后门程序
此恶意内存驻留 DLL(最初由其开发人员命名为Microsoft.IdentityServer.WebExtension.dll)用作针对 AD FS 的后门。它由主 AD FS 服务进程Microsoft.IdentityServer.ServiceHost.exe通过恶意加载程序组件加载。
加载后,后门会启动一个 HTTP 监听器,该监听器侦听包含以下 URI 模式的 HTTP GET 和 POST 请求:
◼HTTP GET URI 模式:/adfs/portal/images/theme/light01/
◼HTTP POST URI 模式:/adfs/services/trust/2005/samlmixed/upload
如下所示,URI 模式在后门中进行了硬编码,并模仿目标的 AD FS 部署使用的合法 URI 的结构。
一旦后门收到包含上述 URI 模式之一的 HTTP 请求,监听器将继续使用 HTTP GET 或 HTTP POST 回调/处理程序方法(分别为ProcessGetRequest()和ProcessGetRequest() )处理请求。
(1)HTTP GET 处理程序
包含 URI 模式/adfs/portal/images/theme/light01/的传入 HTTP GET 请求由后门的ProcessGetRequest()方法处理。
如果针对文件扩展名为.webp的文件/资源发出传入 HTTP GET 请求,则ProcessGetRequest()方法将继续处理该请求。否则,请求将被后门忽略。此外,如果请求的文件名与以下三个硬编码名称之一匹配,则后门会将请求视为攻击者发出的 C2 命令。
以下 URL 模式是 C2 命令:
◼/adfs/portal/images/theme/light01/profile.webp
◼/adfs/portal/images/theme/light01/background.webp
◼/adfs/portal/images/theme/light01/logo.webp
前两个 C2 命令,profile.webp和background.webp(上面截图中的UrlGetFileNames[0]和UrlGetFileNames[1])是通过调用后门的Service.GetCertificate()方法来处理的。
此方法负责从 AD FS 服务配置数据库中检索 AD FS 证书(令牌签名或令牌加密证书,具体取决于传递给该方法的certificateType参数的值)。
如上图所示,当 C2 命令profile.webp ( UrlGetFileNames[0] ) 被发送到后门时(通过对 URI /adfs/portal/images/theme/light01/profile.webp发出 HTTP GET 请求) ,后门检索受感染 AD FS 服务器的令牌签名证书。类似地,当向后门发出C2 命令background.webp ( UrlGetFileNames[1] ) 时(通过对 URI /adfs/portal/images/theme/light01/background.webp发出 HTTP GET 请求),后门检索受感染 AD FS 服务器的令牌加密证书。
第三个 C2 命令logo.webp ( UrlGetFileNames[2] ) 通过向以下 URI 发送 HTTP GET 请求来触发:/adfs/portal/images/theme/light01/logo.webp。C2 命令是通过调用后门的GetInfo()方法来处理的。
GetInfo()方法负责收集受感染服务器的AD FS服务配置数据。
如上所示,AD FS 服务配置数据是通过ServiceSettingsData属性获取的,该属性从 AD FS 服务配置数据库、Windows 内部数据库 (WID) 中检索数据。
在将 C2 命令的输出(即令牌签名证书、令牌加密证书或 AD FS 服务配置数据)以 HTTP 200 响应返回给 C2 之前,后门首先通过调用其方法来混淆输出名为GetWebpImage()。
GetWebpImage()方法负责将C2命令的输出伪装为合法的WebP文件(通过添加适当的RIFF/WebP文件头/字段),并对生成的WebP文件进行编码。
GetWebpImage()使用以下辅助方法来创建和编码包含 C2 命令输出的假 WebP 文件:
◼GetWebpImage()首先调用Webp.GetFrame()方法,该方法负责压缩 C2 命令的输出并将压缩版本复制到新数组(0 填充为 32 字节的倍数)。压缩数据的长度添加为新数组的前四个字节。
为了压缩数据,GetFrame()调用Common.Compress()方法,该方法用于通过利用 C# GZipStream 压缩类来压缩数据。
出于演示目的,假设 C2 命令产生以下数据(一个 256 字节伪随机生成的字节数组)。
给定以上数据(即示例 C2 命令输出),GetFrame()返回以下字节数组。
◼接下来,GetWebpImage()调用Webp.GetWebpHeader()方法,传入上一步中GetFrame()返回的字节数组的大小。GetWebpHeader()负责创建和返回一个包含自定义 RIFF WebP 文件魔法/标头字节的数组。
上面的数组变量包含以下 32 字节硬编码 RIFF/WebP 头字节。
如果传递给GetWebpHeader()的数组大小(由GetFrame()返回)超过 8,192 字节,则标头字节(最初设置为 0x00)的索引 26 和 28 处的字节将替换为 0x80。否则,索引 26 和 28 处的字节将替换为 0x40,如下所示。
GetWebpHeader()然后将上面的自定义 RIFF/WebP 标头返回给GetWebpImage()。
◼接着,GetWebpImage()通过附加自定义RIFF创建新的数组/通过返回WebP的头字节GetWebpHeader()到由返回的数组()的getFrame(含有C2命令输出的压缩版本的阵列)。
GetWebpImage()调用后门的Common.ProtectData()方法对包含压缩字节的新数组部分进行编码。作为第二个参数,GetWebpImage()将第一个压缩字节的偏移量传递给ProtectData()。如上表所示,在这种情况下,0x20 或 32 是第一个压缩字节的偏移量。ProtectData()使用动态XOR 密钥和自定义 XOR 方法对压缩数据进行 XOR 编码。
最初,12 字节硬编码 XOR 密钥数组包含以下种子字节。
如上面的截图所示,压缩数据的每个字节都与 XOR 密钥数组中的一个字节进行异或运算。压缩数据的第一个字节 (0x17) 与位于密钥数组 (0x77) 偏移 8 处的 XOR 密钥字节进行异或运算。
在将压缩数据的第一个字节与位于密钥数组偏移量 8 的 XOR 密钥字节进行异或后,XOR 密钥字节本身会被一个新值覆盖。
例如,位于 XOR 密钥数组 (0x77) 偏移 8 处的 XOR 密钥字节通过以下操作被 0xEE 覆盖。
压缩数据的第二个字节 (0x01) 与位于密钥数组 ( 33 % 12 = 9 )偏移量 9 处的 XOR 密钥字节进行异或运算,依此类推,直到密钥滚动到 XOR 数组的第一个字节 (如上所述,XOR 密钥字节在每次编码操作后都会被覆盖)。下面是示例压缩数组的 XOR 编码版本。
完成上述步骤后,GetWebpImage()将以下示例数据返回给调用它的方法,以混淆和隐藏每个 C2 命令 ( ProcessGetRequest() ) 的输出。
如前所述,ProcessGetRequest()在 HTTP 200 响应中将上面生成的假 RIFF/WebP 文件(包含被盗的令牌签名证书、令牌加密证书或 AD FS 服务配置数据)返回给 C2。
如果后门无法成功执行 C2 命令,则会向 C2 返回 HTTP 404 响应。
(2)HTTP POST 处理程序
与 URI 模式/adfs/services/trust/2005/samlmixed/upload匹配的传入 HTTP POST 请求由ProcessPostRequest()方法处理。
此方法可确保传入 HTTP POST 请求的ContentType值以“xml”(不区分大小写)结尾,并且 HTTP POST 数据包含名为X509Certificate和SignatureValue 的两个 XML 元素(例如,以字符串“ < X509Certificate> ”并以字符串“”结尾)。
如果 XML 数据包含这两个元素,则后门执行以下操作:
◼解码SignatureValue和X509Certificate元素的值,首先使用 Base64 解码这些值,然后对每个解码的值调用Common.UprotectData()方法。
所述UprotectData()方法中Base64的前两个字节的解码值作为一个两字节的异或键。它对其余数据(即第三个字节)调用Common.ProtectData()方法,然后使用两字节 XOR 密钥对Common.ProtectData()返回的数据进行 XOR 解码)。换句话说,UprotectData()利用Common.ProtectData()删除第一层 XOR 编码,然后另一个 XOR 例程删除应用于数据的第二层 XOR 编码。
◼调用Service.ExecuteAssembly()方法来处理解码的SignatureValue和X509Certificate值。如下所示,解码后的X509Certificate值是第一个通过调用Common.Decompress()方法解压的 GZip 。
在新线程中,Service.ExecuteAssembly()调用Service.ExecuteAssemblyRoutine()方法来处理数据。
◼ExecuteAssemblyRoutine()检查解码的X509Certificate值是否以“MZ”开头(或字节0x4D 0x5A,十进制数77和90的十六进制表示,如下面的截图所示)。
◼如果解码后的X509Certificate值以“MZ”开头,则后门会将解码后的数据视为基于 .NET 的程序集/有效负载,并继续调用其Service.ExecuteBinary()方法以在内存中加载和执行 DLL 有效负载。在内存中加载 DLL 后,ExecuteBinary()继续从加载的 DLL 中调用特定方法。调用该方法所需的方法名称和参数在解码的SignatureValue数据中提供给后门。
如果解码后的X509Certificate值不是以 MZ 开头,则后门会将解码后的X509Certificate值视为基于 C# 的有效载荷的源代码,并调用其Service.ExecuteSource()方法在内存中动态编译和执行有效载荷。
在处理包含 XML 元素X509Certificate和SignatureValue的 HTTP POST 请求后,后门使用 HTTP 204 响应代码响应请求。如果 HTTP POST 没有上述元素,后门会使用 HTTP 404 响应代码响应请求。
0x02 获取和解密 AD FS 令牌
Service.GetCertificate()方法负责从 AD FS 服务检索 AD FS 证书(令牌签名或令牌加密证书,具体取决于传递给该方法的certificateType参数的值)配置数据库。
该方法执行以下操作来检索所需的证书:
◼调用另一个名为GetServiceSettingsDataProvider() 的方法,以从已加载的程序集Microsoft.IdentityServer创建Microsoft.IdentityServer.PolicyModel.Configuration.ServiceSettingsDataProvider类型的实例。
◼调用上述ServiceSettingsDataProvider实例的GetServiceSettings()成员/方法以获取 AD FS 服务配置设置。
◼获取 AD FS 服务设置的值(从SecurityTokenService属性),从服务设置中提取EncryptedPfx blob的值,并使用 Base64 解码 blob。
◼调用另一个名为GetAssemblyByName() 的方法以按名称枚举所有加载的程序集并定位已加载的程序集Microsoft.IdentityServer.Service。该方法检索一个名为两个字段的值_STATE和_certificateProtector从类型的对象Microsoft.IdentityServer.Service.Configuration.AdministrationServiceState(从Microsoft.IdentityServer.Service组件)。
AdministrationServiceState类/对象中包含必需的客户端请求的执行和操作配置信息。字段_state用于维护AdministrationServiceState类/对象的当前状态。
AdministrationServiceState对象(存储在state字段中)包含另一个名为certificateProtector的字段。
字段_certificateProtector存储用于分布式密钥管理(DKM)的数据保护器类DkmDataProtector的实例。DkmDataProtector类实现了一个名为Unprotect()的方法,该方法最终调用DKM/IDKM(来自Microsoft.IdentityServer.dll的截图)的Unprotect()方法。
DKM Unprotect()方法从Microsoft.IdentityServer.DKM.DKMBase(Microsoft.IdentityServer.DKM.dll的截图)继承名为Unprotect()的方法。
Microsoft.IdentityServer.Dkm.DKMBase中的Unprotect()方法提供了解密EncryptedPfx blob中存储的加密证书(PKCS12对象)的功能。
借助可通过_certificateProtector字段访问的Unprotect()方法的可用性知识,后门调用Unprotect()方法来解密存储在EncryptedPfx blob中所需证书类型(AD FS令牌签名或加密证书)的加密证书。
AdministrationServiceState类/对象中的_state和_certificateProtector字段来促进对 Data Protector 类DkmDataProtector 的访问(用于访问和调用Unprotect()方法)。
0x03 IOCs
类型 | 后门文件 | 后门类型 | 指标 |
---|---|---|---|
MD5 | FoggyWeb | 加载器 | 5d5a1b4fafaf0451151d552d8eeb73ec |
SHA-1 | FoggyWeb | 加载器 | c896ece073dd01191cbc1d462bc2f47161828a83 |
SHA-256 | FoggyWeb | 加载器 | 231b5517b583de102cde59630c3bf938155d17037162f663874e4662af2481b1 |
MD5 | FoggyWeb | 后门(加密) | 9ff9401315d0f7258a9fcde0cfdef02b |
SHA-1 | FoggyWeb | 后门(加密) | 4597431f26424cb814c917168fa8d74d01ab7cd1 |
SHA-256 | FoggyWeb | 后门(加密) | da0be762bb785085d36aec80ef1697e25fb15414514768b3bcaf798dd9c9b169 |
MD5 | FoggyWeb | 后门(已解密) | e9671d294ce41fe6dbb9637dc0157a88 |
SHA-1 | FoggyWeb | 后门(已解密) | 85cfeccbb48fd9f498d24711c66e458e0a80cc90 |
SHA-256 | FoggyWeb | 后门(已解密) | 568392bd815de9b677788addfc4fa4b0a5847464b9208d2093a8623bbecd81e6 |
本文翻译自:https://www.microsoft.com/security/blog/2021/09/27/foggyweb-targeted-nobelium-malware-leads-to-persistent-backdoor/如若转载,请注明原文地址