SECCON Beginners CTF 2020 Write-Up

[SECCON Beginners CTF 2020](https://score.beginners.seccon.jp/)に参加しました。
個人的な結果はWeb1問、Crypt1問、Misc1問+α。
去年はWeb1問、Reversing1問、Misc1問（Welcome除く）だったので、上達したのかしないのか...

問題ランクは"Beginner", "Easy", "Medium", "Hard"の4種類。
"Beginner"は全部解きたかったなぁ・・・。

さて、ホントの序盤しか解けませんでしたが、せっかくなのでWrite-upを書きます。

# Web Spy (Beginner)

## 問題

```text:Description
As a spy, you are spying on the "ctf4b company".

You got the name-list of employees and the URL to the in-house web tool used by some of them.

Your task is to enumerate the employees who use this tool in order to make it available for social engineering.
```

併せて、`app.py`と`employees.txt`というファイルが与えられます。

リンクにアクセスすると以下のログイン画面が表示されます。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/241323/215b8dcb-cc02-0708-d42c-d669949d47a1.png" width=50%>

`Go to challenge page`をクリックすると、以下の画面に遷移します。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/241323/852e2b57-9678-2bfe-8111-1f784da40b8a.png" width=50%>

ログイン試行して、このツールを使ってる従業員を突き止めなさい、ということのようです。

## 解法

### ログイン試行

`employees.txt`の一番上にある、`Arthur`を使ってログインしてみます。
当然パスワードがわからないので、「Login failed, try agein.」と表示され、失敗します。

併せて画面下部に以下の表示がされます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/241323/e4e8ce14-958a-fe34-35aa-5f9af1a16dd1.png)

どうやらページのロード時間が出る仕組みのようです。

### app.pyの確認

ソースの中にこんなコメントがあります。

> \# auth.calc_password_hash(salt, password) adds salt and performs stretching so many times.
> \# You know, it's really secure... isn't it? :-)

何度もSALTを付けてストレッチングしてパスワード強度上げてるよ！！！ってことらしいです。

先程の画面ではログイン試行すると、ロード時間が表示されていました。
実在するユーザーはソルト付きパスワードがあるから、ロード時間が長いってことでしょう。

### 全従業員の処理時間を確認

全従業員でログイン試行してみます。

|Employee|TAT|Check|
|---|---|:---:|
|Arthur|0.0002906 sec||
|Barbara|0.0003179 sec||
|Christine|0.0002883 sec||
|David|0.0003477 sec||
|Elbert|0.5346012 sec|★|
|Franklin|0.0002535 sec||
|George|0.5338522 sec|★|
|Harris|0.0003005 sec||
|Ivan|0.0003516 sec||
|Jane|0.0003274 sec||
|Kevin|0.0003345 sec||
|Lazarus|0.6669512 sec|★|
|Marc|0.3120870 sec|★|
|Nathan|0.0002705 sec||
|Oliver|0.0004385 sec||
|Paul|0.0002842 sec||
|Quentin|0.0003274 sec||
|Randolph|0.0003133 sec||
|Scott|0.0003194 sec||
|Tony|0.5677608 sec|★|
|Ulysses|0.0003919 sec||
|Vincent|0.0002489 sec||
|Wat|0.0003337 sec||
|Ximena|0.3177634 sec|★|
|Yvonne|0.3856804 sec|★|
|Zalmon|0.0003502 sec||

ターンアラウンドが長い人に★をつけました。
該当の7名をchallenge pageで該当者にチェックを入れるとflagゲットです。

`ctf4b{4cc0un7_3num3r4710n_by_51d3_ch4nn3l_4774ck}`

# Crypt R&B (Beginner)

## 問題

```text:Description
Do you like rhythm and blues?
```

併せて、`r_and_b.zip`というファイルが与えられます。
展開すると、暗号化したスクリプトの一部`problem.py`と、暗号化されたflag`encoded_flag`が得られます。

## 解法

`problem.py`を見てみると、一定のルールでROT13とBase64を使ったエンコードを繰り返しているようです。

