暗号屋から見たEメールの死について(EFAIL解説)

本記事は

のcross-post entryです。また、Dark Depths of SMTP のExtra Chapterでもあります。

さて、メール Advent Calendar 2018では1日目の記事でメールの通信の秘密について法的な面からアプローチしていますが、本記事では メールにおいて通信の秘密が技術的に死んでいる可能性 について紹介することになります。


Dark Depths of SMTP の第4章にてメールの暗号化について触れていますが、ものすごく噛み砕いて説明すると、

  • 通常メールは暗号化されない
  • メールにおけるTLSとはhop間のTLS、つまり悪意のあるサーバーを通過した場合そこで復号されたメールを読むことができる
    • このへんで法的な意味での通信の秘密の部分が効いてくるわけですが…
  • 通信路で暗号化されていたところで最後には復号されて保存されるのでIMAPのストレージ自体を攻撃されたら読まれることがありうる
  • メールそのものの暗号化の方法としてはS/MIMEとPGPの2つがある

…という感じになります。このEメール暗号化の最後の砦であるところのS/MIMEとPGPに対して、MUAでHTMLメールがレンダリングされることを利用して暗号化を無効化できる、という脆弱性が今年の5月に報告されました。MUAの脆弱性とプロトコルそのものの脆弱性をあわせた一連の脆弱性にはEFAILという名前が与えられ、論文はセキュリティ分野のトップカンファレンスの一つであるUSENIX Securityで発表されました。

TL;DR

imgタグのようなリクエストが発生するタグとして復号されるようなものを挿入すると、URLの一部として復号後のメッセージを含むリクエストが行われるため、攻撃者のサーバーに復号後の文字列が送信されることになる、というのが結果を1行で説明したものです。

影響はあるの?

この脆弱性は攻撃者がメール自体への読み書きができる場合に可能な攻撃です。通信路上でも可能ですが、IMAPのストレージ自体への攻撃を想定していると考えるとよいでしょう。これ自体がかなり攻撃の条件として限定的なので一般に影響は少ないといえます(なのでタイトルは過大広告)が、PGPやS/MIMEでの暗号化の目的が通信路上あるいはIMAPストレージ上での安全性の保証なので、これはPGPやS/MIMEへの攻撃が成功したといっても間違いではないでしょう。

Direct Exfiltration Attack

EFAILのサイトにて紹介されている第一の攻撃です。これはApple Mail, iOS Mail, Thunderbirdに存在した脆弱性を使ってPGPやS/MIMEの復号結果をリクエストすることを目指します。攻撃者は3つのpartを含むmultipartメールを送信します:

  • 第1のpartはtext/htmlのメールで、imgタグを含みますが、これのsrc要素のdouble quoteは閉じられていません。
  • 第2のpartに攻撃対象者のMUAに解読してほしいPGP・S/MIMEのメール本体を含みます。
  • 第3のpartで最初のpartで閉じていなかったsrc要素のdouble quoteを閉じ、タグを閉じて完成させます。

こうすると、脆弱性を持つMUAは 最初に第2のPGPあるいはS/MIMEのpartを復号し、全部をひとまとめにくっつけたHTMLメールとしてレンダリングしてしまいます。こうすると攻撃者の指定したsrc要素のURLに暗号化されていたはずのメッセージが含まれることになり、リクエストが発生してしまいます。攻撃者はアクセスログなどからリクエストを探して、結果メールを解読させることが可能になりました。

CBC/CFB Gadget Attack

続いて第二の攻撃として挙げられているCBC/CFB Gadget Attackでは、プロトコルそのものに存在する暗号の脆弱性を使ってDirect Exfiltration Attackが成立しないMUAに対してもリクエストが走ってしまうようにメールを書き換えることを目指した攻撃です。

S/MIMEではメール本体の暗号化にブロック暗号のCBC modeという暗号利用モードを用います。CBCモードでは特定のブロックの復号を行うために、まずは該当の暗号文ブロックを復号関数にかけ、そのうえでその結果を前のブロックの暗号文とXORをとることで最終的なブロックの復号結果を得ます(前のブロックのciphertextを使うのでcipher block chaining、でCBC)。

f:id:sylph01:20181211185041p:plain

