Okta Verify for Windows Remote Code Execution โ CVE-2024-0980
2 May 2024 at 17:41
This article is in no way affiliated, sponsored, or endorsed with/by Okta, Inc. All graphics are being displayed under fair use for the purposes of this article.
Poppin shells with Okta Verify on Windows
These days I rarely have an opportunity to do bug hunting. Fortunately, over the holiday break, I found some free time. This started as it usually does with me looking at what software was running on my computer.
A while back I had installed Okta Verify on my Windows box as it was required for some โenhancedโ 2FA that I was required to have to access a thing. Months later it sat there doing whatever it does. I googled to see if Okta had a bug bounty program because even though I had some time, itโd be nice to get paid if I found a thing. I was thrilled to find that Okta had a bug bounty with Bugcrowd, Okta Verify is in it, and the payouts look good, almost too good.
I started with my usual bug hunting flow when approaching a random Windows service. This typically includes looking for the usual low hanging fruit. A good article for the types of things to look for can be found here.
Firing up Sysinternalโs Procmon, I saw there is a service called Okta.Coordinator.Service that is running as SYSTEM. Without going into the details (namely because Okta hasnโt fixed it or issued it a CVE),ย I found a thing. I submitted the report and was promptly paid.
Well thatโs weird. The bug I submitted is an unequivocal 7.8 CVSS. Which without knowing the voodoo behind P ratings (P1-P4), seems like would be a P2 at least. Instead I get a P3 and paid out at the lower end.
Looking back on it, Iโm betting this is probably an old bug bounty program trick to motivate researchers to dig deeperโฆ because, it worked. I decided to take a closer look since I hadnโt even opened up the binary to see what it was doing โ and I wanted to get that big payout.
Letโs Get Crackinโ
I havenโt popped Okta.Coordinator.Service.exe into a disassembler yet, but Iโm betting itโs a .NET application. My guess comes from its name and the fact that thereโs an Okta.Coordinator.Service.exe.config file right there with it, which you usually see with .NET applications.
When I open up the executable in JetBrains dotPeek, I can confirm it is indeed a .NET application. The binary appears to be a service wrapper. It handles the service related functionality: install, uninstall, start, stop, etc.ย It references a Okta.AutoUpdate.Executor class that just so happens to have a matching DLL in the same directory.
Moving on to the DLL in dotPeek, I found the code used by the service. The first thing I noticed was it sets up a NamedPipe server, which listens for commands to update the Okta Verify software. This is a common design paradigm in Windows for enabling low-privileged applications to communicate with a high-privileged service to perform updates, as these often require elevated privileges. Itโs a complex mechanism thatโs tricky to do right, and often a good place for finding bugs. I was able to confirm the existence of the named-pipe server with a little Powershell.
Next, I investigated how to initiate an update and what aspects of this process could be manipulated by an attacker. The handler for the named pipe processes a straightforward JSON message that includes several fields, some checked against expected values. The primary field of interest is the update URL. If the input data passes validation, the software will attempt to fetch details from the specified URL about the most recent update package available. As shown below, the URL (sub)domain is verified against a whitelist before proceeding. For now, Iโll avoid trying to meet/bypass this requirement and simply add an entry in the hosts file on my test machine.
Typically at this stage, Iโd code up a proof of concept (POC) to send a JSON message to the named pipe and check if the software connected to a web server I control. But since I havenโt spotted any potential vulnerabilities yet, I skipped that step and moved on.
From here I took a look at the code responsible for processing the JSON message retrieved from the attacker controlled update server. The application is expecting a message that contains metadata about an update package including versioning and an array of file metadata objects. These objects contain several pertinent fields such the download URL, size, hash, type, and command line arguments. The provided download URL is validated with the same domain checking algorithm as before. If the check passes, the software downloads the file and writes it to disk. This is where things get interesting. The code parses the download URL from the received metadata and constructs the file path by calling the Path.Combine function.
Several factors are converging here to create a serious vulnerability. The most obvious is the use of the Path.Combine function with user supplied data. I went into depth about this issue in a previous blog post here. The TLDR is if a full path is provided as the second argument to this function, the first argument that typically specifies the parent folder, is ignored. The next issue is how the filename is parsed. The code splits the file location URL by forward slash and takes the last element as the filename. The problem (solution) is a full Windows path can be inserted here using back slashes and itโs still a valid URL. Since the service is running as SYSTEM, we have permissions to right anywhere. If we put all this together our payload looks something like the script below.
Copy to Clipboard