Slide 1

Slide 1 text

/^ReDoSの色々$/

Slide 2

Slide 2 text

今日話すこと ● 正規表現のおさらいとReDoSの仕組み ● ReDoSの事例 ● ReDoSのスキャンツールの紹介 ● ReDoSの予防 ● まとめ 間違いの指摘や質問は @kawada_syogo225 までお願いします!

Slide 3

Slide 3 text

/^自己紹介$/ ● Hazy(@kawada_syogo225) ● WAFの開発・運用・監視と Webの脆弱性のリサーチ ● ^(?:珈琲|カメラ|カフェ巡り|.*)$

Slide 4

Slide 4 text

正規表現のおさらい

Slide 5

Slide 5 text

連接・選択・繰り返し ● 連接 ○ 単純に隣り合う文字を表す。ただの文字列も正規表現。 ○ 例:abcd、hogefuga、regex ● 選択 ○ 並べた正規表現のどれかを選ぶ。 ○ 例:Java(S|s)script ← JavaScript、または、Javascriptを表す。 ● 繰り返し ○ ある正規表現を繰り返し適用する。 ○ 例:(0|1)* ← 0か1が繰り返し出現する文字列にマッチする。つまり、バイナリ。

Slide 6

Slide 6 text

文字クラス・特殊文字・キャプチャ ● 文字クラス ○ アルファベット、数字などのまとまった文字達を表す。 ○ 例:[a-zA-Z]、[0-9]、[02468] ● メタ文字 ○ 数字や非単語構成文字(記号)や行頭、行末などを表す特別な記号 ○ 例:^\s++$ ← 空白のみで構成される行 ● キャプチャ ○ 正規表現のグループ化、演算の優先度の変更、部分的な取り出しに利用できる ○ 例:YYYY/MM/DDをYYYY-MM-DDに変換する正規表現と JavaScriptコード。                 > '2020/07/15'.replace(/(\d\d\d\d)\/(\d\d)\/(\d\d)/, '$1-$2-$3'); '2020-07-15' >

Slide 7

Slide 7 text

色々なJavaScriptにマッチする正規表現

Slide 8

Slide 8 text

繰り返しについてもう少し ● スター演算子 ○ 0回以上の繰り返しを表す ○ 例:\d* ← 空文字か任意個の数字列にマッチ ● プラス演算子 ○ 1回以上の繰り返しを表す ○ 例:\d+ ← 一つの数字か任意個の数字列にマッチ ● 疑問符演算子 ○ 0回か1回の繰り返しを表す ○ 例:\d? ← 空文字か1個の数字にマッチ ● 範囲量指定子 ○ n~m回の繰り返しを表す。( n < m) ○ 例:\d{2,10} ← 2個以上、10個以下の数字の繰り返しにマッチ

Slide 9

Slide 9 text

問題1 ● 文字列「1234」に対して下の正規表現を実行した場合、グループ1と グループ2にキャプチャされる文字列は?

Slide 10

Slide 10 text

答え ● グループ1は「1234」 ● グループ2は空

Slide 11

Slide 11 text

問題2 ● 文字列「1234」に対して下の正規表現を実行した場合、グループ1と グループ2にキャプチャされる文字列は?

Slide 12

Slide 12 text

答え ● グループ1は空 ● グループ2は「1234」

Slide 13

Slide 13 text

(欲張りな|控え目)な量指定子 ● 欲張りな量指定子 ○ 可能な限り長くマッチしようとする。「 *」、「+」、「?」、「{n,m}」が欲張る。 ○ 例:(\d*)(\d*) ← 左のグループが全ての数字を消費する。 ● 控え目な量指定子 ○ 可能な限り短くマッチしようとする。「 *?」、「+?」、「??」、「{n,m}?」が控え目。 ○ 例:(\d*?)(\d+) ← 左のグループは控え目なため、右のグループが欲張る。

Slide 14

Slide 14 text

問題 ● 文字列「xxxxxxxxxxy」に対して下の正規表現を実行した場合、どのような 動きになるか?

Slide 15

Slide 15 text

答え 1. 最初の「x+」が10個の(全て)の「x」にマッチする。 2. 2番目の「x+」が失敗する。 3. 最初の「x+」が「x」を一つ手放して、9個の「x」にマッチする。 4. 2番目の「x」が1個の「x」にマッチする。 5. グループに繰り返し「+」がついているが、これ以上マッチする部分が ない為、1回のマッチで終了する。 6. 「y」が「y」とマッチする。

Slide 16

Slide 16 text

