# REPRO-2026-00183: MapServer: heap-buffer-overflow in SLD Categorize parser (msSLDParseRasterSymbolizer) ## Summary Status: published Severity: high Type: security Confidence: Unknown ## Identifiers REPRO ID: REPRO-2026-00183 GHSA: GHSA-cv4m-mr84-fgjp CVE: CVE-2026-33721 ## Package Name: mapserver Ecosystem: c Affected: 4.2.0 - 8.6.0 Fixed: 8.6.1 ## Root Cause # Root Cause Analysis — CVE-2026-33721 ## Summary MapServer versions 4.2.0 through 8.6.0 contain a heap-buffer-overflow vulnerability in the OGC SLD (Styled Layer Descriptor) XML parser. The function `msSLDParseRasterSymbolizer` in `src/mapogcsld.cpp` allocates a fixed-size buffer for 100 threshold pointers when parsing a `` element. The reallocation guard incorrectly checks `nValues == nMaxThreshold` instead of `nThresholds == nMaxThreshold`. Because `nValues` and `nThresholds` increment at different rates, the buffer is never expanded when more than 100 `` children are present, causing subsequent pointer writes to spill past the 800-byte (100 × 8) array boundary. The bug is reachable unauthenticated via the `SLD_BODY` parameter in a WMS `GetMap` request. ## Impact - **Package**: OSGeo MapServer (`mapserv` CGI binary) - **Affected versions**: 4.2.0 — 8.6.0 (last vulnerable tag: `rel-8-6-0`) - **Fixed version**: 8.6.1 (`rel-8-6-1`) - **Risk level**: High (CVSS 3.1: 7.5) - **Consequences**: At minimum, an attacker can crash the MapServer worker process (denial of service). Depending on heap layout and input control, the overflowed pointers may be further exploitable. ## Root Cause In `src/mapogcsld.cpp`, around line 2880, `msSLDParseRasterSymbolizer` initializes: ```cpp int nMaxThreshold = 100; char **papszThresholds = (char **)msSmallMalloc(sizeof(char *) * nMaxThreshold); ``` As the parser iterates over `` children, every `` increments `nThresholds` and stores a pointer into `papszThresholds`. The growth guard is meant to reallocate the array when it fills: ```cpp // VULNERABLE (rel-8-6-0): if (nValues == nMaxThreshold) { nMaxThreshold += 100; papszThresholds = (char **)msSmallRealloc( papszThresholds, sizeof(char *) * nMaxThreshold); } ``` However, `nValues` tracks a *different* counter (the number of `` elements), while `nThresholds` tracks the actual number of threshold entries. When an SLD contains more than 100 thresholds, `nThresholds` exceeds 100 but `nValues` may still be much lower (e.g., 1), so reallocation never occurs. The next `papszThresholds[nThresholds] = …` write lands beyond the 100-slot buffer, producing a heap-buffer-overflow. **Fix commit**: `ddd246b90acc6c7f920dfd056f33613cebe9154d` **Merge commit**: `7dbe91b` The patch changes exactly one line: ```diff - if (nValues == nMaxThreshold) { + if (nThresholds == nMaxThreshold) { ``` This ensures the array is resized based on the same counter that tracks live entries. ## Reproduction Steps The complete reproduction is automated in `repro/reproduction_steps.sh`. It performs the following: 1. Clones MapServer at `rel-8-6-0` (vulnerable) and `rel-8-6-1` (fixed). 2. Builds both `mapserv` binaries with AddressSanitizer (`-fsanitize=address`). 3. Generates a minimal `test.map`, a tiny GeoTIFF (`tiny.tif`), and a malicious SLD payload (`payload.sld`) containing 200 `` elements. 4. Invokes the vulnerable binary through the real CGI path (`QUERY_STRING` + `REQUEST_METHOD=GET`) with the SLD payload via `SLD_BODY`. 5. Captures the ASAN crash log (`logs/vulnerable_asan.txt`). 6. Runs the same request against the fixed binary and verifies it returns a valid PNG image with no ASAN error (`logs/fixed_response.txt`). ## Evidence - **Vulnerable ASAN log**: `logs/vulnerable_asan.txt` - Key excerpt: ``` ==10268==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x518000008fa0 WRITE of size 8 at 0x518000008fa0 thread T0 #0 0x7fd97611c7d4 in msSLDParseRasterSymbolizer /.../src/mapogcsld.cpp:2895 0x518000008fa0 is located 0 bytes after 800-byte region [0x518000008c80,0x518000008fa0) ``` - **Fixed response**: `logs/fixed_response.txt` - Starts with `Content-Type: image/png` and contains a valid PNG stream, confirming the fixed binary processes the same payload without crashing. - **Runtime manifest**: `repro/runtime_manifest.json` — records binary paths, tags, crash signature, and payload details. - **Validation verdict**: `repro/validation_verdict.json` — marks status `confirmed`. ## Recommendations / Next Steps 1. **Immediate fix**: Upgrade to MapServer 8.6.1 or later. The patch is a single-line bound-check correction and carries no functional regression. 2. **Defensive measure**: If upgrading is not immediately possible, restrict the `SLD_BODY` / `SLD` query parameters at the reverse-proxy or WAF level, or disable WMS SLD support in the MapServer configuration. 3. **Testing**: Add a regression test that submits an SLD with >100 thresholds and asserts the process does not crash. 4. **Code-review note**: When arrays are grown dynamically, always use the same counter for both indexing and resize checks. ## Additional Notes - **Idempotency**: `repro/reproduction_steps.sh` was run twice consecutively with identical results (confirmed heap-buffer-overflow on vulnerable, valid PNG on fixed). - **Edge cases**: The crash is triggered with any SLD containing >100 `` children inside a `` block. The exact threshold count is 100; 101 is sufficient to overflow. The reproduction uses 200 to provide a clear safety margin. - **Limitations**: The reproduction requires a functional GDAL/PROJ/libxml2 environment. The script installs missing Debian/Ubuntu packages automatically if they are absent. ## Reproduction Details Reproduced: 2026-05-28T14:02:42.831Z Duration: 874 seconds Tool calls: 177 Turns: 142 Handoffs: 2 ## Quick Verification Run one of these commands to verify locally: pruva-verify REPRO-2026-00183 pruva-verify GHSA-cv4m-mr84-fgjp pruva-verify CVE-2026-33721 Or open in GitHub Codespaces (zero-friction, auto-runs): https://github.com/codespaces/new?ref=repro/REPRO-2026-00183&repo=N3mes1s/pruva-sandbox Or download and run the script manually: curl -O https://api.pruva.dev/v1/reproductions/REPRO-2026-00183/artifacts/bundle/repro/reproduction_steps.sh chmod +x reproduction_steps.sh ./reproduction_steps.sh WARNING: Run in a sandboxed environment. This exploits a real vulnerability. ## References - GitHub Advisory: https://github.com/advisories/GHSA-cv4m-mr84-fgjp - NVD: https://nvd.nist.gov/vuln/detail/CVE-2026-33721 ## Artifacts - bundle/repro/rca_report.md (analysis, 5443 bytes) - bundle/repro/reproduction_steps.sh (reproduction_script, 7558 bytes) - bundle/vuln_variant/rca_report.md (analysis, 8924 bytes) - bundle/vuln_variant/reproduction_steps.sh (reproduction_script, 8108 bytes) - bundle/context.json (other, 3480 bytes) - bundle/metadata.json (other, 809 bytes) - bundle/ticket.md (ticket, 6591 bytes) - bundle/repro/tiny.tif (other, 466 bytes) - bundle/repro/mapserver.conf (other, 46 bytes) - bundle/repro/gen_sld.py (script, 803 bytes) - bundle/repro/payload.sld (other, 7058 bytes) - bundle/repro/runtime_manifest.json (other, 848 bytes) - bundle/repro/validation_verdict.json (other, 407 bytes) - bundle/repro/gen_tiny.py (script, 549 bytes) - bundle/repro/test.map (other, 358 bytes) - bundle/vuln_variant/root_cause_equivalence.json (other, 2580 bytes) - bundle/vuln_variant/patch_analysis.md (documentation, 4455 bytes) - bundle/vuln_variant/variant_manifest.json (other, 3536 bytes) - bundle/vuln_variant/runtime_manifest.json (other, 1798 bytes) - bundle/vuln_variant/validation_verdict.json (other, 3103 bytes) - bundle/logs/vulnerable_asan.txt (other, 5868 bytes) - bundle/logs/variant_onlythresholds_fixed.log (log, 0 bytes) - bundle/logs/variant_onlythresholds_vuln.log (log, 0 bytes) - bundle/logs/fixed_response.txt (other, 297 bytes) - bundle/logs/variant_run1.log (log, 1288 bytes) - bundle/logs/variant_overflowprobe_vuln.log (log, 5868 bytes) - bundle/logs/variant_overflowprobe_fixed.log (log, 0 bytes) - bundle/logs/variant_getstyles_vuln.log (log, 5854 bytes) - bundle/logs/variant_legendgraphic_vuln.log (log, 5862 bytes) - bundle/logs/variant_baseline_vuln.log (log, 5868 bytes) - bundle/logs/variant_reordered_vuln.log (log, 0 bytes) - bundle/logs/variant_baseline_fixed.log (log, 0 bytes) - bundle/logs/variant_run2.log (log, 1385 bytes) - bundle/logs/variant_reordered_fixed.log (log, 0 bytes) - bundle/logs/variant_legendgraphic_fixed.log (log, 0 bytes) - bundle/logs/variant_getstyles_fixed.log (log, 0 bytes) ## API Access - JSON: https://api.pruva.dev/v1/reproductions/REPRO-2026-00183 - Script: https://api.pruva.dev/v1/reproductions/REPRO-2026-00183/artifacts/bundle/repro/reproduction_steps.sh - Web: https://pruva.dev/r/REPRO-2026-00183 ## For AI Assistants To help users with this reproduction: 1. The `pruva-verify` command is the simplest way to run the reproduction 2. The script downloads and runs automatically in an isolated temp directory 3. Always recommend running in a VM, container, or disposable environment 4. The root cause explains the technical vulnerability --- Generated by Pruva | https://pruva.dev