連載「OSS脆弱性ウォッチ」では、さまざまなオープンソースソフトウェアの脆弱性に関する情報を取り上げ、解説していく。今回は、2018年3月1日に公開されたPostgreSQLの脆弱性情報(CVE-2018-1058)を取り上げる。
「OSSセキュリティ技術の会」の面和毅です。本連載「OSS脆弱性ウォッチ」では、さまざまなオープンソースソフトウェア(OSS)の脆弱(ぜいじゃく)性に関する情報を取り上げ、解説しています。
今回は、2018年3月1日に公開されたPostgreSQLの脆弱性情報(CVE-2018-1058)を取り上げます。
脆弱性の概要は、「A Guide to CVE-2018-1058: Protect Your Search Path」(英語)に詳しく載っています。今回は、この内容に従って説明します。
説明にも載っている通り、これは「脆弱性」というよりも、「PostgreSQLの、もともとの仕様がそうなっている」というニュアンスが強いものです。そのため、脆弱性対応方法もパッケージを新しいものにするだけではなく、上記の概要のURLに即して設定を変更する必要があります。
実際に検証環境をセットアップして見てみます。検証環境として、以下のものを使用します。
PostgreSQL 7.3以降では、「スキーマ」という分割されたネームスペースが導入されました。DBを作成するとスキーマ「public」がデフォルトで作成されます。このスキーマは全てのユーザーが参照できます。publicに作成したオブジェクト(テーブルや関数、演算子など)を使用するなども可能です。
スキーマ名を指定せずにオブジェクトを作成した場合は、オブジェクトはデフォルトでpublicスキーマに入ることになります。実際にスキーマ名を指定せずに「tbl1」テーブルを作成すると、publicスキーマに入るため、select文でtbl1とした場合と「public.tbl1」とした場合では同じ結果が出力されます(図1)。
cve_2018_1058_test=# select * from tbl1; id | name | address | update ----+-------+----------+--------------------- 1 | USER1 | Saitama | 2018-04-27 00:00:00 2 | USER2 | Chiba | 2018-04-28 00:00:00 3 | USER3 | Kanagawa | 2018-04-28 00:00:00 4 | USER4 | Tokyo | 2018-04-26 00:00:00 (4 行) cve_2018_1058_test=# select * from public.tbl1; id | name | address | update ----+-------+----------+--------------------- 1 | USER1 | Saitama | 2018-04-27 00:00:00 2 | USER2 | Chiba | 2018-04-28 00:00:00 3 | USER3 | Kanagawa | 2018-04-28 00:00:00 4 | USER4 | Tokyo | 2018-04-26 00:00:00 (4 行)
テーブルなどのオブジェクトを参照する場合には、「スキーマ検索パス」を用いて参照が行われます。現在のスキーマ検索パスは、「SHOW search_path;」を実行すると見ることができます(図2の4行目)。
cve_2018_1058_test=> SHOW search_path; search_path ---------------- "$user",public (1 行)
このsearch_pathの値は、postgresql.confで設定されます(図3の7行目)。
#------------------------------------------------------------------------------ # CLIENT CONNECTION DEFAULTS #------------------------------------------------------------------------------ # - Statement Behavior - #search_path = '"$user",public' # schema names
インストール直後には「$user」というスキーマはないので無視されますが、これを作成すると、スキーマ名を指定せずに作成したオブジェクトは「$user」以下に配置されます。そのため、「user1」ユーザーで「hogehoge」テーブルを、スキーマ名を指定せずに作成した場合、「user1.hogehoge」オブジェクトになり、「public.hogehoge」ではアクセスできません(図4の14〜16行目)。
(ID:postgresで) cve_2018_1058_test=# CREATE SCHEMA user1 AUTHORIZATION user1; CREATE SCHEMA (ID:uid1で) cve_2018_1058_test=> CREATE TABLE hogehoge AS SELECT 1::int AS id; SELECT 1 cve_2018_1058_test=> select * from hogehoge; id ---- 1 (1 行) cve_2018_1058_test=> select * from public.hogehoge; ERROR: リレーション"public.hogehoge"は存在しません 行 1: select * from public.hogehoge; ^ cve_2018_1058_test=> select * from user1.hogehoge; id ---- 1 (1 行)
それでは今回の脆弱性を確認してみましょう。Postgresqlに「user2」ユーザーでログインして、スキーマを指定せず(つまりpublic以下に)「fugafuga」テーブルを作成し、テーブル内に大文字/小文字を混ぜた英単語を入れておきます(図5)。
cve_2018_1058_test=> CREATE TABLE fugafuga (full_name varchar(255)); CREATE TABLE cve_2018_1058_test=> INSERT INTO fugafuga VALUES ('Kondo isamu'); INSERT 0 1 cve_2018_1058_test=> INSERT INTO fugafuga VALUES ('Okita souji'); INSERT 0 1 cve_2018_1058_test=> INSERT INTO fugafuga VALUES ('Hijikata toshizou'); INSERT 0 1
user2で、SELECT分中で「lower」を入れると、出力される英単語が全て小文字のみの出力になります(図6)。
cve_2018_1058_test=> SELECT * FROM fugafuga; full_name ------------------- Kondo isamu Okita souji Hijikata toshizou (3 行) cve_2018_1058_test=> SELECT lower(full_name) FROM fugafuga; lower ------------------- kondo isamu okita souji hijikata toshizou (3 行)
ここで、user1で「public.lower」としてユーザー定義の関数を作成します(図7)。
CREATE FUNCTION lower(varchar) RETURNS text AS $$ SELECT 'ALICE WAS HERE: ' || $1; $$ LANGUAGE SQL IMMUTABLE;
すると、user2の方でlower関数を実行すると、public中の関数が先に参照され、関数が上書きされることになります(図8の4〜6行目)。
cve_2018_1058_test=> select lower(full_name) from fugafuga; lower ----------------------------------- ALICE WAS HERE: Kondo isamu ALICE WAS HERE: Okita souji ALICE WAS HERE: Hijikata toshizou (3 行)
user2で「\df+」で関数の詳細を確認すると、publicスキーマ以下の関数の方が参照されていることが分かります(図9の5行目)。
cve_2018_1058_test=> \df+ 関数一覧 スキーマ | 名前 | 結果のデータ型 | 引数のデータ型 | 型 | 揮発性 | 所有者 | セキュリティ | アクセス権 | 言語 | ソースコード | 説明 ----------+-------+----------------+-------------------+----------------+--------+--------+--------------+------------+------+----------------------------------+------ public | lower | text | character varying | normal(通常) | 不変 | user1 | 呼び出し元 | | sql | +| | | | | | | | | | | SELECT 'ALICE WAS HERE: ' || $1;+| | | | | | | | | | | | (1 行)
試しに、暗黙的に含まれているシステムスキーマである「pg_catalog」を指定してlower関数を実行すると、本来の関数(出力される英単語が全て小文字になる)が実行されます(図10の4〜6行目)。
cve_2018_1058_test=> select pg_catalog.lower(full_name) from fugafuga; lower ------------------- kondo isamu okita souji hijikata toshizou (3 行)
この挙動を利用することで、例えば悪意のあるユーザーがpublicスキーマに不正な挙動を行うユーザー関数を仕込んでおくことができます。他のユーザー、特にDBでの権限が高いユーザーが、その不正なユーザー関数を一般のユーザー関数として実行することで、攻撃者の権限を上回る権限での攻撃(漏えい、改ざん、権限昇格など)がやり放題になってしまいます。
今回の脆弱性は下記の2点が問題となっています。
そのため、これらに対する修正(パッケージが更新できない場合にはワークアラウンド)が施されることになりました。
今回の問題の修正ですが、PostgreSQLのもともとの仕様が起因するものなので、新しいバージョンでは完全にsearch_pathからpublicスキーマが全て消えているわけではなく、重要な「pg_dump」「pg_upgrade」「vacuumdb」などの、スーパーユーザーが実行するようなアプリケーションに関してsearch_pathからpublicスキーマが取り除かれています。
詳しくは、PostgreSQL 10.3のリリースノートに説明があります。
今回の脆弱性への対応は、運用環境の設定に依存するため、設定を変更することで対応を行います。つまり、下記のようになります。
1.「postgres」ユーザーでALTERコマンドを実行し、search_pathからpublicのスキーマを除外する(図11-2の2〜5行目)。
ALTER ROLE user1 SET search_path = "$user";
cve_2018_1058_test=> show search_path; search_path ------------- "$user" (1 行) cve_2018_1058_test=> SELECT lower(full_name) FROM public.fugafuga;psql -h 172.16.148.155 -U user2 cve_2018_1058_test lower ------------------- kondo isamu okita souji hijikata toshizou (3 行)
または、postgresql.confを修正し、search_pathからpublicのスキーマを除外する(図12の7行目)。
#------------------------------------------------------------------------------ # CLIENT CONNECTION DEFAULTS #------------------------------------------------------------------------------ # - Statement Behavior - #search_path = '"$user"' # schema names
2.ユーザーからpublicスキーマに新しいオブジェクトを追加できる権限を削除する(図13-2の4〜5行目)。
REVOKE CREATE ON SCHEMA public FROM PUBLIC;
cve_2018_1058_test=> CREATE FUNCTION public.lower(varchar) RETURNS text AS $$ SELECT 'ALICE WAS HERE: ' || $1; $$ LANGUAGE SQL IMMUTABLE; ERROR: スキーマ public への権限がありません
PostgreSQLのバージョンを上げられない環境でも、これをワークアラウンドとして実行することで、今回の脆弱性を緩和できます。
今回の件は、そもそもPostgreSQLの仕様から来ているものなので、パッケージを更新するだけではなく、search_pathからのpublicスキーマの除外や、publicスキーマへのユーザーのアクセス権を見直す必要があります。
しかし、search_pathにpublicスキーマが含まれていることや、ユーザーがpublicスキーマにオブジェクトを作成できることが前提となっているアプリケーションや運用環境もあると思われます。そのような場合には、この脆弱性を正しく理解し、運用環境に大きな変更が起こらないように適切に対応する必要があるので、実際の対応は慎重に行わねばならず、悩み深いと思われます。
略歴:OSSのセキュリティ専門家として20年近くの経験があり、主にOS系のセキュリティに関しての執筆や講演を行う。大手ベンダーや外資系、ユーザー企業などでさまざまな立場を経験。2015年からサイオステクノロジーのOSS/セキュリティエバンジェリストとして活躍し、同社でSIOSセキュリティブログを連載中。
CISSP:#366942
近著:『Linuxセキュリティ標準教科書』(LPI-Japan)」
Copyright © ITmedia, Inc. All Rights Reserved.