LDAP Nightmare CVE-2024-49113

Summary

SafeBreach Labs developed a proof of concept exploit for CVE-2024-49113 that crashes any unpatched Windows Server (not just DCs) with no pre-requisites except that the DNS server of the victim DC has Internet connectivity.

The attack flow:

  1. The attacker sends a DCE/RPC request to the Victim Server Machine

  2. The Victim is triggered to send a DNS SRV query about SafeBreachLabs.pro

  3. The Attacker’s DNS server responds with the Attacker’s hostname machine and LDAP port

  4. The Victim sends a broadcast NBNS request to find the IP address of the received hostname (of the Attacker’s)

  5. The Attacker sends an NBNS response with its IP Address

  6. The Victim becomes an LDAP client and sends a CLDAP request to the Attacker’s machine

  7. The Attacker sends a CLDAP referral response packet with a specific value resulting in LSASS to crash and force a reboot of the Victim server

This same attack vector may be leveraged to achieve an RCE; the entire chain noted above, including the first six steps, should be similar, but the last CLDAP packet sent should be modified.

Technical Detail

The researchers believe their findings are significant for several reasons. First, they have shown the criticality of this vulnerability by proving that it can be used to crash multiple unpatched Windows servers. According to Microsoft’s classification, this vulnerability can be further exploited to lead to remote code execution. Second, the researchers verified that Microsoft’s patch fixes the out-of-bounds vulnerability, and the exploit is not capable of crashing patched servers. Finally, they provided a public PoC that organizations can use to test and verify that their servers are protected. For more details, please see the GitHub repository noted at the end of this advisory.

The vulnerability that the SafeBreach Labs PoC exploits affects technology that is in widespread use across enterprise networks, and this flaw could help attackers propagate more easily and effectively.

Below, the SafeBreach Labs research team will explain the exact technical details of how they identified the exploitation path that triggers the vulnerability and crashes a DC (or any Windows Server), provide a step-by-step exploitation summary, and share a proof-of-concept (PoC) tool that executes these steps.

CVE-2024-49113

CVE-2024-49113 was titled as “Windows Lightweight Directory Access Protocol (LDAP) Denial of Service Vulnerability”. LDAP is the protocol that workstations and servers in Microsoft’s Active Directory use to access and maintain directory services information. The title of the vulnerability means that the vulnerability probably has something to do with LDAP-related code. On MSRC’s page for the CVE, Microsoft provided a few details, but on the RCE vulnerability there was additional interesting data:

“How could an attacker exploit this vulnerability?

A remote unauthenticated attacker who successfully exploited this vulnerability would gain the ability to execute arbitrary code within the context of the LDAP service*. However successful exploitation is dependent upon what component is targeted.*

In the context of exploiting a domain controller for an LDAP server, to be successful an attacker must send specially crafted RPC calls to the target to trigger a lookup of the attacker’s domain to be performed in order to be successful.

In the context of exploiting an LDAP client application, to be successful an attacker must convince or trick the victim into performing a domain controller lookup for the attacker’s domain or into connecting to a malicious LDAP server. However, unauthenticated RPC calls would not succeed.“

Based on this information—and assuming the accuracy of Microsoft’s documentation— the researchers made the following assumptions:

[if !supportLists]1. [endif]The attacker does not need to authenticate

[if !supportLists]2. [endif]The vulnerability is an integer overflow type and is sourced in an executable or a Dynamic-Linked Library (DLL) that implements an LDAP client logic

[if !supportLists]3. [endif]There are RPC calls that can be leveraged in order to affect a DC to query an LDAP server controlled by an attacker

[if !supportLists]4. [endif]In the context of a DC, the vulnerability probably lies in lsass.exe or in one of the DLLs that it loads, as lsass.exe implements the LDAP service on a DC

[if !supportLists]5. [endif]Thus, the RPC interface with the RPC call that has the vulnerable LDAP client code is located in lsass.exe or in one of the DLLs that it loads as well

In addition, they also found an interesting insight by Artur Marzano (@MacmodSec) on X that suggested the potential location where Microsoft’s patch for the vulnerability was made, in wldap32.dll:

This insight fits perfectly with the documentation in MSRC’s website, as wldap32.dll implements the logic of an LDAP client.

Triggering a Remote LDAP Request

The research started with proving the first step of exploitation against a DC—affecting it to query an LDAP server controlled by the researchers. They needed to find an RPC call sourced in lsass.exe itself or in a DLL loaded into lsass.exe that imports functions from wldap32.dll. Using RpcView, they listed the available RPC interfaces loaded into lsass.exe:

Out of these RPC interfaces, they listed only the ones sourced in DLLs that are dependent on wldap32.dll and use its exported functions. The researchers were looking for RPC interfaces that did not require authentication, as they assumed that the attacker does not need to authenticate. Two interesting interfaces are found that offered several interestingly named RPC calls that seemed related to LDAP queries and could possibly trigger one were located in lsasrv.dll and netlogon.dll:

Using IDA, RPC calls that actively use one of the functions imported from wldap32.dll are searched. After a long search, DsrGetDcNameEx2 is found. According to Microsoft’s documentation:

“The DsrGetDcNameEx2 method SHOULD return information about a domain controller (DC) in the specified domain and site. If the AccountName parameter is not NULL, and a DC matching the requested capabilities (as defined in the Flags parameter) responds during this method call, then that DC will have verified that the DC account database contains an account for the AccountName specified.

NET_API_STATUS DsrGetDcNameEx2(

[in, unique, string] LOGONSRV_HANDLE ComputerName,

[in, unique, string] wchar_t* AccountName,

[in] ULONG AllowableAccountControlBits,

[in, unique, string] wchar_t* DomainName,

[in, unique] GUID* DomainGuid,

[in, unique, string] wchar_t* SiteName,

[in] ULONG Flags,

[out] PDOMAIN_CONTROLLER_INFOW* DomainControllerInfo

);”

The function actively retrieves a hostname of a domain controller, in addition to verifying that a specific account exists in it. Both the domain name and the account are specified by the caller. That means that if the function uses LDAP to fulfil its purpose, the function is exploitable.

Moving forward, the researchers needed to understand each one of DsrGetDcNameEx2 ‘s arguments and the values that would be set for them:

[if !supportLists]· [endif]ComputerName: The hostname of the target DC – This would be set to the victim’s hostname (further research revealed that this value does not matter at all for the function)

[if !supportLists]· [endif]AccountName: The account name that will be searched in the queried attacker’s domain —it can be any name—it is unimportant if it exists or not

[if !supportLists]· [endif]AllowableAccountControlBits: Controls what will be queried about “AccountName” – can be 0

[if !supportLists]· [endif]DomainName: The domain that will be queried – this will be set to the domain name of the attacker

[if !supportLists]· [endif]SiteName: The site in which the DC must be located – can be set to NULL

[if !supportLists]· [endif]Flags – extra configuration for the call – the default behavior is wanted first, so it is set to 0

[if !supportLists]· [endif]DomainControllerInfo – Output parameter, where the returned information will be placed

For testing purposes, the researchers installed two new DCs in the same subnet and created two new root domains in each one of them. One was called SBRESEARCH.LAB and the other TESTDOMAIN.LAB. The goal was to run on the DC at SBRESEARCH.LAB (the attacker) and get the DC at TESTDOMAIN.LAB (the victim) to query the LDAP server on the DC at SBRESEARCH.LAB.

So running on the attacker DC, the DsrGetDcNameEx2 function is called on the victim DC. The arguments for the call were:

[if !supportLists]· [endif]ComputerName – WIN-ELD41******

[if !supportLists]· [endif]AccountName – user1

[if !supportLists]· [endif]AllowableAccountControlBits – 0

[if !supportLists]· [endif]DomainName – SBRESEARCH.LAB

[if !supportLists]· [endif]SiteName – NULL

[if !supportLists]· [endif]Flags – 0

Looking in Wireshark at the packets that were sent and received by the victim, there is no LDAP request initiated by the victim. However, Wireshark did show something else. The victim sent a DNS query to its DNS server about a subdomain of SBRESEARCH.LAB. The DNS query was replied with an error code specifying that the DNS server did not find any record about that domain. The only way for the victim DC to get a successful answer about this query is if the attacker DC was its DNS server. But it is not possible to change a DNS server of a DC; that alone is likely to be considered a vulnerability:

The specific DNS query that was sent was of type SRV. DNS SRV queries specify a domain name, to which another domain name and a port are mapped in the response. The specific full domain name of the two queries sent by the victim DC were:

[if !supportLists]· [endif]_ldap._tcp.Default-First-Site-Name._sites.dc._msdcs.SBRESEARCH.LAB

[if !supportLists]· [endif]_ldap._tcp.dc._msdcs.SBRESEARCH.LAB

The victim DC was looking for an LDAP server on the attacker domain. If the DNS query gets to be solved successfully, then the LDAP query by the victim could potentially happen.

