SMTPインジェクションを防ぐ:ホワイトボックス入門
Sam Sanoop
2022年9月15日
0 分で読めますSMTPインジェクションの脆弱性は、開発者やセキュリティの専門家にも誤解されやすく、静的解析製品でも見逃されることがあります。このブログでは、ライブラリやアプリケーションに存在しうる一般的なSMTPインジェクションの脆弱性と、それらを迅速に発見・修正するためのヒントを紹介します。
SMTPの概要
Simple Mail Transfer Protocol(SMTP)は、メールの送受信に使用されるプロトコルです。通常、ユーザー向けのメールクライアントは、メールを中継するメールサーバーに送信するためにSMTPを使用します。SMTPサーバーは一般に、ポート番号25(平文通信)および587(暗号化通信)のTransmission Control Protocolを使用します。
現代のソフトウェアやアプリケーションでは、ユーザーの操作に応じてメールを送信するユーザーフローの一部として、SMTPプロトコルがよく使われます。たとえば、登録確認メールでは、ユーザーのメールアドレスに定型のウェルカムメッセージを添えてSMTPサーバーに中継し、ユーザーのメールアカウントに送信します。
SMTPクライアントとサーバー間のセッションでは、クライアントが複数のSMTPコマンドを使用できます。一般的な例をいくつか紹介します。
HELO/EHLO-HELOコマンドはSMTPセッションの通信を開始します。例:HELO snyk.ioMAIL FROM-MAIL FROMコマンドは、送信元のメールアドレスを指定してメール転送を開始します。例:MAIL FROM "[foo@snyk.io](mailto:foo@snyk.io)"RCPT TO-RCPT TOコマンドは、メールの送信先となる受信者を指定します。例:RCPT TO "[foobar@snyk.io](mailto:foobar@snyk.io)"DATA-DATAコマンドを使って、クライアントはサーバーにメールデータの転送許可を求めます。応答コード354が返されると許可が与えられ、クライアントはメール本文を1行ずつ送信します。DATAの送信は、.文字で終了できます
クライアントとサーバー間のSMTP通信の例を以下に示します。
SMTPインジェクションとは
SMTPインジェクションは、クライアントとサーバー間で行われるSMTP通信に、攻撃者が任意のSMTPコマンドを挿入できる場合に発生します。ユーザーが制御できるパラメーターに含まれるCRLF文字が、検証や適切なサニタイズを経ずにSMTPコマンドに含められることで、追加のコマンドが挿入されることがあります。
こうした問題は、SMTPコマンドの送信にライブラリを使用しているWebアプリケーションや、ユーザーが制御するパラメーターを検証していない独自実装でよく見られます。
SMTPインジェクションによる影響は、脆弱性のあるアプリケーションの状況によって異なります。一般的な影響には、次のようなものがあります。
メールのコピーを第三者に送信する。
SMTPサーバーに送信されるメッセージの内容を改ざんする。
SMTPインジェクションの影響を受けるアプリケーションをプロキシとして悪用し、フィッシング攻撃を行う。
SMTPインジェクションをより深く理解するために、次の例を見てみましょう。
SMTPインジェクションのシナリオ1
開発者は、SMTPサーバーへのメール送信にサードパーティ製ライブラリをよく利用します。これは、標準ライブラリのSMTP関数に代わる手段となります。その一例がsmtp-clientです。smtp-clientにはホスト名、認証情報、受信者、送信者を設定するためのさまざまなフィールドがあり、それらをSMTPメッセージの一部として送信できます。
ただし、s.rcptフィールドを設定できず、ユーザーがs.mailだけを制御できる状況を考えてみましょう。その場合、CRLF文字を使ってfrom@sender.com>\r\nRCPT TO:<[attacker@snyk.io](mailto:attacker@snyk.io)のような新しいコマンドを挿入できます。
例:
検証が行われないため、SMTPクライアントはCRLF文字を処理し、RCPT TO:<[attacker@snyk.io](mailto:attacker@snyk.io)を新しいSMTPコマンドとして扱います。以下の画像は、このコマンドがSMTPサーバーに受け入れられる様子を示しています。

