Midnight Sun CTF 2018 Finals参加記

Midnight Sun CTF 2018 Finalsに参加した感想と記録です。

egghunter_20180626165422

まえがき

こんにちは、技術部の松隈です。

ネットワーク診断やバイナリ系のものが関わる診断に携わっています。すこしだけバイナリ系のexploitationに興味があります。
週末には業務兼趣味としてCTF(Capture The Flag、コンピュータセキュリティに関連するスキルの競技で、問題を解いて"flag"というキーワードを手に入れることで得点を得ます)に参加したりしていなかったりします。
競技ではWeb、バイナリ、暗号など様々なジャンルに渡って出題されますが、私は主にPwnというバイナリの脆弱性を攻撃するジャンルを担当しています。

先日、いつものようにCTFの参加に関する出張申請をしたためていたところ、

いやさあ、、きみ何度も海外CTF参加してるし、、、さすがにそろそろブログ記事とかでアウトプットとかね…(。ŏ﹏ŏ)」

というお気持ちを頂戴してしまいました。
たしかにここ数年を振り返ってみると、幾度となく海外CTFの参加を出張にしてもらっていたにもかかわらず、ブログでのアウトプットはゼロでした。記事のネタがあるのなら書くべきですね。。

…というわけで、肩書きが「技術部 穀潰し」になってしまう前に先日参加したMidnight Sun CTF 2018 Finalsの参加記を投稿したいと思います。

Midnight Sun CTF 2018 Finals

6月16日(現地時間)から2日間、スウェーデン/ストックホルムで 開催された Midnight Sun CTF 2018 Finals に、チーム TokyoWesterns として参加しました。

egghunter_20180626165452

学生の部と社会人の部の二部構成となっており(ただし問題は共通)、わたしたちは社会人枠として出場しました。

最終的なランキングは下のようになります。TokyoWesterns は 🥉3 位 でした。
日本から参加していた scryptos は、惜しくも 🏆1 位 の LC↯BC と僅差で 🥈2 位 でした。終了1分前の逆転劇は本当にすごかったです…!

Place Team Name Score Student Team?
1 LC↯BC 6687
2 scryptos 6660
3 TokyoWesterns 5239
4 p4 4874
5 Made In MIM 3260
6 RedRocket 2582
7 dcua 2517
8 GYG 2244
9 /upb/hack 2098
10 Perfect Blue 1892
11 KTHCTF0x1 1214
12 showeremoji 1005
13 VSS 883
14 LiUhack 870

解いた問題の解説

egghunter_20180626165609
egghunter_20180626165701

競技中にバグを探すなどの手伝いをしたものが以下の通りで、

  • Gissa (Pwn: 468pts)
  • Flitbip (Pwn: 485pts)
  • Onion browser (Pwn: 468pts)

個人としては次の問題が解けました。

  • Onion browser (Pwn: 468pts)

Onion browser (Pwn: 468pts)

まずは、CTFで問題を解くときの基本の問題文です(Pwnの場合はあまり関係ないですが。。)

