技術者ブログ
クラウド型WAF「Scutum(スキュータム)」の開発者/エンジニアによるブログです。
金床“Kanatoko”をはじめとする株式会社ビットフォレストの技術チームが、“WAFを支える技術”をテーマに幅広く、不定期に更新中!

高度なコマンドインジェクション攻撃とその対策
はじめに
ScutumはフルマネージドなWAFサービスなので、利用しているユーザさんが気づかない間にどんどん変化し、防御能力を強化しています。これらの強化された点について、これまではあまり宣伝や周知を行っていなかったのですが、今後は少しずつ、このブログでお知らせしていこうと思っています。
防御を強化しているポイントはマニアックな細かなものから重要なものまで多岐に渡ります。今回紹介するのは、我々としてはかなりインパクトがあると考えているものです。
2019年の秋に、コマンドインジェクションに対する防御能力を強化しました。コマンドインジェクションといっても普通のものではなく、比較的最近になって知られるようになった、「WAFを回避する」種類のコマンドインジェクションです。
WAFを回避するコマンドインジェクション
攻撃者とWAFの間では互いに「見つからないように」「見逃さないように」というイタチごっこが展開するのが常ですが、ことコマンドインジェクションに関しては攻撃者側が優位な状況でした。この理由はこちらのブログで紹介されているEvasion(回避)テクニックです。
多くのコマンドインジェクションはLinuxサーバのbashのシェル上で実行されますが、このときbashが持つ豊富な機能をそのまま利用することができます。代表的なのはワイルドカードを使って特定の文字列を隠蔽するパターンで、例えばwgetと書かずに下記のように記述することができます。
/u??/b*/wg??
もしWAFが「wget」という文字列を(シグネチャやルール等で)探すことでコマンドインジェクションを防御しようする場合、上記の文字列を利用されてしまうと完全にお手上げとなってしまいます。シグネチャ型のWAFでは、ほぼ100%確実に、攻撃者はWAFを回避してコマンドインジェクションをウェブサーバに送り届けることができるでしょう。
(?や*などを全て止めるようなシグネチャを書けば一応攻撃は止まりますが、それでは正常通信も止まってしまいます。)
また、例えばiLogScannerのようなツールでアクセスログから攻撃を探そうとする場合も、この手法を用いられていると見つけることができないでしょう。
当時Scutumでも防げなかった
Scutumではコマンドインジェクションの防御はシグネチャではなくベイジアンネットワークで行っていますが、「wget」などの文字列を探す、という点はシグネチャと同じように行います。そのためこの回避テクニックを用いられるとScutumの防御も回避されてしまうことになり、我々開発チーム内では「これは非常にやっかいな問題だ」と議論の対象となりました。
Scutumでは特にSQLインジェクションとコマンドインジェクションについては確実に防ぐことを目標としているので、WAFサービス提供者として非常にまずい状況という認識からスタートしました。その一方で、まだこの回避テクニックを積極的に使っている攻撃は殆ど見られない状況であったので、多少時間がかかっても根本的な対策を目指すことにしました。
苦難の末、開発に成功
この「bashの機能を使うWAF回避」に対抗するための防御機能の開発はとても難航しました。機能的にも性能的にも何度も壁が立ち塞がりましたが、都度エンジニア間での議論を繰り返しながらトラブルシューティングを行い、結果なんとか2019年の秋にデプロイすることができました。開発には予定よりも何ヶ月も多くを要しました。
普段、色々と新しい防御機能の開発を行いますが、今回は通常の開発の10倍くらい大変でした。
- 想定される色々な回避パターンを見破ることができるか、という機能面
- プログラムがクラッシュしたりせず、安定して稼働できるか、という安定性
- 十分に速く動作するか、という性能面
最終的には3つのレイヤーによって構成される防御機能となりました。

1つめがJavaで書かれているパーサ、2つめがCで書かれているパーサ、3つめがJavaで書かれているコマンドインジェクション用の解析エンジン(以前から存在する、ベイジアンネットワークを含むもの)です。
データは上から順番に処理されます。bash向けの文法解析を行うのは2つめのC言語で書かれているパーサです。このパーサは処理が重いので、全てのデータを流してしまうとパフォーマンス上の問題となります。そこで1つめのJavaで実装されたパーサで、先にある程度「当たり」をつけます。
例えば?や*等の記号がまったく含まれないような場合には、2つめのパーサに処理させる必要はありません。このような場合は3つめの解析エンジンに直接データを渡します。
そうではなくbashの機能を使ったコマンドインジェクションである可能性がある程度高そうな場合には、2つめのパーサに渡します。2つめのパーサは「bashの気持ちになって」データをパースし、wgetなどの文字列が潜んでいるかどうかを探します。
3つめの解析エンジンは文字列がコマンドインジェクションであるかどうかをいくつかの解析処理とベイジアンネットワークを使って調べ、最終的な判断を行います。
JavaとCの間でのデータの受け渡しなども色々とトラブルが続き苦労しましたが、最終的にはパフォーマンスチューニングにも成功し、いわゆる(バッチではなく)オンライン的なサービスである、我々のクラウド型WAFに載せることができる速度と安定性の確保までこぎつけました。
これによって、100%ではないかもしれませんが、先述したようなやっかいな「WAFを回避するコマンドインジェクション攻撃」からお客様のサイトを守ることができるようになっています。おそらくScutum以外の多くのWAFでは防げない攻撃だろうと思います。
今後の攻撃の見通しとシグネチャ型WAFでの対策
2020年3月の時点では、まだこの攻撃手法はそれほど広くは使われていないようです。しかし最近ではWAFが入っている環境(ウェブサーバ)は増えているため、今後攻撃者がこのような高度なテクニックを使ってくる場面は増えてくるだろうと予想しています。特に、(例えばDrupalのような)PHP製のCMSに新規に見つかるコマンドインジェクションの攻撃で使われる可能性が高いと思います。
一度このテクニックが「コマンドインジェクション攻撃では当たり前のもの」と認識されてしまったら、その後あらゆるコマンドインジェクションがこの手法を使ってくる可能性もあります。そうなってしまったら、Scutum以外の多くのWAFのような「シグネチャ型のWAF」では、コマンドインジェクションの攻撃の文字列そのもの(例えば、wgetから始まる部分)を探して止めることはできなくなります。
シグネチャ型のWAFでなんとか防御を行いたい場合、下記の2つなどを考えることになりそうです。
- 攻撃に必須となるパスの情報やパラメータ名などのクセを探して止める
- 脆弱性が存在するパラメータの値に、ホワイトリスト的にポリシーを強要する
上記のアプローチはシグネチャ型WAFでも可能ですし、CMSやミドルウェアの脆弱性では上記の1が可能なこともあるので、それなりに実用性があるかと思います。もちろん、これらの手法が使えないケースもありえます。
まとめ
今回は私達が2019年に大量の開発リソースをつぎ込んで実現した、高度なコマンドインジェクションに対する防御機能を紹介しました。
Scutumは10年前にサービスを開始した当初はシグネチャ型でしたが、現在では大きく中身を変え、ベイジアンネットワークや今回紹介したような独自のパーサー等の実装等を多く使い、より高度な攻撃への対応を行っています。Scutumと一般的なシグネチャ型WAFとの違いを示す一例として知っていただければ幸いです。