Jump to content
Kev

Exploiting SIGRed (CVE-2020–1350) on Windows Server 2012/2016/2019

Recommended Posts

by Worawit Wangwarunyoo , DATAFARM Research Team, Datafarm Company Limited

 

This post describes the exploitation (RCE) of SIGRed (CVE-2020–1350) on Windows Server 2012 R2 to Windows Server 2019. For vulnerability detail please see the checkpoint research post

 

Prepare name servers

To reduce steps on setting up a domain name, I configure “Conditional Forwarders” on a target Windows DNS Server as figure below. While I use dnslib to create my malicious DNS client and server for “evildns.com” domain.

 

1*8AdH0b4rM1ir28PtiyvTUQ.png

 

Trigger the bug

If we simply follow on checkpoint post to trigger the bug, we are likely to end up with a crash inside memcpy called from dns!SigWireRead.

 

1*GoDThG1vIrxyrnPDSd-zkg.png

 

The reason is SIG Resource Record is allocated around the end of a process heap. Triggering the bug need overwrite past SIG record buffer around 64KB, so memcpy will attempt writing past a current heap space which is invalid memory region. If we dump memory around address that caused a crash, we see an address is invalid.

 

1*0CJ-Va5ZoU7UiKLizIRP_w.png

 

To exploit a heap overflow bug, normally we have to know a heap internal and some object structure that allocated on heap.

 

WinDNS Heap Manager

WinDNS manages its own memory pools. There are 4 memory pool buckets (dns!StandardAllocLists) for different allocation sizes (0x50, 0x68, 0x88, 0xa0). If a required allocation size is greater than 0xa0, WinDNS will use native Windows heap (with HeapAlloc and HeapFree functions).

Below is pseudocode for dns!Mem_Alloc function used for dynamically allocated memory in dns

 

1*kKvMN4WtU3TFr0k30MfgxQ.png

 

Next is pseudocode for dns!Mem_Free function used for freeing memory allocated by Mem_Alloc

 

1*_hrtRg4t40AZaC13yUWrbg.png

 

After reverse engineering the Mem_Alloc and Mem_Free functions, we can see some issues that help us to exploit the vulnerability

 

WinDNS heap never return memory to native Windows Heap

If a memory size is less than or equal to 0xa0, WinDNS only simply put it to a head of free list. Native Windows heap treats it as used memory. So we can freely corrupt the native Windows heap chunk metadata because a heap metadata is checked when doing allocation and free.

 

All WinDNS heap header value is known

A WinDNS heap header contains metadata such as buffer type, size, bucket index, cookie (fixed value). All metadata value is known without a need of information leak. Because we have to start exploiting by overwriting many objects in heap, this condition is very helpful to exploit this bug without a help from another information leak bug.

 

Free chunks are kept as singly linked list

This kind of data structure can imply that chunks are allocated and freed in reverse order (LIFO). There are known techniques to abuse free chunks in singly linked list after memory corruption. E.g.

  • Fake free list to control next allocation location. This might result to overlapped chunks, arbitrary write (I think arbitrary write is difficult for this case because a cookie 8 bytes is checked before Mem_Alloc returning an address)
  • Controlling chunk allocation order by freeing them in reverse order

 

Monitoring objects in heap

To know what objects in heap are allocated when processing a user query, I add a breakpoint in Mem_Alloc to print stack trace for monitoring objects and code path. Then I send various DNS request to a server. Below is sample of it.

 

1*2L448VcibGcu33_ssaEeaA.png

 

I found a few interesting objects. First, DNS Resource Record (RR) object is created every time when Windows DNS server cache a response from authoritative name server (SIG record that allocated when triggering the bug is RR object too). A RR header has a data size field which can be overwritten and used for information leak later. Another one is timeout object. It contains a pointer to function with 1 argument. We can overwrite them to control rip (Program Counter) register later.

 

Heap buffer overflow without any crash

This step should be easy now after studying a lot of WinDNS heap. All I did is make server do allocation of many RR objects that never be freed while exploiting. Then freeing an object that followed by many RR objects (total size must be >64KB), the freed chunk will be allocated by SIG RR object when triggering the vulnerability.

 

Information Leak

