承接上文 Brute Ratel C4红队框架和EDR的猫鼠游戏(上)
yauv,公众号:红队蓝军Brute Ratel C4红队框架和EDR的猫鼠游戏(上)
stage1创建的线程执行的函数入口,搜索内存找到Nt头的Signature(PE)地址,通过kernel32.dll的hash获取基址
访问PEB->Ldr->InMemoryOrderModuleList
链表并对FullDllName进行ror13 Hash
对比Hash正确
获得kernel32.dll基址
函数GetNtdllAddress_66b2c0通过PEB加偏移获取ntdll.dll的基址
接下来调用GetFunAddressByHash_668050函数通过Hash获取6个对应函数的地址
通过解析kernel32.dll的PE结构导出表,对导出函数进行Hash对比找到对应的函数
成功获取LoadLibraryA函数地址
获取完需要的函数地址之后,对于ntdll的导出函数进行inlineHook检测并动态获取对应的syscall id
检测函数是否被下int3软件断点,并检测函数头前7个字节OPCODE是否和函数固定特征相同,最后通过固定偏移获取syscall id
通过BRc4官网的功能介绍可知检测EDR的HOOK是BRc4的主要功能之一
此时rax为动态获取的syscall id
通过此函数获取了4个ntdll的导出函数syscall id
syscall(间接系统调用)也是BRc4官网介绍的主要功能特征
调用NtSetInformationProcess函数并设置参数PROCESS_INFOMATION_CLASS
为0x28
通过syscall id调用NtSetInformationProcess函数
调用NtAllocateVirtualMemory函数申请了PAGE_READWRITE
权限的内存
syacall id调用NtAllocateVirtualMemory函数
接下来将PE头0x400字节复制到了新申请的内存,通过前面获取的函数地址和此复制操作有分析过类似样本的应该可以大致推断这是ReflectiveDLLInjection(反射式注入),主要逻辑就是模拟系统的PE Loader将PE文件复制到可执行内存中并修复IAT处理重定位数据然后直接调用内存中的函数执行,虽然这是10年前发布的技术但metasploit和cobalt strike等等框架还是普遍有使用ReflectiveDLLInjection技术,此方式可以避免文件落地以减少EDR查杀
接着将全部section逐个复制到新内存
复制下一个section
复制完全部secsion后开始获取IAT函数地址填充修复IAT
循环直到修复完IAT
修复重定位数据
调用NtFlushInstructionCache函数刷新缓存
调用NtProtectVirtualMemory修改各个section的内存权限
所有section被更改的内存权限
我们使用processhack工具查看内存中各个区段的权限
之后将PE头0x200字节内存置0,防止EDR扫描内存特征查杀
最后调用dllmain
我们将内存中的PE文件dump出来,可以看到此PE文件为MinGW-w64编译的x64 DLL
编译时间戳为2022-3-26 4:51:36
,一共有11个sections
有一个无名导出函数为ReflectiveDLLInjection
接下来我们分析dllmain的两个函数sub_7A1500和sub_7A1FB0
sub_7A1500函数通过多个时间戳函数和进程线程ID异或获得12位的唯一ID
sub_7A1FB0函数主要检测确保内存中PE文件的MZSignature(MZ)
和NtHeaderSignature(PE00)
被抹除
接下来分析主要的核心函数,通过Hash获取VirtualFree函数释放了不需要的两处内存
SendRecvDataC2Server_61F89060函数就是主要的加密解密和收发数据到C2服务器
GetAllNeedFunAddressbyHash_61F83A30获取了全部需要用到的函数地址,首先获取了kernel32.dll中需要的函数(由于篇幅限制只截取了部分获取dll导出函数的截图)
获取ntdll.dll中需要用到的函数
除了获取kelnel32.dll和kernelbase.dll和ntdll.dll基址使用了通过PEB加Hash方式,获取其它DLL都使用RC4密钥bYXJm/3#M?:XyMBF
解密dll字符串并使用LoadLibraryA加载
获取通过syscall方式调用的ntdll.dll的导出函数syscall id
最后获取了wininet.dll中的导出函数
接下来DecryptBase64ReqHeader_61F88DC0函数主要解密了base64参数
initDecBox9_61F8B100函数初始化了Brute Ratel C4自定义加密算法需要用的9个box[256],初始化方法将box中每位元素减1
此为其中一个box
base64解码
使用了CryptStringToBinaryA函数解码base64,可以看到解码后的数据还是乱码
之后使用rc4算法和硬编码的密钥bYXJm/3#M?:XyMBF
解密后的数据可以看到有C2的ip地址User-Agent以及一些配置信息
对于解密的User-AgentMozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.93 Safari/537.36
和Brute Ratel C4官网的Documentation的Listener Profile配置教程中User-Agent完全一致,可以判断此样本就是Brute Ratel C4生成的payload
base64解码配置数据头的eyJjb29raWUiOiI=
base64解码In0=
接下来GetSysInfo_61F99FC0函数主要是调用函数获取了用户名进程PID系统版本和位数等等信息
获取的当前进程的绝对路径
并使用base64编码
获取完数据初始化winsocket版本为2.2
函数EncryptDataAndConnectC2_61F8C760首先将将获取到的的系统信息格式化
格式化后的数据
这里使用了Brute Ratel C4自定义的加密算法密钥2Q73HI7Q0OD5BRN7
加密了数据
关于Brute Ratel C4自定义的加密算法官网有进行说明,此加密用于加密和BRc4服务器之间的网络数据,这层加密是在ssl层之下
对于Brute Ratel C4的自定义加密算法其实就是一个比较复杂的xor加密,这里使用我逆向还原的c++代码,关于完整的加密解密算法源码感兴趣的可以去我的github,首先InitXorKeyBox函数根据key2Q73HI7Q0OD5BRN7
生成160字节xorkey,函数首先获取key的后4字节传入replaceFourByteKey函数
replaceFourByteKey函数首先将4字节的key进行ror 8,然后以第1个字节数据当作g_Box1的数组下标取出值异或g_Box2[g_replaceIndex],g_replaceIndex默认值为1每次调用replaceFourByteKey函数值+1,这是第1个字节的处理方式
2到4字节处理方式是直接将数据当作g_Box1的数组下标取出值直接进行替换
replaceFourByteKey函数处理完的4字节数据会和key的前4字节异或生成第二组xorkey的前4字节
第二组xorkey后面每4字节都是通过前4字节的key异或前一组xorkey的后4字节,这里33 ^ 48
生成的就是第二组xorkey的第5个字节,剩下的以此类推
通过以上流程一共176字节11组xorkey生成完毕
接下来这是EncryptData函数的主要逻辑也就是全部的加密过程,每次加密16字节的明文数据
首先调用MyXor函数使用第一组xorkey逐字节和明文数据进行异或
接着replaceBoxData函数将数据作为g_Box1的数组下标进行替换
接着调用ByteOutOfOrder函数将数据乱序
boxXorData函数一共是处理了16字节,我这里就贴了前4个字节的图,因为加密方式都相同主要就是数据作为g_box3和g_box4数组下标取出的值进行异或
再次调用MyXor函数对数据进行异或,本次使用的就是第二组xorkey,这里循环9次前面的3个函数流程相同,MyXor函数一共使用9组xorkey进行异或加密
最后一层加密除了没有调用boxXorData函数其他的调用顺序和9次循环加密里的逻辑相同,至此16字节的明文数据加密完毕,开始加密下16字节的明文数据
通过以上加密算法加密后的数据进行base64编码
使用InternetOpenW函数设置User-Agent为Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.93 Safari/537.36
InternetConnectW函数设置C2的ip地址174.129.157.251
和端口443
HttpOpenRequestW函数涉资请求类型为POST并且请求路径为/content.html
InternetSetOptionW设置Internet选项为INTERNET_OPTION_SECURITY_FLAGS
HttpAddRequestHeadersW函数参数HTTP_ADDREQ_FLAG_ADD
向C2发送POST请求
COFFLoader(BOFs)功能部分代码
C-Sharp内存加载部分代码
由此分析对比官网介绍的C-Sharp、BOFs内存加载功能
ETW patch功能使用的方案是直接将EtwEventWrite函数头改写C3(ret)让函数直接返回,由于没有动态调试我通过自己写的ror13Hash小工具确认函数Hash
AMSI patch的方案是将AmsiScanBuffer函数头改写为B8 57 00 07 80 C3
(mov eax,0x80070057;ret)
我们反汇编amsi.dll可知如果AmsiScanBuffer函数判断传入参数错误的话会跳过扫描分支过程直接将返回值0x80070057存放eax并返回,所以这里的patch方案也是直接将0x80070057存放eax并直接ret返回
Brute Ratel C4是一款非常优秀的商业红队C2框架,使用了众多用于规避和检测EDR的技术,在最新版的BRc4 v1.1(Stoffel's Escape)的更新日志中作者宣布已经对核心程序进行了重写以隐藏内存的的几处痕迹,并且不再更新试用许可证,新申请试用许可证只会获得 v1.0.x 版本的最新更新。作者称此举是为了防止试用用户将最新的payloads上传到Virus Total,可见BRc4作者为了对抗EDR厂商获取最新的payloads分析以进行针对性查杀的努力,BRc4作者的总结很到位这是一场EDR和BRc4之间的猫捉老鼠游戏。
文章来源于:https://www.freebuf.com/articles/system/341336.html
往期推荐
关于利用rundll32执行程序的分析
什么?Windows defender 竟如此脆弱?
最新办法绕过360核晶添加计划任务
什么?你还不会webshell免杀?(七)
一款域信息收集神器
最新绕过360添加用户 | 红队蓝军祝您双节快乐
域内批量获取敏感文件