NahamCon CTF 2021 Writeup

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

esab64 (Warmups)

4バイトごとに逆にすれば、base64デコードできる。さらに3バイトごとに逆にして、パディングしている"_"を削除すると、フラグになった。

with open('esab64', 'r') as f:
    enc = f.read()

b64 = ''
for i in range(0, len(enc), 4):
    b64 += enc[i:i+4][::-1]

dec = b64.decode('base64')

flag = ''
for i in range(0, len(dec), 3):
    flag += dec[i:i+3:][::-1]

flag = flag.rstrip('_')
print flag
flag{fb5211b498afe87b1bd0db601117e16e}

Buzz (Warmups)

$ file buzz
buzz: compress'd data 16 bits
$ mv buzz buzz.Z
$ uncompress buzz.Z
$ cat buzz
flag{b3a33db7ba04c4c9052ea06d9ff17869}
flag{b3a33db7ba04c4c9052ea06d9ff17869}

Shoelaces (Warmups)

$ strings shoelaces.jpg | grep flag
flag{137288e960a3ae9b148e8a7db16a69b0}
flag{137288e960a3ae9b148e8a7db16a69b0}

Pollex (Warmups)

$ file pollex
pollex: JPEG image data, JFIF standard 1.01, aspect ratio, density 1x1, segment length 16, Exif Standard: [TIFF image data, little-endian, direntries=5, description=Man giving thumb up on dark black background., software=Google], baseline, precision 8, 424x283, frames 3

https://www.dcode.fr/exif-thumbnailでサムネイルを抽出する。
f:id:satou-y:20210322204042p:plain

flag{65c34a1ec121a286600ddd48fe36bc00}

Read The Rules (Warmups)

ルールのページのHTMLソースのコメントにフラグがあった。

<!-- Thank you for reading the rules! Your flag is: -->
<!--   flag{90bc54705794a62015369fd8e86e557b}       -->
flag{90bc54705794a62015369fd8e86e557b}

eaxy (Cryptography)

1つのkeyでXORをすると、数か所に以下の形式の文字列になる。

The XOR key you used to find string this is the [数値] character index of the flag :)

この文字列を表示したときのXOR鍵が復号文字で、数値がフラグ文字列のインデックスとなるようだ。これを元にフラグを構成する。

import re

def decrypt(s, key):
    d = ''
    for c in s:
        d += chr(ord(c) ^ key)
    return d

with open('eaxy', 'rb') as f:
    data = f.read()

pattern = 'The XOR key you used to find string this is the (\d+) character index of the flag \:\)'

flag = [''] * 38
for key in range(256):
    d = decrypt(data, key)
    for m in re.finditer(pattern, d):
        index = int(m.group(1))
        flag[index] = chr(key)

flag = ''.join(flag)
print flag
flag{16edfce5c12443b61828af6cab90dc79}

Dice Roll (Cryptography)

サーバの処理概要は以下の通り。

■0. Info
・ランダム値は32bitであることを表示するだけ。

■1. Shake the dice
・32ビットランダム整数をシード設定

■2. Roll the dice (practice)
・32ビットランダム値表示

■3. Guess the dice (test)
・32ビットランダム値を推測し、一致したらフラグが表示される。

Mersenne Twisterの問題そのもの。そのままスクリプトにして実行する。

import socket
import random

def recvuntil(s, tail):
    data = ''
    while True:
        if tail in data:
            return data
        data += s.recv(1)

def untemper(rand):
    rand ^= rand >> 18;
    rand ^= (rand << 15) & 0xefc60000;
 
    a = rand ^ ((rand << 7) & 0x9d2c5680);
    b = rand ^ ((a << 7) & 0x9d2c5680);
    c = rand ^ ((b << 7) & 0x9d2c5680);
    d = rand ^ ((c << 7) & 0x9d2c5680);
    rand = rand ^ ((d << 7) & 0x9d2c5680);
 
    rand ^= ((rand ^ (rand >> 11)) >> 11);
    return rand

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('challenge.nahamcon.com', 31784))

data = recvuntil(s, 'L\n').rstrip()
print data

data = recvuntil(s, '> ')
print data + '0'
s.sendall('0\n')
data = recvuntil(s, '\n').rstrip()
print data

data = recvuntil(s, '> ')
print data + '1'
s.sendall('1\n')
data = recvuntil(s, '\n').rstrip()
print data

N = 624
state = []
for i in range(N):
    data = recvuntil(s, '> ')
    print data + '2'
    s.sendall('2\n')
    data = recvuntil(s, '\n').rstrip()
    print data
    data = recvuntil(s, '\n').rstrip()
    print data
    state.append(untemper(int(data)))

state.append(N)
random.setstate([3, tuple(state), None])

guess = str(random.getrandbits(32))

data = recvuntil(s, '> ')
print data + '3'
s.sendall('3\n')
data = recvuntil(s, '> ')
print data + guess
s.sendall(guess + '\n')
data = recvuntil(s, '\n').rstrip()
print data
data = recvuntil(s, '\n').rstrip()
print data

実行結果は以下の通り。

              _______
   ______    | .   . |\
  /     /\   |   .   |.\
 /  '  /  \  | .   . |.'|
/_____/. . \ |_______|.'|
\ . . \    /  \ ' .   \'|
 \ . . \  /    \____'__\|
  \_____\/

      D I C E   R O L L


0. Info
1. Shake the dice
2. Roll the dice (practice)
3. Guess the dice (test)

> 0
Our dice are loaded with a whopping 32 bits of randomness!

0. Info
1. Shake the dice
2. Roll the dice (practice)
3. Guess the dice (test)

> 1
Shaking all the dice...

0. Info
1. Shake the dice
2. Roll the dice (practice)
3. Guess the dice (test)

> 2
Rolling the dice... the sum was:
457371789

    :

0. Info
1. Shake the dice
2. Roll the dice (practice)
3. Guess the dice (test)

> 2
Rolling the dice... the sum was:
3422905258

0. Info
1. Shake the dice
2. Roll the dice (practice)
3. Guess the dice (test)

> 3
Guess the dice roll to win a flag! What will the sum total be?
> 2720670169
HOLY COW! YOU GUESSED IT RIGHT! Congratulations! Here is your flag:
flag{e915b62b2195d76bfddaac0160ed3194}
flag{e915b62b2195d76bfddaac0160ed3194}