The victim’s DNS server does not know SBRESEARCH.LAB, but it does know other domains. Not all the domain names that are known to the victim’s DNS server were manually configured on it. This DNS server knows “google.com”. What did Google do in order to be known by this DNS server? The reserachers bought a domain on the Internet.

The researchers bought the domain “safebreachlabs.pro” to create two SRV records for:

[if !supportLists]· [endif]_ldap._tcp.Default-First-Site-Name._sites.dc._msdcs.safebreachlabs.pro

[if !supportLists]· [endif]_ldap._tcp.dc._msdcs.safebreachlabs.pro

These SRV records need to return a domain name (IP is not supported) and a port, which are likely to be contacted by the victim as the LDAP server. At first sight, it looks like this might mean that the victim will

Must contact an LDAP server that has a public IP on the Internet, which is a requirement that is preferred not to have, as a firewall might block such communication. But, if the attacker already has access to the DC’s subnet, the attacker can set the domain name that the SRV record returns to be the hostname of the computer that they control in the subnet. So, the researchers mapped both SRV records to the hostname of the attacker DC, and port 389 (its LDAP server).

Following that, the researchers ran another test. Running on the attacker DC, the DsrGetDcNameEx2 function is called again on the victim DC, but this time changed the DomainName parameter to be “safebreachlabs.pro” instead of “SBRESEARCH.LAB”, and it worked. The victim DC issued an LDAP query to the attacker DC.

The query is sent in Connectionless LDAP (CLDAP) and uses UDP instead of TCP. Using Windbg, it is verifiable that even though this request is being made in CLDAP, it is still performed by wldap32.dll.

Sending a Malicious LDAP Response

Once The researchers managed to get the victim DC to query the attacker LDAP server, The next step is to understand what needed to be in the response for that query. That is in order to get the victim to execute the assumed vulnerable function found by Artur Marzano – LdapChaseReferral.

Referrals allow an Active Directory tree to be partitioned between multiple LDAP servers.

When an LDAP server can’t answer a request, it can reply with referrals to other servers that may provide the answers for the query. Then, the client can “chase” these referrals and query the referred servers instead. It’s important to note that a client is not obligated to “chase” these referrals. However, in This case it does chase them.

