AWS WAFをバイパスしてSQLインジェクション攻撃をしてみる
最近はAWS WAFを触っています。こういう防御ツールは、やはり攻撃をどれぐらい防いでくれるか気になります。AWS WAFの場合、SQLインジェクション系の脆弱性を探ってくれるsqlmapをかけたところ、攻撃をブロックしてくれたという記事があります。
記事を読んだり自分でちょっと試したりして、ちゃんとSQLインジェクション攻撃を防いでくれるんだーと思っていました。が、つい先日WAFをバイパスしてSQLインジェクション攻撃をするテクニックがあることを知りました。例えばOWASPのこのページには、そういうテクニックがいくつか紹介されています。
こうなると気になるのは、AWS WAFに対してWAFバイパスのテクニックを使うとどうなるかです。というわけで、実際に試してみました。
単純にSQLインジェクションしてみる
まずは、AWS WAFがないときにSQLインジェクションができること、また、AWS WAFを設定すると同じ方法ではSQLインジェクションができなくなることを確認します。
今回は雑に書いたPython製Webアプリを使います(ソース)。id
とpassword
パラメータを付けてGET /login
すると、DBに該当するユーザがいるかを確認し、いれば200 OK、いなければ401 Unauthorizedレスポンスを返します。内部では次のようにして作られたクエリが実行されています。
"select id from user where id='%s' and pass='%s'" % (username, password)
今回はこのWebアプリのSQLインジェクション脆弱性を突いて、認証を不正にパスしてみます。AWS WAFがない場合、次のようなリクエストでできます。
http://example.com/login?id=admin&password=' or 1=1 --
内部的には、次のようなSQLクエリが発行され、200 OKが返ります。
select id from user where id='admin' and pass='' or 1=1 -- '
次に、AWS WAFを設定します。設定はこちらの記事を参考にしました。
AWS WAFを設定して同じリクエストを送ってみると、403 Forbiddenレスポンスが返ります。WAFのログを見ると、リクエストがブロックされたことがわかります。
WAFバイパスのテクニックをつかってみる1
OWASPのページには数種類のテクニックが紹介されています。まずはNormalization Method
を試してみます。/**/
などのコメントを適宜はさむ方法のようです。
いくつかの箇所にこの/**/
を挟んでみたのですが、どれもブロックされました。例えば次のようなクエリを送ってみました。
http://example.com/login?id=admin&password=' /**/or/**/ 1/**/=/**/1 --
似た方法として、クエリの一部を/*!
と*/
で挟む方法があるようです。MySQLだと/*!
と*/
で囲まれた文字列がそのまま実行される性質を利用しています(MySQLのドキュメント)。次のようなクエリです。
http://example.com/login?id=admin&password=' /*!or 1=1*/ --
ただこれもブロックされました。手強いですね。
WAFバイパスのテクニックをつかってみる2
次に紹介されていたのはHTTP Parameter Pollutionという方法とHTTP Parameter Fragmentationという方法です。どちらも、SQLインジェクションを狙った文字列を、複数のパラメータに分けて送る方法です。
HTTP Parameter Pollutionは、例えば/login?password=A&password=B
のように、同じパラメータが複数回登場するクエリを送る方法です。サーバ側がこれをA,B
のような文字列にして処理してくれれば、SQLインジェクションのための文字列をAとBに分けて送ることができます。
ただしこの方法は、サーバ側がA,B
という文字列を作ってくれなければ、使えません。今回用意した環境だと、最後に出現した値のみが使われ、B
という文字列しか作られません。なのでHTTP Parameter Pollutionの利用は今回はスキップします。
HTTP Parameter Fragmentationはもっと単純で、複数の異なるパラメータが結合した時に、SQLインジェクションのための文字列ができるようにする方法です。次のようなリクエストを送ります。
http://example.com/login?id=admin'/*&password=*/ --
内部的には、次のようなSQLクエリが発行されます。
select id from user where id='admin'/*' and pass='*/ -- '
このクエリはWAFをバイパスして、200 OKが返りました。キタ!
WAFバイパスのテクニックをつかってみる3
最後に紹介されていたのは、SQLインジェクションを狙った文字列の一部を、同じ意味をもつ他の文字列に置き換える方法です。1=1
を2<3
に置き換えて、次のようなリクエストを送ってみます。
http://example.com/login?id=admin&password=' or 2<3 --
これはブロックされました。他にも以下の文字列等を試してみましたが、どれもブロックされました。
http://example.com/login?id=admin&password=' or 2<3 -- http://example.com/login?id=admin&password=' or 1 -- http://example.com/login?id=admin&password=' or 'a'='a' -- http://example.com/login?id=admin&password=' or 'a'<>'b' -- http://example.com/login?id=admin&password=' or 1+1<3 --
まとめ
AWS WAFをバイパスしてSQLインジェクションしてみました。とりあえずHTTP Parameter Fragmentationという方法なら、実際にAWS WAFをバイパスできることがわかりました。WAFはかなりブロックしてくれて助かりますが、やはり万能ではないので、Webアプリの実装にセキュリティ対策を入れ込むのは大事だなと思いました。