notes 2026-09-20 · 4 min read · updated 2026-09-20

XML Parsers and Deserialization Gadget Chains

XXE, XSLT and XPath injection, plus Java, .NET, Python pickle and PHP object injection: when stored data carries code across a trust boundary.

XML Parsers and Deserialization Gadget Chains

Serialization formats that can describe an object graph can usually describe a call. The parser is the vulnerable component, not the payload.

XXE

A small XML document can make the parser open local files or start network connections when external entities remain enabled. XML External Entity attacks use DTD and entity resolution to reference local files or external URLs. Behavior depends on parser and flags. Impact includes file reads, SSRF and denial of service. Blind XXE uses out-of-band interaction when content is not returned. I confirm Content-Type, parser behavior and DOCTYPE acceptance with a canary entity before attempting any file read. Modern libraries disable external entities by default in some configurations. Framework wrappers may re-enable features, so version and configuration matter.

1
2
3
<?xml version="1.0"?>
<!DOCTYPE x [<!ENTITY test SYSTEM "http://oast.example/test">]>
<root>&test;</root>

XSLT Injection

XSLT was designed to transform XML and some engines expose extension functions that are far too powerful for user-controlled stylesheets. XSLT engines have versions, built-in functions and vendor-specific extensions. A user-controlled stylesheet may reach filesystem or host functions in some runtimes. Injection also appears when input is embedded inside a stylesheet built by the server. XPath context and XML escaping change the payload. I identify engine and version with harmless functions, then determine which extensions are enabled. RCE is not universal. Sandboxed engines or disabled extensions may limit impact to disclosure or unexpected transformation.

1
2
3
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/"><xsl:value-of select="system-property('xsl:vendor')"/></xsl:template>
</xsl:stylesheet>

XPath Injection

XPath Injection has the same root cause as SQLi: user data enters the query language and changes the expression. XPath queries XML documents through paths and predicates. Concatenated usernames and passwords may alter boolean logic. Blind XPath uses boolean responses to infer nodes, characters and document structure. I look for authentication or search parameters backed by XML and start with operators that alter conditions without breaking syntax. Prepared statements are less standardized for XPath. Safe construction requires suitable APIs, context-aware escaping or a different storage approach.

1
' or '1'='1

Java Deserialization

Java serialization can rebuild an entire object graph. That power becomes dangerous when untrusted bytes influence which classes are instantiated. ObjectInputStream invokes hooks such as readObject during deserialization. Libraries on the classpath may form gadget chains without introducing new malicious code. Tools such as ysoserial demonstrate known chains, but exact dependency versions decide whether a gadget is present. I confirm magic bytes, endpoint behavior, classpath and error patterns in a lab before using any gadget. Type allowlists, JEP 290 filters and simpler data formats are stronger than blocking known gadget classes one by one.

1
2
file payload.bin
# Java serialization streams often begin with AC ED 00 05.

.NET Deserialization

BinaryFormatter was so dangerous by design that modern .NET treats its use as unsafe. Polymorphic serializers may instantiate types controlled by payload metadata. Gadget chains abuse setters, callbacks and classes already present. BinaryFormatter, LosFormatter and unsafe Json.NET settings appear in legacy material. ViewState also depends on MAC protection and machineKey configuration. I identify format and serializer through traffic, errors and code, then validate whether client-controlled type metadata changes the created object. Exploitability depends on loaded assemblies and settings. Preferred remediation is migration toward typed formats and safer serializers.

1
grep -RnE 'BinaryFormatter|LosFormatter|TypeNameHandling|ObjectStateFormatter' .

Python Pickle

pickle.loads() does more than read data. The format was designed to reconstruct Python objects and can invoke callables during that process. Pickle supports opcodes and methods such as __reduce__ that describe how to reconstruct an object. That may include function invocation. ML models, caches, sessions and asynchronous jobs often load pickle outside the traditional web request path. I identify the file source, who controls the byte stream and which process performs unpickling before building a lab PoC. Cryptographic signing can protect integrity when the key is secure. Better still, avoid pickle for data crossing an untrusted boundary.

1
python3 -m pickletools model.pkl | head -80

PHP Object Injection

unserialize() can create objects and trigger magic methods without the attacker sending executable PHP source code. PHP serialization carries class names and properties. __wakeup, __destruct and other magic methods may execute logic during the object lifecycle. A gadget chain depends on classes loaded by the application, including frameworks and Composer dependencies. I confirm whether controlled bytes reach unserialize and identify available classes before building any chain. allowed_classes=false reduces risk in specific cases. Moving to JSON and simple typed data is usually cleaner.

1
grep -R 'unserialize(' -n app/ vendor/ 2>/dev/null | head

References

This post is licensed under CC BY 4.0 by the author.