CSAW CTF Qualification Round 2020 Writeup

#1. はじめに
2020/9/12(土) 5:00(JST) ～ 2020/9/14(月) 5:00(JST) で CSAW CTF Qualification Round 2020 に「チームN30Z30N」の旗のもと単独参加、Sanity以外に4問を解き255点を獲得、352位/1214チームでした。
今回こそはpwn（一番易しいやつ）を一発入れたかったのですが至らず、無念orz...

#2. Writeup
##2.1 sanity (5点)
###問題文
>Join our discord: hxxps://discord.gg/dwYNJ7M

###解法
上記URLのDiscordに参加し、画面上部を確認します。

###フラグ
**flag{w3lc0m3_t0_csaw2020}**

##2.2 baby mult(rev, 50点)
###問題文
>Welcome to web! Let's start off with something kinda funky :)
>hxxp://web.chal.csaw.io:5018

###解法
**【STEP 1】**
指定されたURLにアクセスしてソースを覗くと、ぱっと見わからないのですが、後半に謎の「ゴミ」がついてきています。これは「ゼロ幅文字」「ゼロ幅スペース」と呼ばれるもので、google先生によるとこれを使ったステガノグラフィがあるそうです。そこで今回は [zwsp_steg](https://github.com/enodari/zwsp-steg-py) の力を借りて解読することとしました。前記Webページのソースを「src.txt」に保存して以下のpythonコードを実行します。

```python:solve1.py
import zwsp_steg
with open("src.txt", "rb") as f:
  encode = f.read().decode()
decoded = zwsp_steg.decode(encode)
print(decoded)
```

これを実行すると、

```output:
YWxtMHN0XzJfM3o=
```

という具合にBase64らしき文字列が出てくるので、これをBase64 decodeして、

```Decode結果：
alm0st_2_3z
```
を得ます。

**【STEP 2】**
STEP1で得た文字列「alm0st_2_3z」をページ内のテキストボックスに入れ[sign up]ボタンからsubmitすると、ページ上に
>/ahsdiufghawuflkaekdhjfaldshjfvbalerhjwfvblasdnjfbldf/&lt;pwd&gt;

と表示されます。
そこで、hxxp://web.chal.csaw.io:5018/ahsdiufghawuflkaekdhjfaldshjfvbalerhjwfvblasdnjfbldf/alm0st_2_3z
にアクセスし、このページのソースをsrc2.txtに保存してSTEP1と同様に解読します。

```python:solve2.py
import zwsp_steg
with open("src2.txt", "rb") as f:
  encode = f.read().decode()
decoded = zwsp_steg.decode(encode)
print(decoded)
```
これを実行すると、

```output:
755f756e6831645f6d33
```

と、いかにも16進っぽい文字列が出てくるので、これを16進整数と見てバイト配列に直すと

```python:solve3.py
from Crypto.Util.number import *
print(long_to_bytes(int("0x755f756e6831645f6d33", 16)))
```

```output:
u_unh1d_m3
```

**【STEP 3】**
前記「u_unh1d_m3」を【STEP 2】の要領でsubmitすると
ページ上に
>/19s2uirdjsxbh1iwudgxnjxcbwaiquew3gdi/&lt;pwd1&gt;/&lt;pwd2&gt;

と表示されます。
そこで、hxxp://web.chal.csaw.io:5018/19s2uirdjsxbh1iwudgxnjxcbwaiquew3gdi/alm0st_2_3z/u_unh1d_m3

にアクセスするとフラグをゲットできます。

###フラグ
**flag{gu3ss_u_f0und_m3}**

##2.3 baby_mult(rev, 50点)
###問題文
>Welcome to reversing! Prove your worth and get the flag from this neat little program!

##配布ファイル
```program.txt
85, 72, 137, 229, 72, 131, 236, 24, 72, 199, 69, 248, 79, 0, 0, 0, 72, 184, 21, 79, 231, 75, 1, 0, 0, 0, 72, 137, 69, 240, 72, 199, 69, 232, 4, 0, 0, 0, 72, 199, 69, 224, 3, 0, 0, 0, 72, 199, 69, 216, 19, 0, 0, 0, 72, 199, 69, 208, 21, 1, 0, 0, 72, 184, 97, 91, 100, 75, 207, 119, 0, 0, 72, 137, 69, 200, 72, 199, 69, 192, 2, 0, 0, 0, 72, 199, 69, 184, 17, 0, 0, 0, 72, 199, 69, 176, 193, 33, 0, 0, 72, 199, 69, 168, 233, 101, 34, 24, 72, 199, 69, 160, 51, 8, 0, 0, 72, 199, 69, 152, 171, 10, 0, 0, 72, 199, 69, 144, 173, 170, 141, 0, 72, 139, 69, 248, 72, 15, 175, 69, 240, 72, 137, 69, 136, 72, 139, 69, 232, 72, 15, 175, 69, 224, 72, 15, 175, 69, 216, 72, 15, 175, 69, 208, 72, 15, 175, 69, 200, 72, 137, 69, 128, 72, 139, 69, 192, 72, 15, 175, 69, 184, 72, 15, 175, 69, 176, 72, 15, 175, 69, 168, 72, 137, 133, 120, 255, 255, 255, 72, 139, 69, 160, 72, 15, 175, 69, 152, 72, 15, 175, 69, 144, 72, 137, 133, 112, 255, 255, 255, 184, 0, 0, 0, 0, 201

```

###解法
バイト列っぽい数値が羅列されたファイルが配られます。16進で見ると「これって、機械語じゃん」とピンときます。
そこで、バイナリデータとしてファイル出力しIDA（Free版）に読ませてアセンブリ化してみると

```
55                                  push    rbp
48 89 E5                            mov     rbp, rsp
48 83 EC 18                         sub     rsp, 18h
48 C7 45 F8 4F 00 00 00             mov     qword ptr [rbp-8], 4Fh
48 B8 15 4F E7 4B 01 00 00 00       mov     rax, 14BE74F15h
48 89 45 F0                         mov     [rbp-10h], rax
48 C7 45 E8 04 00 00 00             mov     qword ptr [rbp-18h], 4
48 C7 45 E0 03 00 00 00             mov     qword ptr [rbp-20h], 3
48 C7 45 D8 13 00 00 00             mov     qword ptr [rbp-28h], 13h
48 C7 45 D0 15 01 00 00             mov     qword ptr [rbp-30h], 115h
48 B8 61 5B 64 4B CF 77 00 00       mov     rax, 77CF4B645B61h
48 89 45 C8                         mov     [rbp-38h], rax
48 C7 45 C0 02 00 00 00             mov     qword ptr [rbp-40h], 2
48 C7 45 B8 11 00 00 00             mov     qword ptr [rbp-48h], 11h
48 C7 45 B0 C1 21 00 00             mov     qword ptr [rbp-50h], 21C1h
48 C7 45 A8 E9 65 22 18             mov     qword ptr [rbp-58h], 182265E9h
48 C7 45 A0 33 08 00 00             mov     qword ptr [rbp-60h], 833h
48 C7 45 98 AB 0A 00 00             mov     qword ptr [rbp-68h], 0AABh
48 C7 45 90 AD AA 8D 00             mov     qword ptr [rbp-70h], 8DAAADh
48 8B 45 F8                         mov     rax, [rbp-8]
48 0F AF 45 F0                      imul    rax, [rbp-10h]
48 89 45 88                         mov     [rbp-78h], rax
48 8B 45 E8                         mov     rax, [rbp-18h]
48 0F AF 45 E0                      imul    rax, [rbp-20h]
48 0F AF 45 D8                      imul    rax, [rbp-28h]
48 0F AF 45 D0                      imul    rax, [rbp-30h]
48 0F AF 45 C8                      imul    rax, [rbp-38h]
48 89 45 80                         mov     [rbp-80h], rax
48 8B 45 C0                         mov     rax, [rbp-40h]
48 0F AF 45 B8                      imul    rax, [rbp-48h]
48 0F AF 45 B0                      imul    rax, [rbp-50h]
48 0F AF 45 A8                      imul    rax, [rbp-58h]
48 89 85 78 FF FF FF                mov     [rbp-88h], rax
48 8B 45 A0                         mov     rax, [rbp-60h]
48 0F AF 45 98                      imul    rax, [rbp-68h]
48 0F AF 45 90                      imul    rax, [rbp-70h]
48 89 85 70 FF FF FF                mov     [rbp-90h], rax
B8 00 00 00 00                      mov     eax, 0
C9                                  leave
```
となりましたので、これをpythonに翻訳します。「乗算結果をつなぎ合わせたものがフラグになる」のはややエスパーっぽいですが、気にしてはいけません。ニュータイプならすぐピンとくるレベルのものなのです、きっと。

```solve.py
from Crypto.Util.number import *

data = [0x4F, 0x14BE74F15, 0x4, 0x3, 0x13, 0x115, 0x77CF4B645B61, 0x2, 0x11, 0x21C1, 0x182265E9, 0x833, 0x0AAB, 0x8DAAAD]
flag = b""

rax = data[0]
rax = rax * data[1]
flag = flag + long_to_bytes(rax)

rax = data[2]
rax = rax * data[3]
rax = rax * data[4]
rax = rax * data[5]
rax = rax * data[6]
flag = flag + long_to_bytes(rax)

rax = data[7]
rax = rax * data[8]
rax = rax * data[9]
rax = rax * data[10]
flag = flag + long_to_bytes(rax)

rax = data[11]
rax = rax * data[12]
rax = rax * data[13]
flag = flag + long_to_bytes(rax)

print(flag.decode())
```

###フラグ
**flag{sup3r_v4l1d_pr0gr4m}**

##2.4 Perfect Secrecy(crypto, 50点)
###問題文
>Alice sent over a couple of images with sensitive information to Bob, encrypted with a pre-shared key. It is the most secure encryption scheme, theoretically...

###配布ファイル
・image1.png
![image1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/429434/dbea9a6c-3aec-f3a7-9ffe-cb34b7436fa8.png)

・image2.png
![image2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/429434/e1b80014-d5df-e84f-b12f-08c6f5c9b699.png)

###解法
MS Paintを使って白を背景色として重ね合わせてみると文字が読めるのですが、XORで重ね合わせたらいい感じになりそうでした。
しかし手元に適当なツールがなかったので、OpenCVを使って重ね合わせの画像を生成してみました。

```python:solve.py
import cv2
import matplotlib.pyplot as plt

filename1='image1.png'
filename2='image2.png'
filename3='image_xor.png'

img1 = cv2.imread(filename1)
img2 = cv2.imread(filename2)
bitwise_xor = cv2.bitwise_xor(img1, img2)
cv2.imwrite(filename3,bitwise_xor)
```

・image_xor.png
![image_xor.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/429434/1b1ae186-baaa-7345-d41d-b313ecefc1ef.png)

この画像の中に出てくる

```Output:
ZmxhZ3swbjNfdDFtM19QQGQhfQ==
```

をBase64 decodeしてフラグを得ます。

###フラグ
**flag{0n3_t1m3_P@d!}**
※個人的には、これはCryoto枠よりMisc枠のほうがしっくり来る気がしました。

##2.5 modus_operandi(crypto, 100点)
###問題文
>Can't play CSAW without your favorite block cipher!
>
>nc crypto.chal.csaw.io 5001

###ヒント
> &lt;200

###解法
サーバに接続すると、まず平文の入力を求められます。入力するとブロック暗号によって前記平文を暗号化した暗号文が提示され、それがECBモードによるものかCBCモードによるものかを問われます。間違うとゲームオーバーとなり、そこでサーバから切断されます。
ECBかCBCかの判断ですが、平文側は同じ文字を繰り返す長めのものを用意し、暗号文ブロックを見てパターン繰り返しがあればECBと判断できるのでそう難しくありません。
最初、10回くらい連続正解すればフラグが表示されるのかと思いましたがそうではなく、仕方がないので自動化するプログラムを組みましたが、100回やってもフラグは出てきません。
しばらくして前記ヒントが提示されたのですが、200回やってもフラグは出てこないので、「？？？」となりましたが、やってみると1問目は毎回「ECB」で、その後の答えのパターンが同じっぽいことから、ECB=0、CBC=1として2進整数を構築し、最後byte列に変換すればフラグになるのかなぁ、と思ったらその通りでした。

```python:solve.py
from Crypto.Util.number import *
from pwn import *
import sys
import time

r = remote('crypto.chal.csaw.io', 5001)

print(r.recvline())
i = 0

tmp = "0b"
try:
  for _ in range(200):
    s = r.recvline()
    if(s[0:5] == b'Enter'):
      print(s)
      buf = long_to_bytes(0x31 + i % 64) * 32
      print(buf)
      r.sendline(buf)
      s = r.recvline()
      s = s[16:]
      print(s)
      t = r.recvline()
      print(t)
      if(s[0:32] == s[32:64]):
        buf = b'ECB'
        tmp = tmp + "0"
      else:
        buf = b'CBC'
        tmp = tmp + "1"
      print(buf)
      r.sendline(buf)
      i = i + 1
    else:
      break
except:
  print("error")

print(long_to_bytes(int(tmp,2)))
print(r.recvline())
print(r.recvline())
print(r.recvline())
r.interactive()
```

ちなみに、投入する平文がずっと同じだと怒られそうな気がしたので64パターン用意したのですが、これは杞憂（無駄）だったようです。

###フラグ
**flag{ECB_re@lly_sUck$}**

#3 おわりに
・フラグにたどり着くまでがしんどい問題が多かったです。エスパー問題とまでは言わないまでも、NT（ニュータイプ）能力を問われているような気がしました。
・昨年のCSAW CTFは1問も解けませんでしたので、去年の自分には勝てました！
・pwn問題（roppity）はアドレスリークまではうまく行ってたっぽいのですが、結局shellは取れませんでしたorz...