1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

SECCON Beginners 2023 解けたものだけ Write Up

Last updated at Posted at 2023-06-05

#ctf4b 2023

SECCON Beginners 2023
全778チーム中191位(505pt)でした。
解けたやつだけの Write UP です。(CTF 歴 8ヶ月の限界)

✅:自分で解いたもの
💠:チームの人が解いたもの
🏳️:割と考えたけど解けなかったもの

Category Challenges Solved
crypto CoughingFox2 💠
crypto Conquer
pwnable poem
misc YARO
misc polyglot4b 🏳️
web Forbidden
web aiwaf
web phisher2 🏳️
reversing Half
welcome Welcome 💠

解けた問題

Conquer

  • lengthflag のビット長
  • keyflag と同じビット長のランダムな値
  • cihper = flag XOR key
  • ROL(bits, N)cipher = cipher XOR key を32 回繰り返す
  • ROL(bits, N)bits = abcdef, N=6 の場合、(a-f = {0, 1})
    • (bits << 1) = abcdef0 (1 ビット左に)
    • 2**length - 1 = 111111 (1 が N 個)
    • bits >> (length - 1) = a (最上位ビットだけ) なので
    • bits = bcdefa と左に 1 ビット巡回シフトしてるはず

これより、flag の長さだけわかれば全て逆順の操作をすると flag を出せるはず...
だけどわからないのでとりあえずローカルで試してみる。

decode.py
def decrypt(length):
    # length = flag.bit_length()
    key = 364765105385226228888267246885507128079813677318333502635464281930855331056070734926401965510936356014326979260977790597194503012948
    cipher = 92499232109251162138344223189844914420326826743556872876639400853892198641955596900058352490329330224967987380962193017044830636379

    for i in range(32):
        cipher ^= key
        # Reverse ROL
        for _ in range(pow(cipher, 3, length)):
            low = key & 0b1
            high = low << (length - 1)
            key = (key >> 1) | high


    flag = cipher ^ key
    print(hex(flag))
    return hex(flag)

# main()
for i in range(1, 500): # i is the length of flag.
    print(decrypt(i))

