TL;DR

The Palo Alto Networks ADEM (Autonomous Digital Experience Management) Windows agent uses curl -k to fetch updates, disabling all TLS certificate validation. Combined with no code signing verification on downloaded MSI installers, this allows any attacker with network-adjacent access to serve a malicious update and achieve arbitrary code execution as NT AUTHORITY\SYSTEM.

The Discovery

Like most security teams, we are always trying to advance. Sometimes it is by upgrading to better software, but most of the time it is by automating recurring tasks to make our reaction time shorter.

Last November, one of my team colleagues wanted to optimize how we use THOR scans in our daily business. We use THOR as a way to perform a full scan of an endpoint after a suspicion is raised. The first step was to automate the deployment and automatic retrieval of scan results. After a couple of tests, the next task was to build a custom filter. We have a lot of false positives that we can rule out without looking at the THOR report even once, so filtering them out was a great way to de-clutter.

After looking at the output of his own machine, my colleague got nervous: "Wait, what is that running on my system?!" He wanted a second pair of eyes, and maybe the reassurance that comes with it. So we sat together and went through each alert when we came across this one:

REASON: Suspicious Curl.EXE Download
SCORE: 70

CommandLine: "C:\Program Files\Palo Alto Networks\DEM\curl.exe"
  https://updates.dem.prismaaccess.com/agents/mu/desktop/windows/appcast.xml
  ?subtenant-id=<redacted>&host-id=<redacted>&version=5.8.15.0
  -k --max-time 300 --ssl-revoke-best-effort --fail
  --output C:\WINDOWS\TEMP\appcast.xml

ParentProcessName: C:\Program Files\Palo Alto Networks\DEM\deployment\DEMUpdateService.exe

THOR flagged this because curl was downloading a file to C:\WINDOWS\TEMP\ - a pattern commonly associated with malware staging. But this was a legitimate Palo Alto Networks process: ADEM, the Autonomous Digital Experience Management agent. I had to look it up too — it's part of Palo Alto's Prisma Access / SASE stack and lives on enterprise endpoints collecting telemetry about network and application performance, so IT teams can figure out why a remote worker's Teams call sounds like a robot. It's usually deployed alongside GlobalProtect VPN, which is why you'll find it on a lot of corporate Windows machines.

So nothing to worry about. Or was it?

Wait, Is that a -k flag?

I couldn't look further, because I knew that the -k flag in curl means --insecure. It tells curl to accept any TLS certificate, valid or not. Self-signed, expired, wrong hostname - it doesn't matter. This effectively disables the entire point of HTTPS. When you think about SSL interception, which Palo Alto's firewalls can do, this might seem reasonable, since they don't want to sabotage their own update mechanism. But I just had to look deeper, after calming down my colleague of course.

I asked another colleague if he wanted to join my rabbit hole investigation in the evening since he was one of the network engineers most knowledgeable about the product itself. Unfortunately (for him) he told me that he had no time for that on that day and so I continued alone.

Understanding the Update Mechanism

The first thing I wanted to look at was if I could just redirect the request to something I control, for which I just added a DNS entry in Windows' hosts file. I redirected updates.dem.prismaaccess.com to my Linux host, started up a http.server using python and I saw the request come in.

172.16.22.132 - - [26/Nov/2025 16:22:55] "GET /favicon.ico HTTP/1.1" 404 -
172.16.22.132 - - [26/Nov/2025 16:25:16] "GET /agents/mu/desktop/windows/appcast.xml?version=5.8.15.0 HTTP/1.1" 200 -

That worked, awesome! Then I looked at what the real appcast.xml looked like:

<rss version="2.0"
     xmlns:sparkle="http://www.andymatuschak.org/xml-namespaces/sparkle"
     xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Palo Alto Networks ADEM Windows-Agent</title>
    <link>https://updates.dem.prismaaccess.com/agents/mu/desktop/windows/appcast.xml</link>
    <description>Generated by the agent-updater service</description>
    <item>
      <pubDate>Wed, 26 Nov 2025 14:15:13 +0000</pubDate>
      <sparkle:version>5.8.15</sparkle:version>
      <enclosure url="https://updates.dem.prismaaccess.com/agents/mu/desktop/windows/GlobalProtect_Autonomous_DEM_x64_v5.8.15.msi"
                 type="application/octet-stream"
                 checksum="348fdd37fbb3fa25db0aed52a541da2b82d6d253e61171752696c1463465a070" />
    </item>
  </channel>
