Aug 12, 2026BTA Team10 min read

The Sanitizer Was the Sink: How CVE-2026-8037 Turns Kemp LoadMaster's escape_quotes() Into a Pre-Auth Root Shell

We stopped what we were doing this week for a bug most defender feeds are treating as a routine patch-cycle item. A single unauthenticated HTTP request to /accessv2 on a Progress Kemp LoadMaster, four single quotes in the apiuser field, a spray of harmless-looking siblings, and root falls out. The interesting part is where the injection actually happens: not in a missing sanitizer, but inside the sanitizer, in the function that was supposed to make this class of bug impossible.

Progress patched CVE-2026-8037 (CVSS 9.6) quietly on June 4 as part of its LoadMaster Critical Security Bulletin. watchTowr Labs published the working exploit and a walkthrough on June 29, and telemetry recorded 792 exploit attempts from 65 IPs across 18 countries in the 41 days that followed. CISA added the CVE to KEV on August 7 with a three-day BOD 26-04 deadline. Progress ships north of 100,000 LoadMasters globally. If yours are internet-exposed and still on GA 7.2.63.1 or LTSF 7.2.54.17, assume the last six weeks did not skip you.

What /accessv2 actually does

LoadMaster is Progress's application delivery controller, the appliance that fronts your web apps, terminates TLS, balances traffic across a real server pool, and does the routine work of adding, removing, and reconfiguring services on the fly. All of that is driven by a management API rooted at /access and /accessv2. Under the hood the API dispatches to shell utilities that reconfigure the appliance, which means user input from the API eventually ends up inside strings that get passed to system(). To make that safe, LoadMaster runs each argument through escape_quotes(), a hand-rolled C helper whose only job is to escape single quotes so a shell string cannot break out of its surrounding quotes and inject a second command. It is the exact control that should have made this class of bug impossible on the box.

The escaping helper that skipped two lines of C

The watchTowr walkthrough shows exactly what escape_quotes() is doing wrong. The function takes an attacker-supplied string, allocates a buffer with malloc(), walks the input byte by byte, doubles each single quote, copies everything else through unchanged, and returns the pointer to the caller. Two lines never made it into the function. The buffer is allocated with malloc() rather than calloc(), so the freshly-returned chunk contains whatever the last owner left in it. The copy loop never writes a trailing \0 byte after the escaped content, so the returned string has no terminator. Both defects share a single failure mode. Any downstream code that treats the return value as a C string keeps reading until it stumbles onto a zero byte somewhere else in the heap, and every part of LoadMaster that eventually hands that string to system() inherits whatever contents were sitting in the adjacent chunk.

From four single quotes to a heap spray

The published PoC does two things in one HTTP request. Its apiuser parameter carries exactly four single quotes, which after the doubling pass expand to eight bytes, the exact width of the malloc chunk header on a 64-bit glibc allocator, and that controlled overflow rewrites the metadata of the chunk immediately after the escape_quotes() output. Alongside that, the same request carries a wall of unrelated keys named g0, g1, all the way to g60, each holding an identically-shaped string that contains the shell payload the attacker wants root to run. Modern allocators cluster same-size allocations for cache locality, so one of the g_N values reliably lands in the chunk whose metadata just got rewritten. escape_quotes() returns its unterminated buffer, another part of LoadMaster concatenates it into a shell command, and the C string reader walks straight past the sanitized bytes into the sprayed payload. That composite string reaches system(), and a shell runs as root on the appliance. No auth, no session, no chained CVE.

Why your existing telemetry never sees it

Nothing in that traffic is a signature-based WAF's problem. The apiuser value is a legal string of single quotes, and the g0..g60 keys look like the pagination or field-list parameters LoadMaster's own APIs actually use. The command payload sits in the sprayed keys, not in the parameter every tuned rule inspects, so the request slides straight through anything watching for classic ;id; or backtick patterns on a single field. On the box, LoadMaster's HTTP daemon spawns shell as part of normal operation because that is how the appliance reconfigures itself, so a busy management interface emits shell children constantly. If your host-based EDR is even watching this device (a big if for something most orgs treat as a sealed network appliance), it sees a process that always spawns shells, and one more child does not draw attention. Two innocuous shapes blend into two other innocuous shapes and the alarm never fires.

Log lines and process trees that catch it

The clean tells are on the wire and in process trees rather than in the request bodies themselves. Anything unauthenticated hitting /accessv2 is already a first-order signal, since real LoadMaster admin traffic authenticates before it touches management routes; eSentire's Threat Response Unit built a hunt shaped exactly this way after they saw the first exploitation wave on June 29. Filter management-interface access logs (aggregated in /var/log/messages on the appliance) for /accessv2 requests whose apiuser value carries single quotes, backticks, semicolons, or an unusually high count of tightly-numbered keys like g0 through g60. On the host, watch the LoadMaster application process for children it has no business spawning, such as bash, sh, curl, wget, nc, python, or busybox, then follow those children for outbound connections to hosts you did not put in your allow list. New writes into /tmp, /var/tmp, or LoadMaster's own configuration directories from a shell child are the second confirmation.

In MITRE ATT&CK terms the chain is T1190 Exploit Public-Facing Application into T1059.004 Command and Scripting Interpreter: Unix Shell. Anything that survives from there typically continues into T1071.001 Application Layer Protocol: Web Protocols for C2 and, because a LoadMaster sits in front of your app tier, T1210 Exploitation of Remote Services for the pivot into whatever the appliance was fronting. If you can prove none of those techniques fired against your LoadMasters since June 29, you can call the fleet clean. If you cannot, treat every reachable appliance as maybe-compromised until it has been rebuilt from a known-good image and every credential on it has been rotated.

The patch, the paths to close, and how to prove it took

Progress fixed the escaper in LoadMaster GA 7.2.63.2 and LTSF 7.2.54.18; anything on 7.2.63.1, 7.2.54.17, or older is still shipping the vulnerable function. Patch first, then hunt, in that order. The interim mitigation Progress will point you at is real: firewall the management interface off the public internet and restrict it to a specific administrator jump host or bastion range, because CVE-2026-8037 does not need credentials and does not care whether you use LDAP or local accounts on the admin UI. Rotate everything the appliance holds. That means local admin credentials, the API access keys, any TLS certificates whose private keys sat on the box, and any back-end service credentials LoadMaster proxies with. Then re-run the watchTowr PoC (or an equivalent replay) against the patched build from a lab node with network access, so you have a live negative test that your version actually rejects the payload rather than assuming the build number tells the whole story.

The reason a bug like this looks quiet on a fleet dashboard and loud in a post-incident review is that the four things that would catch it live in four different consoles. Access-log filters, process-tree hunts, appliance patch state, and a periodic emulation of the actual exploit do not talk to one another until someone stitches them together by hand. Stitching that loop is what we automate at BlueTeamAutomation: continuous BASzy replays of exploits like CVE-2026-8037 against your fleet, EDR and SIEM correlation on the appliance and its neighbors, and SOAR-driven response, so a bug you first hear about on a Wednesday morning is not still a Wednesday afternoon investigation.

See the detection story end to end

BASzy replays exploits like CVE-2026-8037 against your fleet on a schedule you set, correlates the telemetry your EDR and SIEM produce, and hands the SOAR side the response actions to close the loop. Talk to us about wiring your LoadMasters (and everything sitting behind them) into that loop.

Talk to BlueTeamAutomation →