<p style="text-align: center;"><sub><i>Original Date Published: March 2, 2021</i></sub></p>
![[61bb5291502236b4d6cfb438_anatomy-of-exploit.jpg]]
RCE PoC for CVE-2020-1350 SIGRed can be found [here](https://github.com/chompie1337/SIGRed_RCE_PoC).
# Overview
SIGRed, [CVE-2020-1350](https://msrc.microsoft.com/update-guide/en-US/vulnerability/CVE-2020-1350), is a vulnerability in the Microsoft Windows DNS service that was disclosed on July 14, 2020. It was discovered by [Sagi Tzadik](https://twitter.com/sagitz_), of Check Point Research [ [1]](https://research.checkpoint.com/2020/resolving-your-way-into-domain-admin-exploiting-a-17-year-old-bug-in-windows-dns-servers/), who released an in-depth write up of the bug the day the patch was released. The vulnerability received a CVSS score of 10.0, the highest level of severity. On Windows, a DNS server is a Domain Controller, and its Administrators are part of the Domain Admins group. By default, the Domain Admins group is a member of the Administrators group on all computers that have joined a domain, including the domain controllers [ [8]](https://docs.microsoft.com/en-us/windows/security/identity-protection/access-control/active-directory-security-groups). If exploited carefully, attackers can execute code remotely on the vulnerable system and gain Domain Admin rights, effectively compromising the entire corporate infrastructure. This write-up provides a detailed breakdown of the exploitation methods used in the [proof of concept [2]](https://github.com/chompie1337/SIGRed_RCE_PoC) I released. While there have been several proof of concepts for DoS and even a fake RCE PoC that was actually a rickroll, this is the first and (to my knowledge) only publicly released exploit demonstrating real RCE.
# The Vulnerability
CVE-2020-1350 is an integer overflow vulnerability that leads to a heap-based buffer overflow when processing malformed DNS `SIG` resource records. A `SIG` record is a type of DNS resource record that contains a digital signature for a record set(one or more DNS records with the same name and type)[ [10]](https://en.wikipedia.org/wiki/List_of_DNS_record_types). To exploit SIGRed, an attacker can configure an “evil” domain whose `NS` record points to a malicious DNS server. When a client makes a DNS query for the “evil” domain to the victim server, the victim server will query the DNS server above it. The DNS server will respond back with an NS record indicating that the malicious DNS server is the authority for that domain, and the record will be cached by the victim. Afterwards, when a client sends the victim a DNS `SIG` query for the domain, the victim server will query the malicious DNS server. The malicious DNS server will send a malformed DNS `SIG` record as a response.
![[61bb52b1bf2a6e36e2ad69d4_aa91b3_adb7b85135e44f38bcca549851f38d68_mv2.png]]
<p style="text-align: center;"><sub><i>How an attacker can exploit SIGRed</i></sub></p>
The vulnerability is present in the function `dns!SigWireRead` (in the DNS service binary, `dns.exe`), which is used to cache a DNS `SIG` record response from another DNS server.
![[61bb52b12386065d1d0fe40f_aa91b3_ffb865d47c0b4f54925b8c2acb34b036_mv2.png]]
<p style="text-align: center;"><sub><i>Decompilation of the vulnerable function, dns!SigWireRead</i></sub></p>
Refer to line 11 in the decompilation of the vulnerable function `dns!SigWireRead`. The function `RR_AllocateEx` is passed a 16 bit unsigned integer for the size parameter. It is possible to send a DNS `SIG` record response such that the size calculated is greater than `0xFFFF`, which triggers the integer overflow.
## Triggering the Vulnerability
Due to packet size constraints, it is not possible to trigger the vulnerability by solely sending a `SIG` record with a very large signature. In fact, triggering the vulnerability is not possible over `UDP`, because the maximum allowed size of a DNS message over `UDP` is either `512` or `4096` bytes, depending on whether the server supports `EDNS0`. In either case, it is not enough to trigger the vulnerability. Using DNS truncation [ [9]](https://serverfault.com/questions/991520/how-is-truncation-performed-in-dns-according-to-rfc-1035), the malicious DNS server can tell the victim server to retry the request over `TCP`. The total size limit of a DNS message over `TCP` is 64KB (`0xFFFF`). This is still not enough to trigger the vulnerability, as the message limit includes space for headers and the original query.
#### DNS Name Compression
DNS names can be, and often are, compressed in a DNS message. By manipulating the compression of the name in the DNS message over `TCP`, it is possible to inflate `sigName.Length` without increasing the total size of the DNS message.
![[61bb52b12386067cc60fe40e_aa91b3_0f0a903422704ca2b869b12030130780_mv2.png]]
<p style="text-align: center;"><sub><i>DNS name compression in a DNS message packet</i></sub></p>
In the above example, the `0xC0` byte (highlighted in the green box above) has the most significant two bits set. This indicates that the following 14 bits represent the offset of the DNS name, relative to the start of the DNS message. In the example pictured above, it is `0xC` bytes (highlighted in the blue box) from the start of the message.
DNS names are encoded as follows: a single byte denotes the number of characters before a "`.`" character, terminating in a null byte. For example, `www.google.com` may be encoded as:
```
03 77 77 77 06 67 6F 6F 67 6C 65 03 63 6F 6D 00
```
which indicates that the name consists of a string of 3 bytes, a period, 6 bytes, a period, and then three bytes terminated by a null.
In the case pictured in the packet capture above, the offset points to `0x1`, because the requested domain begins in "`9.`". Therefore, if we change the following 14 bits from `0x0C` to `0x0D`, the DNS name offset will point to `0x39`, indicating that the next part of the DNS name is `0x39` bytes forward, which extends into the signature portion of the packet. This way, we can trick the Windows DNS service into calculating the DNS name size to be much larger than the number of actual bytes used to represent the DNS name.
As seen in the decompilation of `dns!SigWireRead`, the total size passed to `RR_AllocateEx` is calculated as: `signatureLength + sigName.Length + 0x14`. The maximum length for a DNS name is `0xFF` bytes. The additional bytes from the fake name length are enough to trigger the vulnerability and also stay under the maximum message size of 65KB!
For a more detailed explanation of the vulnerability and how to trigger it, including how to trigger it from a browser, please see the original [Check Point Research article [1]](https://research.checkpoint.com/2020/resolving-your-way-into-domain-admin-exploiting-a-17-year-old-bug-in-windows-dns-servers/).
# Exploitation
This was my first time writing a RCE exploit in user mode, and I learned a lot about heap based exploitation. It is my hope that this write up will help others who are interested in learning heap exploitation.
The exploitation strategy for this bug is interesting because it requires both a malicious client and server. It also depends on careful heap manipulation to not only achieve RCE but also make the exploitation reliable. This section will describe all the necessary pieces and show how they are used together to obtain RCE on the victim server.
## Triggering the Vulnerability without Crashing
The most time consuming portion of this exploit was understanding the correct way to manipulate the heap. This section will focus on the details of heap grooming to avoid crashes and control heap buffer freeing and allocation.
### WinDNS Heap Manager
It is first necessary to understand how the WinDNS service manages heap memory. The WinDNS service manages its own memory pools [ [3]](https://datafarm-cybersecurity.medium.com/exploiting-sigred-cve-2020-1350-on-windows-server-2012-2016-2019-80dd88594228). If the requested size of a buffer is more than `0xA0` bytes, it will request the memory from the Windows native heap manager (`HeapAlloc`). Otherwise, it will use a memory pool bucket (sizes `0x50`, `0x68`, `0x88`, and `0xA0`). The buffers in each of the buckets are stored in a singly linked list. If there are no more available buffers in the selected bucket, a memory chunk will be requested from the native heap, divided into separate buffers, then added to the list of the corresponding bucket. For buffers of sizes `0x50`, `0x68`, `0x88`, and `0xA0`, memory chunks of sizes `0xFF0`, `0xFD8`, `0xFF0`, and `0xFA0 `are requested, respectively.
![[61bb52b1558c6f604feca71e_aa91b3_3601d110d2484f8198fa362f9253d1cc_mv2.png]]
<p style="text-align: center;"><sub><i>Approximate decompilation of dns!Mem_Alloc</i></sub></p>
When buffers in one of the memory buckets are freed, they are not returned to the Windows native heap. Instead, they are added back to the list of available buffers for that bucket. Buffers are allocated on Last-In-First-Out (LIFO) basis, meaning the last buffer to be freed will be the next to be allocated.
![[61bb52c6237b9623d6e9b672_aa91b3_9c3247819fbb4c8aa6e5c3c291a747af_mv2.png]]
<p style="text-align: center;"><sub><i>Approximate decompilation of dns!Mem_Free</i></sub></p>
#### WinDNS Buffer Structure
The structure of a WinDNS buffer is as follows:
![[61bb52c6222017782f24e4d1_aa91b3_6a9906be77d3468aa059e7ce333c7027_mv2.png]]
This will become useful during exploitation.
### Avoiding a Segmentation Fault During memcpy
The first issue I ran into when writing my exploit was a segmentation fault occurring during the heap overflow itself. The `memcpy` of the large signature into the allocated heap based buffer was hitting virtual address of memory that was not mapped. I had to make sure the entirety of the overflow bytes were copied into a valid area of memory .
While investigating the heap layout, I found that the Windows native heap manager allocated the bucket memory chunks within “internal” heap segment of sizes `0x41FD0-0x41FF0`. From my observations, these heap segments _only_ contain memory chunks used for the WinDNS memory buckets. So, if we ensure the size of the overflown buffer is less than `0xA0`, we can be sure it will be within one of these chunks.
![[61bb52c6a49eaa60f2c2fea5_aa91b3_0e307cff0e4e467e8f4b790713211a5f_mv2.png]]<p style="text-align: center;"><sub><i>Heap segments containing WinDNS memory chunks after heap spray</i></sub></p>
This buffer will be somewhere inside a heap segment of size ~`0x41FF0`. The total number of bytes needed is slightly more than `0xFFFF`. Therefore the probability that the entirety of the overflow ends up at valid memory address is relatively high.
#### Making a hole
We can guarantee our chances of landing at a valid memory address if we can trigger the freeing of a buffer in the middle of a many contiguous heap segments, reallocate it and overflow the buffer. This is a common technique in heap exploitation.
In this case, we can cause a buffer to be freed by making a query to the victim client and having our malicious DNS server return a response with a short TTL (Time-To-Live). Similarly, we can ensure cached record buffers won’t be freed by assigning a long TTL. Expired records are freed every ~2 minutes.
![[61bb52c64336d0dccfe87280_aa91b3_bbbaa7f0334643eb838f44792c9f897f_mv2.png]]
<p style="text-align: center;"><sub><i>Malicious DNS server controls TTL, which can be used for heap grooming</i></sub></p>
The process to make a hole in the heap and reallocate it has a few basic steps:
- Make many queries for subdomains of the evil domain to the victim server.
- The malicious DNS server will give the victim a response, which the victim will cache in heap memory (heap spray).
- The malicious DNS server will assign a long TTL for all subdomains except one, which will be given a short TTL.
- WinDNS frees buffers for expired records every ~2 minutes, so we wait for the buffer to be freed.
- Make another query for the subdomain whose `SIG` record just expired; this time the malicious DNS server will give a malformed response to trigger the overflow.
- Because buffers are LIFO allocated, the new record buffer will have the same address of the expired `SIG` record in memory.
![[61bb52c7d5712e436455c3d3_aa91b3_3524254ae14c4838b8d6e689e3927922_mv2.png]]
<p style="text-align: center;"><sub><i>Making a hole in the heap to avoid SEGFAULT</i></sub></p>
### Avoiding Crashes due to Overwriting Other Objects on the Heap
While I was able to reliably avoid segmentation faults during `memcpy`, I still encountered many other crashes from overwriting objects on the heap.
![[61bb52c74052c720767fe74c_aa91b3_6cc4d9128781460cb9bac4950bf35403_mv2.png]]<p style="text-align: center;"><sub><i>Crash due to overwriting cache tree nodes</i></sub></p>
I looked in `WinDbg` to see what types of buffers were being allocated near the overflown buffer.
![[61bb52c748e9d469516ffaa7_aa91b3_b840978044f845569aebf01f53839875_mv2.png]]
<p style="text-align: center;"><sub><i>Memory allocation tracking in Windbg</i></sub></p>
I could see that new WinDNS memory chunks of size `0xFF0` and `0xFA0` were being allocated near the heap buffer I was overflowing. The latter came as a result of the heap spray (record buffers of size `0xA0`). But what about the chunks of size `0xFF0`? These contained buffers of size `0x88`, used to store objects related to the DNS record cache, which is saved as a binary tree. I was overwriting cache tree objects and causing a crash when the tree was traversed.
The solution became clear at this point. Remember that the overflown buffer is within a heap segment that is shared only by other WinDNS managed memory chunks. This means the objects being overwritten are of a size `<= 0xA0` and fit into one of the memory buckets managed by WinDNS. We know that buffers in these memory buckets are never released back to the native heap, and are instead returned to the free buffer list of the corresponding bucket size. So, we can groom the heap by forcing the allocation of many buffers of size `0x88` and cause them to be freed. Once they are freed, they will be returned to the free buffer list, avoiding the need to allocate new heap memory. We can then spray the heap with many buffers that will not be freed to ensure the buffer we overflow will be in a new heap segment away from the objects we don’t want to overwrite.
![[61bb52c724b7428e604e5aee_aa91b3_a49f5d88fa044d338298fa33a32375e8_mv2.png]]
<p style="text-align: center;"><sub><i>Heap grooming to avoid overwriting important heap objects</i></sub></p>
## Overwriting Objects in the Heap
Now that we have sufficiently groomed the heap to avoid a crash, the next step is to overwrite heap objects that will create the exploit primitives. In the last section, we made a hole for the overflown buffer. With this hole, we are set up to overwrite the “throwaway” cached records we sprayed the heap with.
### Know Your Surroundings
Because we spray the heap, many new memory chunks will be allocated. These buffers are added contiguously to the freelist, meaning they are allocated in contiguous order. Therefore, the order the DNS `SIG` record queries are made will be the order in which they appear on the heap. So, we know exactly what records will be overwritten during the overflow.
![[61bb52c71ac5450edab2a765_aa91b3_513f09773370416da1c0c33f0fb2968a_mv2.png]]
<p style="text-align: center;"><sub><i>Overwriting RR_Record objects with fake ones</i></sub></p>
#### RR_Record Structure
First, let’s look at the structure of a cached WinDNS record:
![[61bb52c8c2e0bb08a5b1629f_aa91b3_2e95baa7527f41e3b4fc7dec0c87a822_mv2.png]]
Knowing this and the structure of `WINDNS_BUFF` will make it easy to craft fake `RR_Record` objects.
#### Controlling Buffer Freeing
Previously, we freed record buffers of our choosing by simply giving them a short TTL and waiting for them to expire and be freed. This is good, but waiting at least two minutes is a problem. Not because we’re impatient, but because it gives us less control over reallocation. It will be useful to be able to trigger the immediate freeing of buffers.
When a `RR_Record` object is retrieved from the cache to respond to a query, the fields `dwTTL` and `dwTimeStamp` are first checked before returning the response. This is because it is possible that the record’s TTL has already expired. Remember, the record cache is only cleaned up every 2 minutes. It is possible a record has expired in between cleanups. We can abuse this by simply zeroing out the `dwTTL` and `dwTimeStamp` fields in a fake `RR_Record` object and sending a query for the corresponding subdomain. This will cause the buffer to be freed.
#### Controlling Buffer Allocation
Now controlling buffer allocation is straight forward. Since WinDNS buffers are allocated LIFO, once we free a buffer, it will be next of that bucket size to be allocated. Even better, because we also control the values in the `WINDNS_BUFF` structure we can fake the size of the original buffer! This means that we can allocate objects of different sizes in the area of the heap that we control.
![[61bb52f77233fa81be04e68f_aa91b3_dc2048cfccc741e897dbdc45ae762324_mv2.png]]
<p style="text-align: center;"><sub><i>Controlling allocation of buffers of different size</i></sub></p>
## Leaking Memory
### Leaking Heap Addresses
We can now leak an address in the heap by doing the following:
- Trigger the freeing of a fake `RR_Record`.
- Give the fake `RR_Record` above the freed one a large `wRecordSize`.
- Send the victim a `SIG` query for the subdomain with the fake large `wRecordSize`.
- The response will go past the real size of the buffer, and include the `WINDNS_FREE_BUFF` structure data of the freed record below it. This leaks a valid heap address in the `pNextFreeBuff` field.
![[61bb52f7161c501cae7cf71c_aa91b3_67311c33ac5a4faea4a0a7839f284245_mv2.png]]
<p style="text-align: center;"><sub><i>Leaking a heap pointer using fake RR_Record objects</i></sub></p>
Great, we obtained our first memory leak! However, we don’t actually know where the leaked pointer is relative to the area of the heap we control. It would be even better if we could get the address of our overflown buffer. To do this, we can simply free two fake `RR_Record` objects, and leak the `WINDNS_FREE_BUFF` of the buffer we freed last. When a buffer is freed, the pointer to the buffer that was freed before it is written to the `pNextFreeBuff` field.
![[61bb52f7dbe92852b3598189_aa91b3_3d7a3b96f79c4588b9c015366dc55366_mv2.png]]
<p style="text-align: center;"><sub><i>Leaking a pointer to the controllable part of the heap</i></sub></p>
We now know the exact address of portion of the heap we control! This will be useful later.
#### Leaking dns.exe Address
Next, we need to leak an address inside `dns.exe` to defeat ASLR [ [5]](https://en.wikipedia.org/wiki/Address_space_layout_randomization). To leak addresses inside of `dns.exe`, we can trigger the allocation of a special kind of object that I will refer to as a `DNS_Timeout` object.
A `DNS_TimeOut` object has the following structure:
![[61bb52f7558c6fdcb6eca7aa_aa91b3_27b27d797c8a42fb8bdc4d2a2537b3af_mv2.png]]
When a DNS record expires, `dns!RR_Free` is called. If a DNS record is type `DNS_TYPE_NS`, `DNS_TYPE_SOA` , `DNS_TYPE_WINS`, or `DNS_TYPE_WINSR` [ [6]](https://docs.microsoft.com/en-us/windows/win32/dns/dns-constants) they are not freed immediately. Instead, `dns!Timeout_FreeWithFunctionEx` is called.
![[61bb52f717b25649351fc088_aa91b3_b9b5250b36a9499ba2d4376f60e28ca0_mv2.png]]
<p style="text-align: center;"><sub><i>Approximate decompilation of dns!RR_Free</i></sub></p>
In `Timeout_FreeWithFunctionEx`, a WinDNS buffer is allocated for a `DNS_Timeout` object. Then, the address of `RR_Free` and a string are written to the `pFreeFunction` and `pszFile` fields, respectively. These will be our `dns.exe` address leaks. If we trigger the allocation of a timeout object in the area of the heap we control, we can use the same method as before to leak the addresses.
![[61bb52f78cd2bc0e395094d4_aa91b3_a3e6de45bb44402ba503f7b68a6cbc03_mv2.png]]
<p style="text-align: center;"><sub><i>Decompilation of dns!Timeout_FreeWithFunctionEx</i></sub></p>
We trigger the allocation of the object by first freeing a fake `RR_Record` object with a fake buffer size of `0x50`, which is the bucket memory size allocated for a `DNS_Timeout` object. Then, we make some `NS` queries to the victim for the evil domain. A timeout object will be allocated for each query once the records expire. It is necessary to make several of these queries in case new buffers of size `0x50` have been freed while waiting for the `NS` records to be expire. We can again leak the memory by making a request for the cached record above it, with a fake large `wRecordSize`.
![[61bb52f83c2a55b0a9f834c4_aa91b3_0faf3a4354db4d17a30d01f93830e3d6_mv2.png]]
<p style="text-align: center;"><sub><i>Leaking dns.exe address by allocating DNS_Timeout object</i></sub></p>
Now that we have leaked addresses inside `dns.exe`, we can use them to calculate the addresses of functions inside the binary. By taking the last 12 bits of the leaked addresses, we can create a mapping to offsets for various versions of `dns.exe`.
Originally, I thought I could trigger the allocation of a timeout object by simply freeing a fake `RR_Record` object `wRecordType = DNS_TYPE_NS`. Thus, avoiding having to wait the 2 minutes for the `NS` records to expire. However, when I tried to do this, some check prevents the call to `RR_Free` on fake `RR_Record`s with a modified `wRecordType`. I ran out of time while investigating the issue, so this is a potential area for improvement.
## Arbitrary Read
Finally, we have all the pieces for an arbitrary read primitive.
Note that we already have the ability to get code execution by overwriting the `pFreeFunction` pointer in the `DNS_timeout` object that was allocated. In the function `dns!Timeout_CleanupDelayedFreelist`, the function address in `pFreeFunction` is called for each timeout object in the `CoolingDelayedFreeList`. This list contains the `DNS_Timeout` objects representing records that are ready to be freed. Luckily, a `DNS_Timeout` object contains a field for one parameter that is passed to this function.
![[61bb52f83c2a55e988f834c3_aa91b3_5268b6c5246d486b8796c1026e1c28f8_mv2.png]]
<p style="text-align: center;"><sub><i>Approximate decompilation for dns!Timeout_CleanupDelayedFreeList</i></sub></p>
We can trigger the vulnerability again after the timeout object is allocated to overwrite these fields.
Modern versions of `dns.exe` are compiled with Control Flow Guard (CFG) [ [4]](https://docs.microsoft.com/en-us/windows/win32/secbp/control-flow-guard). One known way to bypass CFG is to corrupt return addresses on the stack [ [11]](https://improsec.com/tech-blog/bypassing-control-flow-guard-in-windows-10) and execute using a ROP [ [7]](https://en.wikipedia.org/wiki/Return-oriented_programming) method. However, we currently don’t have a stable way to write to the stack. Instead, we can find a valid call target (i.e. a function within `dns.exe`) to use for a primitive. A suitable candidate is `dns!NsecDnsRecordConvert`, which takes one parameter [ [3]](https://datafarm-cybersecurity.medium.com/exploiting-sigred-cve-2020-1350-on-windows-server-2012-2016-2019-80dd88594228).
A parameter to `NsecDnsRecordConvert` should have the following structure:
![[61bb52f83c2a55ae18f834c5_aa91b3_9d45541820f94f49a93ee8e423e4165e_mv2.png]]
Inside this function, a buffer is allocated and a call to `Dns_StringCopy` is made. This is where the read primitive lies. Since we control the function parameter passed in and its content, we can make the `pDnsString` field an address we want to read. Inside `DNS_StringCopy`, a buffer is allocated and the data pointed to by `pDnsString` (up until a null byte) is copied in.
![[61bb52f8db0ccecdb0e7edbb_aa91b3_31668603d0c740689bd13149ef90472a_mv2.png]]
<p style="text-align: center;"><sub><i>Decompilation of dns!NsecDnsRecordConvert</i></sub></p>
Since we also control `wSize`, we control the size of the buffer that gets allocated. So, we can force the the allocation of the new buffer into the area of the heap we control. After the data has been copied, we'll leak the memory using the same method as before.
![[61bb52f87784b7988275f8a1_aa91b3_7152eedcbcb64374bc3bbaf325d91518_mv2.png]]<p style="text-align: center;"><sub><i>Arbitrary read primitive</i></sub></p>
The leaked address should be somewhere within the dns.exe import table, which contains an address from `msvcrt.dll`. I chose `dns!_imp_exit` which contains the address to `msvcrt!exit`. The reason for this was it was an address in the import table whose offset did not contain a null byte (recall that `Dns_StringCopy` expects to copy a null-terminated string). This breaks the ASLR of `msvcrt.dll` and with this we can calculate the address of `msvcrt!system`.
Note: `Dns_StringCopy` expects to copy a null-terminated string. If the least significant byte of the address is `0x00`, the size of the calculated string will be `1`, and the address won’t be copied. In the samples I obtained this wasn’t a problem for `msvcrt!exit`, but I did not test all possible versions of `msvcrt.dll`.
## Remote Code Execution
All of the of the pieces are now in place to get remote code execution. We can again trigger the allocation of a `DNS_Timeout` object. Then, we overwrite the `pFreeFunction` with `msvcrt!system` and `pFreeFuncParam` with a heap address to memory that contains the payload command. To obtain a reverse shell, I chose to use `mshta.exe` to execute a `HTA` shell from an attacker hosted `HTTP` server. I found this to be the easiest solution, but there are many other possibilities. The exploit could also be reworked to use any other function(s) instead of `system`.
## Demo
<iframe width="100%" height="500" src="https://www.youtube.com/embed/yiqLmfQCqeY" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>
# Detecting Exploitation and Workaround Fix
To implement a rule for your preferred SIEM, look for invalid child processes of `dns.exe`. Note that this rule will only detect exploitation with the released PoC and DoS exploits. It is possible an attacker can rework the exploit to implement a payload that stays contained within the `dns.exe` process context.
If patching is not possible, a workaround fix is available:
```
reg add "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\DNS\Parameters" /v "TcpReceivePacketSize" /t REG_DWORD /d 0xFF00 /f
net stop DNS && net start DNS
```
The workaround blocks exploitation by limiting the DNS `TCP` receive message size to `0xFF00` bytes. This prevents the integer overflow from occurring when calculating the buffer size needed to cache a `SIG` record.
# Acknowledgements
[Sagi Tzadik](https://twitter.com/sagitz_) of CheckPoint research, for the original vulnerability discovery and [write-up](https://research.checkpoint.com/2020/resolving-your-way-into-domain-admin-exploiting-a-17-year-old-bug-in-windows-dns-servers/).
[maxpl0it](https://twitter.com/maxpl0it), who wrote the first public DoS [PoC](https://github.com/maxpl0it/CVE-2020-1350-DoS) for this bug, and answered a ton of my questions. He also kindly provided me with some of his research notes. My exploit uses code from his PoC as a basis.
[Worawit Wang](https://twitter.com/sleepya_), who released a [write up](https://datafarm-cybersecurity.medium.com/exploiting-sigred-cve-2020-1350-on-windows-server-2012-2016-2019-80dd88594228) about exploiting SIGRed. I used several of the discussed techniques.
[Andréa](https://twitter.com/and_zza), for her excellent work creating the graphics in this write-up.
[Connor McGarr](https://twitter.com/33y0re), who also wrote a DoS PoC for SIGRed and answered my questions.
[Michael Maltsev](https://twitter.com/m417z), the creator of [Winbindex](https://winbindex.m417z.com/), from which I was able to obtain many samples of `dns.exe` and `msvcrt.dll` to preprogram offsets.
# References
1. https://research.checkpoint.com/2020/resolving-your-way-into-domain-admin-exploiting-a-17-year-old-bug-in-windows-dns-servers/
2. https://github.com/chompie1337/SIGRed_RCE_PoC
3. https://datafarm-cybersecurity.medium.com/exploiting-sigred-cve-2020-1350-on-windows-server-2012-2016-2019-80dd88594228
4. https://docs.microsoft.com/en-us/windows/win32/secbp/control-flow-guard
5. https://en.wikipedia.org/wiki/Address_space_layout_randomization
6. https://docs.microsoft.com/en-us/windows/win32/dns/dns-constants
7. https://en.wikipedia.org/wiki/Return-oriented_programming
8. https://docs.microsoft.com/en-us/windows/security/identity-protection/access-control/active-directory-security-groups
9. https://serverfault.com/questions/991520/how-is-truncation-performed-in-dns-according-to-rfc-1035
10. https://en.wikipedia.org/wiki/List_of_DNS_record_types
11. https://improsec.com/tech-blog/bypassing-control-flow-guard-in-windows-10