In order for a server to indicate that it does not have the answer for the query and refer the client to different servers, it needs to reply with the “referral” LDAP result code (equals to 10). The response must also contain valid LDAP URLs (starts with “ldap://” or “ldaps://”).

Going back to the researcher’s exploitation scenario, in order to trigger the LdapChaseReferral function, a custom LDAP UDP server is created that allows sending such a referral response packet.

Looking at the logic of the patch, Microsoft added a condition that verifies that a certain value is not bigger than another value. Based on the logs printed next to this logic, these compared values are named as “lm_referral” and “referral table size”. “lm_referral” is taken from an “ldap_message” struct and “referral table size” is taken from an “ldap_connection” struct. The condition checks whether the “lm_referral” value is inside the range of the “referral table”. This range is the “referral table size”.

In the vulnerable version without the patch, this “lm_referral” value is indeed used to access a certain offset inside the referral table:

In these tests with Windbg, it is shown that the value in “lm_referral” is always equal to 0, while the pointer to the referral table is also equal to 0. However, the condition determining whether the code accesses the “referral table” only verifies whether the “lm_referral” value is not zero. That means that in order to trigger the vulnerability, the “lm_referral” variable must be controlled and made non-zero. If successful, the code will dereference a pointer that can be controlled using lm_referral’s value.

Searching for where the “lm_referral” variable inside the “ldap_message” struct is populated, the researchers looked for other occurrences in wldap32.dll where the offset of “lm_referral” inside the “ldap_message” struct is being used (+0x3C). This resulted in two functions: LdapInitialDecodeMessage and LdapChaseReferral:

A code is identified that sets “lm_referral” in LdapInitialDecodeMessage:

It is shown that “msgid” and “lm_referral” are taken from the same part of the packet.

In the above screenshot, the “value_from_response_packet” must be a 4-byte DWORD in order to make “lm_referral” a non-zero WORD, due to the shift by 25.

In the default response packet that is sent using custom UDP LDAP server, the value of “value_from_response_packet” can be fully controlled (seen in the above screenshot), and it is one byte long. What is learned is that this value is prefixed with its length.

Then The researchers understood what They needed to do in order to set a non-zero value for “lm_referral”:

[if !supportLists]· [endif]Change the byte that represents the length of “value_from_response_packet” (combination of “lm_referral” and “lm_msgid”) to 4 instead of 1.

[if !supportLists]· [endif]Now “value_from_response_packet” is 4 bytes long, and setting the most significant byte from it will affect “lm_referral”. Keep in mind that this byte can be set only to a value that can be divided equally by 2, or otherwise the value of “lm_msgid” will be affected.

These two actions will point the flow of the code into the scope of the vulnerable code and create an access violation once the dereferencing happens:

Since “ref_table” is equal to NULL, and “lm_referral” at this point is a non-zero value, the last line of code in the above image will trigger a dereference for a non-existent address resulting in out-of-bounds read and crashing LSASS and the entire operating system.

Next Steps: On the Way to Achieve RCE

Based on the initial research outlined here, the researchers continued working towards the implementation of a full RCE chain (CVE-2024-49112). The researchers have made some good progress towards it. In this section, the progress will be described.

In order not to crash, and continue the exploit, a way to trigger an integer overflow in the code that parses the CLDAP response needs to be found, as MSRC documented that CVE-2024-49112 is triggered by an integer overflow.

The researchers analyzed the diff between the patched and the unpatched version of the DLL and found the integer overflow fixes. The fixes are achieved by adding calls to the functions ULongMult, ULongAdd, and ULongSub. These functions perform the addition/multiplication/substitution and check whether an overflow happened. In addition, they store the result in an output parameter.

These functions are known to be Microsoft’s usual mitigation for previous integer overflow vulnerabilities, for example MS16-098. See previous analysis of this patching method here.

Here are the xrefs to these three new added functions in the patched DLL:

The researchers started going through the fixes, and at least for now, concluded that the ones They checked will probably not lead to the RCE. Analysis started with LdapParseResult. On the left, it is shown the unpatched version of the function LdapParseResult and on the right, the patched version is shown, which has the added call to ULongMult before calling ldapMalloc.

The researchers verified that the exploit code triggers this function if They send back a regular CLDAP response instead of the one crashing LSASS, but it was not enough. This function will get to this patched code only if the sixth parameter is not zero. This sixth parameter is a unicode string representing referrals. Based on their check, the RPC method that They call leads the parameter to be always zero. There were other xrefs to LdapParseResult, but an attempt to find a flow that calls it and sets the sixth parameter to a non-zero value using the RPC call that They use is not successful. It is possible that it can be triggered with a different RPC call than the one currently used – DsrGetDcNameEx2. For now, DsrGetDcNameEx2 is used.

The researchers decided to shift to a different attack flow. They analyzed the other calls to ULongMult and focused on the function SendLdapSearch.

Just below the call to ULongMult, an error print is found that verifies an integer overflow mitigation:

This is the unpatched code:

They were able to reach this function and even reached the call to ldapMalloc:

In the relevant code, it is understood that the variable that is passed to strlenW is an LDAP filter sent as part of a search that resulted from the RPC call. If filterLength is multiplied by 2, and filterLength is long enough, an overflow will happen and the allocation will be very small compared to the filter length. An important thing to note is that some elements are controlled in this filter such as the username and DnsDomain:

The researchers tried sending a very long username, but it seems like the required length for the username value to trigger this overflow is too big to be passed to the DsrGetDcNameEx2 RPC call.

Investigation of the other references to the three integer overflow mitigation functions is still in progress.

Mitigation

To mitigate the risk of this vulnerability, organizations should implement the patch released by Microsoft detailed here.

As noted above, SafeBreach Labs verified that the patch sufficiently prevents the exploitation and crashing of the tested servers. Patching this vulnerability is time critical, but it is also understandable that patching a DC and Windows Servers must be done carefully and with the proper caution.

As such, organizations are recommended to implement detections to monitor suspicious Events:

[if !supportLists]· [endif]CLDAP referral responses (with the specific malicious value set)

[if !supportLists]· [endif]DsrGetDcNameEx2 calls

[if !supportLists]· [endif]DNS SRV queries

Conclusion

This research set out to explore whether the LDAP CVE-2024-49113 vulnerability could be exploited. The research proved that not only can it be exploited against Domain Controllers, it also affects any unpatched Windows Server.

In addition, an exploit PoC for testing purposes is provided, noted in the section above. The researchers also believe that this will make exploitation of CVE-2024-49112 more likely in the near future, so They recommend patching both vulnerabilities.

0
Subscribe to my newsletter

Read articles from FPT Metrodata Indonesia directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

FPT Metrodata Indonesia
FPT Metrodata Indonesia