</rss>

That looked interesting, I only saw that one request but was wondering what happens when I 1up the version number. So I downloaded the xml, created the folder structure and changed the version to 5.8.16 which prompted another request for the file itself. When looking on the client I can see in the log file C:\Program Files\Palo Alto Networks\DEM\palo_alto_networks_dem_update_service.log that it got the xml but it found a mismatch in the version that was supplied and the file name.

[2025-11-26 16:25:16.224 WARN] Failed to download parameterised appcast.
System.Exception: Failed to download appcast.
 ---> System.Exception: The version 5.8.16 mismatches with the version 5.8.15 in the installer file name.

Interesting that this is validated and aborted on, but hey let's keep going. Let's see what happens when I change the filename and supply a piece of software for it to "install". I'll use a msfvenom payload since I wanted to see fireworks if it happens. And fireworks we got!!

[2025-11-26 17:25:16.268 INFO] Upgrading from version 5.8.15.0 to version 5.8.16.
[2025-11-26 17:25:16.269 INFO] AgentLogEvent: { "timestamp": "1764174316", "payload": "Start downloading installer[5.8.16] from 'https://updates.dem.prismaaccess.com/agents/mu/desktop/windows/GlobalProtect_Autonomous_DEM_x64_v5.8.16.msi' to 'C:\\Windows\\TEMP\\GlobalProtect_Autonomous_DEM_x64_v5.8.16.msi' using 'C:\\Program Files\\Palo Alto Networks\\DEM\\curl.exe'. Timeout in 00:20:00 at 11/26/2025 17:45:16" }
[2025-11-26 17:25:16.424 INFO] Bypassing proxy for https://updates.dem.prismaaccess.com/agents/mu/desktop/windows/GlobalProtect_Autonomous_DEM_x64_v5.8.16.msi, EProxy.exe returned DIRECT.
[2025-11-26 17:25:16.424 INFO] Executing C:\Program Files\Palo Alto Networks\DEM\curl.exe ["https://updates.dem.prismaaccess.com/agents/mu/desktop/windows/GlobalProtect_Autonomous_DEM_x64_v5.8.16.msi","-k","--max-time","1200","--ssl-revoke-best-effort","--fail","--output","C:\\Windows\\TEMP\\GlobalProtect_Autonomous_DEM_x64_v5.8.16.msi"]
[2025-11-26 17:25:16.625 INFO] AgentLogEvent: { "type": "AgentLogAppcastResponse", "timestamp": "1764174316" }
[2025-11-26 17:25:16.625 INFO] Using the default MSI name from the server.
[2025-11-26 17:25:16.627 INFO] Calculated checksum e5ca22bdc0b86d6225413150a2e64a018e85ed07b5021d78b4f35ecb6bf3bb38 for installer at 'C:\Windows\TEMP\GlobalProtect_Autonomous_DEM_x64_v5.8.16.msi' matches the checksum from the appcast.
[2025-11-26 17:25:16.642 INFO] Launching installer at 'C:\Windows\TEMP\GlobalProtect_Autonomous_DEM_x64_v5.8.16.msi' with args[/i,C:\Windows\TEMP\GlobalProtect_Autonomous_DEM_x64_v5.8.16.msi,/quiet,/qn,/L*v,C:\Program Files\Palo Alto Networks\DEM\palo_alto_networks_dem_update_service-msiexec.log] to move from current[InstallationDetails { Version = 5.8.15.0, FeatureSet = AgentFeatureSet { UIRequired = False } }]->target[InstallationDetails { Version = 5.8.16, FeatureSet = AgentFeatureSet { UIRequired = False } }] with log at 'C:\Program Files\Palo Alto Networks\DEM\palo_alto_networks_dem_update_service-msiexec.log'...
[2025-11-26 17:25:16.660 INFO] MSI Installer at 'C:\Windows\TEMP\GlobalProtect_Autonomous_DEM_x64_v5.8.16.msi' is fully launched.

This log shows that it downloaded the file, checked the SHA256 checksum and just ran it?!! Our security stack lit up like a christmas tree since I used a meterpreter payload because that was the command I had at hand from a CTF. I expected that the executable needed to be signed by Palo Alto, but nope. Since the Service is running as SYSTEM I could do whatever I wanted to the system. I verified this using an msi-payload that creates a local admin user with success!

One more detail worth noting: the agent checks for updates every 30 minutes. The log line recheck in "00:30:00" makes the cadence explicit. That's the window an attacker has to play with.

