August 24, 2026
Globalprotect and the story of a failed registry save
In my last post I outlined how the Globalprotect service can be directed to perform an upgrade from a user specified MSI file. This file…
By Nullsgotroot
4 min read
In my last post I outlined how the Globalprotect service can be directed to perform an upgrade from a user specified MSI file. This file has to be signed by a trusted certificate with a specific name, which is the whole point of CVE-2024–5921. We're not going to go over that again, but rather what else happens as part of the upgrade process. This issue was confirmed to apply to at least 6.0.5, to 6.3.2, other versions weren't tested.
While working on the previous bypass, at some point I buggered something and the upgrade process broke completely. I looked through the disassembly trying to troubleshoot the issue when I noted something interesting. Before msiexec.exe is even called for the uninstall, reg.exe is used to save a specific registry key to c:\windows\temp\uninstall.reg, yeah, you can see where this is going, but hang on one second.
After this export the service runs a bat file from the program directory that uninstalls/installs the service using msiexec.exe, and imports the registry settings again.
"What would cause the upgrade process to hang?" I hear you not ask. A quick look at the process list confirms that this flow is indeed the problem. The initial reg.exe command hangs, which blocks the entire process flow. It should be exiting quickly, but it just sits there humming silently to itself in the corner rocking back and forth clutching its knees.
To find out why this hangs we can simply run the command and see what happens.
That makes sense, I must have broken the installer mid install in a previous attempt, which left uninstall.reg in place, and now reg.exe was blocking on that, waiting for user input that will never come.
So if you were thinking we can just create uninstall.reg ourselves with some bad content earlier, it's not that easy. So how do we give it specific content that doesn't cause reg.exe to block? We've seen that we can't create the file first, and we can't modify it after the reg.exe export since it would be owned by system. It seems we're stuck.
What if we could create the evil uninstall.reg file, and have reg.exe bomb out (gracefully) before it prompted for the overwrite? What would take higher priority in reg.exe than the check that the file already exists? As luck would have it, there's a code path that does exactly that. Enter the CreateFile Win32 API call.
Our hero comes in the form of the dwShareMode parameter. This allows us to share the file while we are processing it. By not specifying it the file sharing is set to not allow any type of sharing.
The code in reg.exe that handles saving files checks for errors in the file open action, and processes them first before trying to write to the file, makes sense really. We can hold a lock on the file while reg.exe is trying to save the file, which makes it bomb out, then we release the lock before the file is used by the bat file to import the settings again and delete the file.
The code for this turned out to be incredibly simple. Create the file with no sharing mode set, write a malicious payload and hold on to the file until we see msiexec.exe running. Now all we need is a way to trigger the upgrade process. Lucky for us we've done that already in the previous post. We can strip that down to remove the other RPC commands, and do only the update trigger. You can find the code for this on my Github page.
The registry has loads of things an attacker can abuse if they had unrestricted access. Two privesc methods comes to mind, the first is changing the permissions on a service binary, then pointing it's path to a malicious binary we control. The second is Image File Execution Options, which specifies that another process should be launched instead of a targeted one but with the same privileges.
We're going to go for the first approach, simply change the permissions on the PanGPS service to give everyone full control.
This gives us the control we need to make any changes to the service. It's worth noting that these settings only apply after a reboot (a limitation that isn't present in the Image File Execution Options vector).
After a restart we can launch our payload, a simple script that launches a program as a privileged user but in the desktop session of the currently logged on user.
It's interesting to note that a past fix moved the bat file from temp folder to the application directory, indicating that there were previous fiddling attempts (or successes?), but no steps were ever taken to address the uninstall.reg file.
On that note, another change was due to a race condition on where msiexec.exe installed the MSI file from, prompting a change so the MSI was copied to the program folder and the certificate checked again.
Lastly, there was a silent patch on the MSI file delete code (fixed in 6.3.2), preventing a time-of-check-time-of-use symlink vulnerability that enabled an arbitrary file delete and potential privesc.
It's clear that a fair bit of time has been put into the update procedure by both researchers and the Globalprotect team, yet bugs can still fall out if you get new eyes on them.
The mitigations for this is pretty easy, move the uninstall.reg file to the application directory as was done with the bat and MSI files, alternatively, give it a random name so attackers can't guess it before the write happens. Even better, do both. Microsoft is also planning a change where SYSTEM would have its own temp directory, but we're still waiting on that one. Happy hacking.