记一次某红队样本分析_自研C2框架

记录下对某样本(加载器、shellcode)分析过程,总结学习其使用的相关技术

Posted by Ga0weI on October 15, 2024

0x01 背景

分析了一个比较有意思的样本,通过分析,发现该样本具备较强的免杀能力,并且其制作者应该具备较强的免杀能力以及安全开发能力,分析下来收获不小;有段时间没有更新blog正好也就写篇文章记录下。

0x02 样本基础信息

利用微软应用的dll劫持漏洞,白加黑运行

image-20241014173733876

1
2
3
4
5
6
7
8
9
10
黑dll
name:mpclient.dll
md5:3f88191d6325df64713af7ed06631787
others:无签名

白exe
name:MpCopyAccelerator.exe
md5:262002fac594250753bf94512d17f097
others:微软签名

0x03 分析

虚拟机直接运行,观察到操作非常规文件

image-20241015194243449

查看该文件操作的堆栈调用找到调用点:MpUtilsExportFunctions

image-20241015194313314

IDA分析该函数:(该函数和下面的NvPluginGetinfo同地址)

image-20241014174313840

函数实现如下,shellcode分离并通过回调函数加载,加载位于C:\\Users\\Public\\Config.ini的shellcode:

image-20241014174345294

shellcode分析:其调用sub_42e1b

image-20241014174637062

实现如下:

image-20241014174718729

简单分析上图不难看出,sub_519AB应该做了“还原操作“,下面的假循环里面调用了对应地址;

分析sub_519ab

其实现开头如下:

image-20241014175134344

通过fs寄存器拿 ldr_InMemoryOrderLinks,然后做了一些条件判断(while、if 等)

简单跟踪一遍,这对while、if逻辑,其实就是特征码计算逻辑,v5本质就是一个跳转条件,配合其大小的关系和if的大小条件判断,从而控制循环流;还原之后的特征码算法如下:

1
循环读取dllname每字节的值,判断是否>0x60;大于就先-0x20:转大写, 然后减(131*v3)结果赋值v3;如果小于直接减(131*v3)结果赋值v3

python还原:

1

获取到特征码对应dll的基址之后,调用了的如下函数(sub_2A51B即下面的重命名为get_address_bytezhenma),将其dll基址作为第一个参数,第二个参数应该是特征码,获取到到性格三个变量,这里不难看出大概率是通过特征码+dllbase获取函数地址的操作;

image-20241015110403930

上面dll特征码0x1CCA9CE6对应的dll对应的是kernel32.dll

image-20241015110117928

第一次调用get_address_bytezhenma是获取getprocaddress ,特征码:1AB9B854

image-20241015110933602

第二次调用get_address_bytezhenma是获取loadlibrary,特征码:7F201F78

image-20241015111033224

第三次调用get_address_bytezhenma是获取VirtualProtect,特码码:6c6ec404

image-20241015111126190

接着又是一个看似复杂的大逻辑,主要操作代码是红框里面的;

image-20241015141207498

我们先看逻辑:

其实整个这大块的逻辑和上面一样就是根据特征码找dll,没找到就自己调用刚刚拿到的loadlibrary加载,然后根据指定位置存储的特征码,循环获取对应特征码的函数地址,并加密存储到指定位置;

image-20241015141825289

image-20241015142105944

然后我们再来看主逻辑:

image-20241015142151484

里面调用的get_address_bytezhema通过动态调试,这里是获取memmove函数的地址,对应特码:0x26A39FE2

image-20241015114729975

该地址加密存储到后面指定地址,加密方式:低8位取反 and 0XCAFECAFE,全位 and 0xFFFFFFFF35013501,然后两者做or;

image-20241015144057777

继续获取memset地址,特征码:0x12F66A19

image-20241015144308603

加密存储到后面指定地址,

image-20241015145221285

循环遍历操作,获取如下函数地址并加密存储:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
 Kernel32.SetErrorMode  0x33522634
 Kernel32.GetCurrentProcess  0X5D7B766E
 advapi32.OpenProcessTokenv 0x7920A8C8
 advapi32.LookupPrivilefgeValueA 
 advapi32.AdjustTokenPrivileges
 Kernel32.VirtualProtect
 
 ...
 ws2_32.bind、recv、gethostbyname、connect等
 Winhttp.WinHttpConnect等
 kernel32.CreateToolhep32Snapshot\Thread32First\GetCurrentThreadId\Thread32Next\OpenThread\GetThreadCOntext
\ResumeThread\CopyFile\CreatFile\FindNextFile\CreateDirectory\MoveFile
 iphlpapi.GetIpForwardTable\GetIpAddrTable\DupicateTokenEx
 advapi32.GetUserNameW\GetComputerNameW\LongonUserW
 kernel32.OpenProcess\DuplicateHandle\CreatePipe\TerminateProcess\ConnectNamedPipe
 ntdll.NtMapViewOfSection\
 Kernel32.CreateService\
 ...
 
 
 
 

刚开始的特征码存储:(截图此时前六个已经找到对应特征码的函数地址并加密存储)

image-20241015150312859

最后存储的加密地址:

image-20241015160536189

sub_519ab大致操作就是这些,返回来到sub_42e1b

image-20241015162628069

