54
PHP いじり倒す 1 0 方法 10 ways to “exploit” PHP that you might not know 小泉 守義 (Moriyoshi Koizumi) < [email protected] >

Phpをいじり倒す10の方法

Embed Size (px)

DESCRIPTION

 

Citation preview

Page 1: Phpをいじり倒す10の方法

PHPを“いじり倒す”

10の方法10 ways to “exploit” PHP that you might not know

小泉 守義 (Moriyoshi Koizumi) <[email protected]>

Page 2: Phpをいじり倒す10の方法

復 習しましょう PHPとは何かテンプレートエンジン界のオーバーテクノロジーThe most overengineered template engine ever.

文法上の特徴により、よくプログラミング言語と勘違いされるOften mistaken as a sort of programming language due to its “your-favorite-language-like” syntatic features.

世界で初めてテンプレートエンジンでテンプレートエンジンが実装されたThe world’s first template engine upon which another template engine is implemented.

brushup: What is PHP?

Page 3: Phpをいじり倒す10の方法

Uh, so... do you mean PHP is not a programming language?

Page 4: Phpをいじり倒す10の方法

テンプレートエンジンをカスタマイズするのはごく自然な行為

Page 5: Phpをいじり倒す10の方法

です から

Page 6: Phpをいじり倒す10の方法

PHPもカスタマイズして当然

Why not customize PHP so it would fit more to your project?

Page 7: Phpをいじり倒す10の方法

と、いう話です。

Page 8: Phpをいじり倒す10の方法

PHPの内部構造を知ろう!

Page 9: Phpをいじり倒す10の方法

PHPのアーキテクチャ

ZendEngine2SAPI

SAPI module

Extensions

Page 10: Phpをいじり倒す10の方法

SAPI module

ZendEngine2SAPI

SAPI module

Extensions

Webサーバの実装とのglue(のり)となる部分

コマンドライン用のPHPもSAPI moduleとして実装されている。

Page 11: Phpをいじり倒す10の方法

SAPI

ZendEngine2SAPI

SAPI module

Extensions

SAPI moduleと各extensionとのインターフェイス

通常拡張モジュールは直接SAPI moduleに触れることはない

Page 12: Phpをいじり倒す10の方法

ZendEngine

ZendEngine2SAPI

SAPI module

Extensions

言わずと知れたPHPのコア

言語パーサの他に、設定ファイル (.ini) のパーサなどいろいろなものが含まれている。

Page 13: Phpをいじり倒す10の方法

Extensions

ZendEngine2SAPI

SAPI module

Extensions

新たな関数を追加したりクラスを追加したりする通常のextensionと、ZendEngine自体を拡張するzend extensionの2種類がある。

厳密な違いではない

Page 14: Phpをいじり倒す10の方法

その他の構成要素: TSRMThread-Safe Resource Managerの頭文字を取ったもの。正体はクロスプラットフォームのTLS (thread-local-storage) ライブラリfork型のWebサーバをターゲットに作られたPHPを無理矢理multi-threadedなWebサーバに対応させるために誕生ソースコード中に登場するTSRMLS_CやTSRMLS_Dといったマクロは、TLSのスロットへのポインタを関数引数として引き回してパフォーマンスを向上する工夫。

Page 15: Phpをいじり倒す10の方法

TSRMのイメージSlot #1

Slot #2

Slot #n

module global

threads

TLS

TLS

TLS

Page 16: Phpをいじり倒す10の方法

ZendEngineの構造

Allocator

Parser

ini parser

Lexer

Hashtable

basic data structure

Stack Linked List

Utilities

Parser

Garbage Collector

language core

Opcode emitter

Virtual Machine

Objects API

Lexer

zend_alloc.c

zend_API.czend_float.czend_stream.czend_qsort.c

zend_stack.czend_ptr_stack.czend_llist.czend_hash.c

zend_ini.czend_ini_parser.yzend_ini_scanner.c

zend_objects.czend_object_handlers.czend_objects_API.c

zend_execute.czend_execute_API.czend_vm_execute.hzend_operators.c

zend_gc.c

zend_compile.czend_opcode.c

zend_language_parser.y zend_language_scanner.l

Page 17: Phpをいじり倒す10の方法

Compiler

<?php$a = 1;$b = 2;$c = $a + $b;?> ?

Page 18: Phpをいじり倒す10の方法

Compiler<?php$a = 1;$b = 2;$c = $a + $b;?>

Lexer

T_OPEN_TAGT_VARIABLE‘=’T_LNUMBER‘;’T_VARIABLE‘=’T_LNUMBER‘;’T_VARIABLE‘=’T_VARIABLE‘+’T_VARIABLE‘;’T_CLOSE_TAG

Page 19: Phpをいじり倒す10の方法

