A lot of blog posts I have read go over interesting vulnerabilities and exploits but do not typically share the process behind discovery. I want to show how sometimes just manually poking around can quickly uncover vulnerabilities you might miss with other approaches to vulnerability discovery.
In my previous blog, I highlighted a tool named IO Ninja and shared why it is helpful when working with Inter-Process Communications (IPC). In this blog, I would like to show you how this tool helped me find two vulnerabilities: CVE-2021-4198 and CVE-2021-4199 in Bitdefender’s antivirus software on Windows. The exploit I wrote targets a named pipe used by the Vulnerability.scan.exe
process. The first vulnerability results in a denial-of-service on almost any process within the AV. The second results in local privilege escalation by exploiting a link following issue.
Now you might be thinking, wait a minute... how does a tool that lets you communicate with named pipes allow you to find a link following vulnerability? That is what we are going to dive into next.
The pipelist tool from Sysinternals reveals that Bitdefender uses several pipes for IPC.
bdservicehost.exe
is one of the executables that runs as SYSTEM after installing Bitdefender. It uses these pipes to communicate with other processes on the system and might be an interesting target.
Using IO Ninja, we can spawn a pipe server as our standard user to see if we can trigger a connection from the client. Without reading any code, we can poke around the agent GUI to see what IPC connections are being created and what the traffic looks like.
The screenshot below shows that we receive a connection from the client of the pipe that sends data to our named pipe server every time we open the agent GUI. This can be done by double clicking the Bitdefender icon in the system tray or opening it from the start menu.
What is interesting here is that the connection remains open. This allows us to send data back to the client. I took the first 16 bytes of data that was being sent to the server and echoed it back to the client multiple times to see if anything interesting would happen. I was also looking to see if the pipe connection would remain open after sending the data.
After sending several messages, the client disconnects, and we get a popup box from Bitdefender informing the user that something went wrong within the AV. Take note of the “x” at the top right for dismissing the dialog box. This will become relevant later.
After seeing this message, I knew that debugging information in some shape or form must be getting saved or logged. I fired up Process Monitor from Sysinternals to see if a crash dump file, which I can further analyze in a debugger, was created.
As it turns out, an executable named “BDReinit.exe”, which is Bitdefender’s own crash handler, produces a crash dump file for us on the system.
Another interesting discovery here was finding out that the folder to which the .dmp file is being written to is readable and writeable by standard users.
Unfortunately, after looking at the dump file, I concluded the crash alone was not very interesting, and the process being crashed runs as our current user. At this point, I knew I had a potential local DoS on the AV but there had to be more to chew on here.
After crashing this first process “seccenter.exe,” which is Bitdefender’s main GUI interface, I realized that I was still getting connections to my named pipe server. I was able to crash almost any process related to the AV by simply spamming the incoming pipe connections with those 16 bytes as soon as a client connected. I sat in front of my computer, watching the AV processes fall like dominos one after the other in process explorer.
I knew that kicking off a “Vulnerability Scan” in the GUI would spawn vulnerability.scan.exe
, which ran as SYSTEM. I quickly tried to see if I could also crash this privileged process.
Ahh, the same “BDReinit.exe” process runs again, but this time it runs as SYSTEM. Based on the file name of the dump, we know we can also crash “Vulnerability.scan.exe,” and it writes the dump to disk as SYSTEM.
When looking for link following vulnerabilities, there are a few directories that are of immediate interest.
C:\ProgramData
C:\windows\temp
C:\Users\[standard user]\AppData
I often look for privileged file writes and delete operations in these directories.
In this case, a .dmp
file is being written to this directory as SYSTEM when a highly privileged process that is part of the AV crashes. This alone is not very interesting as we do not control the contents of the dump being written, and it’s the same exact crash from before.
I further analyzed the Process Monitor log and discovered that “BDReinit.exe” was doing two very dangerous things. First, it was writing a very permissive DACL to a file with a predictable name in a different directory. This directory also allowed a standard user read and write access, and the DACL write happened every time a crash occurred.
The permissive DACL would get written to this file as SYSTEM if you crashed a process running as SYSTEM within Bitdefender. In this case, I just chose to again crash “Vulnerability.scan.exe” because of how easy it was to trigger. All I had to do was kick off a vulnerability scan in the GUI, and I would get a connection to my named pipe server from this process.
One of my favorite parts about logic bugs like this is that exploiting them is typically very quick and easy. All we need to do to escalate to SYSTEM is redirect the permissive DACL write to a DLL used by the system, modify the contents of that DLL, and then have it get loaded. To do this redirection we just need one symbolic link and a target DLL.
One great candidate for the job is:
C:\Windows\System32\spool\Drivers\x64\3\PrintConfig.dll
This is because a standard user can easily trigger a print job which will force svchost.exe
to load our modified DLL as SYSTEM.
Putting this altoghther, the following screenshot demonstrates executing code at SYSTEM using the techniques described.
The second major issue I found was related to the crash dump itself. If you clicked the “x” at the top right of the popup box I showed earlier, “BDReinit.exe” or “BdSubWiz.exe” which is the submission wizard, would delete the crash dump as your current user. If the user waited or ignored it for a while, the dialog box would timeout and delete the dump file as SYSTEM if the process that crashed was running as SYSTEM!
In the above screenshot, you can see how we can redirect this highly privileged file deletion to any file on the system. You can also use the techniques described in this blog to achieve privilege escalation and execute code at SYSTEM. Again, this is because the crash dump was being written and deleted from a location that our standard user had read and write access to regardless of the user that was running the crashed process. Furthermore, the files in that folder were not protected at all, so deleting them and replacing one with our link before the timeout was not an issue.