August 24, 2026
How Globalprotect tried to fix a vuln but didn’t…
In my last article I outlined how Globalprotect stored and “encrypted” (*cough* obfuscated *cough*) user credentials. While I was digging…
By Nullsgotroot
6 min read
Update: This issue was assigned CVE-2025–0117, which was partially addressed in 6.3.3. It could be bypassed however, though the reason the bypass worked was not obvious. See my separate blog post dealing with the bypass only. The rest of this post outlines the vuln without the fix installed.
In my last article I outlined how Globalprotect stored and "encrypted" (cough security-through-obscurity cough) user credentials. While I was digging into all the functions that made use of the crypto I found a number of interesting uses. These included credential management, RPC comms, and file operations. These issues were confirmed to apply to at least 6.0.5, to 6.3.2, other versions weren't tested.
For this post I want to look at how the client-service model works, and how their patch to verify certificates failed to address the underlying issue. In short, the underlying mechanism that was exploited was still vulnerable.
My first gripe with the patch is that it not only requires you to install the patched version, but also to set a specific registry value (though this can be done as a parameter to msixec.exe). Riddle me this, how many admins will go look at the change-log, look up the patch, read the recommendations, and apply a registry key or install with a specific parameter at scale, and how many will just push the patch and go for a pint.
Putting that aside, let's dig into how GP works, and why the patch doesn't cover all scenarios even if installed according to their recommendations. The flow diagram gives a minimal overview of the flow when the GUI client "PanGPA.exe", connects to the service "PanGPS.exe". The full process, especially when logging in for the first time, is a fair bit more complicated, but for the exploit we can distill it quite a bit. This RPC messages happens over TCP on port 4767. Alas, it only listens on localhost, or we would have had something special.
Each packet between the client and service is encrypted with the same algorithm as before. The encrypted packet is then prepended with a 16 byte blob, of which the first n bytes is a decimal string containing the length of the encrypted part. I'm not sure why you would send a decimal string just to decode it again on the other side. You could just use a regular 4 byte int, but ok. All of the following packets are encrypted with this scheme, in some cases the crypto is even applied twice. Just know that these packets are encrypted in transit but we're looking at them with the first level crypto stripped off.
After the client connects to the service, it sends a portal command containing settings like creds and the required gateway, among many others. For the exploit we can get by with the bare minimum, just a username, password, and portal. We can make all of this up since it won't be processed in any meaningful way, though the portal address should be a domain with a valid certificate:
The service follows this up with a status message, which we can safely ignore:
It then requests the client to fetch a "prelogon" page from a specified URL. Yup, a privileged process sends an unprivileged process a request for a user specified URL, even though it could query it itself. Basically going "please get us this important information we're going to use to do privileged things, and don't lie about, we won't know, but you'll feel bad".
It's worth noting that the HTTP parameter data in this request is encrypted using the same algorithm used everywhere else, and then ASCII hex encoded. The full packet containing this blob is then encrypted, as outlined above (ie, the HTTP request is double encrypted). Because, you know, double encryption with the same key is twice as good. The HTTP parameters are:
Which after the first level of encoding becomes this:
The exploit replies to this message with the bare minimum, just a success message, also using ASCII hex encoding and crypto for the body:
Which after decoding gives us:
The service will then connect to the specified portal and validate the certificate, and if invalid, error out. There is a setting that can potentially disable this in the portal command, but it doesn't make a difference for our flow (yeah, I didn't check, it worked fine without it) so we can just use any random legit site, let's do www.paloaltonetworks.com.
Now here's the juicy bit, the service sends the client a "getconfig" request containing a URL it wants us to fetch from the portal which it verified. Since we have full control over the RPC stream, we can tell the service the verified portal said whatever we want. The request data looks like this:
Which after the first layer of encoding, becomes:
Now this is the part where we start playing with CVE-2024–5921, originally found by AmberWolf (tl;dr, a malicious VPN server installs a CA and serves a backdoored MSI signed with this CA) We respond with a message containing an attacker generated CA certificate, which the service happily installs for us. Before decoding we have:
Which gets decoded to reveal our given CA certificate:
The service will then send us a HIP request, which we can ignore:
Lastly we send a request with the "software-upgrade" command. For this we use a backdoored MSI signed by the CA we specified in the previous response:
And that's it, we receive 3 more status messages we can ignore and we wait for the update process to finish. For some reason there is a 120 second delay between receiving the upgrade command and actually starting the process . Let's pretend I patched that out for testing and didn't wait for it ever single time. The MSI then gets installed by SYSTEM, giving us a easy path to a privileged account.
So I'm not going to lie, I didn't follow the entire process of how the mitigation works (Diaphora falls over when trying to analyse a binary this large), but I did identify some of the code that does the checks.
A couple of notes about the exploit, which you can find on my Github page. The service does a check to verify that the client it is receiving commands from is located within the Globalprotect directory. To bypass this check I simply spawn PanGPA in a suspended state, and overwrite the entrypoint with a DLL loader shellcode blob. When the process thread is resumed it loads the exploit DLL which kicks off the pewpew. We also have to kill the currently running PanGPA process to release the socket, but since it's ours this isn't an issue. Lastly, when the exploit is successful, expect to see error messages as the service shuts down for the upgrade.
The big problem with the way the service is structured, is that it performs sensitive actions based on data received from an unprivileged source. The service shouldn't be making the unprivileged client perform its web requests. Secondly, it shouldn't have the ability to install a CA certificate to begin with, that should fall solidly in the active directory certificate services domain.
When the mitigation for CVE-2024–5921 was released, I was surprised that they didn't mention a registry key/value that's been there for at least 2 years (that's how far back I checked, it's probably older). I found this key via reversing, and there was no reference to this value in the docs so presumably it's long forgotten. Setting this key to 1 gives an alternate code path that skips the CA import code:
HKLM\Software\Palo Alto Networks\GlobalProtect\PanGPS\DisableImportCAHKLM\Software\Palo Alto Networks\GlobalProtect\PanGPS\DisableImportCAAdding only this key is not sufficient however, as other attacks also make use of the RPC comms to gain elevated access using the service. Happy hacking.