CompilerT_OPEN_TAGT_VARIABLE‘=’T_LNUMBER‘;’T_VARIABLE‘=’T_LNUMBER‘;’T_VARIABLE‘=’T_VARIABLE‘+’T_VARIABLE‘;’T_CLOSE_TAG

ParserOpcode emitter

zend_op

zend_op_array

ASSIGN

ASSIGN

ADD

zend_op

zend_op

zend_opASSIGN

Page 20: Phpをいじり倒す10の方法

zend_op_arrayopcodeを格納する配列を持つ構造体opcode以外にもopcode列に対応する関数の情報などが格納される詳細は割愛

Page 21: Phpをいじり倒す10の方法

zend_op1つのopcodeを表す構造体opcode: opcode番号を表すhandler: opcode番号を関数ポインタに解決したものop1, op2, extented_value:オペランドを表すメンバresult: 実行結果の格納先を表す

op1 op2

extended_value

result

zend_op

opcode handler

Page 22: Phpをいじり倒す10の方法

znode

op_type

constant varopline_num

jmp_addr

op_array

構文解析のときにsemantic valueとして使われる。opcodeのオペランドとして流用される。正体は複雑怪奇な共用体 (union)constant: 定数を表すvar: tmp_var番号を表すopline_num / jmp_addr:JMP命令の飛び先を表すop_array: 関数の呼び出し先を表す

Page 23: Phpをいじり倒す10の方法

TMP_VARについて実際のCPUにおけるレジスタのようなものop_arrayに格納されたopcodeを実行するたびにVMにより確保され、実行が終わった時点で解放されるzend_op_array構造体の”T”という変数に使用数が格納されているopcodeの結果を格納して別のopcodeに引き渡すのに使われるzend_opをノードとして表現された抽象構文木 (AST) のエッジにあたるものと思うと分かりやすい

Page 24: Phpをいじり倒す10の方法

TMP_VARとzend_op

op1 op2

result

ADD

op1 op2

result

ADD

op1 op2

result

ASSIGN

#0変数: $b

#1変数: $c

#3変数: $d

#5変数: $a

TMP_VAR

$a = $b + $c + $d;

ADD

ASSIGN

ADD実行順

zend_opの抽象構文木的な

表現

#2一時変数

#4一時変数

opcodeとしての表現

Page 25: Phpをいじり倒す10の方法

そのほかcompilerで使われる構造体 (一部)compiler_globals専らコンパイルで使う一時的な情報が格納される

zend_function関数を表す構造体

zend_arg_info関数引数に関する情報が格納されている。

zend_compiled_variable「コンパイル済み変数」のエントリを表す

Page 26: Phpをいじり倒す10の方法

そのほかcompilerで使われる構造体 (一部)zend_brk_cont_elementbreak や continue の分岐情報を管理する

zend_try_catch_elementtry - catch 節でのcatch先の分岐情報を管理する

Page 27: Phpをいじり倒す10の方法

Virtual Machine

zend_op_array

ASSIGN

ASSIGN

ADD

zend_op

zend_op

zend_opASSIGN

zend_op ASSIGN

FETCH_R

FETCH_W

ECHO

ADD

FETCH_DIM_R

FETCH_DIM_W

handlers

Page 28: Phpをいじり倒す10の方法

Virtual machineで使われる構造体executor_globalsVMの状態が格納される

execute_dataVMの実行フレームを表す

zend_function_state呼び出し中の関数の状態を表す構造体

Page 29: Phpをいじり倒す10の方法

てなわけで…

Page 30: Phpをいじり倒す10の方法

改造しましょう!

Page 31: Phpをいじり倒す10の方法

改造 #1: 関数を追加する htmlspecialchars()と打つのがめんどくさい!第2引数のデフォルトがENT_COMPATになっているのがうざい!もっというと第3引数のデフォルトはUTF-8でよくない?新しい関数を追加しよう!

Page 32: Phpをいじり倒す10の方法

改造のポイントhtmlspecialchars()を実装している箇所を探すgrep -r “PHP_FUNCTION(htmlspecialchars)”とか grep -r “PHP_FE(htmlspecialchars)”

中身を見る真似して関数を実装するPHP_FUNCTION(...)を追加PHP_FE(...) を適当な場所に追加ZEND_BEGIN_ARG_INFO() ~ ZEND_END_ARG_INFO() を追加

Page 33: Phpをいじり倒す10の方法

...

Page 34: Phpをいじり倒す10の方法

改造 #2: autoboxを追加PHPでもRubyみたいにスカラやarrayとオブジェクトを分け隔てなく使いたい!たとえばこんな感じarray(1, 2, 3, 4, 5)->join(’,’)JavaにあるautoboxingをPHPでも実装すればいいんじゃ?autoboxが必要なときに、__autobox()というグローバル関数を、対象の値を引数に呼び出すようにする。

