Cobaltstrike4.0 —— shellcode分析(二)

简介: Cobaltstrike4.0 —— shellcode分析

2、第二阶段(解密beacon中的PE文件)

如下是来到了返回的内容:其实这里就是cobaltstrike里面的beacon了:

在流量侧我们看到的是:MDwT是符合checkSum8算法(cs里面对uri资源判断的一个算法,详情可参考笔者另一篇文章

接下来我们进入beacon的内容来看这里面干了什么:如下图:beacon里面上来就是实现了一个解密操作,将46偏移之后内容通过和一个固定的3d偏移的内容,以4字节为单位做异或,还原对应内容。

还原的长度是:0x33000

内存中没解密之前的数据是这样的:

解密之后:稍微留心就会发现这个解密之后的内容其实就是一个PE文件,开头是4D5A,下面的是Dos引导区,再后面13D偏移的位置还有PE头:

3、第三阶段(通过PE头部引导,运行beacon解密出来的PE文件中的reflectiveloader函数)

继续往下看,发现通过一个pop edi,jmp edi, 直接跳到了 刚刚还原出来的数据 45偏移的位置开始执行:

这里就比较奇怪了,明明是个PE文件结构的内容,为啥beacon直接把这个当shellcode去运行执行了,就强行将jmp过去,从pe的头开始执行。

嗯,笔者之前写过一篇文章是记录一次对Cobaltstrike powershell 上线的分析,当时笔者也是遇到了这个问题,powershell上线的时候也是莫名其妙的构造了一个shellcodeloader,然后把dll文件丢到loader里面,具体为什么这样做,感兴趣可以看下那篇文章。

这里也不绕圈子了,其实就是利用反射dll修复技术,一般我们称其为Reflective DLL Injection技术,是由研究员 Stephen Fewer 再2009年提出的,后续在15年

加入了一些shellcode 技巧和引导程序来完善,发展的较为成熟,应用到恶意软件的中,很多apt组织使用了这个技术,当时基本可以过所有的av,从而大火。

关于这个技术的参考项目:

https://github.com/stephenfewer/ReflectiveDLLInjection

之前了解这个技术原理的师傅们可以去看看。

言归正传,我们继续在od中看下这个dll怎么被“执行”的:如下图是跟进之后,发现指令混到一起了

为了方便我们接下来的调试,我们将这个PE文件的内容dump下来:从这个0x29F0045开始,一直到后面没有指令的位置,或者你实在不放心,就dump长度为33000的长度,这个长度是刚刚解密的时候解密内容的长度。

这里我们手动dump下来就行,选中想要dump的内容,右键——backup——savetofile 就完事了,选中对应内容的时候还有一个小技巧,直接CTRL+G来找头和尾的位置,然后按住Shift 点两下就行了,就把要dump的内容全选了。

dump下来之后如下:

然后我们简单和之前一样,做个简易的loader,做的内容上文有,这里我就不罗嗦了:

如下代码:

#include   
#include   
using namespace std;  
void run()  
{  
    HANDLE hfile = CreateFileA("afterdecryptothingDLL1.mem", FILE\_ALL\_ACCESS, 0, NULL,  
        OPEN\_EXISTING, FILE\_ATTRIBUTE\_NORMAL, NULL);  
    LPVOID exec = VirtualAlloc(NULL, 0x4000000, MEM\_COMMIT, PAGE\_EXECUTE\_READWRITE);  
    DWORD realRead = 0;  
    ReadFile(hfile, exec, 0x4000000, &realRead, NULL);  
    ((void(\*)())exec)();  
}  
int main() {  
    run();  
}

最后生成exe:(运行的时候记得把afterdecryptothingDLL1.mem这个文件放过来)

首先我们测下,这个有问题没:运行下,如下图没问题,运行之后成功上线

接着我们使用od对其调试,看下里面干了什么:

前面loader的代码我就不说了,这里我们直接来到强行执行PE的位置:如下图,上来就是一个经典的call pop,找到当前的位置:

然后将位置放到edi里面存着,后面跟了个push edx,和inc ebp ,来还原前面”4d 5a(dec ebp, pop edx)“干的事情:

然后同push ebp 和 mov ebp,esp,另起一个栈,并将刚刚获取到的真实位置+8150的偏移,刚刚获取到的是这段分配到空间的0007偏移位置的真实地址,所以我们得到的就是这段分配到空间的8157偏移的位置,调用对应位置执行:

在分析到对应位置之前,我们回过头来看下这个afterdecryptothingDLL1.mem 这个文件,使用PE查看器打开:

可以看到给出的信息是一个dll文件:

导出表:存在一个导出函数:RVA是08d57,对应的文件偏移就是:0x08d57-0x1000+0x400=0x08157,正好就是我们上面要跳往的地址:

所以也就是说,接下来就是在执行这个ReflectiveLoader函数:如下图,进来,先通过以下手段,找到PE头部的真实位置

然后就是三环fs寄存器那套了:获取到加载模块的名称:

然后计算hash,和特征码作比较:如下图

紧接着,不匹配就继续遍历下一个模块,匹配上了的话就获取对应dll的基址:如下图,在struct _LDR_DATA_TABLE_ENTRY结构中,下面的eax在本身就是在0x08偏移位置,这里加0x10,就变成了0x18,这个位置就是对应模块的基址位置,

如下图,可以看到这个”6A4ABC5B“特征码找的是kernel32这个模块:

接着又是老套路,从基址找到导出表,找到以函数名称导出的函数的个数,找到函数名称导出表INT:

接着就是遍历函数名,计算特征码:

计算出来的特征码,和以下六个特征码比较:其实就是找到下面六个特征码对应的函数,并且这些函数都是在kernel32里面的:

如果特征码相等,那么就接着获取对应IAT表(导出地址表)对应的导出地址(、这个过程是由 从导出名称表(INT)中获取到循环的次序,从循环的次数到导出序号表获取导出序号,从导出序号到导出地址表(INT)中获取导出地址),存在栈中:

这些函数是:

GetProcessAddress、GetModuleHandleA、LoadLibraryA、LoadLibraryExA、VirtualAlloc、VirtualProtect

在Reflective dll inject 技术里面,这里的reflectiveloader方法是用来处理这个DLL的,因为我们解密还原出来的dll其实是以“文件形式”存在内存中的,所以这里

reflectiveloader这个函数的主要功能就是:将这个文件形式存在的dll,加载到内存里面,也就是把reflectiveloader这个函数自己所在的dll加载到内存里面。

这里面要经过的过程是:

1、从文件形式到内存形式的拉伸,因为内存对齐和文件对齐是不一样的。这个过程主要过程就是复制,先将文件头复制到新开辟的空间,然后再按区节一个个复制到对应的相对位置。

2、修复导入表,之前以文件格式存储的时候被我们直接复制过来了,此时这里的导入表还是双桥结构,也就是INT(导入名称表)和IAT(导入地址表)是一样的,所以当我们把其加载到内存中的时候,我们要修复IAT表。修复过程就是根据导入名称表遍历导入的模块名称和函数名称,然后利用上面获取到的LoadLibrary函数和GetProcessAddress函数来找到对应函数名称的真实地址。

3、如果有重定位表,修复重定位表(这里肯定是有的,因为后面我们再这个dll的oep 也就是dllmain中还要实现我们c2 client的逻辑,不可能全部用PIC(位置无关代码)来写把),而且这里我们这个dll新加载的位置,是我们新通过virtualalloc开辟的,肯定和这个dll本身的预期的基址(imageaddress)是不一样的,没有加载到指定的基址,那么这里有重定位表,我们就要对其进行修复:

修复重定位表,在PE结构中考虑到重定位表的空间占用问题,重定位表里面并不是,存的一个个需要重定位内容的地址,而是以基址+偏移的方式来存储,几个或者多个偏移位置共用一个基址,从而使空间利用率提高:如下图这种格式:

结构体:

存储内容的形式:

内存对齐单位使0x1000h,来分页,在重定位表里面也是这样来做的,相同的1页共用一个上面的块结构体,也就是说如果一个pe文件的有8页,并且每页里面多多少少都有需要重定位的内容,那么这里就会有8个上面的块结构。

这里我们简单计算下页里面的大小,0x1000h 也就是说有4096个单位,那么要找到4096个页内偏移的可能呢,需要用几位来标记呢,2的12次就是4096,所以我们只要有12位bit就可以表示这里页内的偏移,pe里面用的2byte 也就是16位bit,其中高4位固定位0011,后12位为偏移位置。

如下是这个dll的重定位表:

简单看下对应汇编里面对上述三步的实现:

1、复制数据过:

如下:用virtualAlloc开辟新空间,大小是sizeofimage:

接着,将整个文件pe头复制过去:

然后一些判断,如下:判断pe头中的characteristics最低位是否为1:这里就是判断重定位表

接下来就是循环复制每个节块过去:

如下图,先找到块表:

获取块表的相关属性,然后循环复制到新空间:

2、修复导入表:

如下图:这里先找到dll的name

然后调用前面获取到的LoadLibrary,加载这个模块:

然后通过上面获取到的GetProcAddress,找到我们这里在导出表里面遍历的函数的内存地址,然后将内存地址写到其IAT表里面,从而实现导入表的修复:

3、修复重定位表:

如下图,找到重定位表,根据格式对其每个块里面的内容进行遍历,每个块中存的重定位资源的个数: (块—>size -8 %2),-8是把前面的virtualAddress和size占8个字节干掉,除2,是因为这里我们每个相对偏移占2个字节。

如下图,对里面的偏移进行修复

最后,如下图:在reflectiveloader这个函数里面,找到这个我们加载进内存dll的EP(入口点),压入参数并调用:这里我们看那个压入的参数”1“,其实就是dllmain里面的fdwReason参数,如下图中第二个红色部分:(但是这里要注意一个小细节,这里的ep并不是dllmain,笔者之前一直以为dll的ep就是dllmain函数,在cs实现的这个dll里面这里调用的是:DllEntryPoint函数,根据这个函数间接调用dllmain函数,并且这两个方法的参数是一样的)

我们来看下这个DllEntryPoint的实现:如下图,其实就是对fdwReason进行判断,如果等于1,就执行一个A call,然后执行B call;如果不等于1,直接执行Bcall。其实这里的B call就是dllmain, A call是一个__securiyt_init_cookie()的函数(这个函数好像用来提供缓冲区溢出保护的,不知道也没关系)

接着我们来看下B Call ,也就是dllmain:

dllmain函数的参数:这里我们需要重点注意第二个参数fdwReason:

根据msdn上对这个参数的解释:如下图,这个参数为1的时候一般用来做初始化操作:

CS沿用了这一特性,在这里其实也是在做初始化。

我们先不着急跟进对应call 到dllmain中,如下图,可以看到reflectiveLoader函数在下图中的标记的3处中,call下面有一句:

这里将这个dll的ep,放到了eax里面,然后后续返回,所以这个dllmain方法就被带出去了,返回之后继续来到PE头部的引导区:如下图:可以看到这里利用eax带回去的地址,继续压入参数并调用,此时压入的fwdReason是4:也就是说又调用了一遍dllmain方法。

接下来就是研究这个dllmain方法里面干了啥了,为啥要一共调用两次(一次fdwReason参数是1,一次fdwReason参数是4),等于1的时候是我们上文说到的,第一次是在做初始化吗?

相关文章
|
12月前
|
开发工具
编译mimikatz
编译mimikatz
46 0
|
存储 API 数据安全/隐私保护
1.13 导出表劫持ShellCode加载
在`Windows`操作系统中,动态链接库`DLL`是一种可重用的代码库,它允许多个程序共享同一份代码,从而节省系统资源。在程序运行时,如果需要使用某个库中的函数或变量,就会通过链接库来实现。而在`Windows`系统中,两个最基础的链接库就是`Ntdll.dll`和`Kernel32.dll`。Ntdll.dll是Windows系统内核提供的一个非常重要的动态链接库,包含了大量的系统核心函数,如文件操作、进程和线程管理、内存操作等等。在进程启动时,操作系统会先加载`Ntdll.dll`,并将其映射到该进程的地址空间中。由于`Ntdll.dll`是如此重要,所以任何对其的劫持都是无效的。这
100 0
|
存储 Python Windows
1.7 完善自定位ShellCode后门
在之前的文章中,我们实现了一个正向的匿名管道`ShellCode`后门,为了保证文章的简洁易懂并没有增加针对调用函数的动态定位功能,此类方法在更换系统后则由于地址变化导致我们的后门无法正常使用,接下来将实现通过PEB获取`GetProcAddrees`函数地址,并根据该函数实现所需其他函数的地址自定位功能,通过枚举内存导出表的方式自动实现定位所需函数的动态地址,从而实现后门的通用性。
71 1
|
存储 安全 网络协议
1.12 进程注入ShellCode套接字
在笔者前几篇文章中我们一直在探讨如何利用`Metasploit`这个渗透工具生成`ShellCode`以及如何将ShellCode注入到特定进程内,本章我们将自己实现一个正向`ShellCode`Shell,当进程被注入后,则我们可以通过利用NC等工具连接到被注入进程内,并以对方的权限及身份执行命令,该功能有利于于Shell的隐藏。本章的内容其原理与`《运用C语言编写ShellCode代码》`中所使用的原理保持一致,通过动态定位到我们所需的网络通信函数并以此来构建一个正向Shell,本章节内容对`Metasploit`工具生成的Shell原理的理解能够起到促进作用。
98 0
|
存储 Serverless 数据安全/隐私保护
1.9 动态解密ShellCode反弹
动态解密执行技术可以对抗杀软的磁盘特征查杀。其原理是将程序代码段中的代码进行加密,然后将加密后的代码回写到原始位置。当程序运行时,将动态解密加密代码,并将解密后的代码回写到原始位置,从而实现内存加载。这种技术可以有效地规避杀软的特征码查杀,因为加密后的代码通常不会被标记为恶意代码。
139 0
|
存储 安全 Shell
1.6 编写双管道ShellCode后门
本文将介绍如何将`CMD`绑定到双向管道上,这是一种常用的黑客反弹技巧,可以让用户在命令行界面下与其他程序进行交互,我们将从创建管道、启动进程、传输数据等方面对这个功能进行详细讲解。此外,本文还将通过使用汇编语言一步步来实现这个可被注入的`ShellCode`后门,并以此提高代码通用性。最终,我们将通过一个实际的漏洞攻击场景来展示如何利用这个后门实现内存注入攻击。
77 0
|
安全 Windows
【工具分享】免杀360&火绒的shellcode加载器
【工具分享】免杀360&火绒的shellcode加载器
378 0
|
存储 算法 安全
Cobaltstrike4.0 —— shellcode分析(一)
Cobaltstrike4.0 —— shellcode分析
376 0
|
存储 算法 安全
Cobaltstrike4.0 —— shellcode分析(三)
Cobaltstrike4.0 —— shellcode分析
344 0