マッチ対象に「y」がなかったら? 1. 最初の「x+」が2つ「x」を手放して8つの「x」にマッチ。 2. 2番目の「x+」が2個の「x」にマッチ。 3. グループの繰り返しに失敗。 4. yのマッチに失敗。 5. 2番目の「x+」が「x」を手放して1つの「x」にマッチ。 6. グループの繰り返しに失敗。 7. yのマッチに失敗。 8. 最初の「x+」が3つの「x」を手放して8つの「x」にマッチ。 9. 2番目の「x」が3つの「x」にマッチ。 10. 以下、繰り返し。 文章で説明するとめんどい。。。

Slide 17

Slide 17 text

regex101で正規表現の動きを追ってみる https://regex101.com/

Slide 18

Slide 18 text

バックトラック ● 正規表現はマッチするまであらゆる組み合わせを試行する。 ● 直前の例でみたような、後戻りして再試行するような動作を バックトラックという。 ● (x+x+)+ だけでもマッチ組み合わせが 大量に存在するが、その全てを 試行してもマッチすることはない。 ○ (9,1)、(8,2)、(8,1)、(5,5)、(1,9) ○ ((7,1),(1,1)) ○ ((1,1),(1,1),(1,1),(1,1),(1,1))

Slide 19

Slide 19 text

ReDoSとは ● ReDoSとは入力に対し、組み合わせやバックトラックによる試行の回数が 増大することにより計算リソースが消費され、可用性が失われる脆弱性。 ● 入力された文字列の長さに対して O(2n) や O(2^n) で計算量が増加する。

Slide 20

Slide 20 text

ReDoSの事例

Slide 21

Slide 21 text

Stack Overflowの例 ● 2016年7月20日にReDoSで34分の停止が発生。 ● 「-- play happy sound for player to enjoy」の後に2万個のスペースが続く 投稿が原因でDoSった。 ● 原因の特定に10分、コードの修正に14分、修正の適用に10分かかった らしい。。。対応、早くない?! ● 行頭と行末のスペースを除去するために「^[\s\u200c]+|[\s\u200c]+$」 という正規表現を使用していた。 ● https://stackstatus.net/post/147710624694/outage-postmortem-july-20-2016

Slide 22

Slide 22 text

Cloudfrareの例 ● 2019年7月2日に27分の停止が発生。 ● XSSを検知すためのWAFルール(正規表現)で過度なバックトラックが 発生しCPUリソースが枯渇したことが原因。 ● 詳細なレポートは下記を参照。 ○ https://blog.cloudflare.com/details-of-the-cloudflare-outage-on-july-2-2019/

Slide 23

Slide 23 text

ModSeucrity CRSの例 ● ModSecurityはOWASPが提供するOSSなWAF。 ● CRS(Core Rule Set)はOWASPが提供するOSSなWAFルール。 ● 2019年にCRSのReDoSが5つ見つかっている。 ○ https://coreruleset.org/20190425/regular-expression-dos-weaknesses-in-crs/

Slide 24

Slide 24 text

Expressの例 ● 2016年にExpress が依存する nagotiator という Accept-Language ヘッダー を扱うライブラリでReDoSが見つかった。 ● 修正コミット ○ https://medium.com/node-security/regular-expression-denial-of-service-affecting-express-js-9c397c164c43 ● 以下のようなコードを書いてるとDoSる。 app.get('/', (req, res) => { if (req.acceptsLanguages ('ja')) { res.send('ピエン'); } else { res.send });

Slide 25

Slide 25 text

ReDoSの検出ツール

Slide 26

Slide 26 text

w3af ● 右のようなReDoSを引き起こしそうな文字列を送信して RTTが通常より4倍遅くなればReDoSと判定。 ● 「a」や「1」は10個ずつ増やしていき、最終的に80個 まで試す。 ● ソースコードは以下。 ○ https://github.com/andresriancho/w3af/blob/master/w3af/plugins/audit/redos.py aaaaaaaaaaX! aaaaaaaaaaaaaaaaaaaaX! aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaX! a@a.aaaaaaaaaaX! a@a.aaaaaaaaaaaaaaaaaaaaX! a@a.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaX! 11111111119! 111111111111111111119! 1111111111111111111111111111119!

Slide 27

Slide 27 text

Wisdom/RegEx-DoS ● esprimaというES7まで対応したJavaScriptのパーサを使って 正規表現リテラルを摘出。 ● new RegExp(‘aaaa’) や動的に正規表現を組み立てている場合は 摘出できない。 ● ReDoSの判定自体は safe-regex に丸投げ。 ● リポジトリ ○ https://github.com/Wisdom/RegEx-DoS

Slide 28

Slide 28 text

safe-regex ● regexp-tree というライブラリを使って正規表現をASTに変換して、 ASTを再帰的に走査して、一番ネストした * 演算子を探す。 ● ネストの深さが「1」以上、または、* 演算子が25個より多く 使用されている場合は危険と判定。 ● 「星の高さ」という概念を基にしている。 ● 例えば、(b|aa*b)*aa* の星の高さは 2 。 ● 入れ子になった * 演算子は組み合わせ爆発を誘発しやすい。 ● リポジトリ ○ https://github.com/davisjam/safe-regex/

Slide 29

Slide 29 text

davisjam/vuln-regex-detector ● 複数の検出器によるチェックを行い、指定した言語の正規表現エンジンにて テストを行うことで検知の精度を高めている。 ● 正規表現エンジンの機能や独自の最適化によって特定のエンジンのみ処理時間が 悪化するケースがある為、このような方針を採用している。 ● Githubリポジトリを指定してのスキャンが可能で、ソースコードからの正規表現の 摘出はJavaScriptとPythonしているが、動的に正規表現を組み立てている場合は 対応できない。 ● 正規表現の摘出、ReDoSの検知、テストがそれぞれ別のスクリプトに なっているため、単体で利用可能。 ● リポジトリ ○ https://github.com/davisjam/vuln-regex-detector/

Slide 30

Slide 30 text

google/re2 ● Googleが開発した正規表現エンジン。 ● ユーザーからの信頼できない入力に対してリスクなく正規表現を扱えるよう 設計・実装されている。 ● 入力された文字列に対して線形時間でマッチングを行えることを 保証している。 ● その代わりに、他の正規表現エンジンと比べて機能が制限されている。 ○ https://github.com/google/re2/wiki/WhyRE2 ● 先述のCloudflareの事例で、事後対応としてRE2の採用が上がっている。

Slide 31

Slide 31 text

ReDoSの予防

Slide 32

Slide 32 text

正規表現のパフォーマンスに気を付ける 以下のような基本的なお作法を守る。 1. 量指定子を入れ子にしない 2. 曖昧さを取り除く 3. 控え目な量指定子や強欲な量指定子を使ってバックトラックを抑制する。 場合によっては、独自にロジックを書いた方が正規表現より高速な場合もある。

Slide 33

Slide 33 text

ツールやエンジンのオプションを活用する ● ReDoSのチェックは時間がかかるため、チェック済みの正規表現を ライブラリとして持っておくのも良いかも。 ● エンジンによってはバックトラックの上限が設定できたりするので、 確認する。 ● PHPは「pcre.backtrack_limit」で可能。 ● ModSecurityは「SecPcreMatchLimit/SecPcreMatchLimitRecursion」 ● .NETはRegexコンストラクタの第三引数でタイムアウトを設定可能。

Slide 34

Slide 34 text

まとめ ● ReDoSは正規表現のマッチングの計算量の増大により引き起こされる。 ● ReDoSはビジネスロジック、ライブラリ、ミドルボックス等至る所に 潜んでいる。 ● WAFでの防御ができない。むしろ、WAFが攻撃対象になる。 ● 現時点では脆弱性診断が難しい。 ● 予防のために、パフォーマンスに関する作法を学ぼう。 ● ツールを使用して疑わしい正規表現をチェックする。 ● 正規表現エンジンや言語の設定で計算リソースを制限する。

Slide 35

Slide 35 text

追記:ReDoSを使ったサイドチャネル攻撃 ● ReDoSはサイドチャネルにも使えるよってツッコミがあったので、 参考資料を載せておきます。 ● https://speakerdeck.com/lmt_swallow/revisiting-redos-a-rough-idea-of-data-exfiltration-by-redos-and-side-channel-techniques

Slide 36

Slide 36 text

参考文献 ● 正規表現技術入門 ー最新エンジン実装と理論的背景ー ○ https://gihyo.jp/book/2015/978-4-7741-7270-5 ● 詳説 正規表現 ○ https://gihyo.jp/book/2015/978-4-7741-7270-5 ● 正規表現入門 星の高さを求めて ○ https://www.slideshare.net/sinya8282/ss-32629428 ● Runaway Regular Expressions: Catastrophic Backtracking ○ https://www.regular-expressions.info/catastrophic.html ● The Impact of Regular Expression Denial of Service (ReDoS) in Practice ○ https://medium.com/bugbountywriteup/introduction-987fdc4c7b0 ● The Regular Expression Denial of Service (ReDoS) cheat-sheet ○ https://levelup.gitconnected.com/the-regular-expression-denial-of-service-redos-cheat-sheet-a78d0ed7d865 ● A Sense of Time for JavaScript and Node.js ○ https://medium.com/@davisjam/a-sense-of-time-for-javascript-and-node-js-68c9114f5d48 ● 正規表現とセキュリティ / Regular Expressions and Their Security-Related Aspects ○ https://speakerdeck.com/lmt_swallow/regular-expressions-and-their-security-related-aspects