ここで、IV(初期化ベクタ: これは通常平文で与えられる)とP_0(平文の第1ブロック)を知っているとして、本来の初期化ベクタのところに IV xor P_0 を突っ込んだとしましょう。

f:id:sylph01:20181211185354p:plain

こうすると、最初のブロックは0で埋められることになります(これがEFAILサイトにおける画像の(b)の部分の説明)。IV xor P_0 xor M というものを突っ込めば、第1ブロックをMにすることができます。で、第1ブロックの平文がなんでわかるかというと、S/MIMEではメール本体が Content-type: multipart/signedで始まる ことが決まっているためです。これを実際に攻撃に応用するには(サイトの図の(c))、初期化ベクタに<img ignore="に展開されるような文字列を、また第2ブロックに" src=efail.de/(URLは十分短い文字列であればよい)に展開されるような文字列を、それぞれ IV xor P_0 xor M で作り配置します。2ブロック後である必要があるのは、第1ブロックの暗号文は第2ブロックにchainingされるためで、これによって生成されるランダムな*1文字列をimgタグのignore要素で打ち消します。このようにすると、第3ブロックには同様の原理によりランダムな文字列が現れ、第4ブロック以降には本来のメッセージに含まれる平文が現れます。これらの平文メッセージはimgタグのsrc要素のURLの一部に現れるため、MUAがリクエストを行ってしまえばあとは先ほどと同様に攻撃者は平文の内容を取り出すことができます。

PGP(ここでは特にOpenPGPプロトコルを指す)では少々事情が変わってきます。

  • 暗号利用モードとしてCFBモードを用いる: これはCBCモードと少々やり方が違うが同じようなやり方で文章の改ざんが可能
  • 改ざん検知が存在する: これは以下の2つの方法のどちらかで突破可能。
    • OpenPGPのパケットのうちSEIP(Symmetrically Encrypted and Integrity Protected Packet)と言われるパケットをSE(Symmetrically Encrypted)パケットに挿し替えてダウングレードさせる
    • 暗号文から最後の22バイトを取っ払うことでModification Detection Code(MDC)自体をなくしてしまう。MDCがなかった場合はクライアントがチェック自体をできない。
  • デフォルトで圧縮が行われる: 詳細の説明は省略しますが、論文の9ページ目(ページ番号556)の図を見ると、以下のように圧縮を利用してほしい文字列に展開させることが可能。
    • CFBモードの弱点を利用して「OpenPGPの構造を示す部分」「imgタグ相当のブロック2つ」に展開されるものを作り、
    • それらをLZ77圧縮のbackreferenceで指すようにする
    • そうすると展開後の結果は「OpenPGPの構造」「imgタグ」「攻撃対象の平文」の順に並ぶ

ポイント

本記事ではメール暗号化の主要なオプションであったPGPとS/MIMEが改ざんに対して脆弱で、その結果として秘密性も攻撃されてしまった、ということを紹介しました。

今ではCBCモードやCFBモードは単体で用いるとここで説明されているような方法で文章の改ざんが可能で完全性を保つことができないことが知られていますが、これらのプロトコルが実装された時点ではMAC(Message Authentication Code)を使って完全性を担保する、という考え方が浸透していなかったため、秘密性を守ることはできても完全性を守ることについてまで発想が至っていませんでした。この攻撃は、暗号文の改ざんによって完全性が攻撃された結果、MUAがHTTPリクエストをしてしまうことで秘密性が攻撃された、という、セキュリティの2つの要素に渡る攻撃になっています。

これを受けて、IETFのlamps WGではS/MIME 4.0というプロトコル自体のアップグレードが提案されています。Changes for S/MIME v4.0という節では暗号化方式がAES-256 GCMあるいはChaCha20-Poly1305という認証付き暗号を用いるように更新されたと書かれていて、これによってEFAIL脆弱性で可能であった暗号文の書き換えによるメール本体の改ざんができなくなります。

宣伝

Dark Depths of SMTPの電子版の頒布を開始しました。 (12/13 update)

*1:正確には「今回生成する文字列を復号化関数にかけ、第1ブロックの暗号文とXORをとったもの」。復号化関数の出力結果が普通攻撃者にはわからないため実質ランダム。