When triggering the bug, we can overwrite a valid RR object by modified only data size field. Then, we can query an overwritten RR to leak adjacent chunk. Leaking overflown data is useless, so we should free adjacent chunk first. WinDNS heap will write a pointer to next free chunk, then we can leak pointer to a heap address.

 

With carefully craft overflown data and free order, we can leak a heap address in overflown area. Because we can fully control overflown data, so we can create a fake free list in there. Then new objects will be allocated in our control area. We can read/write them.

 

Then I tried to leak dns.exe module by finding some heap object that contains address in an executable module. I found 2 objects (one points to BSS section, another one points to string section). Do a query to make a server allocates object that has a pointer to dns.exe then read its content followed by calculating base dns.exe address.

 

Quote

While leaking DNS address, we can determine a target OS and version from least significant 12 bits because module must be load at start of memory page.

 

Controlling Program Counter (rip)

As mentioned in previous section, timeout object contains a pointer to function with 1 argument. We can make it allocated in overflown area then overwrite it to control PC. A pointer to function in timeout object is used in dns!Timeout_CleanupDelayedFreeList function. But dns.exe since Windows Server 2012 is compiled with Control Flow Guard (CFG). With CFG enabled, we can only jump to a function that is in allowed list. If we try to jump at function epilogue (for starting ROP), we end up crashing inside ntdll!LdprValidateUserCallTarget same as below figure (from Windows Server 2012 R2).

 

1*yeiSxsWZMEKVVSm1QXwtIg.png

 

Common procedure to do code execution, when CFG is enabled, is modifying a return address in stack, which need arbitrary write ability. But we cannot do arbitrary write now. We also don’t know any thread stack address. Now we can only control heap in overflown area. It is very rare to see a program store stack address in a heap object. I don’t even try finding a stack address in heap.

 

Next, I tried to do arbitrary read by searching an object that contains a pointer to data. Then make it allocated in our control area and overwrite a pointer. I expected a server will dereference pointer and copy a data to me. But what I found needs so many conditions, cannot be used (there might be an object that can be used for arbitrary read but I cannot find it).

 

Then I checked functions in dns.exe that is allowed by CFG with a hope to find something useful. Most functions can be skipped because we can control only 1 argument. We can completely ignore functions with argument more than 1. After spending countless hours to bypass CFG, I found dns!NsecDNSRecordConvert function.

 

1*CHd_x3hAsYc2kkQFQ_aTPQ.png

 

From a decompiled code, param_1 is obviously a pointer to struct. At line 15, a server finds a buffer length for copying from string pointer at param_1+0x20. At line 18, a server allocates a memory for storing data. At line 22, a server copies string to a new allocated memory. With fully controlled function argument, we can make the function copies data (as string) from any address into a new allocated memory. Also, we can make a new allocated memory in our controlled area. Then read a data. So we can do arbitrary read with dns!NsecDNSRecordConvert function.

 

Code Execution

With ability to call a function in CFG allowed list with 1 argument and arbitrary read, I was thinking to do code execution with kernel32!WinExec or msvcrt!system. I choose msvcrt!system because kernel32.dll is more likely to be modified by Microsoft monthly patch. Offset in msvcrt.dll should be usable on any patch level.

 

To do code execution with msvcrt!system, I find msvcrt!memcpy by reading from dns.exe import table, then calculating msvcrt!system address. Finally, repeating the controlling PC steps but jumping to msvcrt!system. Hooray, get code execution.

 

After I succeed developing an exploit for Windows Server 2012R2. I just modified offsets of dns.exe and msvcrt.dll for Windows Server 2016 and Windows Server 2019. And it works perfectly.

 

Quote

Dlls in Windows Server 2019 are compiled with CFG export suppression (below figure is msvcrt.dll). If dns.exe enables it, the exploit path should be much more difficult.

 

1*NAOq1HCAJCeSqKEnKuogVw.png

 

Demonstration Video

Here is a demo video for Windows Server 2012 R2, Windows Server 2016 and Windows Server 2019

 

RCE on Windows Server 2012 R2 with CVE-2020–1350 (SIGRed)

 

 

 

RCE on Windows Server 2016 with CVE-2020–1350 (SIGRed)

 

 
RCE on Windows Server 2019 with CVE-2020–1350 (SIGRed)
 
  • Upvote 1
Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.


×
×
  • Create New...