Skip to main content

Snyking In:msパッケージにおける正規表現によるサービス拒否の脆弱性を悪用する

Snyking in small

2019年3月13日

0 分で読めます

「Snyking In」エクスプロイトシリーズの新しいエピソードへようこそ。前回は、stライブラリにおけるディレクトリトラバーサル脆弱性の悪用について取り上げました。今回は、正規表現によるサービス拒否の脆弱性を取り上げ、悪用方法と、データやシステムにもたらす潜在的なリスクを解説します。

この種の脆弱性をアプリケーション内で見つけて修正する方法もご紹介します。さっそく、エクスプロイトの動画をご覧ください。その後、正規表現によるサービス拒否の脆弱性について詳しく解説します。

Snyking In - Regular Expression Denial of Service vulnerability exploit in the ms package

正規表現によるサービス拒否

サービス拒否(DoS)とは、正規のユーザーがシステムを利用できない状態にすることを目的とした攻撃の総称です。DoS攻撃にはさまざまな種類があります。多数のマシンから大量のトラフィックを発生させてシステムのネットワークを飽和させる分散型サービス拒否(DDoS)攻撃から、細工したリクエストを送信してシステムをクラッシュさせたり、処理に不釣り合いな時間をかけさせたりする攻撃まであります。

正規表現によるサービス拒否(ReDoS)は、DoS攻撃の一種です。正規表現は非常に強力ですが、直感的に扱えるとは限りません。結果として、攻撃者にサイトをダウンさせる隙を与えてしまうことがあります。

Snykが発表した最新のオープンソースセキュリティの現状レポートによると、正規表現によるサービス拒否の脆弱性の公開件数は、わずか1年間で143%増加しています。

「正規表現によるサービス拒否(ReDoS)の脆弱性公開が増加」と題された折れ線グラフ。公開件数は2016年の14件から、2017年の30件、そして72件へと増加している。

致命的なバックトラッキング

次の正規表現を見てみましょう。

regex = /A(B|C+)+D/

この正規表現は、次の条件を満たす文字列にマッチします。

  • A:文字列は文字Aで始まる必要があります

  • (B|C+)+ 次に、文字Aの後に文字Bまたは文字Cが1回以上続く(+は1回以上の繰り返しにマッチします)。この部分の末尾にある+は、この部分全体が1回以上繰り返されることを示します。

  • D 最後に、この文字列の部分がDで終わることを確認する

この正規表現は、ABBD、ABCCCCD、ABCBCCCD、ACCCCCDなどの入力にマッチします。通常、有効な文字列であれば、正規表現エンジンがマッチを見つけるまでにそれほど時間はかかりません。

$ time node -e '/A(B|C+)+D/.test("ACCCCCCCCCCCCCCCCCCCCCCCCCCCCD")'
0.04s user 0.01s system 95% cpu 0.052 total

$ time node -e '/A(B|C+)+D/.test("ACCCCCCCCCCCCCCCCCCCCCCCCCCCCX")'
1.79s user 0.02s system 99% cpu 1.812 total

30文字の文字列に対するテスト全体は、約52ミリ秒で完了します。しかし、無効な文字列を渡すと、テストの完了までにほぼ2秒かかり、有効な文字列のテストに比べて10倍以上の時間がかかります。この大きな差は、正規表現の評価方法に起因します。

ほとんどの正規表現エンジンは、細かな違いはあるものの、よく似た動作をします。まず、現在の文字にマッチする最初の方法を選び、次の文字へ進みます。そこでマッチに失敗すると、前の文字を処理する別の方法があったかどうかを確認するため、バックトラックします。文字列が最終的にマッチしないと判明するまで試行を続け、複数の文字に対して正規表現上の有効な経路が複数ある場合、バックトラックの回数が膨大になることがあります。これが、致命的なバックトラッキングと呼ばれる現象です。

msのエクスプロイト

次のコマンドは、Snyk GoofのToDoアプリケーションにToDo項目を追加します。コンテンツ内のin 20 minutesという部分は、時間を表す文字列として正規表現エンジンにマッチします。たとえば、アプリケーションのビジネスロジックでリマインダーやアラートの作成に利用できます。

$ echo 'content=Call mom in 20 minutes' | http --form http://localhost:3001/create -v

次のようにして、ToDo項目の長さを総当たりで調べられます。このコマンドは、分数として5を60000個出力しますが、パターンにマッチするため、すぐに処理が返ってきます。

$ echo 'content=Buy milk in '`printf %.0s5 {1..60000}`' minutes' | http --form http://localhost:3001/create -v

サービス拒否を引き起こすには、致命的なバックトラッキングが発生する文字列を渡します。前の例のような長い入力文字列が必要ですが、それだけでなく、正規表現エンジンがパターンにマッチしないようにし、失敗するまであらゆる可能性を試してバックトラックさせる必要があります。これによって、狙いどおり処理の遅延、つまりサービス拒否が発生します。そのためには、コンテンツ内のminutesを、たとえばminuteaに変更します。これは正規表現エンジンがマッチしないパターンです。次のエントリー追加コマンドを実行すると、約10~15秒間サービス拒否が発生します。5を600000個渡すと、サーバーがリクエストを処理し終えるまで、残りの一日中待つことになりかねません。

$ echo 'content=Buy milk in '`printf %.0s5 {1..60000}`' minutea' | http --form http://localhost:3001/create -v

msなどのサードパーティライブラリに脆弱性がないかアプリケーションをテストするには、Snykを無料でお試しください。

CTFを始めよう

オンデマンドのバーチャル入門ワークショップを視聴して、CTFの課題の解き方を学びましょう。

カテゴリー: