12月11, 2020

CVE-2020-17140 Windows SMB Information Disclosure Analysis

CVE-2020-17140 Windows SMB Information Disclosure Analysis

Author:k0shl of 360 Vulcan Team

Summary

Microsoft patched a SMB remote information disclosure vulnerability this patch tuesday I reported in September, it may affect over Windows 7 to Windows 10, more detail you can find in MSRC Acknowledgements: https://msrc.microsoft.com/update-guide/vulnerability/CVE-2020-17140 , I will public the vulnerability detail in this blog.

Background

Microsoft introduced a new SMB shared file caching mechanism called "LEASE" to improve read and write performance of shared file and directory since SMBv2 and later. We can add a lease to a shared file or directory by setting SMB packet structure SMB2_CREATE_CONTEXT->Buffer field value with SMB2_CREATE_REQEUST_LEASE_V2.

The structure of SMB2_CREATE_REQUEST_LEASE_V2 packet shown as following:

smb1.PNG

I use SMB2_CREATE_REQUEST_LEASE_V2 to add a lease to shared file, this structure only can be used in SMB 3.x dialects.(You can also try SMB2_CREATE_REQUEST_LEASE to trigger this vulnerability)

After adding lease name to shared file, we can change lease name by SMB2_SET_INFO packet, the structure of it like this:

smb2.PNG

There is a feild named InfoType in SMB2_SET_INFO request structure

smb3.PNG

If we set InfoType to SMB2_0_INFO_FILE, it will finally invoke NtSetInformationFile with Class FileRenameInformation to rename filename, and call Smb2ContinueSetInfo to update lease name buffer.

__int64 __fastcall Smb2ExecuteSetInfoReal(__int64 a1)
{
    [...]
    switch ( *(_BYTE *)(v1 + 160) ) //check InfoType
    {
      case 1:
        [...]
        LODWORD(v37) = *(_DWORD *)(v1 + 180);
        v6 = NtSetInformationFile(v10, v7, v9, v8, v37);// <-------- rename
        [...]
        if ( v15 >= 0
          || (v27 = WPP_GLOBAL_Control, WPP_GLOBAL_Control == (PDEVICE_OBJECT)&WPP_GLOBAL_Control)
          || !(HIDWORD(WPP_GLOBAL_Control->Timer) & 0x10000000)
          || BYTE1(WPP_GLOBAL_Control->Timer) < 1u )
        {
LABEL_20:
          *(_DWORD *)(*(_QWORD *)(v2 + 104) + 48i64) = v15;
          return Smb2ContinueSetInfo(v2);// <--------- update lease name buffer
        }
        [...]
    }
    [...]    
}

CVE-2020-17140 Analysis

The root cause of CVE-2020-17140 is a code logic error that will cause use after free, vulnerability exists in srv2!Smb2UpdateLeaseFileName, the code trace like this:

Smb2ExecuteSetInfo
      |     
       --- Smb2ExecuteSetInfoReal
                    |     
                     --- Smb2ContinueSetInfo
                                 |     
                                  --- Smb2UpdateLeaseFileName

srv2.sys driver will check if shared file have lease name after rename file name. If lease name existed, it will update lease name after invoke NtSetInformationFile.

__int64 __fastcall Smb2ContinueSetInfo(__int64 a1)
{
      [...]
      if ( *(_QWORD *)(*(_QWORD *)(v2 + 64) + 0x90i64) ) // <---------- Check there is lease name stored in buffer
      {
        v5 = *(_QWORD *)(v2 + 168);
        v6 = Smb2ReferenceLeaseFromFile();
        v7 = (_BYTE *)v6;
        if ( v6 )
        {
          if ( (signed int)Smb2UpdateLeaseFileName(v6, (_WORD *)(v5 + 20), *(_DWORD *)(v5 + 16)) >= 0 )
            v7[113] = 0;
          Smb2DereferenceLease(v7);
        }
      }
      [...]
}


signed __int64 __usercall Smb2LeaseAcquireOrUpgrade@<rax>(__int64 a1@<rcx>, __int64 a2@<rdx>, char a3@<r8b>, __int64 a4@<r9>, __int64 a5@<r13>)
{
  [...]
  *(_QWORD *)(v5 + 0x188) = v6 + 136;
  *(_QWORD *)(v5 + 0x190) = v14;
  *v14 = v5 + 0x188;
  *(_QWORD *)(v6 + 0x90) = v5 + 0x188;
  *(_QWORD *)(v5 + 0x90) = v6; //  <----------  set pointer
  [...]
}

"v5+0x90" store a pointer when client request lease name, if we set SMB2_CREATE_REQUEST_LEASE_V2 in SMB2_CREATE_CONTEXT->Buffer field, it will invoke Smb2LeaseAcquireOrUpgrade to set pointer.

//Set pointer
srv2!Smb2LeaseAcquireOrUpgrade+0x177:
fffff802`44a68d17 48899f90000000  mov     qword ptr [rdi+90h],rbx ds:002b:ffffc70c`3f68eab0=0000000000000000
rdi=ffffc70c3f68ea20
rbx=ffffc70c3be41d70 <----------- set pointer

//Check pointer
srv2!Smb2ContinueSetInfo+0x8b:
fffff802`44ab8587 4883b99000000000 cmp     qword ptr [rcx+90h],0 ds:002b:ffffc70c`3f68eab0=ffffc70c3be41d70

It will hit the vulnerability function, srv2!Smb2UpdateLeaseFileName will update lease name. A completely file name format like this: [FileName]:[LeaseName], if new file name is longer than old, it will invoke ExAllocatePoolWithTag to allocate a new NonPagedPoolNx pool to store the file name format, then copy full file name(old file name and new lease name) to new buffer.

But there is a obvious error, the function first free the old buffer, and copy free pool buffer content as file name to new buffer.

signed __int64 __fastcall Smb2UpdateLeaseFileName(__int64 a1, _WORD *a2, unsigned int a3)
{
      [...]
      v12 = (unsigned __int16)v4 + 2 * v11; // <------ get completely file name([filename]:[leasename]) length
      [...]
      if ( v12 > *(unsigned __int16 *)(v6 + 0x18A) )// <------ if file name is longer than old
        {
          v13 = ExAllocatePoolWithTag((POOL_TYPE)0x200, v12, 0x6C32534Cu); // <------ allocate a new buffer
          if ( !v13 )
          {
            v3 = -1073741670;
            goto LABEL_16;
          }
          if ( *(_BYTE *)(v6 + 114) )  // <------- [1]
            ExFreePoolWithTag(*(PVOID *)(v6 + 400), 0);// <------ free old buffer
          if ( v11 )
            memmove(v13, *(const void **)(v6 + 400), 2i64 * v11); // <------- copy free buffer,trigger use after free.
          *(_WORD *)(v6 + 394) = v12;
          *(_QWORD *)(v6 + 400) = v13;
          *(_BYTE *)(v6 + 114) = 1; // <------- [2]
        }
       memmove((void *)(*(_QWORD *)(v6 + 400) + 2i64 * v11), v5, (unsigned __int16)v4);// <------- copy new lease name
      [...]
}

There is a boolean check[1] if there already exists a old file name buffer, if old buffer exists, "v6+114" is set to 1[2]. So, for triggering UaF, we need to send a SMB2_SET_INFO packet with a new lease name longer than old lease name which created by SMB2_CREATE_CONTEXT, and "v6+114" will be set to 1, then send another packet with a more longer lease name to trigger UaF.

Conclusion

This vulnerability can copy a free pool buffer or another pool buffer which occupy this free hole as a file name, it will cause kernel info leak. If this pool became a invalidate memory after free, it also will cause BSoD.

Crash Dump:

rax=ffffda0aa0488fa0 rbx=0000000000000000 rcx=ffffda0aa0488fa0
rdx=ffffda0aa0486fc0 rsi=0000000000000000 rdi=0000000000000000
rip=fffff80166a6fc8f rsp=ffffa601e6b2a918 rbp=0000000000000054
 r8=000000000000000e  r9=ffffda0aa0400e80 r10=ffffda0a9a41d160
r11=0000000000000000 r12=0000000000000000 r13=0000000000000000
r14=0000000000000000 r15=0000000000000000
iopl=0         nv up ei ng nz na pe cy
srv2!memcpy+0xf:
fffff801`66a6fc8f 4c8b1a          mov     r11,qword ptr [rdx] ds:ffffda0a`a0486fc0=????????????????

Stack Trace:

ffffa601`e6b2a780 fffff801`66a6fc8f     : fffff801`66a76812 00000000`00000007 ffffda0a`a0450e40 ffffda0a`a049ab00 : nt!KiPageFault+0x469
ffffa601`e6b2a918 fffff801`66a76812     : 00000000`00000007 ffffda0a`a0450e40 ffffda0a`a049ab00 00000000`00000100 : srv2!memcpy+0xf
ffffa601`e6b2a920 fffff801`66ab85b5     : 00000000`00000000 ffffda0a`a049a930 ffffda0a`a049af10 00000000`00000000 : srv2!Smb2UpdateLeaseFileName+0x8522
ffffa601`e6b2a970 fffff801`66ab7f03     : 00000000`00000000 ffffa601`e6b2aa69 ffffda0a`a049ac50 ffffda0a`a049a930 : srv2!Smb2ContinueSetInfo+0xb9
ffffa601`e6b2a9a0 fffff801`66ab87af     : 00000000`00000000 ffffda0a`a049a940 fffff801`6815f601 ffffda0a`a0434f00 : srv2!Smb2ExecuteSetInfoReal+0x133
ffffa601`e6b2aad0 fffff801`66aba51b     : 00000000`00000050 00000000`00000080 ffffffff`ee1e5d00 ffffda0a`a045afe0 : srv2!RfspThreadPoolNodeWorkerProcessWorkItems+0x13f
ffffa601`e6b2ab50 fffff801`6815f637     : ffffda0a`9abec000 ffffda0a`9eaf2040 00000000`00000151 00000000`00000000 : srv2!RfspThreadPoolNodeWorkerRun+0x11b
ffffa601`e6b2abb0 fffff801`67cad9a5     : ffffda0a`9eaf2040 fffff801`6815f600 ffffb88b`e1997d90 000fa46f`bd9bbdff : nt!IopThreadStart+0x37
ffffa601`e6b2ac10 fffff801`67e07868     : fffff801`6643f180 ffffda0a`9eaf2040 fffff801`67cad950 00000000`00000246 : nt!PspSystemThreadStartup+0x55
ffffa601`e6b2ac60 00000000`00000000     : ffffa601`e6b2b000 ffffa601`e6b25000 00000000`00000000 00000000`00000000 : nt!KiStartSystemThread+0x28

Patch

After patch, function will copy old file name to new buffer before free buffer:

signed __int64 __fastcall Smb2UpdateLeaseFileName(__int64 a1, _WORD *a2, unsigned int a3)
{
  [...]
  if ( v12 )
    memmove(v15, *(const void **)(v6 + 0x190), 2i64 * v12);
  if ( *(_BYTE *)(v6 + 0x72) )
    ExFreePoolWithTag(*(PVOID *)(v6 + 0x190), 0);
  [...]
}

Reference

https://msrc.microsoft.com/update-guide/vulnerability/CVE-2020-17140

https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/e8fb45c1-a03d-44ca-b7ae-47385cfd7997

https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/ee9614c4-be54-4a3c-98f1-769a7032a0e4

本文链接:https://blogs.360.cn/post/CVE-2020-17140-Analysis.html

-- EOF --

Comments