利用Azure Attest Service持久化
source link: https://yanghaoi.github.io/2022/08/29/li-yong-azureattestservice-chi-jiu-hua/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
0x01 简介
AzureAttestService
是安装SQL Server 2019
后存在的服务,用于远程验证平台的可信度和其中运行的二进制文件的完整性,详细说明见 Microsoft Azure Attestation。AzureAttestService
存在DLL类型服务文件AzureAttestService.dll
和服务安装程序AzureAttestServiceInstaller.exe
,可以通过服务安装程序对服务进行安装、启动停止和卸载。服务安装程序对DLL文件的验证存在问题,其未对要安装的服务DLL进行合法性检测,可将任意服务DLL安装为系统服务,通过这种方式可以绕过安全软件防御进行权限维持。本文测试系统环境为WIN10 x64 1809
。
0x02 服务程序测试
AzureAttestServiceInstaller.exe
和 AzureAttestService.dll
均是由微软签名的文件,其中AzureAttestServiceInstaller.exe
有4个参数用于服务的操作(-h):
其中 -Install <path to service dll>
参数可以将DLL注册成为svchost.exe
托管的system
权限系统服务:
注册完成后可以在注册表HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\AzureAttestService
项目中查询到对应注册信息:
Parameters
项中可查询服务DLL路径和导出函数信息:
因为AzureAttestServiceInstaller.exe
可以将DLL注册为服务,那么可以编写一个服务类型DLL交给其注册即可。在实际利用中,如果直接使用自编写的服务DLL进行安装,安全软件可能会进行弹窗提醒,此时可以通过AzureAttestServiceInstaller.exe
命令安装服务、停止服务后替换服务DLL达到绕过目的。在实践之前需要先编写一个DLL类型的服务。
0x03 服务DLL编写
在DLL中实现服务的操作,需要具有一个导出函数并与注册表中的ServiceMain
键值对应,以便svchost.exe
执行,默认导出函数是ServiceMain
。查找到的代码示例有MSDN
的编写ServiceMain函数和IRed
上的Persisting in svchost.exe with a Service DLL。如果要详细分析服务代码和各个参数作用可以参考《Windows核心编程》中Windows服务篇。本文仅对要编写的代码进行介绍,先需要在ServiceMain
函数中通过RegisterServiceCtrlHandler
注册一个回调函数来处理SCM
的服务控制请求,如对服务的启动、停止等。因为需要通过 AzureAttestServiceInstaller.exe
来注册DLL服务的,所以在DLL中的RegisterServiceCtrlHandler
函数第一个参数服务名称需要和该安装程序中默认的名称AzureAttestService
保持一致 :
在dwServiceType
参数中设置SERVICE_WIN32_SHARE_PROCESS
表示服务与其他服务可共享进程以便资源、环境变量等共享,一般宿主进程是svchost.exe
。
接下来通过ReportSvcStatus
向SCM
返回服务状态,然后就进入用户处理函数ExecuteServiceCode
。IRed
的示例中是在ExecuteServiceCode
函数的合适位置执行持久化代码:
在这里执行执行代码时需要考虑到线程阻塞问题,如果直接使用指针方式(*(void(*)())buffer)()
执行shellcode
,会导致SCM
无法正常对该服务进行控制。以下代码通过创建线程执行shellcode
注入函数:
注入shellcode
时使用远程线程注入的方式进行,这样做可以让要执行的shellcode
与当前DLL
模块分离开,代码会在当前svchost.exe
进程中分配空间执行:
编写完服务DLL代码后,以64位Release
模式编译,得到一个服务DLL(ServiceDLL.dll
):
该DLL文件成功通过了AzureAttestServiceInstaller.exe
的服务安装、启动、停止和卸载命令测试。为了在实际中使用该方法进行权限维持,还需对服务DLL使用必要的安全规避技术,如对shellcode
进行编码或加密等。
0x04 服务DLL安全规避
在本节中会使用随机延时,shellcode
加密,主机指纹识别这三种方法来提高服务代码的安全软件规避能力。
0x4-1 随机延时
随机延时功能将注入shellcode
的线程延后600~900秒执行,以争取安全软件扫描超时。在延时函数的选择上,不使用常见的Sleep
函数进行延时,可以参考synchapi.h头文件中的API
函数,如下通过WaitForSingleObject
进行5秒延时:
HANDLE hProcess = GetCurrentProcess();
// wait for 5 s
DWORD dw = WaitForSingleObject(hProcess, 5000);
有了延时函数后还需要一个随机数生成函数配合进行延时,在MSDN
上搜索到相关函数rand的示例代码,改动部分代码后可以生成一个伪随机数(种子不变时,每次生成的随机数都一样):
#include <stdlib.h> // rand(), srand()
#include <stdio.h> // printf()
int main(void)
{
srand(882);
int r = ((double)rand() / RAND_MAX) * static_cast<__int64>(10000 - 1000) + 1000;
printf("%d\n", r);
}
为了让随机数种子随机一点,通过获取当前时间的毫秒数来设置种子,获取600-900之间的随机数:
#include <stdlib.h> // rand(), srand()
#include <stdio.h> // printf()
int main(void)
{
SYSTEMTIME stLocal;
GetLocalTime(&stLocal);
printf("stLocal.wMilliseconds:%d\n", stLocal.wMilliseconds);
srand(stLocal.wMilliseconds);
int r = ((double)rand() / RAND_MAX) * static_cast<__int64>(900 - 600) + 600;
printf("%d\n", r);
return 0;
}
发现这样实现的随机数好像都在700以下:
发现可以通过随机两次来获得比较随机的值:
#include <windows.h>
#include <stdlib.h>
#include <stdio.h>
void RangedRandDemo(int range_min, int range_max, int n,int isrand,int* intWait)
{
for (int i = 0; i < n; i++)
{
*intWait = ((double)rand() / RAND_MAX) * (static_cast<__int64>(range_max) - static_cast<__int64>(range_min)) + range_min;
printf("%6d\n", *intWait);
}
}
int main(void)
{
SYSTEMTIME stLocal;
GetLocalTime(&stLocal);
printf("stLocal.wMilliseconds:%d\n", stLocal.wMilliseconds);
srand(stLocal.wMilliseconds);
int intWait = 600;
RangedRandDemo(600,900,2, stLocal.wMilliseconds,&intWait);
printf("intWait:%d\n", intWait);
}
现在执行结果如下,intWait
即为我们需要等待的秒数:
当WaitForSingleObject
等待超时后即可执行操作:
DWORD dw = WaitForSingleObject(GetCurrentProcess(), intWait * 1000);
if (WAIT_TIMEOUT == dw) {
printf("[+] Run my code");
}
0x4-2 Shellcode加密
一般来说将shellcode
分离加载的规避效果要好于直接硬编码,该位置使用AES
算法对原始shellcode
文件进行加密,在加密后安全软件无法确定程序意图,之后在服务DLL中读取加密文件内容后AES
解密执行。我们通过复用Brownie中的AES
相关代码来实现对shellcode
的加解密,要注意在char*
和std::string
类型转换时,如果存在\x00
会导致截断,所以使用了C++ string::assign()
方法赋值:
还需要注意的是,加密脚本中将iv
值放在加密文件末尾,API
函数GetFileSize
在读取文件时如果在文件末尾存在\x00
时会忽略该数据,导致读取到的数据大小与实际不符:
在AES
加密的python
脚本中有对应注释:
实际使用的是随机提取的iv
值 iv = Random.new().read(AES.block_size)
,在没完全理解该随机方法之前错误估计该值最后一位有几率取到\x00
,改了下代码把最后一个元素设置为了固定值,导致了后续解密时一个bug
产生:
这样改动后解密出来的shellcode
就有部分字节与源数据不一致:
意识到解密存在错误后,将加密代码恢复原样后,成功在测试程序中解密并执行了shellcode
,同时windows defender
保持静默:
0x4-3 主机指纹识别
在获得目标主机权限后需要获取其产品uuid
,然后硬编码到服务DLL中用于和WQL
查询得到的主机uuid
信息对比,检测该主机是否为预设主机,以此规避沙箱环境。命令模式下可使用wmic csproduct list full
查询产品信息中的主机uuid
:
GUI
下可使用wbemtest
命令打开实用工具枚举实例 Win32_ComputerSystemProduct
实例:
DLL中通过C++
使用COM
编程执行WQL
查询获取uuid
的代码直接修改自MSDN
上的示例wmi-data-from-the-local-computer,需要查询Win32_ComputerSystemProduct
类中uuid
的值:
查询到结果后判断uuid
是否与预设相等:
程序执行后成功确认了主机uuid
:
在测试程序中实现uuid
检测后,需要将测试好的代码移植到服务DLL中,完成后运行测试发现一个之前遇到的CoInitializeSecurity
已被调用问题,因为是在线程中进行COM
调用,可能在之前已经有过COM
调用,所以加入返回值判断即可:
启动服务后查看写入的日志,发现已经成功执行shellcode
:
最后将官方AzureAttestService.dll
(147kb)的资源信息也拷贝到自编写的服务DLL中(72kb):
这一步的规避措施就编写完毕了,接下来对服务DLL进行测试。
0x05 安装服务DLL
在上文中已经编写好了服务类型DLL并使用了一些规避手段,直接通过安装程序命令安装服务:
在PID为1664的svchost.exe
进程中成功执行了shellcode
返回beacon
:
在实际利用过程中,如果直接将自编写的服务DLL进行注册,会被安全软件行为拦截:
所以应该先使用都具有签名的文件注册服务,然后停止服务并替换服务DLL文件。刚好签名文件AzureAttestServiceInstaller.exe
提供了操作需要的命令:
// 安装签名服务DLL
AzureAttestServiceInstaller.exe -Install AzureAttestService.dll
// 完成后停止服务,解除DLL文件占用
AzureAttestServiceInstaller.exe -StopService
// 替换服务DLL
move AzureAttestService.dll AzureAttestService.dll.bak
move ServiceDLL.dll AzureAttestService.dll
// 启动服务,执行恶意代码
AzureAttestServiceInstaller.exe -StartService
服务成功在PID为2920的svchost.exe
中启动:
之后成功返回了beacon
:
最终我们可以在windows defender
和数字卫士全家桶保护的主机中成功启动PID为428的svchost
服务进程:
在等待一段时间之后,成功返回了beacon
:
0x06 文末总结
本文记录了从发现AzureAttestService
服务注册机制,提取出具有签名的服务安装程序和服务DLL,随后利用签名文件注册服务、停止服务绕过安全软件行为监控获得已创建完成的系统服务,再替换服务DLL文件获得持久化的过程。
在服务DLL中实现了代码加密,随机延迟,主机uuid
检测等规避手段,绕过两种安全软件保护达到了在目标主机进行持久化效果。
0x07 参考链接
writing-a-servicemain-function
persisting-in-svchost.exe-with-a-service-dll-servicemain
https://docs.microsoft.com/en-ca/azure/attestation/overview
https://github.com/slaeryan/AQUARMOURY/tree/master/Brownie
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK