PostgreSQLの“仕様”による、運用者にとって悩み深い脆弱性とはOSS脆弱性ウォッチ(6)

連載「OSS脆弱性ウォッチ」では、さまざまなオープンソースソフトウェアの脆弱性に関する情報を取り上げ、解説していく。今回は、2018年3月1日に公開されたPostgreSQLの脆弱性情報(CVE-2018-1058)を取り上げる。

» 2018年05月16日 05時00分 公開
[面和毅OSSセキュリティ技術の会]

 「OSSセキュリティ技術の会」の面和毅です。本連載「OSS脆弱性ウォッチ」では、さまざまなオープンソースソフトウェア(OSS)の脆弱(ぜいじゃく)性に関する情報を取り上げ、解説しています。

 今回は、2018年3月1日に公開されたPostgreSQLの脆弱性情報(CVE-2018-1058)を取り上げます。

脆弱性概要

 脆弱性の概要は、「A Guide to CVE-2018-1058: Protect Your Search Path」(英語)に詳しく載っています。今回は、この内容に従って説明します。

 説明にも載っている通り、これは「脆弱性」というよりも、「PostgreSQLの、もともとの仕様がそうなっている」というニュアンスが強いものです。そのため、脆弱性対応方法もパッケージを新しいものにするだけではなく、上記の概要のURLに即して設定を変更する必要があります。

検証環境準備

 実際に検証環境をセットアップして見てみます。検証環境として、以下のものを使用します。

  • OS:CentOS 7.4(centos74.jsosug.jp、172.16.148.155、VMware環境)
  • Samba:postgresql-9.2.21-1.el7.x86_64(CentOS 7.4のDVDに同梱)

検証

「public」スキーマの確認

 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 行)
図1 「Select * from tbl1」と「select * from public.tbl1」は同じ結果になる

 テーブルなどのオブジェクトを参照する場合には、「スキーマ検索パス」を用いて参照が行われます。現在のスキーマ検索パスは、「SHOW search_path;」を実行すると見ることができます(図2の4行目)。

cve_2018_1058_test=> SHOW search_path;
  search_path   
----------------
 "$user",public
(1 行)
図2 現在のsearch_pathは、「SHOW search_path;」で出力される

 この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 行)
図4 ユーザーネームと同じスキーマをDBに作成した場合の参照パス

今回の脆弱性の確認

 それでは今回の脆弱性を確認してみましょう。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
図5 user2でfugafugaテーブルを作成。大文字小文字を含むデータを入れておく

 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 行)
図6 user2でfugafugaテーブルのデータを参照。lower関数を使うと、データの全てが小文字になって表示される

 ここで、user1で「public.lower」としてユーザー定義の関数を作成します(図7)。

CREATE FUNCTION lower(varchar) RETURNS text AS $$
SELECT 'ALICE WAS HERE: ' || $1;
$$ LANGUAGE SQL IMMUTABLE;
図7 publicスキーマにlower関数を作成する

 すると、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 行)
図8 user2でlower関数を実行すると、publicスキーマにuser1が作成した関数の方が参照される

 user2で「\df+」で関数の詳細を確認すると、publicスキーマ以下の関数の方が参照されていることが分かります(図9の5行目)。

cve_2018_1058_test=> \df+
                                                                               関数一覧
 スキーマ | 名前  | 結果のデータ型 |  引数のデータ型   |       型       | 揮発性 | 所有者 | セキュリティ | アクセス権 | 言語 |           ソースコード           | 説明 
----------+-------+----------------+-------------------+----------------+--------+--------+--------------+------------+------+----------------------------------+------
 public   | lower | text           | character varying | normal(通常) | 不変   | user1  | 呼び出し元   |            | sql  |                                 +| 
          |       |                |                   |                |        |        |              |            |      | SELECT 'ALICE WAS HERE: ' || $1;+| 
          |       |                |                   |                |        |        |              |            |      |                                  | 
(1 行)
図9 user2で「\df+」で関数の詳細を確認すると、publicスキーマ以下の関数の方が参照されていることが分かる

 試しに、暗黙的に含まれているシステムスキーマである「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 行)
図10 user2で暗黙的に入っているpg_catalogスキーマを指定してlower関数を実行すると、通常の関数の方が参照される

 この挙動を利用することで、例えば悪意のあるユーザーがpublicスキーマに不正な挙動を行うユーザー関数を仕込んでおくことができます。他のユーザー、特にDBでの権限が高いユーザーが、その不正なユーザー関数を一般のユーザー関数として実行することで、攻撃者の権限を上回る権限での攻撃(漏えい、改ざん、権限昇格など)がやり放題になってしまいます。

今回の問題

 今回の脆弱性は下記の2点が問題となっています。

  • スキーマ検索パスにpublicが含まれている
  • 全てのユーザーがpublic以下に関数などのオブジェクトを作成できる

 そのため、これらに対する修正(パッケージが更新できない場合にはワークアラウンド)が施されることになりました。

今回の修正とワークアラウンド

 今回の問題の修正ですが、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";
図11-1 postgresユーザーで一般ユーザーのsearch_pathからpublicを消去する
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 行)
図11-2 user1でsearch_pathからpublicが消えていることを確認する

 または、postgresql.confを修正し、search_pathからpublicのスキーマを除外する(図12の7行目)。

#------------------------------------------------------------------------------
# CLIENT CONNECTION DEFAULTS
#------------------------------------------------------------------------------
 
# - Statement Behavior -
 
#search_path = '"$user"'         # schema names
図12 postgresql.conf中のsearch_pathの設定

2.ユーザーからpublicスキーマに新しいオブジェクトを追加できる権限を削除する(図13-2の4〜5行目)。

REVOKE CREATE ON SCHEMA public FROM PUBLIC;
図13-1 postgresユーザーで全てのDBに対して、publicスキーマにオブジェクトを追加できる権限を削除する
cve_2018_1058_test=> CREATE FUNCTION public.lower(varchar) RETURNS text AS $$
SELECT 'ALICE WAS HERE: ' || $1;
$$ LANGUAGE SQL IMMUTABLE;
ERROR:  スキーマ public への権限がありません
図13-2 user1で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.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。