这里一个假循环调用了一个溢出区域的函数,该函数正式还原的SetErrorMode函数,参数为2;这里应该是为什么防止用户发现windows的ui报错,从而禁止windows报错,以免在运行不成功的时候windows报错引起用户警觉;

最后来到sub_41b2b函数,如下,先是调用sub_423bb地址,然后又是一个循环,调用了溢出内存的地址:

image-20241015163210385

sub_423bb中异是调用了一堆溢出的地址

image-20241015163819465

动态调试的时候我们可用发现,其实就是获取到刚刚加密存储的函数地址并解密调用:

image-20241015164339907

解密并调用WSAStartup()

image-20241015164517066

然后调用 sub_4EC2B

该函数,因该是一个类似解密操作拿到一个字符串:

image-20241015165346113

动态调试,这里我们拿到SeDebugPrivilege字符串:

image-20241015165412365

接着作为参数传入:sub_13EEB里面

sub_13EEB函数:

image-20241015165548832

image-20241015165713305

第一个溢出地址调用,调用的是kernel32.GetCurrentProcess

image-20241015165819344

这里我们可用发现一个细节,该样本的函数调用,都是动态解密拿到函数地址再调用的,并且其数组顺序就是调用顺序的规律:

第一次获取WSAStartup()的时候去获取加密地址:230A54EF43B,第二次获取GetCurrentProcess的时候获取加密地址:230A54EF443,顺延第二个;

image-20241015170629932

sub_13EEB其实就是复制token 提权到system操作(借助微软windowsdefender 进程的高权限token):

image-20241015171627137

sub_423BB 里面

image-20241015175755980

后续就是实现一些功能,其中逻辑还是使用了循环混淆,然后动态运行解密相关函数调用,去做的;

之后的分析:我们直接通过上面该shellcode获取过哪些函数可以大致看出其做了哪些操作,然后对应函数断点,开展分析即可;

image-20241015180152665

如,看到ws3_32以及winhttp,肯定是调用来外联的,我们直接打断点拿到c2即可(这里有一个问题,样本其实是做了反调试的,所以我们先要把检查过去,然后在打断点,具体到这个样本,我们在调用sub_519AB之前先不要打断点,获取到相关函数地址之后再打)

image-20241015182959457

拿到c2:47.93.173.165

回连url:https://47.93.173.165/api/v1/pods

0x04 分析总结

1、该shellcod的全部循环代码都做过混淆,相关逻辑循环被混淆成随机数字的大小比较逻辑来控制,十分巧妙;(应该是有相关混淆工具)

2、shellcode的apihash的生成算法没有使用已知的c2框架,而是自定义生成;

3、shellcode中相关功能代码,所有函数调用都是通过动态解密调用:通过循环遍历内置的特征码资源段使用自制的特征码计算方法,挨个加载对应dll以及遍历相关函数名称计算,拿到的后面要使用的函数真实地址,获取到的函数地址也是加密才存储,运行时动态解密调用;

4、通过widnowsdefender的MpCopyAccelerator.exe来实现白加黑劫持,相较普通白加黑劫持对抗av的效果应该会好些,因为windows df作为原生的windows av,很多av可能会对其加白来减少一些误报之类的;

5、简单的shellcode作为文件分析加载,将shellcode文件放到的用户目录下伪装为ini配置文件,这也是一些黑会场喜欢使用的操作;

0x05 攻击者画像

1、攻击者具备shellcode自研开发能力,大概率,有一套自研的c2框架;

2、攻击者具备较强的shellcode免杀能力和对抗分析的能力,常见的样本中很少有攻击组织的样本能够考虑到条件逻辑的免杀,尤其是对于一些结合al的沙箱和wd之类的,其会对条件逻辑遍历从而对抗一些样本对沙箱的逻辑检测;更有甚者会将样本的逻辑图作为查杀项之一,通过该样本的混淆逻辑判断可以成功绕过这些,并且增大的分析难度;

3、攻击者可以将免杀思路落地并开发专门的混淆工具,处理二进制文件,较强的安全工具开发能力;

结合该样本是某次演习时拿到的,以及上述相关特性,笔者认为该攻击组织大概率是国内较强红队,并且的结合其具备自研c2框架和混淆工具,至少是成体系的,所以该红队规模应该不小;范围很小了;严格意义上来说的是黑产组织的概率也不小,这里我们结合其shellcode行为(如:没有去对抗杀软,我们这里指的对抗是指没有去检测杀软,然后干掉杀软。这也是红队和黑产不同的一个主要项;即场景不同,攻击队一般来说是能够拿到项目目标av的一些情报的,又或者是可以从一次攻击中拿到,所以可以不用做上面提到的对抗,相反黑产一般又两种情况,一些比较无脑的黑产,就是堆量,上来查av,看到av就通过一些手段干掉;因为不干掉其后续的载荷大概率是要被查杀的,要做一些非常明显的风险行为;还有一种黑产,就是主打的一个稳,检测杀软,发现过不去的杀软不会去杀,而是直接退出,从而使其样本和活动的存活周期能够稍微拉长些,扯得有点远了,有机会就这个话题写篇文章)以及c2基础设施的特征,是基本可以把黑产的可能排除的;