処理の内容としては、
ROT13の場合は先頭に"R"を付与して、エンコード後の文字列を後ろに結合
Base64の場合は先頭に"B"を付与して、エンコード後の文字列を後ろに結合
という動きのようです。

その逆をすればflagが得られそうですね。

復号するプログラムを作ってみました。（最初は力技でCyberChefで手動で戻していったなんて言えない）

```py:decrypt.py
import sys
import base64
import codecs

format = 'ctf4b{'

input_file = sys.argv[1]
fp = open(input_file, 'r')
crypto = fp.read()

print('Target Crypto : ' + crypto)

# 先頭6文字が"ctf4b{"になったらbreak
while crypto[0:6] != format:
    # 先頭が"B"の場合、Base64 Decode
    if crypto[0] == "B":
        crypto_byte = base64.b64decode(crypto[1:])
        crypto = crypto_byte.decode("ascii")
        print("Base64 Decode : " + crypto)
    # 先頭が"R"の場合、ROT13 Decode
    if crypto[0] == "R":
        crypto = codecs.decode(crypto[1:],'rot13')
        print("ROT13 Decode : " + crypto)

# 意味ないけど気持ち的に
flag = crypto

print()
print('Flag : ' + flag)
```

```terminal:実行結果
> python decrypt.py encoded_flag 
Target Crypto : BQlVrOUllRGxXY2xGNVJuQjRkVFZ5U0VVMGNVZEpiRVpTZVZadmQwOWhTVEIxTkhKTFNWSkdWRUZIUlRGWFUwRklUVlpJTVhGc1NFaDFaVVY1Ukd0Rk1qbDFSM3BuVjFwNGVXVkdWWEZYU0RCTldFZ3dRVmR5VVZOTGNGSjFTMjR6VjBWSE1rMVRXak5KV1hCTGVYZEplR3BzY0VsamJFaGhlV0pGUjFOUFNEQk5Wa1pIVFZaYVVqRm9TbUZqWVhKU2NVaElNM0ZTY25kSU1VWlJUMkZJVWsxV1NESjFhVnBVY0d0R1NIVXhUVEJ4TmsweFYyeEdNVUUxUlRCNVIwa3djVmRNYlVGclJUQXhURVZIVGpWR1ZVOVpja2x4UVZwVVFURkZVblZYYmxOaWFrRktTVlJJWVhsTFJFbFhRVUY0UlZkSk1YRlRiMGcwTlE9PQ==        

Base64 Decode : BUk9IeDlWclF5RnB4dTVySEU0cUdJbEZSeVZvd09hSTB1NHJLSVJGVEFHRTFXU0FITVZIMXFsSEh1ZUV5RGtFMjl1R3pnV1p4eWVGVXFXSDBNWEgwQVdyUVNLcFJ1S24zV0VHMk1TWjNJWXBLeXdJeGpscEljbEhheWJFR1NPSDBNVkZHTVZaUjFoSmFjYXJScUhIM3FScndIMUZRT2FIUk1WSDJ1aVpUcGtGSHUxTTBxNk0xV2xGMUE1RTB5R0kwcVdMbUFrRTAxTEVHTjVGVU9ZcklxQVpUQTFFUnVXblNiakFKSVRIYXlLRElXQUF4RVdJMXFTb0g0NQ==
Base64 Decode : ROHx9VrQyFpxu5rHE4qGIlFRyVowOaI0u4rKIRFTAGE1WSAHMVH1qlHHueEyDkE29uGzgWZxyeFUqWH0MXH0AWrQSKpRuKn3WEG2MSZ3IYpKywIxjlpIclHaybEGSOH0MVFGMVZR1hJacarRqHH3qRrwH1FQOaHRMVH2uiZTpkFHu1M0q6M1WlF1A5E0yGI0qWLmAkE01LEGN5FUOYrIqAZTA1ERuWnSbjAJITHayKDIWAAxEWI1qSoH45
ROT13 Decode : BUk9IeDlSckh5eUR4dTVySElIbjBnV0h4eXVESGNTR1JFNUZIU1dyUUhrRlQxR29hTmtJMklrSHdJU0ZKU0NJeDFXcEhXa3JRT2ZFM3VLcXljVkwycVpyUnloRTFBU0ZISTZIME1uWnpneEdUU3dEejU1SDBnUEZIU2hvMGcxSUh1Z0d6Z1JyS1N5R0lTV0dJYzNxR01YRTA5SHBLeVdNMGN1REhJaFowNWVGUnlXQVJNNkRJV1dFbU45
Base64 Decode : ROHx9RrHyyDxu5rHIHn0gWHxyuDHcSGRE5FHSWrQHkFT1GoaNkI2IkHwISFJSCIx1WpHWkrQOfE3uKqycVL2qZrRyhE1ASFHI6H0MnZzgxGTSwDz55H0gPFHSho0g1IHugGzgRrKSyGISWGIc3qGMXE09HpKyWM0cuDHIhZ05eFRyWARM6DIWWEmN9
ROT13 Decode : BUk9EeUllQkh5eUVUa0tJUklhQUpFTER5SUFJeDUxSG1TbnAxV2VxUjVFSWFPVk1JcUJxeDBsR3hXdlpIY2dMeEluR1NFSUV6U0ZaMmtkTGFjQm55U0tCSUFub0t1VUhtTmtEeXFlTVFJTVp3dTZKR09UcXlJZ0phQUVuM05rSElJNEZ6QVJJRzA9
Base64 Decode : RODyIeBHyyETkKIRIaAJELDyIAIx51HmSnp1WeqR5EIaOVMIqBqx0lGxWvZHcgLxInGSEIEzSFZ2kdLacBnySKBIAnoKuUHmNkDyqeMQIMZwu6JGOTqyIgJaAEn3NkHII4FzARIG0=     
ROT13 Decode : BQlVrOUllRGxXVEVnNWRYQlVNVk51UzFac1JrdE5RVnBIZVdOdk0yTkJiMUptYkVaTFRVRmFSM2xqYnpOalFXOVNabXhHUzAxQldrZDVZMjh6WTBGdlVtWnNRa3AxUVV4SmNEVT0=       
Base64 Decode : BUk9IeDlWTEg5dXBUMVNuS1ZsRktNQVpHeWNvM2NBb1JmbEZLTUFaR3ljbzNjQW9SZmxGS01BWkd5Y28zY0FvUmZsQkp1QUxJcDU=
Base64 Decode : ROHx9VLH9upT1SnKVlFKMAZGyco3cAoRflFKMAZGyco3cAoRflFKMAZGyco3cAoRflBJuALIp5
ROT13 Decode : BUk9IYU9hcG1FaXIySXZNMTlpb3pNbEsySXZNMTlpb3pNbEsySXZNMTlpb3pNbEsyOWhNYVc5
Base64 Decode : ROHaOapmEir2IvM19iozMlK2IvM19iozMlK2IvM19iozMlK29hMaW9
ROT13 Decode : BUnBnczRve2ViZ19vbmZyX2ViZ19vbmZyX2ViZ19vbmZyX29uZnJ9
Base64 Decode : Rpgs4o{ebg_onfr_ebg_onfr_ebg_onfr_onfr}
ROT13 Decode : ctf4b{rot_base_rot_base_rot_base_base}

Flag : ctf4b{rot_base_rot_base_rot_base_base}
```