Onion Browser is a minimal and super secure browser! Fortunately, our boys at FRA/NSA left a backdoor in the source code. If only we knew someone skilled enough to exploit it :(

Solves: 4

Service: nc pwn.midnightsunctf.se 31337 | nc 34.254.34.57 31337

Download: onionbrowser.tar.gz

Author: likvidera

ミニマルなブラウザをexploitするチャレンジみたいですね。

詳細なwrite upは別の記事で載せるため(あくまでも「つもり」です、強靭な気力があればいつかきっと。。。)、ここでは簡単な流れの解説のみを行います。

概要
Onion browserは、JavaScriptエンジン(以下、「JSエンジン」とします)に埋め込まれた脆弱性をexploitするタイプのPwn問題(以下、「js pwn」と呼びます)です。js pwnの問題は、ここ最近の大きめのCTFではちょくちょく出題されている形式で、大抵は主要ブラウザの大規模JSエンジン(V8やSpiderMonkeyなど)がターゲットになります。今回のターゲットはWebブラウザを模したx86 ELFバイナリで、JSエンジンにはduktapeが採用されていました。

このバイナリの挙動は、

  1. ユーザーが指定したURLに対してlibcurlでアクセスしてリソースを取得
  2. アクセス先のURLの拡張子によって次の処理を行う
    • .htmlの場合: 取得したリソースをレンダリングをせずに表示
    • .jsの場合: 取得したリソースをduktapeで実行した結果を表示
    • それ以外の場合: 対応していない旨のエラーを表示

というものでした。

つぎに、ターゲットのバイナリに施されている保護機構を示します。

RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH      FORTIFY Fortified Fortifiable  FILE
No RELRO        Canary found      NX enabled    No PIE          No RPATH   No RUNPATH   Yes     0               21      (censored)

続いて、問題の脆弱性の解析とそのexploitについて軽く解説します。

解説
js pwnの問題には、脆弱性を埋め込むためのパッチファイルが添付されています。題意としては「脆弱性を作り込むパッチが当たった状態のJSエンジンをexploitしなさい」というものです。Onion browserにも次のようなパッチが用意されていました。

diff --git a/duktape/src-input/duk_bi_buffer.c b/duktape_patch/src-input/duk_bi_buffer.c
index 9b2f6d7..ac78ea2 100644
--- a/duktape/src-input/duk_bi_buffer.c
+++ b/duktape_patch/src-input/duk_bi_buffer.c
@@ -1591,6 +1591,24 @@ DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_copy(duk_hthread *thr) {
*  See test-bi-typedarray-proto-set.js.
*/

+#if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
+DUK_INTERNAL duk_ret_t duk_bi_typedarray_midnight(duk_hthread *thr) {
+       duk_hbufobj *h_this;
+       h_this = duk__require_bufobj_this(thr);
+       DUK_ASSERT(h_this != NULL);
+       DUK_ASSERT_HBUFOBJ_VALID(h_this);
+
+       if (h_this->buf == NULL) {
+               DUK_DDD(DUK_DDDPRINT("source neutered, skip copy"));
+               return 0;
+       }
+       h_this->length = 31337;
+       duk_hbuffer * buf = h_this->buf;
+       buf->size = 31337;
+       return 0;
+}
+#endif
+
#if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
DUK_INTERNAL duk_ret_t duk_bi_typedarray_set(duk_hthread *thr) {
        duk_hbufobj *h_this;

diff --git a/duktape/src-input/builtins.yaml b/duktape_patch/src-input/builtins.yaml
index d4c4225..0e79be8 100644
--- a/duktape/src-input/builtins.yaml
+++ b/duktape_patch/src-input/builtins.yaml
@@ -974,6 +974,7 @@ objects:
        id: bi_array_constructor
        attributes: "wc"
        present_if: DUK_USE_ARRAY_BUILTIN
+
    - key: "toString"
        value:
        type: function
@@ -4071,6 +4072,12 @@ objects:
        typedarray: true
        es6: true
        present_if: DUK_USE_BUFFEROBJECT_SUPPORT
+      - key: "midnight"
+        value:
+          type: function
+          native: duk_bi_typedarray_midnight
+          length: 0
+        present_if: DUK_USE_BUFFEROBJECT_SUPPORT
    - key: "subarray"
        value:
        type: function

さて、このパッチを読むとターゲットのduktapeでは、

  • TypedArrayに対してmidnight()というメソッドが実装されている
    • 対象のTypedArrayのsize、これに対応するArrayBufferlengthフィールドをそれぞれ31337に設定する

ということがわかります。
ちなみにTypedArrayによる値の読み書きは、内部的に対象のTypedArrayに対応するArrayBufferのindex番目に対してta.BYTES_PER_ELEMENT分の読み書きを行います。

midnight()を呼び出した場合、実際に確保されているサイズはそのままの状態で、対象のTypedArrayのsizeと当該ArrayBufferlengthが書き換わってしまいます。例えばそのArrayBufferの元々のlengthが更新後の値よりも小さい場合は、領域外のアクセス(Out-of-bound Read/Write: OOB R/W)が可能となります。Onion browserで問われている脆弱性はこれでした。

以下に脆弱性を再現するためのPoCを示します。

let ta = new Uint32Array(1);    // Any type is Ok :)  
console.log(ta.byteLength);     // => 4
ta.midnight();                  // Update the length of `ta`'s internal buffer to 31337
console.log(ta.byteLength);     // => 31337

とても意図的でシンプルな脆弱性ですね。このようなArrayBufferに関するOOB R/Wは強力なものですが、exploitationの目的である"/bin/sh"の起動の実現をするにはまだすこし弱いです。

さて、オーバーフローの発生する箇所の後方に任意のオブジェクトを配置可能な場合の攻略方法として、TypedArrayのサイズに関するフィールドを上書きして任意アドレスの読み書き(Arbitrary Address Read/Write: AAR/AAW)を実現するというものがあります。
この脆弱性をexploitして、OOB WriteのあるArrayBufferに隣接する他のTypedArrayと当該ArrayBufferlength(1<<32) / ta.BYTES_PER_ELEMENTに上書きすることで、当該プロセスの全メモリ空間で読み書きをすることが可能となります。
実際にAAR/AAWを実現するためには、書き込み対象のアドレス(targetAddress)が上書き対象のTypedArrayのArrayBufferの存在するアドレス(addressOfArrayBuffer、OOB Readにより取得・計算可能)よりも小さい場合は、addressOfArrayBufferをオフセットとして考えてtargetAddress += (1<<32) - addressOfArrayBufferを行ってから書き込む必要があります。

以上でAAR/AAWが手に入りました。あとは書き込み先を考えるだけです。先に述べた保護機構の情報から以下のことがわかります。

  • RELRO: No RELRO
    • RELRO(RELocation Read-Only)がないためGOTの領域が書き込み可能である。GOT OverwriteによりEIPの奪取が容易に実現可能である
  • PIE: No PIE
    • PIE(Position Independed Executable; 位置独立実行形式)ではない。バイナリのロードされるアドレスは固定であるため、書き込み対象のアドレスをリークする必要はない

今回は次の手順で攻略しました。

  1. 脆弱性の発火により得たOOB R/Wを利用して、AAR/AAWを実現する
  2. AARよりGOTのエントリから適当なlibc上のアドレスをリークして、__libc_systemのアドレスを計算する
  3. AAWより、第1引数が制御可能なlibc関数のGOTのエントリを(1)で求めた__libc_systemのアドレスに上書きする
  4. 第1引数に"/bin/sh"を設定した状態で上書きした関数を呼ぶことでシェルを起動する

あとがき

まずは個人的な感想ですが、久々の決勝参加と初のヨーロッパで純粋によかったです!また、最近はCTFでflagのサブミットがめっきりできていなかったのもあって、Onion browserに時間はかけてしまったものの解けた点に関しては安心しました。

チームとしては、上位に入ることができたのでよかったなあと思っています。ただ、Onion browserが解けたあとで集中が切れてしまい、結果として1問だけしか解けていないという残念な結果になったため個人としての得点への貢献は薄くなってしまいました。次のCTFではうまいことテンポよく解いていきたいと思います。

また、Onion browserの詳細なwrite upも近々(…🤔?)投稿するつもりですので、どうぞお楽しみに!

© 2016 - 2024 DARK MATTER / Built with Hugo / テーマ StackJimmy によって設計されています。