smtp-clientに加えて、SnykはEmail MIMEとNet::SMTPのPerlパッケージにも、複数のフィールドを介したSMTPインジェクションの脆弱性があることを発見しました。このセキュリティ問題は該当するライブラリのメンテナーに報告され、これらのパッケージの最新バージョンでは修正が実装されています。
SMTPインジェクションのシナリオ2
開発者がSMTPサーバーとの通信に使用できる低レベルライブラリは、さまざまな言語のエコシステムに存在します。その一例がsmtp-channelです。
smtp-channelの場合、開発者はユーザー提供の入力を使ってメールを作成し、そのデータをストリームとしてsmtp-channelに渡すことがあります。その場合、攻撃者はヘッダーを追加し、既存のSMTPセッションを利用してメールを偽造できます。以下のコードは、この問題を示しています。
SMTPインジェクションのシナリオ3
SMTPクライアントと組み合わせて使われるメール作成ライブラリも、SMTPインジェクションの影響を受ける可能性があります。この問題の例はemail Python packageに見られます。emailパッケージは、メールメッセージを管理するためのライブラリです。この場合、mail.headerregistry.AddressフィールドにCRLF文字を渡すことで、SMTPインジェクションが可能になります。
以下の画像は、既存のRCPTコマンドを>文字で閉じ、\r\nを使って新しいcCヘッダーを挿入する様子を示しています。これが新しいRCPTコマンドとして送信されます。

この脆弱性は、Pythonセキュリティチームによって3.Xリリースで修正されています。こちらをクリックして、リリースノート全文をご確認ください。ただし、2.Xバージョンは引き続き脆弱です。
SMTPインジェクションのシナリオ4
SMTPインジェクションの脆弱性を調査する際は、ホスト名や送信元アドレスなどのフィールドでもCRLF文字が許可され、SMTPインジェクションにつながる可能性がある点に注意してください。
次の例では、FromフィールドとToフィールドからaiosmtplibに渡される値はサニタイズされています。しかし、source_addressに任意のSMTPコマンドを挿入することは依然として可能です。これを実証する概念実証(PoC)を以下に示します。
これにより、次のSMTP通信が作成されます。

その他の検討事項
場合によっては、メールライブラリがコード実行につながる経路を許すこともあります。一例がmail()関数です。ユーザー入力がmail()関数の5番目のパラメーターに渡されると、攻撃者にコード実行へ悪用される可能性があります。こちらをクリックして、この問題の例をご確認ください。
SMTPインジェクションの脆弱性は、メールインジェクションの脆弱性と混同されることがよくあります。メールインジェクションでは、攻撃者がSMTP関数を使って任意のメールを送信し、メール機能を悪用してフィッシング攻撃を行う可能性があります。一方、SMTPインジェクションではCRLF文字を使って任意のヘッダーを挿入し、それを利用してメールを偽造し、フィッシング攻撃を行うことができます。
SMTPインジェクションを防ぐ
SMTPインジェクションは、開発者やオープンソースライブラリのメンテナーに見落とされがちな脆弱性です。多くの場合、こうした問題はライブラリのメンテナーが修正する必要があります。JavaMail、PHPMailer、RubyMailなどのよく知られたライブラリの多くは、CRLF文字をサニタイズしてSMTPインジェクションをすでに防いでいます。smtp-channelのような低レベルライブラリでは、利用する開発者がユーザー入力を検証・サニタイズする責任を負います。
オープンソースコミュニティのセキュリティ向上を支援するため、Snykのセキュリティチームは以下のライブラリにおけるSMTPインジェクションの脆弱性も開示しました。
ライブラリ | 言語 | 修正済みバージョン |
|---|---|---|
| C | Masterで修正済み |
| Perl | 修正プログラムなし |
| Perl | 修正プログラムなし |
| Python | 1.1.7で修正済み |
| NodeJS | 修正プログラムなし |
CTFを始めよう
オンデマンドのバーチャル入門ワークショップを視聴して、CTFの課題の解き方を学びましょう。
