技術者ブログ
クラウド型WAF「Scutum(スキュータム)」の開発者/エンジニアによるブログです。
金床“Kanatoko”をはじめとする株式会社ビットフォレストの技術チームが、“WAFを支える技術”をテーマに幅広く、不定期に更新中!
2017年のJoomla LDAPインジェクション脆弱性について
はじめに
2017年にJoomlaに見つかったLDAPインジェクションの脆弱性、CVE-2017-14596について調査をしたので、簡単にブログにまとめたいと思います。
怪しい動画
脆弱性があったのは./plugins/authentication/ldap/ldap.phpの中です。ユーザ入力をそのままエスケープ処理することなくLDAPのSearchに渡してしまっているという、典型的なものでした。
これを発見したのはRIPSTechという企業のようで、こちらのブログ記事が一時情報源となっているようです。この記事からはこの脆弱性を突くデモ動画へのリンクが存在しているのですが、この動画は非常に興味深い(?)です。
一見するとLDAPインジェクションの脆弱性をうまく突いて、管理者のパスワードを抜いているようであり、「あちゃ〜、Joomlaダメだこりゃ...」と思います。しかしよく考えると、かなり脚色されている部分があることに気づくでしょう。
まず、パスワード入力欄が伏せ字になっておらず、通常のテキスト表示になっています。また、LDAPインジェクション攻撃においてパスワードを一文字づつ総当り攻撃しているようですが、通常LDAPサーバにおいてパスワードはクリアテキストで格納されているわけではないので、userPassword=a*というような検索をしても意味を成さず、攻撃は成立しないはずです。
極めつけに、最後には「User credentials found! admin: secretpassword」と、攻撃によって見つけたらしい管理者のパスワードがJoomlaの通知ダイアログに出てきます。通常CMSが「パスワードはこれです!」とログイン画面に出すわけはなく、これが完全な脚色であることは明らかです。セキュリティ企業が自ら発見した脆弱性を派手に売り込みたいために色々やりたくなる気持ちはわかりますが、これはかなりやりすぎの部類に入るでしょう。
脆弱性の分析
さて冷静にJoomlaのコードを読んでみたところ、たしかにユーザ入力をそのままLDAPの検索に突っ込んでしまっているようです。つまり典型的なLDAPインジェクションである、という事については嘘はなさそうです。しかし先述したようにuserPassword=a*という形での攻撃は現実的ではありません。そこで私は「本当に現実的には、どのような攻撃が可能だろうか?」という視点で考えてみました。
ログイン部分のLDAPインジェクションの脆弱性なので、「(攻撃者がパスワードを知らない)ユーザに成りすましてログインする」ことが基本的な攻撃ベクターであると仮定しました。
コードを読みつつ、OpenLDAPとJoomla3.7.4の環境を作って攻撃してみましたが、結局誰かに成りすましてログインすることはできませんでした。私のLDAP注入力が足りていない可能性もかなりありそうですが、「これは攻撃できないのでは?」と判断した具体的な理由は下記になります。
まず「SearchしてからBind」の方式でプラグインが設定されている場合、たしかにSearchに対してインジェクションが可能なのですが、その直後にパスワード入力欄の値を使ったBindが行われ、このBindが失敗すればログインできません。つまり正しいパスワードを知らないと攻撃できません。(具体的なコードはここです。)
次にBind方式でプラグインが設定されている場合、まず最初にパスワード入力欄の値を使ったBindが行われ、ここで失敗すればインジェクションの対象となるSearchは実行されません。つまり、やはり正しいパスワードを知らないと攻撃はできないことになります。(こちらのコードはここです。)
ということで、LDAPインジェクションではありつつも、現実的には被害が出ないようなバグなのではないかと考えています。もしこの脆弱性について調査された方がいらっしゃいましたら、情報を共有させていただければとてもありがたいです。
WAFサービス開発者の視点から
私はWAFサービス開発者なので、RIPSTechの記事を読んだ際にもっとも注目したのは、具体的にどんな文字列が攻撃で使われるのか、WAFで見つけやすいかどうか、ということです。
非常に興味深いことに
XXX;(&(uid=Admin)(userPassword=A*))
のようにXXX;という文字列に続いてLDAPフィルタの文字列が登場しています。通常、LDAPインジェクションというのはフィルタの中に文字列を注入する(上記で言えば「Admin」の部分への攻撃となる)ので、このようにフィルタ全体がきれいに攻撃文字列に入ってくることはありません。WAFで最も警戒すべきなのは
Admin)(userPassword=A*)
のような「LDAPフィルタの部分文字列」なのではないかと考えています。
そのため、このJoomlaの例の「XXX;」とは何なのだろうか?また、フィルタ全体が入っている文字列で攻撃が成立するのはなぜか?というのが私が感じた疑問でした。
Joomlaのldap関連の処理を行っているコードを読んでみたところ、この理由がわかりました。
LdapClientのsimple_searchという関数において、セミコロンで文字列を分割し、それぞれに対してLDAPのSearchを行っている実装となっています。このログイン部分でこの処理が行われる必要性はまったくないので、この部分も危険なバグだと言えるでしょう。例えばtomというユーザがログインしたい場合、ユーザIDにdummy;uid=tomという文字列を入力してもログインすることが可能です(パスワードは正しいものが必要です)。
この場合
(uid=dummy)
という検索と、
(uid=tom)
という検索が行われます。セミコロンで分割した後のそれぞれの文字列の前後にカッコは追加されるので、実際には
XXX;(&(uid=Admin)(userPassword=A*))
だと攻撃にならず、
XXX;&(uid=Admin)(userPassword=A*)
が攻撃になるはずです。F5という会社のブログ記事でもこの脆弱性が取り上げられていますが、攻撃リクエストの例は上記の攻撃として成立しない方のパターンとなっています。おそらく実際にはJoomla環境を作って検証せずに、RIPSTech社の記事のパターンをコピペしているだけだと思います。意外と皆さん、雑な感じの調査結果をインターネット上で公表しているのだな、と驚きました。
まとめ
JoomlaのCVE-2017-14596はLDAPインジェクションの典型的なパターンではありつつも、実際にはそれほど深刻な被害が出るものではなかった可能性がありそうです。ただし私の調査もそれほど完璧であるという自信はないので、万が一誤りがあった場合にはご指摘いただければ幸いです。
この記事をまとめたのは私(金床)ですが、調査段階ではビットフォレストのScutumチームのエンジニアである野村真作・野村健太郎の両名に助けて頂きました。