Attacker Value
High
(1 user assessed)
Exploitability
Moderate
(1 user assessed)
User Interaction
None
Privileges Required
Low
Attack Vector
Local
1

CVE-2023-21768

Disclosure Date: January 10, 2023
Exploited in the Wild
Add MITRE ATT&CK tactics and techniques that apply to this CVE.
Privilege Escalation
Techniques
Validation
Validated

Description

Windows Ancillary Function Driver for WinSock Elevation of Privilege Vulnerability

Add Assessment

4
Ratings
Technical Analysis

Interesting bug that was announced on Twitter at https://twitter.com/chompie1337/status/1633498392125997056 and later posted on GitHub at https://github.com/xforcered/Windows_LPE_AFD_CVE-2023-21768. Bug occurs in afd.sys which is the Windows Ancillary Function Driver for WinSock, and allows privilege escalation from a local user to NT AUTHORITY\SYSTEM.

Whats interesting about this bug though is that unlike other EoP bugs, this only seems to affect Windows 11 and Windows Server 2022 according to https://msrc.microsoft.com/update-guide/en-US/vulnerability/CVE-2023-21768. Additionally the exploit at https://github.com/chompie1337/Windows_LPE_AFD_CVE-2023-21768 is noted to only work on vulnerable Windows 11 22H2 systems, which raises the question of if there were some specific items that needed to be hardcoded for a specific version of Windows, such as offsets or similar.

The current exploit that is available, from my brief look at things, appears to use the I/O Ring R/W primitive that Yarden Shafir talked about at https://windows-internals.com/one-i-o-ring-to-rule-them-all-a-full-read-write-exploit-primitive-on-windows-11/ and later released a PoC on at https://github.com/yardenshafir/IoRingReadWritePrimitive. I’m somewhat surprised that Microsoft still hasn’t patched this several months later given how strict they have been at trying to fix such items in the past, but these things typically require complicated change to the OS so perhaps I shouldn’t be all too surprised, particularly given its dealing with I/O buffers which are heavily utilized.

After leaking some addresses it appears to do the standard SYSTEM token replacement using the leaked EPROCESS address of the SYSTEM process and then takes that security token and replaces the chosen processes’s security token with the SYSTEM security token. Note I say chosen here as the exploit takes a PID as an argument and uses that PID to find out which process’s token it should replace with the SYSTEM security token.

Looking closer at the exploit to perform some of the leaks there seems to be a reference to NtQuerySystemInformation which is commonly used for leaking information, however I don’t actually see that being used anywhere, so it looks like it is potentially left over code.

The main vulnerability exploit code appears to be centralized into ArbitraryKernelWrite0x1 in https://github.com/xforcered/Windows_LPE_AFD_CVE-2023-21768/blob/master/Windows_AFD_LPE_CVE-2023-21768/exploit.c, which is then used to overwrite some entries in the IO ring buffer to transform it from arbitrary kernel write to arbitrary kernel read and write.

Looking into ArbitrarykernelWrite0x1 it seems it takes in a pointer to the address to overwrite, then creates an IPv4 extended TCP socket attribute structure, creates an IoCompletion object using NtCreateIoCompletion that allows for both querying and modifying the state, and allows one thread of concurrent access to the object.

We then call NtSetIoCompletion (documented to some degree at http://undocumented.ntinternals.net/UserMode/Undocumented%20Functions/NT%20Objects/IoCompletion/NtSetIoCompletion.html) on the same object with a completion key of 0x1337 and pass it the empty IO_STATUS_BLOCK object IoStatusBlock so we can keep track of the IO status of this IoCompletion object. We set the completion status to 0, aka not completed, and set the number of bytes transferred in a manually finished I/O operation to 0x100. Not sure why this value was chosen but eh.

If this completes successfully, then we go ahead and make a UNICODE_STRING called ObjectFilePath that holds the string \\Device\\Afd\\Endpoint, which according “Reverse Engineering Windows AFD.sys” by Steven Vittitoe which was presented at Recon 2015 at https://recon.cx/2015/slides/recon2015-20-steven-vittitoe-Reverse-Engineering-Windows-AFD-sys.pdf, is an endpoint that allows access to 70+ IOCTLs that are defined within the afd.sys driver.

Looking more at this paper we can also see that AFD.sys handles everything from TCP/IP to SAN and that its listed as the “Ring 0 entrypoint for WinSock”, aka all WinSock calls will end up going through this driver which executes at the kernel level. This is backed up by their point that it handles all socket() calls.

Anyway getting back to this we can see we also set an OBJECT_ATTRIBUTES object to the name of this string as the ObjectName parameter, and then set its attributes to 0x40, aka OBJ_CASE_INSENSITIVE as noted at http://www.jasinskionline.com/technicalwiki/Print.aspx?Page=Constants-Windows-API&AspxAutoDetectCookieSupport=1, as I guess we need case insensitive operations for this? Idk though without further info.

Finally we call NtCreateFile to create this file and save the handle into hSocket. We ask for the maximium permissions possible on this object, pass in the ObjectAttributes object attributes object we created earlier so that we use the \\Device\\Afd\\Endpoint and use case insensitive naming, pass in the IoStatusBlock for the I/O completion object, allow read and write sharing, pass in nothing for the creation options since this device should already be created, and pass in 1 aka FILE_OPEN so we open the existing file. Finally we pass in bExtendedAttributes which will hold the hardcoded extended attributes for a IPv4 TCP socket.

If all goes well then we should now have an file handle in hSocket however the AFD driver still isn’t fully aware of this socket. To complete this we then create a new Data object of type AFD_NOTIFYSOCK_DATA. This object type is not publicly documented anywhere as far as I can tell, and appears to have been guessed at via reverse engineering. In this structure we can see that we have a few pointers, a handle to hCompletion which explains our earlier completion object creation call, and some DWORDs.

The next few lines of the code will set the hCompletion parameter of this structure to the hCompletion object we created earlier, and after this we set pData1 and pData2 to 0x2000 byte long heap buffers that are readable and writeable and which have been reserved and committed in memory.

We then set dwCounter to 1 to indicate one entry, and set dwLen to 1. Not sure what dwLen controls though but we’ll have to wait for the blog for more details. We interestingly set dwTimeout to an insanely large value of 100000000. Its possible this may be related to an overflow which leads to the out of bound write, or it could be set this way to hold the connection open whilst the exploit happens and prevent timeouts. Finally we see that the address we wish to overwrite is placed into Data.pPwnPtr suggesting that this structure is responsible for the arbitrary overwrite and perhaps doesn’t validate that the address is actually a kernel address and not a user address like it should be.

Finally we create an event using CreateEvent and then call IOCTL 0x12127 on AFD, which the exploit notes as AFD_NOTIFYSOCK_IOCTL. My guess is that this processes the malicious Data structure of type AFD_NOTIFYSOCK_DATA and then fails to notice that the Data.pPwnPtr is out of the expected address range, allowing for an arbitrary write vulnerability.

CVSS V3 Severity and Metrics
Base Score:
7.8 High
Impact Score:
5.9
Exploitability Score:
1.8
Vector:
CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H
Attack Vector (AV):
Local
Attack Complexity (AC):
Low
Privileges Required (PR):
Low
User Interaction (UI):
None
Scope (S):
Unchanged
Confidentiality (C):
High
Integrity (I):
High
Availability (A):
High

General Information

Vendors

  • microsoft

Products

  • windows 11 21h2,
  • windows 11 22h2,
  • windows server 2022 -

Exploited in the Wild

Reported by:

Additional Info

Technical Analysis