連載「OSS脆弱性ウォッチ」では、さまざまなオープンソースソフトウェアの脆弱性に関する情報を取り上げ、解説していく。今回は、2018年6月9日に公開された「GNU Privacy Guard」の脆弱性(CVE-2018-12020)を取り上げる。
「OSSセキュリティ技術の会」の面和毅です。本連載「OSS脆弱性ウォッチ」では、さまざまなオープンソースソフトウェア(OSS)の脆弱(ぜいじゃく)性に関する情報を取り上げ、解説しています。
今回は、2018年6月9日に公開された「GNU Privacy Guard」(以下、GPGまたはGNUPG)の脆弱性(CVE-2018-12020)を取り上げます。
今回の脆弱性の概要は、GPG 2.2.8のアナウンスメールに詳しく載っています。
GPGでは、ファイルを検証する際に、「-v」オプションを付けると、元のファイル名が表示されます。図の例では「test.txt」というファイル名のファイルをテストに使用し「gpg -vd --status-fd 2」で検証を行っています。今回は、ステータスを表示するファイルディスクリプタを「2」として設定しています(図1、図2)。
この際に、元のファイル名(この例ではtest.txt)が出力される箇所で意図しない文字に関するチェックを行っていなかったため、ファイル名に改行や制御コードを入れることで、ファイル検証結果の出力をごまかせる可能性があります。
検証環境として、下記を使用します。
検証を行う際にエラーがあった場合に、ステータスとしてどのようなものが表示されるかを確認します。先ほど作成したgpgファイルをちょっとだけ改変したものを用い、「gpg -vd --status-fd 2」としたときにステータスがどのように表示されるかを確認しています(図3、図4)。
jsosug@localhost:~/GnuPG$ echo "This is modify test." >> test.txt.gpg
「test.txt」という文字列の後に改行や出力文、制御コードを入れたファイル名を作成します。今回はC言語でプログラムを書いて空のファイルを作成します。ファイル名としては、図4で確認した部分の「[GNUPG:]」で始まるステータスの出力部分も入れたものを作成します(図5)。
#include <stdlib.h> #include <fcntl.h> void main(void) { char file[] = "test.txt\'\n\n[GNUPG:] PLAINTEXT 62 1528617371 test.txt\n[GNUPG :] PLAINTEXT_LENGTH 0\n[GNUPG:] DECRYPTION_OKAY\n[GNUPG:] GOODMDC\n[GNUPG:] END_ DECRYPTION\n\n\n"; int fd; if ((fd = open(file, O_WRONLY|O_CREAT|O_TRUNC, 0644)) == -1) err(EXIT_FAILURE, "creating file failed"); close(fd); return ; }
※悪用を避けるために、今回テストで使用するファイル名作成プログラムでは、メッセージを完全に偽造することは避けており、エラーメッセージも出力されるようにしています。
先ほど作成したファイルをコンパイルし、適当なディレクトリで実行します。今回は「PoC」というディレクトリで実行します。ステータス出力と制御コードを含むファイル(空のファイル)が作成されます(図6)。
gpgを使って署名を行います。「test.txt.gpg」というファイル名が署名付きメッセージファイルとして出力されます(図7)。
先ほど作成、改変したgpgファイルを、「gpg -vd --status-fd 2」を用いて検証し、ステータス(同じくファイルディスクリプタを「2」と設定している)がどのように表示されるかを確認します(図8)。
今回の問題は、ファイル名を出力するオプションが呼ばれた際に、ファイル名として与えられた文字列をそのまま引き渡して出力してしまっていたことです。そのため、ファイル名として与えられた文字列に制御コードが含まれていた際にも、そのまま出力されてしまいます(図9の8行目)。
proc_plaintext( CTX c, PACKET *pkt ) { -- 省略-- if (pt->namelen == 8 && !memcmp( pt->name, "_CONSOLE", 8)) log_info (_("Note: sender requested \"for-your-eyes-only\"\n")); else if (opt.verbose) log_info (_("original file name='%.*s'\n"), pt->namelen, pt->name);
そのため、今回の修正では、ファイル名を一度「make_printable_string()」関数に入れて意図しない文字を含むかの確認を行い、その結果を表示するように修正されています(図10の13〜15行目)。
proc_plaintext( CTX c, PACKET *pkt ) { --省略-- if (pt->namelen == 8 && !memcmp( pt->name, "_CONSOLE", 8)) log_info (_("Note: sender requested \"for-your-eyes-only\"\n")); else if (opt.verbose) { /* We don't use print_utf8_buffer because that would require a * string change which we don't want in 2.2. It is also not * clear whether the filename is always utf-8 encoded. */ char *tmp = make_printable_string (pt->name, pt->namelen, 0); log_info (_("original file name='%.*s'\n"), (int)strlen (tmp), tmp); xfree (tmp);
今回の脆弱性は、gpgを用いて「-v」でステータスを得て、その結果でファイルが正しいかどうかを判定しているようなアプリケーション(例えば、メールリーダーなど)に影響します。
元のGPGのリリースノートでも言及されていますが、最新の「GpgOL」(「Gpg4win」に含まれるOutlookプラグイン)、「KMail」(KDEデスクトップ環境用メールクライアント)などのメールリーダーはGPGME(GPGをアプリから扱いやすいように改良したもの)を使用しているので問題はありません。しかし、その他のアプリケーションや、自前でGPGを用いて検証するようなプログラムを自作している場合には、注意が必要です。
自分で使用しているGPGに脆弱性があるかどうかは、次の手順で分かります。
-----BEGIN PGP MESSAGE----- jA0EBwMC1pW2pqoYvbXl0p4Bo5z/v7PXy7T1BY/KQxWaE9uTBRbf4no64/+5YYzX +BVNqP+82aBFYXEsD9x1vGuYwofQ4m/q/WcQDEPXhRyzU+4yiT3EOuG7sTTaQR3b 8xAn2Qtpyq5tO7k9CN6dasaXKSduXVmFUqzgU+W9WaTLOKNDFw6FYV3lnOoPtFcX rzhh2opkX9Oh/5DUkZ6YmUIX3j/A0z+59/qNO1i2hQ== =zswl -----END PGP MESSAGE-----
cat /tmp/abc |gpg --no-options -vd 2>&1 | grep '^\[GNUPG:] INJECTED'
実行結果として「INJECTED」の行が出力される場合には、GPGパッケージに脆弱性があります(図12)。
今回の件は、ログとして出力されるファイル名自身に意図しない文字が利用された場合を想定していなかったことが原因です。このようなこともあるため、プログラムを開発する場合には、出力する文字列は、必ず意図しない文字列が混入しないように心掛けましょう。
また、今回対象のGPG(2.2.7)を使用している場合には、ディストリビューションの情報などをチェックして、早めにパッケージのアップデートを行いましょう。
略歴:OSSのセキュリティ専門家として20年近くの経験があり、主にOS系のセキュリティに関しての執筆や講演を行う。大手ベンダーや外資系、ユーザー企業などでさまざまな立場を経験。2015年からサイオステクノロジーのOSS/セキュリティエバンジェリストとして活躍し、同社でSIOSセキュリティブログを連載中。
CISSP:#366942
近著:『Linuxセキュリティ標準教科書』(LPI-Japan)」
Copyright © ITmedia, Inc. All Rights Reserved.