解けました。
`ctf4b{rot_base_rot_base_rot_base_base}`

# Misc emoemoencode (Easy)

## 問題

```text:Description
Do you know emo-emo-encode?
```

併せて、`emoemoencode.txt`というファイルが与えられます。

```txt:emoemoencode.txt
🍣🍴🍦🌴🍢🍻🍳🍴🍥🍧🍡🍮🌰🍧🍲🍡🍰🍨🍹🍟🍢🍹🍟🍥🍭🌰🌰🌰🌰🌰🌰🍪🍩🍽
```

## 解法

絵文字はUnicodeだろうなと当たりをつけて、Charcodeを調べてみます。
[CyberChef](https://gchq.github.io/CyberChef/)の"Tocharcode"を使用しました。いつもありがとう。

```text
🍣🍴🍦🌴🍢🍻🍳🍴🍥🍧🍡🍮🌰🍧🍲🍡🍰🍨🍹🍟🍢🍹🍟🍥🍭🌰🌰🌰🌰🌰🌰🍪🍩🍽
　↓
01f363 01f374 01f366 01f334 01f362 01f37b 01f373 01f374 01f365 01f367 01f361 01f36e 01f330 01f367 01f372 01f361 01f370 01f368 01f379 01f35f 01f362 01f379 01f35f 01f365 01f36d 01f330 01f330 01f330 01f330 01f330 01f330 01f36a 01f369 01f37d
```

フラグのお決まり部分"ctf4b{}"のCharcodeも調べてみます。

```text
ctf4b{}
　↓
63 74 66 34 62 7b 7d
```

見比べてみると、下2桁が一致していることがわかります。
では、すべての下2桁をとって、`Fromcharcode`を適用してみます。

```text:下2桁とったもの
63746634627b73746567616e306772617068795f62795f656d3030303030306a697d
```

```text:Fromcharcodeを適用
ctf4b{stegan0graphy_by_em000000ji}.
```

解けました。
`ctf4b{stegan0graphy_by_em000000ji}`

# Misc readme (Easy)

この問題は、一人では解けませんでした。悔しい・・・
ヒントをチームメンバーにもらいました。

## 問題

```text:Description
nc readme.quals.beginners.seccon.jp 9712
```

併せて、`readme.zip`というファイルが与えられます。
展開すると、`server.py`が得られます。

netcatしてみましょう。

```terminal
$ nc readme.quals.beginners.seccon.jp 9712
File: /hoge
[-] Not a file
```

何やらファイルのパスを入れると、応答があるプログラムのようです。

`server.py`を見てみましょう。
`/home/ctf/flag`に答えはあるけど、インプットしたパスに`ctf`が入っていると、"Path not allowed"と怒られる仕組みのようです。

"ctf"という文字列を使わずにどうアクセスするか、という問題ということですね。

## 解法

### ヒントもらってから

`/proc/self`を見ると現在実行しているプロセスの環境が調べられるというヒントをいただきました。
調べてみると他のCTFの過去問でも使われていたようですね。これは大変勉強になった。

リファレンスとしては、以下が分かりやすかったです。
[Ubuntu Manpage: proc - プロセスの情報を含む擬似ファイルシステム](http://manpages.ubuntu.com/manpages/trusty/ja/man5/proc.5.html)_

というわけで、調べてみます。
手元のLinux環境でまず`ls -l /proc/self/`を実行して、どういったものがあるのかを見て、役に立ちそうなものの当たりをつけました。

#### /proc/self/cmdline

```terminal
$ nc readme.quals.beginners.seccon.jp 9712
File: /proc/self/cmdline
python3./server.py
```

server.pyはどこかのディレクトリから、相対パスで起動しているのがわかりました。

#### /proc/self/environ

```terminal
$ nc readme.quals.beginners.seccon.jp 9712
File: /proc/self/environ
HOSTNAME=b2a8444bdc32PYTHON_PIP_VERSION=20.1SHLVL=1HOME=/home/ctfGPG_KEY=0D96DF4D4110E5C43FBFB17F2D347EA6AA65421DPYTHON_GET_PIP_URL=https://github.com/pypa/get-pip/raw/1fe530e9e3d800be94e04f6428460fc4fb94f5a9/get-pip.pyPATH=/usr/local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/binLANG=C.UTF-8PYTHON_VERSION=3.7.7PWD=/home/ctf/serverPYTHON_GET_PIP_SHA256=ce486cddac44e99496a702aa5c06c5028414ef48fdfd5242cd2fe559b13d4348SOCAT_PID=20231SOCAT_PPID=1SOCAT_VERSION=1.7.3.3SOCAT_SOCKADDR=172.21.0.2SOCAT_SOCKPORT=9712SOCAT_PEERADDR=60.112.171.203SOCAT_PEERPORT=2621
```

`$HOME`が`/home/ctf`と分かりました。
これを使ったら何かできるのでは？と試行錯誤

```terminal
$ nc readme.quals.beginners.seccon.jp 9712
File: $HOME/flag
[-] File not found

$ nc readme.quals.beginners.seccon.jp 9712
File: ~/flag
[-] File not found
```

環境変数はどうも使えなさそうです。

#### /proc/self/loginuid

```terminal
$ nc readme.quals.beginners.seccon.jp 9712
File: /proc/self/loginuid
4294967295
```

ユーザーが分かりました。このユーザーはつなぎ直しても変わりませんでした。
ユーザーのホームディレクトリでいったらどうなるか試してみます。

```terminal
$ nc readme.quals.beginners.seccon.jp 9712
File: /~4294967295/flag
[-] File not found
```
やっぱり環境変数はダメでした。

#### /proc/self/cwd

cwdはプロセスがカレントワーキングディレクトリとして利用しているディレクトリへのシンボリックリンクとのことです。
`$HOME`が`/home/ctf/`だったこともあり、その辺で実行していることも考えられそうです。

```terminal
$ nc readme.quals.beginners.seccon.jp 9712
File: /proc/self/cwd/flag
[-] File not found
```

`$HOME`直下ではないようです。
flagがあるディレクトリかなと試してみます。

```terminal
$ nc readme.quals.beginners.seccon.jp 9712
File: /proc/self/cwd/../flag
ctf4b{m4g1c4l_p0w3r_0f_pr0cf5}
```

ビンゴでした。
`ctf4b{m4g1c4l_p0w3r_0f_pr0cf5}`

（追記）
ヒントいただいた方から補足もらいました。
`/proc/self/environ`の中に`PWD=/home/ctf/server`がありました。
それが分かっていれば、すぐに`/proc/self/cwd/../flag`が導けますね。
完全に見落としてました。

### 一人で試してダメだったやつ（読み飛ばしていいと思います）

色々やって結局ダメでしたが、記録として。
アホっぽいのもありますが多めに見てください笑

インプットした結果がどう処理されてるのか知りたかったので、ローカルで`server.py`を動かしたんですが、ncで動かしたときとで動作が違うことがありました。一体なんなんでしょう。
ローカルだと文字列括らないと怒られるし、動作結果が違うのもあった。

```terminal
# Charcode
File: 0x2f 0x68 0x6f 0x6d 0x65 0x2f 0x63 0x74 0x66 0x2f 0x66 0x6c 0x61 0x67
[-] File not found

# Charcodeスペース詰め
File: 0x2f0x680x6f0x6d0x650x2f0x630x740x660x2f0x660x6c0x610x67
[-] File not found

# To HEX
File: \x2f\x68\x6f\x6d\x65\x2f\x63\x74\x66\x2f\x66\x6c\x61\x67
[-] File not found

# To HEX ローカルだとこうなる（2行目はprint文いれたもの）
$ python server.py
File: '\x2f\x68\x6f\x6d\x65\x2f\x63\x74\x66\x2f\x66\x6c\x61\x67'
/home/ctf/flag
[-] Path not allowed

# ダブルクォートでくくってみる
File: "/""h""o""m""e""/""c""t""f""/""f""l""a""g"
[-] File not found

# シングルクォートでくくってみる（これもローカルだと"Path not allowed"になる）
File: '/''h''o''m''e''/''c''t''f''/''f''l''a''g'
[-] File not found

# Charcode変換して、渡したいパスになるやつ（これもローカルだと（ｒｙ）
File: chr(0x2f)+chr(0x68)+chr(0x6f)+chr(0x6d)+chr(0x65)+chr(0x2f)+chr(0x63)+chr(0x74)+chr(0x66)+chr(0x2f)+chr(0x66)+chr(0x6c)+chr(0x61)+chr(0x67)
[-] File not found
```

---
以上です。勉強が足りませんね。
やっぱりpicoCTFをやるかなぁ。

\# "mask"はgdbで上手くbreakpoint張れなかったのは何だったのか・・・
\# あれを表層解析だけでやるのは私には無理です。