Attack Scenarios

Public WiFi / hotel networks: Think of it like a Fliegenfalle — a fly trap. Set up a rogue access point at a hotel, airport, or conference. Since the attacker controls the AP, they also control DHCP and DNS — any client that connects will resolve updates.dem.prismaaccess.com to whatever the attacker says. Laptops with an ADEM agent that connect during the attack window and happen to check for updates at that moment will reach out on their own. With the 30-minute check interval, the wait isn't long. The attacker doesn't need to target anyone specifically. Just turn it on and wait. Traveling executives, sales teams, remote workers - they all come to you.

Corporate network: An attacker on the same network — compromised WiFi, VPN, or just a network port in a meeting room — can intercept update checks for ADEM agents on the same network segment, again via DNS spoofing or ARP poisoning. The update runs automatically, no user interaction needed. Serve a malicious update, and affected endpoints are SYSTEM-level compromised by the next update cycle.

Compromised infrastructure: An attacker with access to upstream DNS or network infrastructure could intercept update traffic at scale, hitting multiple organizations at once. This is the less likely scenario, but the impact would be massive.

Disclosure Process

I reported the vulnerability to Palo Alto PSIRT the same evening. The fix was checked in by mid-January and released in v5.10.14 on March 6. The disclosure process itself took over four months, with two timeline extensions and several rounds of CVSS discussion.

Palo Alto assessed this at CVSS 4.0 base score 7.7 with the following vector:

CVSS:4.0/AV:A/AC:L/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N/E:U

I disputed two metrics. First, Attack Requirements (AT:P) — the -k flag is present unconditionally in all deployments, no special system state required. The MitM prerequisite is already captured by AV:A. Second, Subsequent System Impact (SC/SI/SA:N) - SYSTEM-level access on a domain-joined enterprise endpoint enables credential extraction (LSASS, Kerberos, DPAPI) and lateral movement to other systems, well documented in MITRE ATT&CK (T1003, T1558, T1550).

The score went through multiple revisions — briefly revised to 8.5, then reverted to 7.7 with a different rationale each time. The final score is 7.7. I still disagree, but that's where it landed.

The CVE assignment had a similar trajectory. Palo Alto initially confirmed one would be assigned, then reversed that decision mid-process, citing an internal policy of not issuing CVEs for "SaaS products" when the fix deploys automatically. After further discussion — and after I shared a draft of this article — they reversed course again and assigned CVE-2026–0233.

Timeline

Nov 26, 2025 — Vulnerability discovered and reported to Palo Alto Networks PSIRT Nov 27, 2025 — Report acknowledged (DEM-12310); 90-day coordinated disclosure agreed Jan 16, 2026 — Preliminary CVSS 7.7 shared; fix already checked in Jan 30, 2026 — First timeline extension: Feb 11 publication moved to March Feb 24, 2026 — 90-day deadline passed; publication confirmed for March 11 Mar 6, 2026 — Fix released in v5.10.14; CVSS scoring disputed Mar 13, 2026 — Initial response: no advisory or CVE planned Mar 24, 2026 — Advisory and CVE confirmed for publication Mar 30, 2026 — CVE-2026–0233 assigned Apr 8, 2026 — Security advisory published; this blog post

Recommendations

The fix is in version 5.10.14, which Palo Alto has been auto-deploying since early March. If you're still running an older version of the ADEM agent, upgrade now. If you're unsure, check — don't assume the auto-update worked, especially given what this blog post is about.

For vendors: update mechanisms deserve the same security rigor as the product itself. At a minimum:

  1. Never disable TLS certificate validation. If you must use curl, do not use -k - or at least
  2. verify code signatures. Every executable or installer downloaded over the network should have its Authenticode signature verified before execution.
  3. Defense in depth. TLS protects the channel. Code signing protects the payload. Checksums protect integrity. You need all three — they solve different problems.

Closing Thoughts

A security monitoring agent — one that's supposed to improve your visibility into endpoint health — turned out to be the easiest path to full system compromise. It runs on enterprise endpoints because those endpoints matter. That same trust is what makes it dangerous when the update mechanism is this broken.

No memory corruption, no race conditions, no exotic techniques. Just curl -k and a missing signature check. My colleague was nervous about a THOR alert, and it turned out to be one of the simplest and most impactful bugs I've found. Sometimes that's how it goes.

God bless you!

Ref: https://security.paloaltonetworks.com/CVE-2026-0233