DEV Community

Simon Köck
Simon Köck

Posted on

How I found an XXE in a multi-tenant cloud platform through a translation file upload

Tolgee is an open-source localization platform. Teams upload translation files in formats like Android XML, XLIFF, .resx, and Apple stringsdict. Every one of those formats is XML.

When I see an app that parses user-uploaded XML server-side, the first thing I check is whether the parser disables external entity resolution. In Java, XMLInputFactory and DocumentBuilderFactory ship with dangerous defaults: external entities are enabled out of the box.

I cloned the repo and grepped for those two classes. Six results. None of them had any security configuration.

The exploit is simple. Upload an Android XML translation file like this:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE resources [
  <!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<resources>
  <string name="test_key">&xxe;</string>
</resources>
Enter fullscreen mode Exit fullscreen mode

The parser resolves &xxe;, reads /etc/passwd, and returns the contents as the translation value. I confirmed this on Tolgee's own cloud platform at app.tolgee.io. The response came back with an Alpine Linux passwd file.

Beyond local file read, the same bug enables SSRF. Swap file:// for http://169.254.169.254/... and you're hitting the cloud metadata service, potentially leaking IAM credentials.

This affected six separate import processors. Tolgee fixed it by creating a centralized XmlSecurity utility that all parsers now use, plus regression tests covering every importer.

CVE-2026-32251, CVSS 9.3.

I wrote the full writeup with the vulnerable code, exploitation details, and the fix here: https://simonkoeck.com/writeups/tolgee-xxe-translation-import

Takeaway for Java developers: never use XMLInputFactory.newInstance() or DocumentBuilderFactory.newInstance() without disabling external entities. The defaults will betray you.

Top comments (0)