Page 35: Phpをいじり倒す10の方法

改造のポイントとりあえず「->」演算子を評価し、オブジェクトのメソッドが呼ばれる箇所 zend_vm_def.h の中から見つける。

zend_vm_def.h は前述のhandlerが定義されている場所zend_vm_execute.h は zend_vm_def.h から自動生成されるものなのでいじっても無駄ッ!無駄ッ!

「->」演算子がパースされている箇所も見ておく普通 1->toString() とか書けないもんね多分 zend_language_parser.y もいじる必要あり

Page 36: Phpをいじり倒す10の方法

...

Page 37: Phpをいじり倒す10の方法

改造 #3: テンプレートエンジンとしてのPHPを強化PHPってテンプレートエンジンでしょ、Smartyとか意味あるの?論理構造を無視したテンプレートエンジンって使いづらいPHPのパーサでなんとかならんのかじゃあ拡張しよう

Page 38: Phpをいじり倒す10の方法

<?php $a = << ?><?html><body> <?div id=”{$id}”>test</?div></body></?html>

<?php// $a には構造がDOMみたいに格納されるvar_dump($a);?>

Page 39: Phpをいじり倒す10の方法

改造のポイントzend_language_scanner.l にタグのパースに必要なトークンを生成するような記述を追加zend_language_parser.y にタグのパースのためのルールを追加基本的に見よう見まねしましょう!

Page 40: Phpをいじり倒す10の方法

...

Page 41: Phpをいじり倒す10の方法

改造にあたってのTips--enable-debug つきで configure しよう!phpのソースツリーの一番上にある .gdbinit を活用しよう!printzvprintzopszbacktraceなどなど

Page 42: Phpをいじり倒す10の方法

閑話休題

Page 43: Phpをいじり倒す10の方法

PHPのextensionを簡単に作る方法教えます!!

Page 44: Phpをいじり倒す10の方法

これまでのextension作成手法ext_skelを使う使い方がどこにも書いてない

PECL_CodeGenを使うそれでも面倒くさい

じゃあ、あと何がある?

Page 45: Phpをいじり倒す10の方法

Boost.PHP

Page 46: Phpをいじり倒す10の方法

Boost.PHPとは?C++の各種機能を利用して、簡単にPHPの拡張モジュールを書けるようにするライブラリCodeGen_Pecl? ext_skel? なにそれおいしいの?Boost.Python に強く影響されつつ作りましたBoost.* を名乗っているものの、まだ公式にBoost library suiteの一員ではありません (サーセン!)C++を知らなくても使えます(たぶん)http://github.com/moriyoshi/boost.php/

Page 47: Phpをいじり倒す10の方法

どのくらい簡単?#include "boost/php/module.hpp"#include "boost/php/function.hpp"

using namespace boost;

class m001_module : public php::module, public php::function_container<m002_module> {public: class handler : public php::module::handler { public: handler(m001_module* mod) :php::module::handler(mod) {} };public: m001_module(zend_module_entry* entry) : php::module(entry) { // entry->functions = defun("your_function", &handler::your_function); }};

#define BOOST_PHP_MODULE_NAME m001#define BOOST_PHP_MODULE_CAPITALIZED_NAME M001#define BOOST_PHP_MODULE_VERSION "0.1"#define BOOST_PHP_MODULE_CLASS_NAME m001_module

#include "boost/php/module_def.hpp"

これがテンプレです

Page 48: Phpをいじり倒す10の方法

...

Page 49: Phpをいじり倒す10の方法

モジュール定義次の#defineを書くBOOST_PHP_MODULE_NAMEBOOST_PHP_MODULE_CAPITALIZED_NAMEBOOST_PHP_MODULE_VERSIONBOOST_PHP_MODULE_CLASS_NAME

module classを定義する(名前は BOOST_PHP_MODULE_CLASS_NAME で指定したもの)

Page 50: Phpをいじり倒す10の方法

関数を定義する

moduleクラスのコンストラクタに渡されるentry構造体のfunctionsにdefun(”function_name”, 関数ポインタ)をアサインする以上

Page 51: Phpをいじり倒す10の方法

クラスを定義する

module classの__initialize()メソッドの中身にdef_class<C++のクラス名>(”PHPのクラス名”, ...)を追加する。def_class()の戻り値に対して、先ほどと同じ要領でdefun()を呼び出す。以上

Page 52: Phpをいじり倒す10の方法

ね、簡単でしょう?

Page 53: Phpをいじり倒す10の方法

ご清聴ありがとうございましたThank you for listening!

Page 54: Phpをいじり倒す10の方法

Questions?