MS 17-010:NSA Eternalblue SMB 漏洞分析
环境
EXPLOIT:
Eternalblue-2.2.0.exe
TARGET:
win7 sp1 32bits
srv.sys 6.1.7601.17514
srvnet.sys 6.1.7601.17514
PATCH:
MS17-010
漏洞原理
srv.sys在处理SrvOs2FeaListSizeToNt的时候逻辑不正确导致越界拷贝。我们首先看下漏洞的触发点:
unsigned int __fastcall SrvOs2FeaToNt(int a1, int a2)
{
int v4; // edi@1
_BYTE *v5; // edi@1
unsigned int result; // eax@1
v4 = a1 + 8;
*(_BYTE *)(a1 + 4) = *(_BYTE *)a2;
*(_BYTE *)(a1 + 5) = *(_BYTE *)(a2 + 1);
*(_WORD *)(a1 + 6) = *(_WORD *)(a2 + 2);
_memmove((void *)(a1 + 8), (const void *)(a2 + 4), *(_BYTE *)(a2 + 1));
v5 = (_BYTE *)(*(_BYTE *)(a1 + 5) + v4);
*v5++ = 0;
_memmove(v5, (const void *)(a2 + 5 + *(_BYTE *)(a1 + 5)), *(_WORD *)(a1 + 6)); //这里产生的越界覆盖
result = (unsigned int)&v5[*(_WORD *)(a1 + 6) + 3] & 0xFFFFFFFC;
*(_DWORD *)a1 = result - a1;
return result;
}
发生越界的地方见上面第二个memmove。调试的时候可以这样下断点:
kd> u srv!SrvOs2FeaToNt+0x4d
srv!SrvOs2FeaToNt+0x4d:
9877b278 ff15e0a07698 call dword ptr [srv!_imp__memmove (9876a0e0)]
9877b27e 0fb74606 movzx eax,word ptr [esi+6]
9877b282 8d441803 lea eax,[eax+ebx+3]
9877b286 83e0fc and eax,0FFFFFFFCh
9877b289 83c418 add esp,18h
9877b28c 8bc8 mov ecx,eax
9877b28e 2bce sub ecx,esi
9877b290 5f pop edi
//最后一次越界的拷贝的长度是0xa8
ba e1 srv!SrvOs2FeaToNt+0x4d ".if(poi(esp+8) != a8){gc} .else {}"
这么设断点的原因是最后一次越界的拷贝的长度是0xa8,断下来后可以发现:
kd> dd esp
99803b38 88c8dff9 a3fc203a 000000a8 88c8dff8
99803b48 a3fc2039 00000000 a3fb20d8 a3fc2035
99803b58 a3fd2030 99803b7c 9877b603 88c8dff0
99803b68 a3fc2035 88307360 a3fb20b4 a3fb2008
99803b78 a3fc2035 99803bb4 98794602 88c8dff0
99803b88 99803bbc 99803ba8 99803bac 88307360
99803b98 a3fb2008 00000002 a3fb20b4 a3fb20d8
99803ba8 00010fe8 00000000 00000000 99803c00
kd> !pool 88c8dff9
Pool page 88c8dff9 region is Nonpaged pool
*88c7d000 : large page allocation, tag is LSdb, size is 0x11000 bytes
Pooltag LSdb : SMB1 data buffer, Binary : srv.sys
kd> !pool 88c8e009
Pool page 88c8e009 region is Nonpaged pool
88c8e000 size: 8 previous size: 0 (Free) ....
88c8e008 doesn't look like a valid small pool allocation, checking to see
if the entire page is actually part of a large page allocation...
*88c8e000 : large page allocation, tag is LSbf, size is 0x11000 bytes
Pooltag LSbf : SMB1 buffer descriptor or srvnet allocation, Binary : srvnet.sys
kd> ? 88c7d000 +11000
Evaluate expression: -2000101376 = 88c8e000
kd> ? 88c8dff9 +a8
Evaluate expression: -2000101215 = 88c8e0a1 //这里明显越界了。
我们可以从上面的调试记录看到明显的越写拷贝操作。可以看到被覆盖的是SMB1的buffer是有srvnet.sys分配的。这里exploit精心布局好的,是通过pool喷射的将两个pool连接在一起的。覆盖后面的这个pool有啥用后面会提到。
有同学会说”这只是现象,漏洞真正的成因在哪里呢?”。往下看:
unsigned int __fastcall SrvOs2FeaListSizeToNt(int pOs2Fea)
{
unsigned int v1; // edi@1
int Length; // ebx@1
int pBody; // esi@1
unsigned int v4; // ebx@1
int v5; // ecx@3
int v8; // [sp+10h] [bp-8h]@3
unsigned int v9; // [sp+14h] [bp-4h]@1
v1 = 0;
Length = *(_DWORD *)pOs2Fea;
pBody = pOs2Fea + 4;
v9 = 0;
v4 = pOs2Fea + Length;
while ( pBody < v4 )
{
if ( pBody + 4 >= v4
|| (v5 = *(_BYTE *)(pBody + 1) + *(_WORD *)(pBody + 2),
v8 = *(_BYTE *)(pBody + 1) + *(_WORD *)(pBody + 2),
v5 + pBody + 5 > v4) )
{
//
// 注意这里修改了Os2Fea的Length,自动适应大小
// 初始值是0x10000,最终变成了0x1ff5d
环境 copyright 无奈人生
EXPLOIT:
Eternalblue-2.2.0.exe 内容来自无奈安全网
TARGET: 本文来自无奈人生安全网
win7 sp1 32bits
内容来自无奈安全网
srv.sys 6.1.7601.17514 copyright 无奈人生
srvnet.sys 6.1.7601.17514 内容来自无奈安全网
PATCH:
无奈人生安全网
MS17-010 www.wnhack.com
漏洞原理 本文来自无奈人生安全网
srv.sys在处理SrvOs2FeaListSizeToNt的时候逻辑不正确导致越界拷贝。我们首先看下漏洞的触发点: www.wnhack.com
unsigned int __fastcall SrvOs2FeaToNt(int a1, int a2)
{
int v4; // edi@1
_BYTE *v5; // edi@1
unsigned int result; // eax@1
v4 = a1 + 8;
*(_BYTE *)(a1 + 4) = *(_BYTE *)a2;
*(_BYTE *)(a1 + 5) = *(_BYTE *)(a2 + 1);
*(_WORD *)(a1 + 6) = *(_WORD *)(a2 + 2);
_memmove((void *)(a1 + 8), (const void *)(a2 + 4), *(_BYTE *)(a2 + 1));
v5 = (_BYTE *)(*(_BYTE *)(a1 + 5) + v4);
*v5++ = 0;
_memmove(v5, (const void *)(a2 + 5 + *(_BYTE *)(a1 + 5)), *(_WORD *)(a1 + 6)); //这里产生的越界覆盖
result = (unsigned int)&v5[*(_WORD *)(a1 + 6) + 3] & 0xFFFFFFFC;
*(_DWORD *)a1 = result - a1;
return result;
}
发生越界的地方见上面第二个memmove。调试的时候可以这样下断点:
无奈人生安全网
kd> u srv!SrvOs2FeaToNt+0x4d
srv!SrvOs2FeaToNt+0x4d:
9877b278 ff15e0a07698 call dword ptr [srv!_imp__memmove (9876a0e0)]
9877b27e 0fb74606 movzx eax,word ptr [esi+6]
9877b282 8d441803 lea eax,[eax+ebx+3]
9877b286 83e0fc and eax,0FFFFFFFCh
9877b289 83c418 add esp,18h
9877b28c 8bc8 mov ecx,eax
9877b28e 2bce sub ecx,esi
9877b290 5f pop edi
//最后一次越界的拷贝的长度是0xa8
www.wnhack.com
ba e1 srv!SrvOs2FeaToNt+0x4d ".if(poi(esp+8) != a8){gc} .else {}"
这么设断点的原因是最后一次越界的拷贝的长度是0xa8,断下来后可以发现:
www.wnhack.com
kd> dd esp
99803b38 88c8dff9 a3fc203a 000000a8 88c8dff8
99803b48 a3fc2039 00000000 a3fb20d8 a3fc2035
99803b58 a3fd2030 99803b7c 9877b603 88c8dff0
99803b68 a3fc2035 88307360 a3fb20b4 a3fb2008
99803b78 a3fc2035 99803bb4 98794602 88c8dff0
99803b88 99803bbc 99803ba8 99803bac 88307360
99803b98 a3fb2008 00000002 a3fb20b4 a3fb20d8
99803ba8 00010fe8 00000000 00000000 99803c00
kd> !pool 88c8dff9
Pool page 88c8dff9 region is Nonpaged pool
*88c7d000 : large page allocation, tag is LSdb, size is 0x11000 bytes
Pooltag LSdb : SMB1 data buffer, Binary : srv.sys
kd> !pool 88c8e009
Pool page 88c8e009 region is Nonpaged pool
88c8e000 size: 8 previous size: 0 (Free) ....
88c8e008 doesn't look like a valid small pool allocation, checking to see
if the entire page is actually part of a large page allocation...
无奈人生安全网
*88c8e000 : large page allocation, tag is LSbf, size is 0x11000 bytes
Pooltag LSbf : SMB1 buffer descriptor or srvnet allocation, Binary : srvnet.sys
kd> ? 88c7d000 +11000
Evaluate expression: -2000101376 = 88c8e000
kd> ? 88c8dff9 +a8
Evaluate expression: -2000101215 = 88c8e0a1 //这里明显越界了。
我们可以从上面的调试记录看到明显的越写拷贝操作。可以看到被覆盖的是SMB1的buffer是有srvnet.sys分配的。这里exploit精心布局好的,是通过pool喷射的将两个pool连接在一起的。覆盖后面的这个pool有啥用后面会提到。 copyright 无奈人生
有同学会说”这只是现象,漏洞真正的成因在哪里呢?”。往下看: 无奈人生安全网
unsigned int __fastcall SrvOs2FeaListSizeToNt(int pOs2Fea)
{
unsigned int v1; // edi@1
int Length; // ebx@1
int pBody; // esi@1
unsigned int v4; // ebx@1
int v5; // ecx@3
int v8; // [sp+10h] [bp-8h]@3
unsigned int v9; // [sp+14h] [bp-4h]@1
v1 = 0;
Length = *(_DWORD *)pOs2Fea;
pBody = pOs2Fea + 4;
v9 = 0;
v4 = pOs2Fea + Length;
while ( pBody < v4 )
{
if ( pBody + 4 >= v4
|| (v5 = *(_BYTE *)(pBody + 1) + *(_WORD *)(pBody + 2),
v8 = *(_BYTE *)(pBody + 1) + *(_WORD *)(pBody + 2),
v5 + pBody + 5 > v4) )
{
//
// 注意这里修改了Os2Fea的Length,自动适应大小
// 初始值是0x10000,最终变成了0x1ff5d
内容来自无奈安全网
[1] [2] [3] [4] [5] [6] [7] [8] 下一页
无奈人生安全网