RITSEC CTF Writeup~前半~

#はじめに

あまり時間が取れなかったのですが, 久々に1000点もいれることができたのでWriteup(とflagに至るまでの試行錯誤など)を綴っていきます.

## Onion Layer Encoding(Misc, 100pt)

> Encoding is not encryption, but what if I just encode the flag with base16,32,64? If I encode my precious flag for 150 times, surely no one will be able to decode it, right?

リード文の通りです. flagをBase16, Base32, Base64でひたすらエンコーディングしまくったファイルが渡されます. ファイルは[GitHub](https://github.com/taiyaki8926/RITSEC)にあげています. 

以下のサイト

[CTF Crypto - A painter and a black cat](https://raintrees.net/projects/a-painter-and-a-black-cat/wiki/CTF_Crypto)[^1]

を見ると, 
・Base16：使用文字は `0-9A-F`
・Base32：使用文字は `A-Z2-7`
・Base64：使用文字は `A-Za-z0-9+/`
です. まず最初は小文字が使われているのでBase64でエンコードされていると推測します.

```Python3
import base64

with open('onionlayerencoding', mode='r') as f:
    s = f.read()
f.close()

s = base64.b64decode(s)

print(s)

# 344234453537343834313...
```

今度は数字ばかりなのでBase16っぽいですね. 1行追加してみます.

```Python3
import base64

with open('onionlayerencoding', mode='r') as f:
    s = f.read()
f.close()

s = base64.b64decode(s)

# Add
s = base64.b16decode(s)

print(s)

# 4B4E5748415443534E4E5...
```

今度は大文字ばかりなのでBase32っぽいです.

```Python3
import base64

with open('onionlayerencoding', mode='r') as f:
    s = f.read()
f.close()

s = base64.b64decode(s)
s = base64.b16decode(s)

# Add
s = base64.b32decode(s)

print(s)
```

…なんと `# Error: Non-base32 digit found`が返ってきました.
さて, Base32には2種類あり, 使用する文字が `0-9A-V`のBase32hexというものも存在します[^2].
これはbase64モジュールには含まれていないので, 別途インストールする必要があります(実際は必要ありません. やりたい人はどうぞ).

```
$ pip install base32hex --user
```

さて, 再開しましょう.

```Python3
import base64
import base32hex

with open('onionlayerencoding', mode='r') as f:
    s = f.read()
f.close()

s = base64.b64decode(s)
s = base64.b16decode(s)

# Add
s = base32hex.b32decode(s)

print(s)
```

…終わらん. ここでかなり詰まりました.
Base32hex自体はアルゴリズムが簡単(Decodeについては8文字ごとに区切って5文字ずつに圧縮している)なので, この `s`にだけ適用できるように自作のBase32hexのデコード関数まで作って高速化を図りました(なんとか終わりましたが結局徒労に終わります).

さて, そもそも最初の8文字 `4B4E5748`をBase32hexでデコードしてみると

```Python3
print(base32hex.b32decode('4B4E5748'))

# '"\xc8\xe2\x9c\x88'
```
そもそもAsciiで英数字とすらなりません. 結論として, この `4B4E5748`から始まる文字列はBase32でもBase32hexでもないエンコーディング方式, ということになります.

(ここでの私の失敗は, Base64$\rightarrow$Base16ときたので次はBase32に違いない, と決め込んだことです.)

```Python3
import base64

with open('onionlayerencoding', mode='r') as f:
    s = f.read()
f.close()

s = base64.b64decode(s)
s = base64.b16decode(s)

# Add
s = base64.b16decode(s)
# s = base64.b64decode(s) でもエラーは吐きませんが, 返り値が英数字ではなくなります.

print(s)

# KNWHATCS...
```

以上を踏まえて, 以下のような流れのコーディングをしていくことになります.

1. ファイルを読み込む.
2. Base16でデコードしてみる(Errorがでるかもしれないので例外処理をしておく).
3. デコードできない場合はBase32でデコードする(同じく例外処理する).
4. デコードできない場合はBase64でデコードする(同じく例外処理する).
5. 2~4を150回繰り返す.

となります. 上でも述べたような, Base16と64など複数のモードでデコードできてしまう場合はBase16から優先するようにしました(もちろん, これでうまくいく保証はなく, 詰まったらまたその都度考えるつもりでした).

```Python3
import base64

with open('onionlayerencoding.txt', mode='r') as f:
    s = f.read()
f.close()

for i in range(150):
    try:
        s = base64.b16decode(s)
    except:
        try:
            s = base64.b32decode(s)
        except:
            try:
                s = base64.b64decode(s)
            except:
                print('Decode Error...')
    print(i)
```

32回目から `Decode Error`を吐き始めました. 見てみましょう.

```Python3:solve.py
import base64

with open('onionlayerencoding.txt', mode='r') as f:
    s = f.read()
f.close()

# 150ではなく32にしただけ
for i in range(32):
    try:
        s = base64.b16decode(s)
    except:
        try:
            s = base64.b32decode(s)
        except:
            try:
                s = base64.b64decode(s)
            except:
                print('Decode Error...')
    # print(i)

print(s)
```

`b'RITSEC{0n1On_L4y3R}'`

結局, Base32hexは使われておりませんでしたが, 知識が増えたのでよしとしましょう.


## AlPhAbEtIcAl Challenge(Misc, 280pt)

> Hint: A potential avenue of attack might be to take a look at how often things occur!

> 59:87:57:51:85:80{:40:50:56:08:82:58:81:08:18:85:57:87:48:85:88:40:50:56:59:15:56:11:18:85:59:51:}

ヒントの"how often things occur"から頻度分析, つまり単一換字式暗号を疑います. これについては(経験則から)思いつくしかないでしょう…
flagの形式は `RITSEC{…}`なので, 
・59 → R
・87 → I
・57 → T
・51 → S
・85 → E
・80 → C
ですね. 一旦置き換えてみます.

R:I:T:S:E:C{:40:50:56:08:82:58:81:08:18:E:T:I:48:E:88:40:50:56:R:15:56:11:18:E:R:S:}

…うーん、全然置き換わってないので何もわかりません. 
一旦上の対応表は忘れて適当にアルファベットを当てはめます. コロンも抜いておきます.
ここでは, 2桁の数字が出てきた順に59をAに, 87をBに, 57をCに, …とABCを順番に割り振っただけです.

ABCDEF{GHIJKLMJNECBOEPGHIAQIRNEAD}

これを頻度分析してくれる[このサイト](https://quipqiup.com)に投げます. 
(注釈)一度何かしらのアルファベットに変換しないとうまく解析できません.

そして, 先頭のABCDEFはそれぞれRITSECに対応することはわかっているので
Cluesのところに「ABCDEF=RITSEC」を入力してsolveを押すと, それらしいのを返してくれます. 今回はこれが答えでした.

`RITSEC{YOUALPHABETIZEDYOURNUMBERS}`



# pre-legend(Crypto, 100pt)

> 9EEADi^^8:E9F3]4@>^4=2J32==^D@>6E9:?8\FD67F=\C:ED64

> Wrap the flag in RITSEC{ }

いやこれノーヒントで出されても何もわからないですが…. 
困ったら以下のサイト
[Cyber Chef](https://gchq.github.io/CyberChef/)
に投げます. そして, 左のOperationsからMagicを選び, 真ん中のRecipeにドラッグ&ドロップします. しかし, 何も有益な情報が得られません…

困ったらググりましょう. 先頭の何文字か, 今回なら `9EEADi^^8:E9`で検索をかけます.
こういう時になぜかヒットするんですよね…. ザーッと見ていくと, どうやら文字化け？している感じがします.

さらに検索結果を見ていくと, ありました. 「Free HTML tools - Online ROT, ROT5, ROT13, ROT18, ROT47 ...」なるタイトルの記事. Cryptoの問題なのでROT確定でしょう. 

さて, 再び登場です.
[CTF Crypto - A painter and a black cat](https://raintrees.net/projects/a-painter-and-a-black-cat/wiki/CTF_Crypto)
を見てみると, ROTの代表として13と47があります. 13ではないことは明らかでしょう. 上のリンクからさらに[Wikipediaのページ](https://ja.wikipedia.org/wiki/ROT13#ROT47)に飛んでも似たような文字が登場するので, ここで勝利を確信します.

では, またも[Cyber Chef](https://gchq.github.io/CyberChef/)に投げます. 左のOperationのすぐ真下, Searchの箇所に"ROT"を打ち込むとROT47が出てくるので, 先ほどと同じようにRecipeにドラッグ&ドロップします. するとURLが表示されます.

https://github.com/clayball/something-useful-ritsec

え, 2段構えだったか. ここに載っているソースコードをまた読み解くのか…と落胆しました. というか15分くらいマジで解析してました.

ですが, 試しにこのURLそのものを
`RITSEC{https://github.com/clayball/something-useful-ritsec}`
として入力してみると通りました. 焦った, 多分どこかで使うんでしょうね. 

まだ3問くらいWriteup書くので, 続きは後ほど. 




[^1]: めちゃくちゃ綺麗にまとまっているのでCryptoやる時は必要不可欠といっても過言ではないです.
[^2]: [Wikipedia](https://ja.wikipedia.org/wiki/三十二進法#Base32)より.