preg_replaceのe修飾子を用いたPHPコードの難読化のメモ

先日、preg_replaceのe修飾子を用いて難読化が施されたPHPコードを目にする機会があったのでメモ。

以下のサンプルコードを解析してみる。

<?php

$HOge = 'pr'.'eg_'.'re'. 'place';

$fUgaFuGA = "uryAh33333geloqodjkIQs" ^ "\x5a\x13\x3b\x22\x0c\x76\x75\x54\x5b\x5a\x0d\x0e\x20\x02\x1f\x00\x34\x3b\x39\x1a\x7e\x16";

$HOge($fUgaFuGA, "AEdkqoooohNNpnklmdqOMppMOqdmlknpNNhooooqkdEAxyzqSfj" ^ "\x24\x33\x05\x07\x59\x4d\x06\x09\x47\x01\x3d\x3d\x15\x1a\x43\x30\x31\x40\x17\x20\x22\x59\x59\x6d\x34\x51\x14\x1f\x05\x05\x1a\x58\x69\x06\x0d\x03\x03\x00\x4f\x06\x04\x16\x29\x25\x5f\x50\x41\x51\x2e\x44\x43", "aBcdEFghijkLmnoPQRS");

?>

上記のコードは変数$HOgeに格納されたpreg_replace関数を実行する。変数$HOgeをechoすれば、置換後のデータを見られるのではと思い、以下のようにecho文を追加してコードを実行してみた。

<?php

$HOge = 'pr'.'eg_'.'re'. 'place';

$fUgaFuGA = "uryAh33333geloqodjkIQs" ^ "\x5a\x13\x3b\x22\x0c\x76\x75\x54\x5b\x5a\x0d\x0e\x20\x02\x1f\x00\x34\x3b\x39\x1a\x7e\x16";

echo $HOge($fUgaFuGA, "AEdkqoooohNNpnklmdqOMppMOqdmlknpNNhooooqkdEAxyzqSfj" ^ "\x24\x33\x05\x07\x59\x4d\x06\x09\x47\x01\x3d\x3d\x15\x1a\x43\x30\x31\x40\x17\x20\x22\x59\x59\x6d\x34\x51\x14\x1f\x05\x05\x1a\x58\x69\x06\x0d\x03\x03\x00\x4f\x06\x04\x16\x29\x25\x5f\x50\x41\x51\x2e\x44\x43", "aBcdEFghijkLmnoPQRS");

?>

しかし、何のデータも出力されなかった。

$ php example_code.php 
$ 

続いて変数$fUgaFuGAとpreg_replaceのreplacementのデータをechoしてみた。

<?php

$HOge = 'pr'.'eg_'.'re'. 'place';

$fUgaFuGA = "uryAh33333geloqodjkIQs" ^ "\x5a\x13\x3b\x22\x0c\x76\x75\x54\x5b\x5a\x0d\x0e\x20\x02\x1f\x00\x34\x3b\x39\x1a\x7e\x16";

echo $fUgaFuGA;
echo "\n";
echo "AEdkqoooohNNpnklmdqOMppMOqdmlknpNNhooooqkdEAxyzqSfj" ^ "\x24\x33\x05\x07\x59\x4d\x06\x09\x47\x01\x3d\x3d\x15\x1a\x43\x30\x31\x40\x17\x20\x22\x59\x59\x6d\x34\x51\x14\x1f\x05\x05\x1a\x58\x69\x06\x0d\x03\x03\x00\x4f\x06\x04\x16\x29\x25\x5f\x50\x41\x51\x2e\x44\x43";

$HOge($fUgaFuGA, "AEdkqoooohNNpnklmdqOMppMOqdmlknpNNhooooqkdEAxyzqSfj" ^ "\x24\x33\x05\x07\x59\x4d\x06\x09\x47\x01\x3d\x3d\x15\x1a\x43\x30\x31\x40\x17\x20\x22\x59\x59\x6d\x34\x51\x14\x1f\x05\x05\x1a\x58\x69\x06\x0d\x03\x03\x00\x4f\x06\x04\x16\x29\x25\x5f\x50\x41\x51\x2e\x44\x43", "aBcdEFghijkLmnoPQRS");

?>

すると以下のデータが出力された。

$ php example_code.php 
/aBcdEFghijkLmnoPQRS/e
eval("if(isset(\\$foo)) { print('Hello world'); }")

上記より、preg_replace関数はsubject aBcdEFghijkLmnoPQRSからpattern /aBcdEFghijkLmnoPQRS/e を検索し、replacement eval("if(isset(\\$foo)) { print('Hello world'); }") というPHPコードに置換することが判明した。

pattern /aBcdEFghijkLmnoPQRS/e にはe 修飾子が指定されているので、置換後のeval関数はPHPコードとして実行されることになる。

ちなみにeval関数はAEdkqoooohNNpnklmdqOMppMOqdmlknpNNhooooqkdEAxyzqSfjを鍵として、XORエンコードされている。

eval関数を眺めたところ、変数$HOgeをechoしただけでは置換後のコードが現れなかった理由が判明した。

eval("if(isset(\\$foo)) { print('Hello world'); }")

上記のコードは変数$fooが宣言されていた場合のみ、print関数を実行してHello worldというメッセージを出力する。コードの中では変数$fooは宣言されていないため、eval関数によってprint関数が実行されることはない。そのため、変数$HOgeをechoしただけでは隠されたコードを確認することはできない。

※置換後にe修飾子によって実行されるPHPコードのソースコードを表示したいなら、preg_replace関数そのものよりも、preg_repalce関数に渡されるデータをechoした方が良い。(preg_replace関数そのものをechoすると、PHPコードの実行結果が出力される。)

サンプルコードは無害なメッセージを出力するだけだが、実際に調査したケースでは、同様の手法を用いてHTTPのリクエストに乗せられたPHPコードを実行するWebshellコードが隠されていた。

参考

徳丸 浩著 安全なWebアプリケーションの作り方 脆弱性が生まれる原理と対策の実践 第2版 (SB Creative発行)P.348 - 349

Leave a Reply

Your email address will not be published. Required fields are marked *