Recommended
PPTX
徳丸本に載っていないWebアプリケーションセキュリティ
PPTX
PDF
PHP-FPM の子プロセス制御方法と設定をおさらいしよう
PPTX
PenTesterが知っている危ないAWS環境の共通点
PDF
PDF
PPTX
SSRF対策としてAmazonから発表されたIMDSv2の効果と破り方
PDF
OpenAPI 3.0でmicroserviceのAPI定義を試みてハマった話
PPTX
SPAセキュリティ入門~PHP Conference Japan 2021
PDF
PDF
The Twelve-Factor Appで考えるAWSのサービス開発
PDF
PDF
イエラエセキュリティMeet up 20210820
PDF
FridaによるAndroidアプリの動的解析とフッキングの基礎
PDF
AWS EC2 Eメール制限解除 - 逆引き(rDNS)設定 申請手順
PPTX
PDF
【SQLインジェクション対策】徳丸先生に怒られない、動的SQLの安全な組み立て方
PPTX
20220409 AWS BLEA 開発にあたって検討したこと
PPTX
reg-suitとQA Wolfを活用したVisual Regression Test
PDF
20200526 AWS Black Belt Online Seminar AWS X-Ray
PDF
PPTX
セキュリティの基本とAWSでのセキュリティ対策をフルコースで味あう
PDF
PPT
UnicodeによるXSSとSQLインジェクションの可能性
PDF
PDF
そんなトランザクションマネージャで大丈夫か?
PDF
PPTX
PPTX
Webサイトをめぐるセキュリティ状況と効果的な防御方法(WordPress編)
PDF
著名PHPアプリの脆弱性に学ぶセキュアコーディングの原則
More Related Content
PPTX
徳丸本に載っていないWebアプリケーションセキュリティ
PPTX
PDF
PHP-FPM の子プロセス制御方法と設定をおさらいしよう
PPTX
PenTesterが知っている危ないAWS環境の共通点
PDF
PDF
PPTX
SSRF対策としてAmazonから発表されたIMDSv2の効果と破り方
PDF
OpenAPI 3.0でmicroserviceのAPI定義を試みてハマった話
What's hot
PPTX
SPAセキュリティ入門~PHP Conference Japan 2021
PDF
PDF
The Twelve-Factor Appで考えるAWSのサービス開発
PDF
PDF
イエラエセキュリティMeet up 20210820
PDF
FridaによるAndroidアプリの動的解析とフッキングの基礎
PDF
AWS EC2 Eメール制限解除 - 逆引き(rDNS)設定 申請手順
PPTX
PDF
【SQLインジェクション対策】徳丸先生に怒られない、動的SQLの安全な組み立て方
PPTX
20220409 AWS BLEA 開発にあたって検討したこと
PPTX
reg-suitとQA Wolfを活用したVisual Regression Test
PDF
20200526 AWS Black Belt Online Seminar AWS X-Ray
PDF
PPTX
セキュリティの基本とAWSでのセキュリティ対策をフルコースで味あう
PDF
PPT
UnicodeによるXSSとSQLインジェクションの可能性
PDF
PDF
そんなトランザクションマネージャで大丈夫か?
PDF
PPTX
Similar to 安全なPHPアプリケーションの作り方2016
PPTX
Webサイトをめぐるセキュリティ状況と効果的な防御方法(WordPress編)
PDF
著名PHPアプリの脆弱性に学ぶセキュアコーディングの原則
PPTX
PDF
徳丸本に学ぶ 安全なPHPアプリ開発の鉄則2011
PPTX
脆弱性は誰のせい? PHP、MySQL、Joomla! の責任やいかに
PDF
PDF
今日こそわかる、安全なWebアプリの作り方2010
PDF
PPTX
PDF
徳丸本に学ぶ 安全なPHPアプリ開発の鉄則2012
PDF
PDF
PDF
「最近のwebアプリケーションの脆弱性やそれを悪用する攻撃の動向」OWASP Kansai
PDF
PDF
PPTX
デバッガでWordPress本体やプラグインの脆弱性を追いかけてみよう
PDF
PPT
PDF
PPTX
More from Hiroshi Tokumaru
PPTX
XXE、SSRF、安全でないデシリアライゼーション入門
PPTX
オニギリペイのセキュリティ事故に学ぶ安全なサービスの構築法 (PHPカンファレンス2019)
PPTX
PPTX
ウェブ・セキュリティ基礎試験(徳丸基礎試験)の模擬試験問題
PPTX
PPTX
Railsエンジニアのためのウェブセキュリティ入門
PPTX
PPTX
PPTX
文字コードの脆弱性はこの3年間でどの程度対策されたか?
PPTX
『例えば、PHPを避ける』以降PHPはどれだけ安全になったか
PPTX
PPT
PPTX
PPTX
脅威分析の手法によりウェブサーバーにウイルス対策ソフトが必要かを検証する
PPTX
CMS四天王への攻撃デモを通じて、WordPressの効果的な防御法を学ぼう
PPTX
SecurityとValidationの奇妙な関係、あるいはDrupalはなぜValidationをしたがらないのか
PPTX
PDF
Rails SQL Injection Examplesの紹介
PPTX
PPTX
introduction to unsafe deserialization part1
安全なPHPアプリケーションの作り方2016 1. 2. 徳丸浩の自己紹介
• 経歴
– 1985年 京セラ株式会社入社
– 1995年 京セラコミュニケーションシステム株式会社(KCCS)に出向・転籍
– 2008年 KCCS退職、HASHコンサルティング株式会社設立
• 経験したこと
– 京セラ入社当時はCAD、計算幾何学、数値シミュレーションなどを担当
– その後、企業向けパッケージソフトの企画・開発・事業化を担当
– 1999年から、携帯電話向けインフラ、プラットフォームの企画・開発を担当
Webアプリケーションのセキュリティ問題に直面、研究、社内展開、寄稿などを開始
– 2004年にKCCS社内ベンチャーとしてWebアプリケーションセキュリティ事業を立ち上げ
• 現在
– HASHコンサルティング株式会社 代表 http://www.hash-c.co.jp/
– 独立行政法人情報処理推進機構 非常勤研究員 http://www.ipa.go.jp/security/
– 著書「体系的に学ぶ 安全なWebアプリケーションの作り方」(2011年3月)
「徳丸浩のWebセキュリティ教室 」(2015年10月)
– 技術士(情報工学部門)
Copyright © 2016 HASH Consulting Corp. 2
3. 4. 5. CVE-2016-8869等はどのような問題か?
• どのような問題か?
– ユーザー登録時に、管理者権限を設定されてしまう(CVE-2016-8869)
– ユーザー登録を許可していない設定でもユーザー登録ができてしまう(CVE-2016-8870)
• なにが原因だったか?
– Joomla!内にユーザー登録のメソッドが2つ存在した
• UsersControllerRegistration::register()
• UsersControllerUser::register()
– UsersControllerUser::register() の方は、ユーザー登録許可の設定を確認していない!
– 同じく、外部から権限を示すコードを設定可能
– UsersControllerUser::register()を外部から呼び出す経路が存在した
• どう対策したか?
– UsersControllerUser::register()の削除
• 利用者はどうすればよいか?
– Joomla!のバージョンアップ
Copyright © 2016 HASH Consulting Corp. 5
Demo
6. CVE-2016-8869等から得られる教訓
• 使わなくなったコードは速やかに削除しよう
• 脆弱性診断の古典的な観点ではあるが…
– ソースを追わないと判らない場合が多い
– 内部構造を熟知していないと、短期間の脆弱性診断では指摘できない
• 脆弱性診断で、「使わないコードを削除する」ように勧めている理由
– 古いコードには脆弱性があるかもしれない
– 拡張を.bak等に変更しているとソースコードが閲覧できてしなう
– デバッグ機能等の場合は、バックドアとして悪用される可能性
• 「古いコード」がこれほどまでに「悪用可能」な例は珍しく、ちょっ
と興奮した
• やはり、古いコードは速やかに削除しよう
Copyright © 2016 HASH Consulting Corp. 6
← 違法ではないが一部不適切
7. Joomla!には類似脆弱性の”前科”がある
• Joomla! 2.5.2以前に存在した脆弱性
• ユーザー登録時に、権限情報を付与し、
かつバリデーションエラーを発生させる
ことで権限昇格が可能。
• NICT(情報通信研究機構)の研究者のサ
イトが改ざんされる事件に悪用された
Copyright © 2016 HASH Consulting Corp. 7
https://developer.joomla.org/security-centre/395-
20120303-core-privilege-escalation.html より引用
8. 9. Joomla2.5.2の権限昇格脆弱性
components/com_users/controllers/registration.php register()関数
$data = $model->validate($form, $requestData);
// Check for validation errors.
if ($data === false) {
// Save the data in the session.
$app->setUserState('com_users.registration.data', $requestData);
// Redirect back to the registration screen.
$this->setRedirect(JRoute::_('index.php?option=com_users&view=registration', false));
return false;
// 中略
// バリデーションが正常の場合
// Flush the data from the session.
$app->setUserState('com_users.registration.data', null);
Copyright © 2016 HASH Consulting Corp. 9
バリデーションエラーの場合、リクエストデータを
まるごとセッション変数に放り込んでいる
権限の情報も含まれている
10. components/com_users/models/registration.php getData() 関数内
$temp = (array)$app->getUserState('com_users.registration.data', array());
foreach ($temp as $k => $v) {
$this->data->$k = $v; // セッションのデータをモデルに放り込んでいる
}
【中略】
$this->data->groups = isset($this->data->groups) ?
array_unique($this->data->groups) : array();
// $this->data->groups = array(); 2.5.3でこのように修正
Copyright © 2016 HASH Consulting Corp. 10
セッション汚染、Trust Boundary Violation
と呼ばれる問題
11. セッション汚染脆弱性とは?
• セッション変数を外部から変更できる脆弱性
• 例: phpMyAdmin CVE-2011-2505
Copyright © 2016 HASH Consulting Corp. 11
if (strstr($_SERVER['QUERY_STRING'],'session_to_unset') != false)
{
parse_str($_SERVER['QUERY_STRING']);
session_write_close();
session_id($session_to_unset);
session_start();
$_SESSION = array();
session_write_close();
session_destroy();
exit;
}
parse_str関数に外部入力を与え、
第二引数なしで使うと、
register_globals以上に危険。
セッション変数の書き換えが可能
libraries/auth/swekey/swekey.auth.lib.php 266行目以降
12. セッション汚染でセッション変数にオブジェクトを突っ込む
• これができると危険だが…
– 脆弱性(PHPあるいはアプリケーション)がないとできない
• Joomla!みたいにRequestをごそっとセッション変数に代入したり、
parse_str関数を使っていても、オブジェクトは挿入できない
• 以下のようなケース
– PHPの脆弱性を使う
• CVE-2015-6835 (Joomla! に対するゼロデイ攻撃で悪用された)
• CVE-2016-7125 (後述)
– 入力値をunserialize関数を通している
• phpMyAdminの CVE-2011-2505 (前述)
– その他、eval等(オブジェクトインジェクション以前に危険だが…)
• オブジェクトインジェクション以前の問題として危険
Copyright © 2016 HASH Consulting Corp. 12
13. 14. 脆弱なサンプル(続き)
<?php // Logger.php
class Logger {
const LOGDIR = '/tmp/'; // ログ出力ディレクトリ
private $filename = ''; // ログファイル名
private $log = ''; // ログバッファ
public function __construct($filename) { // ファイル名を指定
if (! preg_match('/A[a-z0-9.]+z/i', $filename)) {
throw new Exception(‘Logger: ファイル名は英数字とドットで…');
}
$this->filename = $filename; // ファイル名
$this->log = ''; // ログバッファ
}
public function add($log) { // ログ出力
$this->log .= $log; // バッファに追加するだけ
}
}
Copyright © 2016 HASH Consulting Corp. 14
15. 脆弱なサンプル(続き)
public function __destruct() { // デストラクタではバッファの中身をファイルに書き出し
$path = self::LOGDIR . $this->filename; // ファイル名組み立て(局所的な脆弱性)
$fp = fopen($path, 'a');
if ($fp === false) {
die('Logger: ファイルがオープンできません' . htmlspecialchars($path));
}
if (! flock($fp, LOCK_EX)) { // 排他ロックする
die('Logger: ファイルのロックに失敗しました');
}
fwrite($fp, $this->log); // ログの書き出し
fflush($fp); // フラッシュしてからロック解除
flock($fp, LOCK_UN);
fclose($fp);
}
}
Copyright © 2016 HASH Consulting Corp. 15
16. 17. 先のサンプルはオブジェクト・インジェクション以前の問題
• 本質的な問題はSession Poisoning
• 以下のような攻撃
• http://example.jp/?userid=yamada&login=1
– $_SESSION[‘userid’] === ‘yamada’
– $_SESSION[‘login’] === ‘1’ (TRUEと評価され得る)
– → セッションハイジャックができてしまう
• すなわち、オブジェクト・インジェクション以前の問題として、セッ
ション汚染単体で問題となる可能性が高い
Copyright © 2016 HASH Consulting Corp. 17
18. CVE-2016-7125の対策
• PHPを最新版にする
– RHEL / CentOSではパッチが出ていないことに注意
– Debian / Ubuntu / Fedora のサポート中のバージョンは問題ない
• そもそもセッション汚染の状態を避ける
Copyright © 2016 HASH Consulting Corp. 18
https://access.redhat.com/security/cve/cve-2016-7125 より引用
19. 20. 21. 22. ケータイキット for Movable Type の脆弱性 (CVE-2016-1204) に関する注意喚起
各位
JPCERT-AT-2016-0019
JPCERT/CC
2016-04-26(新規)
2016-05-06(更新)
<<< JPCERT/CC Alert 2016-04-26 >>>
ケータイキット for Movable Type の脆弱性 (CVE-2016-1204) に関する注意喚起
https://www.jpcert.or.jp/at/2016/at160019.html
I. 概要
アイデアマンズ株式会社のケータイキット for Movable Type には、OS コマンドインジェクションの脆弱性 (CVE-2016-1204) があります。この
脆弱性を悪用された場合、当該製品が動作するサーバ上で任意の OS コマンドを実行される可能性があります。
本脆弱性や影響の詳細については、以下を参照してください。
Japan Vulnerability Notes JVNVU#92116866
ケータイキット for Movable Type に OS コマンドインジェクションの脆弱性
https://jvn.jp/vu/JVNVU92116866/
なお、本脆弱性を悪用した攻撃活動が確認されているとの情報があります。
https://www.jpcert.or.jp/at/2016/at160019.html より引用 22
23. 24. 脆弱なソースコード(Ver 1.641)
$wallpaper = isset($_GET['mtkk_wallpaper'])? unserialize($_GET['mtkk_wallpaper']): null;
// 中略
$cl = (isset($wallpaper['left']) && $wallpaper['left'])? $wallpaper['left']: 0;
// 中略
$options['left'] = $cl;
// 中略
if($options['left'] != 0 || $options['top'] != 0 || $options['width'] != $id['w']
|| $options['height'] != $id['h']) {
$dest_tmp = "$dest-crop.bmp";
$option = " -crop{$options['width']}x{$options['height']}+{$options['left']}+{$options['t
op']}";
$execute = "$convert $option $src $dest_tmp";
if($this->debug_mode) {
// 中略
} else {
exec($execute);
}
24
25. 対策版(Ver 1.65)
$wallpaper = isset($_GET['mtkk_wallpaper'])? unserialize($_GET['mtkk_wallpaper']): null;
// 中略
$cl = (isset($wallpaper['left']) && $wallpaper['left'])? $wallpaper['left']: 0;
// 中略
$options['left'] = $cl;
// 中略
if($options['left'] != 0 || $options['top'] != 0 || $options['width'] != $id['w']
|| $options['height'] != $id['h']) {
$dest_tmp = "$dest-crop.bmp";
$option = " -crop " . escapeshellarg("{$options['width']}x{$options['height']}+{$options
['left']}+{$options['top']}");
$execute = "$convert $option " . escapeshellarg($src) . " " . escapeshellarg($dest_tmp);
if($this->debug_mode) {
// 中略
} else {
exec($execute);
}
25
26. 27. 28. 29. もっと良い方法があった!
• ポイントはこれだけです
1. シェルスクリプトは静的な文字列として定義する。
2. パラメーターは環境変数で渡す。
3. エスケープ不要!
• 要は SQL のプリペアードステートメントみたいなものです。
29
#!/usr/bin/ruby
def getent_egrep_sed(db, pattern, script)
env = {
'db' => db,
'pattern' => pattern,
'script' => script,
}
system(env, 'getent -- "$db" |egrep -- "$pattern" |sed -- "$script"')
end
getent_egrep_sed(*ARGV[0..2])
https://fumiyas.github.io/2013/12/21/dont-use-shell.sh-advent-calendar.html より引用
30. PHPによるサンプルコード
$descriptorspec = array(
0 => array("pipe", "r"), // stdin
1 => array("pipe", "w"), // stdout
2 => array("file", "/tmp/error-output.txt", "a") // stderr .. temporary file
);
$arg2 = "; cat /etc/passwd #ntest test test";
$env = array('e_arg1' => 'aeiou; "xxx"', 'e_arg2' => $arg2);
$process = proc_open('./argdump "$e_arg1" "$e_arg2"', $descriptorspec, $pipes, getcwd(), $env);
if (is_resource($process)) {
fclose($pipes[0]);
while(!feof($pipes[1]))
echo fread($pipes[1], 10);
fclose($pipes[1]);
$return_value = proc_close($process);
echo "command returned $return_valuen";
}
Copyright © 2016 HASH Consulting Corp. 30
31. ふみやす方式による"安全なsystem関数"
function e_system($cmd, ...$args) {
$descriptorspec = array(
0 => array("pipe", "r"),
1 => array("pipe", "w"),
2 => array("file", "/tmp/error-output.txt", "a"));
$cmdline = $cmd;
$env = [];
foreach ($args as $key => $value) {
$argname = 'e_arg_' . $key; // これは本当はよろしくない。局所的な脆弱性
$cmdline .= ' "$' . $argname . '"';
$env[$argname] = $value;
}
$process = proc_open($cmdline, $descriptorspec, $pipes, getcwd(), $env);
if (is_resource($process)) {
fclose($pipes[0]);
while(!feof($pipes[1]))
echo fread($pipes[1], 10);
fclose($pipes[1]);
return proc_close($process);
}
return FALSE;
}
e_system('/usr/sbin/sendmail', '[email protected] ; cat /etc/passwd'); // パラメータは別個の引数として指定
Copyright © 2016 HASH Consulting Corp. 31
32. 改善方針1
function e_system($cmd, ...$args) {
$descriptorspec = array(
0 => array("pipe", "r"),
1 => array("pipe", "w"),
2 => array("file", "/tmp/error-output.txt", "a"));
$cmdline = $cmd;
$env = [];
for ($i = 0; $i < count($args); $i++) { // わかりやすく for文にしてみました
$argname = 'e_arg_' . $i; // これは本当はよろしくない。局所的な脆弱性
$cmdline .= ' "$' . $argame . '"';
$env[$argname] = $args[$i];
}
$process = proc_open($cmdline, $descriptorspec, $pipes, getcwd(), $env);
if (is_resource($process)) {
fclose($pipes[0]);
while(!feof($pipes[1]))
echo fread($pipes[1], 10);
fclose($pipes[1]);
return proc_close($process);
}
return FALSE;
}
e_system('/usr/sbin/sendmail', '[email protected] ; cat /etc/passwd'); // パラメータは別個の引数として指定
Copyright © 2016 HASH Consulting Corp. 32
33. 改善方針2 (for文を使うと死んでしまう人向け)
function e_system($cmd, ...$args) {
$descriptorspec = array(
0 => array("pipe", "r"),
1 => array("pipe", "w"),
2 => array("file", "/tmp/error-output.txt", "a"));
$cmdline = $cmd;
$env = [];
foreach (array_values($args) as $key => $value) { // array_values関数でキー文字列を削除
$argname = 'e_arg_' . $key; // これは本当はよろしくない。局所的な脆弱性
$cmdline .= ' "$' . $argname . '"';
$env[$argname] = $value;
}
$process = proc_open($cmdline, $descriptorspec, $pipes, getcwd(), $env);
if (is_resource($process)) {
fclose($pipes[0]);
while(!feof($pipes[1]))
echo fread($pipes[1], 10);
fclose($pipes[1]);
return proc_close($process);
}
return FALSE;
}
e_system('/usr/sbin/sendmail', '[email protected] ; cat /etc/passwd'); // パラメータは別個の引数として指定
Copyright © 2016 HASH Consulting Corp. 33
34. 35. phpMyAdmin: CVE-2013-3238
Copyright © 2016 HASH Consulting Corp. 35
case 'replace_prefix_tbl':
$current = $selected[$i];
$newtablename = preg_replace("/^" . $from_prefix . "/", $to_prefix, $current);
preg_replace("/^/e0/", "phpinfo();", "test");
preg_replace("/^/e", "phpinfo();", "test");
$from_pref = "/e0“;
$to_prefix = "phpinfo();”;
PHP5.4.3以前では、0以降は無視される
36. 脆弱性が混入した要因
• preg_replaceに渡す正規表現をエスケープしていなかった
– 最低限、/ をエスケープする必要がある
• えーっと、preg_quoteって、マルチバイト対応だっけ?
– Shift_JIS以外では問題ない?
• そもそも、正規表現を外部から(信頼境界を超えて)渡す実装は
避けるべき(実際にもその方向で改修された)
Copyright © 2016 HASH Consulting Corp. 36
preg_replace(“/^” . $from_prefix . “/”, …
↓
reg_replace("/^" . preg_quote($from_prefix, '/') . "/", …
37. phpMyAdmin 4.6.2 librariestransformations.lib.php
function PMA_Transformation_globalHtmlReplace($buffer, $options = array())
{
if (! isset($options['string'])) {
$options['string'] = '';
}
if (isset($options['regex']) && isset($options['regex_replace'])) {
$buffer = preg_replace(
'@' . str_replace('@', '@', $options['regex']) . '@si',
$options['regex_replace'],
$buffer
);
}
// Replace occurrences of [__BUFFER__] with actual text
$return = str_replace("[__BUFFER__]", $buffer, $options['string']);
return $return;
}
Copyright © 2016 HASH Consulting Corp. 37
正規表現中の @ を @ にエ
スケープしているが不完全
38. 39. 40. 41. Zend Frameworkとは?
• PHPの心臓部であるZend Engineを開発しているZend Technologies社
が開発したアプリケーションフレームワーク
• 柔軟な構造であり自由な使い方ができる
• 依存関係が弱くコンポーネントとして利用が容易
• PHPのオブジェクト指向を活用している
• …
• 要はZend謹製のフレームワーク
Copyright © 2008-2015 HASH Consulting Corp. 41
42. Zend_Dbの使い方
require_once 'Zend/Db.php';
$params = array('host' => 'localhost',
'username' => DBUSER,
'password' => DBPASSWD,
'dbname' => DBNAME);
$db = Zend_Db::factory('PDO_MYSQL', $params);
$select = $db->select()
->from('products')
->order('name'); // 列 nameでソート
$result = $db->fetchAll($select);
// 生成されるSQL文
SELECT `products`.* FROM `products` ORDER BY `name` ASC
Copyright © 2008-2015 HASH Consulting Corp. 42
43. orderメソッドあれこれ
// 単純
order('name') ORDER BY `name` ASC
// 降順
order('name desc') ORDER BY `name` DESC
// 識別子のエスケープ
order('na`me') ORDER BY `na``me` ASC
// 配列による複数ソートキー指定
order(array('name', 'id'))
ORDER BY `name` ASC, `id` ASC
// 式も書けるよ
order('(name + id)') ORDER BY (name + id) ASC
Copyright © 2008-2015 HASH Consulting Corp. 43
44. ここで一つ疑問ががが
• 識別子はクォートとエスケープがされる
– name → `name`
– na`ma → `na``me`
• 式はそのまま
– (name + id) → (name + id)
• どうやって識別子と式を区別しているの?
• ソースを見よう!
if (preg_match('/(.*)/', $val)) {
$val = new Zend_Db_Expr($val);
}
Copyright © 2008-2015 HASH Consulting Corp. 44
// ( と ) があれば
// 式とみなす
45. 46. CVE-2014-4914 (Zend Framework 1.12.6以前)
• orderの引数文字列に ( と ) がありさえすれば式とみなされエスケープ
対象外となる
• 1 ; 攻撃文字列 -- () とかでも おk
SELECT `products`.* FROM `products` ORDER BY 1; 攻撃文字列 -- () ASC
• 公表されたPoCは以下の通り
order('MD5(1); drop table products --')
↓ 生成されるSQL文
SELECT `products`.* FROM `products` ORDER BY MD5(1); drop table products --
ASC
• 参考: http://framework.zend.com/security/advisory/ZF2014-04
Copyright © 2008-2015 HASH Consulting Corp. 46
47. Zend Framework 1.12.7 での修正
• 式の判定が以下のように修正された
// 1.12.6以前
if (preg_match('/(.*)/', $val)) {
$val = new Zend_Db_Expr($val);
}
// 1.12.7
if (preg_match('/^[w]*(.*)$/', $val)) {
$val = new Zend_Db_Expr($val);
}
// 英数字0文字以上に続けて ( があり、末尾に ) があれば式とみなす
Copyright © 2008-2015 HASH Consulting Corp. 47
48. 49. Zend Framework 1.12.7 に対する攻撃
• 従来のPoC
order('MD5(1); drop table products --')
↓ 生成されるSQL文
SELECT `products`.* FROM `products` ORDER BY `MD5(1); drop table products --`
ASC
// order by 以降が ` で囲まれて識別子となる
• 新しいPoC
order('MD5(1); drop table products -- )')
↓ 生成されるSQL文
SELECT `products`.* FROM `products` ORDER BY MD5(1); drop table products -- )
ASC
// 式とみなされる条件を満たすので「そのまま」SQL文に
Copyright © 2008-2015 HASH Consulting Corp. 49
50. Zend Framework 1.12.8 での修正
• 式の判定が以下のように修正された
// 1.12.7
if (preg_match('/^[w]*(.*)$/', $val)) {
$val = new Zend_Db_Expr($val);
}
// 英数字0文字以上に続けて ( があり、末尾に ) があれば式とみなす
// 1.12.8
if (preg_match('/^[w]*([^)]*)$/', $val)) {
$val = new Zend_Db_Expr($val);
}
// 英数字0文字以上に続けて ( があり、途中は ) 以外が続き、
// 末尾に ) があれば式とみなす
Copyright © 2008-2015 HASH Consulting Corp. 50
51. 52. これはフレームワークの脆弱性なのか?
• Ruby on Railsの場合、orderメソッドに指定する文字列は「式」と決
まっているので、アプリケーション側のバリデーション等で対策する
ことが求められる
• Zend Frameworkの場合は、文字列の内容により識別子か式かが決ま
るので、責任境界があいまい
• 本来、識別子用のメソッドと式用のメソッドは、名前などで明確に区
別するべし
• つまり、Zend Frameworkの仕様の問題である
Copyright © 2008-2015 HASH Consulting Corp. 52
53. 54. 55. Zend Framework 1.12.10 での修正
• Zend Framework 1.12.10で関数呼び出し(括弧)のネストを許容した
• 式の判定が以下のように修正された
// 1.12.10
if (preg_match('/^([w]*(([^)]|(?1))*))$/', (string) $val)) {
$val = new Zend_Db_Expr($val);
}
Copyright © 2008-2015 HASH Consulting Corp. 55
https://regexper.com/ を利用
56. Zend Framework 1.12.10 での修正(続き)
• この修正により、以下のようなORDER BY句が許容された
– ORDER BY ((list_price - discount_price) * quantity)
• だが、穴はないのか?
• Zend Framework 1.12.10で混入した脆弱性
– MD5("(");DELETE FROM p2; #)
Copyright © 2008-2015 HASH Consulting Corp. 56
https://regexper.com/ を利用
57. 58. Zend Framework 1.12.19 での修正
• 式の判定が以下のように修正された
// 1.12.19
if (preg_match'/^([w]+s*(([^()]|(?1))*))$/', (string) $val)) {
$val = new Zend_Db_Expr($val);
}
Copyright © 2008-2015 HASH Consulting Corp. 58
禁止文字に開き括
弧が追加された
https://regexper.com/ を利用
59. 60. Zend Framework 1.12.19への攻撃
• 穴はないのか?
• Zend Framework 1.12.19に対する攻撃
– MD5(“a(");DELETE FROM p2; #)
Copyright © 2008-2015 HASH Consulting Corp. 60
https://regexper.com/ を利用
61. Zend Framework 1.12.20 での修正
• ORDER BY句のチェックに先立ち、SQLコメントを取り除く処理が追
加された
Copyright © 2008-2015 HASH Consulting Corp. 61
const REGEX_SQL_COMMENTS = '@
((['"]).*?[^]2) # $1 : Skip single & double quoted expressions
|( # $3 : Match comments
(?:#|--).*?$ # - Single line comments
| # - Multi line (nested) comments
/* # . comment open marker
(?: [^/*] # . non comment-marker characters
|/(?!*) # . ! not a comment open
|*(?!/) # . ! not a comment close
|(?R) # . recursive case
)* # . repeat eventually
*/ # . comment close marker
)s* # Trim after comments
|(?<=;)s+ # Trim after semi-colon
@msx';
62. 63. Zend Framework 1.12.20 への攻撃はできないのか?
• 実はまだ穴は残っている (;´Д`)
• Zend Framework 1 は2016年9月28日でEnd of Life(EOL)となった
https://framework.zend.com/blog/2016-06-28-zf1-eol.html
• すなわち、もう改修の見込みはない
• ダメ元で報告したけど、「アプリケーション側で対応せよ」という回答だった
• 実は僕もそう思う
Copyright © 2008-2015 HASH Consulting Corp. 63
64. 対策
• Zend Framework 1 をお使いの方は…
– 最新のZend Framework 1.12.20 に移行する かつ
– orderメソッドの引数をバリデーション
• できるだけ早期にZend Framework 2 または 3、あるいはその他のフ
レームワークに移行しましょう
※ Zend Framework 2 にはこの問題はありません
Copyright © 2008-2015 HASH Consulting Corp. 64
65. 66. JavaScriptの文字列リテラルのエスケープは難しい
• イベントハンドラ onxxxx= のエスケープは、
– まず文字列をJavaScript文字列としてエスケープして、
– HTMLの属性値としてエスケープする
• script要素の中身のエスケープは、
– JavaScript文字列としてエスケープするだけでよいが、
– </script>を注入されることによる攻撃の対策が必要となる
– 以下のようなケース
<script>
foo("☆ここを動的生成☆");
</script>
Copyright © 2016 HASH Consulting Corp. 66
67. 68. script要素内の文字列リテラル動的生成アプローチ
• 過剰エスケープ
– 徳丸本が紹介している方法の一つ
– 英数字以外を全てユニコードエスケープ uXXXX とエスケープする
– 実はあまり好きではない
• HTMLノードとして文字列を生成して、JavaScriptから参照する
– 古典的にはhiddenフィールド(徳丸本で紹介している方法の一つ)
– 最近だと id="hoge" data-foo="<% bar %>" しておいて $("#hoge").data('foo')
でとりだすのが主流かと思います。
http://b.hatena.ne.jp/entry/blog.ohgaki.net/javascript-string-escape
• 奥一穂氏の提唱する方法(Inline JSONP)
– ひとことでいうと、JSONPと同形式の呼出をサーバサイドで生成しSCRIPTタグ
として埋め込む、という手法を採るべきだと思います。
http://d.hatena.ne.jp/kazuhooku/20131106/1383690938
Copyright © 2016 HASH Consulting Corp. 68
69. 70. 71. 72. 73. わかりやすく書こう-うますぎるプログラムはいけない*1
• こなれた機能を使ってシンプルに実装すること
• 「危険な機能」は原則として避ける
– eval、system、call_user_func …
– 複雑な正規表現も避けた方が良い
Copyright © 2016 HASH Consulting Corp. 73
const REGEX_SQL_COMMENTS = '@
((['"]).*?[^]2) # $1 : Skip single & double quoted expressions
|( # $3 : Match comments
(?:#|--).*?$ # - Single line comments
| # - Multi line (nested) comments
/* # . comment open marker
(?: [^/*] # . non comment-marker characters
|/(?!*) # . ! not a comment open
|*(?!/) # . ! not a comment close
|(?R) # . recursive case
)* # . repeat eventually
*/ # . comment close marker
)s* # Trim after comments
|(?<=;)s+ # Trim after semi-colon
@msx'; Zend Framework 1.12.20 に出て来る正規表現
*1 プログラム書法、 B.カーニハン著、木村泉訳より引用
74. 75.