ctf4b{ は HexString で 0x63746634627b だからこれから始めるやつを探す。

decode.py → output
...
0x1ee93f2ed5a6a18e8937b056f1c4e67abcd4adb3f6b80cf1a382cce01c602514dc54a018da71083629f03d51fde6114dc0a432752a5978
0x1ee93f2ed5a6a18e8937b056f1c4e67abcd4adb3f6b80cf1a382cce01c602514dc54a018da71083629f03d51fde6114dc0a432752a5978
0x63746634627b53656d69434952434c457243616e616c73486176654265656e436f6e7175657265644279546865434952434c452121217d
0x63746634627b53656d69434952434c457243616e616c73486176654265656e436f6e7175657265644279546865434952434c452121217d
0x976e990c74598678799961485336b0d6384ff98ea091b46b6e61e97b8e91fbf9ed246eeb410d49bc778adcec167ee5c34d3b3760794e36
0x976e990c74598678799961485336b0d6384ff98ea091b46b6e61e97b8e91fbf9ed246eeb410d49bc778adcec167ee5c34d3b3760794e36
...

decode してctf4b{Semi...} になる。

poem

プログラムを見る限り、0-4 を入力すると配列の対応したところに格納されている文章を出すだけっぽい。flag は別の値として格納されてるけど C 言語の変数の格納アドレスってどうだっけ…?
考えるより手探りで 6 とか -1 とか入れてみる方が早そうなので順に入れていくと...

% nc poem.beginners.seccon.games 9000
Number[0-4]: -4
ctf4b{y0u_...}

YARO

まず実行してみる

 % nc yaro.beginners.seccon.games 5003
rule:
【Enter を入力】
OK. Now I find the malware from this rule:

Not found: ./redir.sh
Not found: ./server.py
Not found: ./flag.txt
Not found: ./requestments.txt

問題文は「サーバーにマルウェアが混入している可能性があるので、あなたの完璧なシグネチャで探してください」
YARA というツールがあるらしいので調べながら書いてみる。おそらく入力した YARA のルールに基づいて 4 つのファイルを調べてるっぽい?

yara0.txt
rule find_flag {
        strings:
            $s = /ctf4b\{[\w_]*/
        condition:
            $s
    }

を入力してみると

yara0.txt → output
% nc yaro.beginners.seccon.games 5003 < yara0.txt
rule:
OK. Now I find the malware from this rule:
rule find_flag {
        strings:
            $s = /ctf4b\{[\w_]*/
        condition:
            $s
    }
Not found: ./redir.sh
Not found: ./server.py
Found: ./flag.txt, matched: [find_flag]
Not found: ./requestments.txt

$s = /ctf4b\{[\w_]*/ の部分をなんとかすれば良さそう。
$s = /ctf4b\{[A-Z]/ にして実行すると Found になるから最初の文字は大文字のアルファベット。あとは[A-M]に分けたり(2分探索)を繰り返しで (実質ブルートフォースで) 突き詰めていくと

yara.txt
rule find_flag {
        strings:
            $s = /ctf4b\{Y3t_...(実際のファイルには記載)\}/
        condition:
            $s
    }
yara.txt → output
% nc yaro.beginners.seccon.games 5003 < yara.txt
rule:
OK. Now I find the malware from this rule:
rule find_flag {
        strings:
            $s = /ctf4b\{Y3t_...\}/
        condition:
            $s
    }
Not found: ./redir.sh
Not found: ./server.py
Found: ./flag.txt, matched: [find_flag]
Not found: ./requestments.txt

Forbidden

flaghttps://forbidden.beginners.seccon.games/flag にあるらしいけど、見てみると

Forbidden :(

と…
プログラムを見ると

index.js
var express = require("express");
var app = express();

const HOST = process.env.CTF4B_HOST;
const PORT = process.env.CTF4B_PORT;
const FLAG = process.env.CTF4B_FLAG;

app.get("/", (req, res, next) => {
    return res.send('FLAG はこちら: <a href="/flag">/flag</a>');
});

const block = (req, res, next) => {
    if (req.path.includes('/flag')) {
        return res.send(403, 'Forbidden :(');
    }

    next();
}

app.get("/flag", block, (req, res, next) => {
    return res.send(FLAG);
})

var server = app.listen(PORT, HOST, () => {
    console.log("Listening:" + server.address().port);
});

"/flag" を含む URL をブロックするらしい。
そういえば URL は大文字小文字を区別しなかった気がする。
→"/flaG" にしてみる?

https://forbidden.beginners.seccon.games/flaG
ctf4b{403_...}

aiwaf

個人的に一番面白かった問題。
URL のクエリ ?file=<FILE> を入力すると ./books/<FILE> にアクセスするらしい。
ディレクトリ構造は

.
├── books
│   ├── book0.txt
│   ├── book1.txt
│   └── ...
└── flag

になってるので ?file=../flag にすると良さそう。
ただ問題はサーバプログラムで「 ../flag が含まれている場合には Yes を返す」ように指示された AI (ChatGPT ?) の検査をしてるのでアクセスできない...けどプログラムから察するにその検査対象になってる URL は最大 50 文字までっぽいので適当なクエリを増やす。
https\://aiwaf.beginners.seccon.games/?random=jwndsnbhjnbghjvgnbhguihjbvhjfyiuhbvhjcfyuihbjvhjffyuhjbvcgfygvmcgfjygvmjfhgbvhjbvmhc&file=../flag とかでアクセスすると flag が出てくる。

Half

バイナリデータの中身を見てみる。

% strings half
...(略)...
%99s%*[^
Invalid FLAG
ctf4b{ge4_t0_kn0w_the
_bin4ry...}
Correct!
:*3$"
...(略)...

実際には % hexdump -C half で解きました。

Welcome

歓迎(洗礼)を受けました。

解きたかった問題

phisher2

自力でできたこと

わかったことは
HTTPリクエスト(POST)

POST / HTTP/1.1
Host: phisher2.beginners.seccon.games
Connection: close
Content-Type: application/json

{"text":"<URL>"}

を送ると、<URL> をチェックして色々返してくれる。
チェックは

  • 受け取った URL (input_url) をサーバにてchromeで開き、
  • スクリーンショットを撮って文字列として抽出し (ocr_url)
  • それぞれ r"https?://[\w/:&\?\.=]+" が含まれてかつ
  • ocr_url が指定の URL (おそらくhttps://phisher2.beginners.seccon.games/ ?) から始まるなら
  • input_rul に ** flagを含んだ** HTTP リクエストを送る。

ので考えとしては

  • input_url に HTTP リクエストを送ってくれないと flag がわからない。
  • ocr_url は基本的には input_url と同じになるはず。(後述)
  • ocr_urlhttps://phisher2.beginners.seccon.games/ から始まらないと HTTP リクエストは送られない。

色々考え、なぜかスクリーンショットから文字列検索していることに注目し、最初に送る POST リクエストに HTML ぶちこんで画像を操作できないかと考えた。

POST / HTTP/1.1
Host: phisher2.beginners.seccon.games
Connection: close
Content-Type: application/json
Content-Length: 101

{"text":"<span display='none'>https://example.com</span> https://phisher2.beginners.seccon.games/"}

こうすれば ocr_urlhttps://phisher2.beginners.seccon.games/input_urlhttps://exsample.com になるのかと期待してたら

HTTP/1.1 200 OK
Server: nginx/1.25.0
Date: Mon, 05 Jun 2023 09:21:36 GMT
Content-Type: application/json
Content-Length: 182
Connection: close

{"input_url":"<span display='none'>https://example.com</span> https://phisher2.beginners.seccon.games/","message":"admin: It's not url or safe url.","ocr_url":"https://example.com"}

だめでした。

ここから解けなかったとこ

チェックの正規表現で % が使われていないので

POST / HTTP/1.1
Host: phisher2.beginners.seccon.games
Connection: close
Content-Type: application/json
Content-Length: 75

{"text":"https://%65xample.com https://phisher2.beginners.seccon.games/"}

こうするといいらしい。
URL に %ascii 使っていいのか…

そもそも flag を含んだ HTTP リクエストを受け取るためにサーバを用意しなければなかったらしい…
ローカル(とCTFサーバ)以外の範疇なんて考えてもなかった…

polyglot4b

自力でできたこと

プログラムを見る限り file コマンドに JPG, PNG, GIF, ASCII の全てだと思わせれば良さそう?
GIF89a を入力すれば GIF, ASCII だとは認識してくれる。
ただ JPG, PNG のマジックナンバーはそもそも非 ASCII だし無理では?

ここから解けなかったとこ

サンプルが含まれてる意味をもっと考えるべきでした。

% file -bkr sushi.jpg 
JPEG image data, Exif standard: [TIFF image data, big-endian, direntries=4, description=CTF4B], baseline, precision 8, 1404x790, components 3
- data

[description=CTF4B] って任意の文字列を入れているのであれば同様のことができそう?
どうやって入れるのかはいまだにわかってない…

The game is over.

まあ初参加ならこんなもんでしょう。まだ強くなる余地があると思えば…

1
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
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?