Low-level Reversing of SIGred (CVE-2020–1350)

Authored by: Ricardo Narvaja

Note: This work was originally done by Cristian Rubio and Ricardo Narvaja of Core Labs on Windows Server 2008 SP1 32 and 64-bit. There are not many differences in other versions of Windows.

While the basis of the SIGred bug is quite simple, it’s critical to explore exactly how this vulnerability can exploited.

All of our work was based on the Checkpoint blogpost and completed with our own research.

First, we set up our laboratory with two virtual machines, one Windows 2008 32-bit and the other Windows 2008 64-bit, alternating on one being the attacker and the other being the target.

VM IP Address

We configured each conditional forwarder of each DNS server to point to the IP of the other virtual machine, so that we could use it first as an attacker, and then as a target. First, we used the 64-bit machine as the attacker and the 32-bit machine as the target.

32 bit patch

The above screenshot shows the patch in the 32 bits version.

The patch avoids processing sizes greater than 0xffff and uses the safe function _UshortAdd to avoid integer overflow when adding.

Below are the additions performed in the unpatched version:

32 bit patch additions

In these additions an integer overflow can occur and be allocated a size much smaller than what is to be copied later. The mechanism of how the vulnerability is triggered is explained in detail in the original blogpost, but here are the steps, briefly:

  1. A DNS request to the vulnerable DNS server is performed.
  2. The vulnerable DNS server finds the conditional forward to the malicious servers and, acting as a client, connects to the fake UDP server.
  3. The fake UDP server responds and then the DNS server connects to the false TCP server which finally responds with the crafted packet that will produce the overflow in the vulnerable DNS server.
Integer overflow

We could control, within certain limits, the size with which we produced the integer overflow. The problem occurred when trying to copy the data that overflowed from the section where the copy was performed.

copy overflow problem

To be able to trigger the overflow EDI needed to have a value of a little less than 0xFFFF, so that when AX and 0x14 were added, the size to allocate ended with a value greater than zero but lower than 0xFFFF. (We could get allocation sizes from 0 to approximately 0xd8.)

But the saved size was still much larger—as we said, it was close to 0xFFFF. If later copies use this size, it would generally produce a crash.

Size change allocation

To bypass that crash, we looked for a code that performs a simple allocation and is the same size as the one that occurs here. This code could be created and released whenever we wanted.

The memory allocator in the 32 bits version checks if the size to be allocated is greater than 0x68 and uses native Windows heap (HeapAlloc, HeapFree etc).

memory allocator

If the size is smaller or equal than 0x68, the heap is managed by the program using StandardAllocLists.

In the 64 bits version, sizes greater than 0x90 use native Windows heap. Otherwise it uses StandardAllocLists.

Spray heap allocation

The screenshot above shows the allocation used by us to spray the heap.

An allocation of size 0xd4 occurred and was easily administered to make a spray that would stay alive and would be released at will.


We could then spray the heap with chunks of size 0xd4, close some connections to free and achieve a gap and then locate the allocation that is made with SigWireRead in it.

If we massaged the heap in an appropriate way, we could ensure the allocation is in the initial part of the section, avoiding the crash in the memcpy function.

Code execution

By manipulating the heap and using some pointer leaks, code execution could easily be achieved.


Now, we’ll go through the other scenario, which had the 64-bit virtual machine as the target and the 32-bit as the attacker.

64 bit VM

We could not use the same strategy massaging the heap, because the size of the Tcp_ConnectionCreate allocations is 0x108 and our packet that reached SigWireRead cannot allocate that amount due to packet limitations.

With the 64-bit machine, the allocations are great than 0x100. In order to use the same strategy as the 32-bit machine, we needed to allocate 0x118 with a SIGred package. However, this was impossible due to the restrictions of the package.

package restrictions

By managing the sizes and the signature we were able to increase the value in a fixed address.

RAX 24

Managing the signature a little more we were also able to write what we wanted where we wanted. When complemented with a good spray and the necessary pointer leaks, this allowed RCE.

RBX spray
heap spray fixed addresses

We used a heap spray but only to get fixed addresses to help us with the execution.

End heap spray
potential leaks

We had some potential leaks too, which needed working on.

recurse connect callback

The leaked pointers were necessary to get the RCE.

Hopefully, this furthers understanding of this bug.


Learn how to exploit other vulnerabilities

CTA Text

Learn other pen testing techniques with the Reversing and Exploiting With Free Tools series.