IrisCTF 2024 Writeup

この大会は2024/1/6 9:00(JST)~2024/1/8 9:00(JST)に開催されました。
今回もチームで参戦。結果は1566点で1207チーム中87位でした。
自分で解けた問題をWriteupとして書いておきます。

Sanity Check (Welcome)

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

irisctf{welc0me_to_1risc7f_2024}

Discord (Welcome)

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

irisctf{disc0rd_1s_a_gr34t_w4y_to_c0nn3ct_w1th_our_t34m_4nd_0th3r_pl4yer5}

Disclaimer (Radio Frequency)

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

irisctf{its_radioing_time_and_then_you_radio_all_of_them}

Czech Where? (Open-Source Intelligence)

画像検索すると、以下のようなページが見つかる。

http://tabiichigo.livedoor.biz/archives/51921024.html
https://www.tabikobo.com/tabi-pocket/europe/czechrepublic/article36629.html

プラハ城の黄金の小道にあるらしい。以下の場所辺りが写真の場所になりそう。

https://www.google.co.jp/maps/@50.0919934,14.404112,3a,90y,338.1h,105.04t/data=!3m7!1e1!3m5!1sAF1QipMPt-UbfeBMHH-JwJGzO9NNJVgqt70CX0UTos_Z!2e10!3e11!7i3840!8i1920?hl=en&entry=ttu

住所は以下の通り。

Zlatá ulička u Daliborky 22/20, 119 00 Praha 1-Hradčany, Czechia
irisctf{zlata_ulicka_u_daliborky}

Rune What's that? (Reverse Engineering)

平文の前後のASCIIコードを足して暗号化しているので、元に戻す。なお最初の文字はそのまま暗号化の文字にする。

#!/usr/bin/env python3
with open('the', encoding='utf-8') as f:
    enc = f.read()

z = 0
flag = ''
for i in range(len(enc)):
    flag += chr(ord(enc[i]) - z)
    z = ord(enc[i]) - z

flag = flag[:flag.index('}') + 1]
print(flag)
irisctf{i_r3411y_1ik3_num63r5}

Baby Charge (Cryptography)

$ nc babycha.chal.irisc.tf 10100
== proof-of-work: disabled ==
This cipher is approved by Disk Jockey B.

1. Encrypt input
2. Encrypt flag

> 2
0802111650540215100c44460241091d69b33f796916f6d35fd63f8fe259244e31e69b9c64
> 1
? aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
b8521d817f0e5809145fcb616161616161616179c2cdeca782d01ca042db82cb2d00a040c2

初期化直後のbuffer48バイト目から56バイト目まで必ず\x00になる。フラグは37バイトなので、以下のようにして接続し直しながらフラグの部分文字列を抽出する。

1.フラグの最初の8バイトを抽出する。
・48バイトの任意の文字列で暗号化
・フラグを暗号化(37バイト)→最初の8バイトがフラグの部分文字列

2.フラグの8バイト目から16バイト目までを抽出する。
・40バイトの任意の文字列で暗号化
・フラグを暗号化(37バイト)→8バイト目から16バイト目がフラグの部分文字列
       :
#!/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)

flag = b''
for i in range(5):
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect(('babycha.chal.irisc.tf', 10100))

    try_pt = 'a' * (48 - i * 8)
    data = recvuntil(s, b'> ')
    print(data + '1')
    s.sendall(b'1\n')
    data = recvuntil(s, b'? ')
    print(data + try_pt)
    s.sendall(try_pt.encode() + b'\n')
    data = recvuntil(s, b'> ')
    print(data + '2')
    s.sendall(b'2\n')
    data = recvuntil(s, b'\n').rstrip()
    print(data)
    flag += bytes.fromhex(data[i*16:i*16+16])

    s.close()

flag = flag[:37].decode()
print(flag)

実行結果は以下の通り。

== proof-of-work: disabled ==
This cipher is approved by Disk Jockey B.

1. Encrypt input
2. Encrypt flag

> 1
? aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
001119045241050f18034c530a4104151a236f8f60677ffe97dce72dfc52f0b135c859d1f6f01115e0663f2876b178a9
> 2
697269736374667bb6155a85989820fc1cd8285599b089d3b64f461d0fd755986d5285c39f
== proof-of-work: disabled ==
This cipher is approved by Disk Jockey B.

1. Encrypt input
2. Encrypt flag

> 1
? aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
001119045241050f18034c530a410415534565c854d4e7fa0c138101bf905394ceaee6c8e5c37c26
> 2
fefefb23109e54b9696e697469616c6917e96b831cbc15d8933d0e0de0e7dc6090d504e90e
== proof-of-work: disabled ==
This cipher is approved by Disk Jockey B.

1. Encrypt input
2. Encrypt flag

> 1
? aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
001119045241050f18034c530a4104154e71069cf88442c3f2889d4f2aa0137a
> 2
c581745801eb7fcf70273aeb7a70d0e87a6174696f6e5f69266aaa4955d6262df75645e257
== proof-of-work: disabled ==
This cipher is approved by Disk Jockey B.

1. Encrypt input
2. Encrypt flag

> 1
? aaaaaaaaaaaaaaaaaaaaaaaa
001119045241050f18034c530a4104151376c9e32047942b
> 2
58342c70ec0d59842536d2d1ffa641f969a56e49c46fe802735f6e6f5f70726f4fae9215b6
== proof-of-work: disabled ==
This cipher is approved by Disk Jockey B.

1. Encrypt input
2. Encrypt flag

> 1
? aaaaaaaaaaaaaaaa
001119045241050f18034c530a410415
> 2
99747c98378b18c80437e3aebe510eed81014910c42cb78a7f5d62586905226a626c656d7d
irisctf{initialization_is_no_problem}
irisctf{initialization_is_no_problem}

Accessible Sesamum Indicum (Cryptography)

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

・16回以下繰り返し
 ・res = vault()
  ・pin: ランダム16進数文字列4バイト
  ・digits = ["z", "z", "z", "z"]
  ・counter = 0
  ・以下繰り返し
   ・attempt: 入力→リスト化したもの
   ・attemptの数だけ以下繰り返し
    ・attemptの末尾からポップし、digitsの先頭に挿入
    ・digitsの末尾からポップ
    ・digitsの結合文字列がpinと一致する場合、Trueを返却
    ・counter += 1
    ・counterが65536より大きい場合、Falseを返却
  ・Falseを返却
 ・resがFalseの場合、終了
・フラグを表示

ブルートフォースで4バイトを指定し、pinを当てればよいが、途中で接続を切られる。シフトするごとにチェックされるので、4バイトごとに指定する必要はない。文字列に4バイトのpinが含まれている場合は、追加しないようにし、attemptの文字列を作成して指定する。

#!/usr/bin/env python3
import socket
import itertools

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

chars = '0123456789abcdef'

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('accessible-sesasum-indicum.chal.irisc.tf', 10104))

for _ in range(6):
    data = recvuntil(s, b'\n').rstrip()
    print(data)

for n in range(16):
    for _ in range(15):
        data = recvuntil(s, b'\n').rstrip()
        print(data)

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

    attempt = ''
    for c in itertools.product(chars, repeat=4):
        pin = ''.join(c)
        if pin not in attempt:
            attempt += pin

    data = recvuntil(s, b'> ')
    print(data + attempt[:32] + '...')
    s.sendall(attempt.encode() + b'\n')
    data = recvuntil(s, b'\n').rstrip()
    print(data)

for _ in range(2):
    data = recvuntil(s, b'\n').rstrip()
    print(data)

data = recvuntil(s, b'}')
print(data)

実行結果は以下の通り。

== proof-of-work: disabled ==
You're burgling a safehouse one night (as you often do) when you run into a
vault. The vault is protected by a 16-digit pad for a 4-digit PIN. The
safehouse is guarded by an alarm system and if you're not careful, it'll go
off, which is no good for you. After this, there are 15 more vaults.

You've made it to vault #1.

|---|---|---|---|
| 0 | 1 | 2 | 3 |
|---|---|---|---|
|---|---|---|---|
| 4 | 5 | 6 | 7 |
|---|---|---|---|
|---|---|---|---|
| 8 | 9 | a | b |
|---|---|---|---|
|---|---|---|---|
| c | d | e | f |
|---|---|---|---|

What is the 4-digit PIN?
Attempt> 00000001000200030004000500060007...
You've defeated this vault.
You've made it to vault #2.

|---|---|---|---|
| 0 | 1 | 2 | 3 |
|---|---|---|---|
|---|---|---|---|
| 4 | 5 | 6 | 7 |
|---|---|---|---|
|---|---|---|---|
| 8 | 9 | a | b |
|---|---|---|---|
|---|---|---|---|
| c | d | e | f |
|---|---|---|---|

What is the 4-digit PIN?
Attempt> 00000001000200030004000500060007...
You've defeated this vault.
You've made it to vault #3.

|---|---|---|---|
| 0 | 1 | 2 | 3 |
|---|---|---|---|
|---|---|---|---|
| 4 | 5 | 6 | 7 |
|---|---|---|---|
|---|---|---|---|
| 8 | 9 | a | b |
|---|---|---|---|
|---|---|---|---|
| c | d | e | f |
|---|---|---|---|

What is the 4-digit PIN?
Attempt> 00000001000200030004000500060007...
You've defeated this vault.
You've made it to vault #4.

|---|---|---|---|
| 0 | 1 | 2 | 3 |
|---|---|---|---|
|---|---|---|---|
| 4 | 5 | 6 | 7 |
|---|---|---|---|
|---|---|---|---|
| 8 | 9 | a | b |
|---|---|---|---|
|---|---|---|---|
| c | d | e | f |
|---|---|---|---|

What is the 4-digit PIN?
Attempt> 00000001000200030004000500060007...
You've defeated this vault.
You've made it to vault #5.

|---|---|---|---|
| 0 | 1 | 2 | 3 |
|---|---|---|---|
|---|---|---|---|
| 4 | 5 | 6 | 7 |
|---|---|---|---|
|---|---|---|---|
| 8 | 9 | a | b |
|---|---|---|---|
|---|---|---|---|
| c | d | e | f |
|---|---|---|---|

What is the 4-digit PIN?
Attempt> 00000001000200030004000500060007...
You've defeated this vault.
You've made it to vault #6.

|---|---|---|---|
| 0 | 1 | 2 | 3 |
|---|---|---|---|
|---|---|---|---|
| 4 | 5 | 6 | 7 |
|---|---|---|---|
|---|---|---|---|
| 8 | 9 | a | b |
|---|---|---|---|
|---|---|---|---|
| c | d | e | f |
|---|---|---|---|

What is the 4-digit PIN?
Attempt> 00000001000200030004000500060007...
You've defeated this vault.
You've made it to vault #7.

|---|---|---|---|
| 0 | 1 | 2 | 3 |
|---|---|---|---|
|---|---|---|---|
| 4 | 5 | 6 | 7 |
|---|---|---|---|
|---|---|---|---|
| 8 | 9 | a | b |
|---|---|---|---|
|---|---|---|---|
| c | d | e | f |
|---|---|---|---|

What is the 4-digit PIN?
Attempt> 00000001000200030004000500060007...
You've defeated this vault.
You've made it to vault #8.

|---|---|---|---|
| 0 | 1 | 2 | 3 |
|---|---|---|---|
|---|---|---|---|
| 4 | 5 | 6 | 7 |
|---|---|---|---|
|---|---|---|---|
| 8 | 9 | a | b |
|---|---|---|---|
|---|---|---|---|
| c | d | e | f |
|---|---|---|---|

What is the 4-digit PIN?
Attempt> 00000001000200030004000500060007...
You've defeated this vault.
You've made it to vault #9.

|---|---|---|---|
| 0 | 1 | 2 | 3 |
|---|---|---|---|
|---|---|---|---|
| 4 | 5 | 6 | 7 |
|---|---|---|---|
|---|---|---|---|
| 8 | 9 | a | b |
|---|---|---|---|
|---|---|---|---|
| c | d | e | f |
|---|---|---|---|

What is the 4-digit PIN?
Attempt> 00000001000200030004000500060007...
You've defeated this vault.
You've made it to vault #10.

|---|---|---|---|
| 0 | 1 | 2 | 3 |
|---|---|---|---|
|---|---|---|---|
| 4 | 5 | 6 | 7 |
|---|---|---|---|
|---|---|---|---|
| 8 | 9 | a | b |
|---|---|---|---|
|---|---|---|---|
| c | d | e | f |
|---|---|---|---|

What is the 4-digit PIN?
Attempt> 00000001000200030004000500060007...
You've defeated this vault.
You've made it to vault #11.

|---|---|---|---|
| 0 | 1 | 2 | 3 |
|---|---|---|---|
|---|---|---|---|
| 4 | 5 | 6 | 7 |
|---|---|---|---|
|---|---|---|---|
| 8 | 9 | a | b |
|---|---|---|---|
|---|---|---|---|
| c | d | e | f |
|---|---|---|---|

What is the 4-digit PIN?
Attempt> 00000001000200030004000500060007...
You've defeated this vault.
You've made it to vault #12.

|---|---|---|---|
| 0 | 1 | 2 | 3 |
|---|---|---|---|
|---|---|---|---|
| 4 | 5 | 6 | 7 |
|---|---|---|---|
|---|---|---|---|
| 8 | 9 | a | b |
|---|---|---|---|
|---|---|---|---|
| c | d | e | f |
|---|---|---|---|

What is the 4-digit PIN?
Attempt> 00000001000200030004000500060007...
You've defeated this vault.
You've made it to vault #13.

|---|---|---|---|
| 0 | 1 | 2 | 3 |
|---|---|---|---|
|---|---|---|---|
| 4 | 5 | 6 | 7 |
|---|---|---|---|
|---|---|---|---|
| 8 | 9 | a | b |
|---|---|---|---|
|---|---|---|---|
| c | d | e | f |
|---|---|---|---|

What is the 4-digit PIN?
Attempt> 00000001000200030004000500060007...
You've defeated this vault.
You've made it to vault #14.

|---|---|---|---|
| 0 | 1 | 2 | 3 |
|---|---|---|---|
|---|---|---|---|
| 4 | 5 | 6 | 7 |
|---|---|---|---|
|---|---|---|---|
| 8 | 9 | a | b |
|---|---|---|---|
|---|---|---|---|
| c | d | e | f |
|---|---|---|---|

What is the 4-digit PIN?
Attempt> 00000001000200030004000500060007...
You've defeated this vault.
You've made it to vault #15.

|---|---|---|---|
| 0 | 1 | 2 | 3 |
|---|---|---|---|
|---|---|---|---|
| 4 | 5 | 6 | 7 |
|---|---|---|---|
|---|---|---|---|
| 8 | 9 | a | b |
|---|---|---|---|
|---|---|---|---|
| c | d | e | f |
|---|---|---|---|

What is the 4-digit PIN?
Attempt> 00000001000200030004000500060007...
You've defeated this vault.
You've made it to vault #16.

|---|---|---|---|
| 0 | 1 | 2 | 3 |
|---|---|---|---|
|---|---|---|---|
| 4 | 5 | 6 | 7 |
|---|---|---|---|
|---|---|---|---|
| 8 | 9 | a | b |
|---|---|---|---|
|---|---|---|---|
| c | d | e | f |
|---|---|---|---|

What is the 4-digit PIN?
Attempt> 00000001000200030004000500060007...
You've defeated this vault.
You unlock the vault and find the flag.

irisctf{de_bru1jn_s3quenc3s_c4n_mass1vely_sp33d_up_bru7e_t1me_f0r_p1ns}
irisctf{de_bru1jn_s3quenc3s_c4n_mass1vely_sp33d_up_bru7e_t1me_f0r_p1ns}

dhash (Cryptography)

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

・e = 65537
・N: 2048ビット素数(N - 1 が e と互いに素)
・hash = MySeededHash(N, e)
 ・hash.N = N
 ・hash.e = e
 ・hash._state = b"\x00" * 256
 ・hash.seen: 空集合
・hash.N, hash.eを表示
・preimage: 入力→hexデコード
・preimageの長さが256未満または256の倍数でない場合、エラー
・zero = hash.update(preimage).hexdigest()
 ・preimageの256バイトごとのblockに対して以下を実行
  ・hash._state: hash._stateとhash._hash_block(block)のXOR
   ・hash._hash_block(block)
    ・blockがhash.seenにある場合、エラー
    ・hash.seenにblockを追加
    ・data: blockを数値化したもの
    ・dataが2より小さいか、N-1以上の場合、エラー
    ・data = pow(data, hash.e, hash.N)
    ・dataがhash.seenにある場合、エラー
    ・hash.seenにdataを追加
    ・dataの256バイト文字列化したものを返却
 ・hash._stateの16進数文字列を返却
・zeroを表示
・zeroが'00'*256と一致している場合、フラグを表示

例えばブロックごとに以下の構成をとることを考える。

1ブロック目:2 --(hash)--> pow(2, e, N)
2ブロック目:3 --(hash)--> pow(3, e, N)
3ブロック目: x --(hash)--> pow(x, e, N)

この場合、以下の式を満たす必要がある。

pow(2, e, N) ^ pow(3, e, N) ^ pow(x, e, N) = 0

これから以下が算出できる。

pow(x, e, N) = pow(2, e, N) ^ pow(3, e, N)
phi = N - 1
d = inverse(e, phi)
x = pow(pow(x, e, N), d, N)

あとは2, 3, xをブロック単位で指定すればよい。

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

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

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('dhash.chal.irisc.tf', 10101))

data = recvuntil(s, b'\n').rstrip()
print(data)
data = recvuntil(s, b'\n').rstrip()
print(data)
N = int(data.split('(')[1].split(', ')[0])
e = int(data.split(')')[0].split(', ')[1])

c = pow(2, e, N) ^ pow(3, e, N)
phi = N - 1
d = inverse(e, phi)
x = pow(c, d, N)

b0 = (2).to_bytes(256, 'big').hex()
b1 = (3).to_bytes(256, 'big').hex()
b2 = (x).to_bytes(256, 'big').hex()
preimage = b0 + b1 + b2

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

実行結果は以下の通り。

== proof-of-work: disabled ==
MySeededHash(20856703289904463430614936967447399003010219239766788023499304705341829448605677737530659065543047577901016843669332171739778801288266130649313946832286498817360113232142516763331618511644971217872013312748089418628377787560744473886899059105412813483871290613581925999782738861052958511454017591532389448873544493156653379389937302800793592743880327606061706063880169508047774037472497251242866205416358529109526926204514508849250470465031811692600988474527176959033393532522001777134189809921599451130722100647042748784863663934357387534701514620590264656433755077591566527365859327092086435832750196364260261738943, 65537)
Give me your string that hashes to 0...
> 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000380ef96387871a50d7e7e8904a4445012fe6aaff4c864941b9f440711e79cb8d7d1a25f580a081cb7c9a6846b025eefcf96d8d96ce8b4ebb98db1aebed4ac618826a00614b4ff3a6f24bdfe80e35cb00ebd737ac2135525ce23716c0019e934b35d6c4b5318669b79b9c731681ec77d0e6566d5833df23bc8339936464729450d3a4f13319a7b7c344686c205dfb19895329f8137ce97aba2d12788cd8bf5efcef16f970755be8d72f6a8a918f1b43592c08bf77dedb0f17a09f81003d58ca8cf24a1a73ed54a2a2119ddd006e6ef16c154885788a928aaf96ecb75a6ef9363b7119b1fd76e73a6b5ec13024a0faa5a13046c17eac4e930687f379fd6cdf11e7c
hash(input) == 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
irisctf{no_order_factorization_no_problem}
irisctf{no_order_factorization_no_problem}

Integral Communication (Cryptography)

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

・key: ランダム16バイト文字列
・以下繰り返し
 ・sel: 入力
 ・selが1の場合
  ・msg: 入力
  ・iv, command = create_command(msg)
   ・payload = {"from": "guest", "act": "echo", "msg": msg}
   ・payload: payloadをJSONダンプし、文字列化
    ※例:{"from": "guest", "act": "echo", "msg": "<msg>"}
   ・payloadの長さが16バイトの倍数になるようb'\x00'をパディング
   ・iv, payload = encrypt(payload)
    ・iv: ランダム16バイト文字列
    ・ivを16進数表記で表示
    ・ivとpayloadのAES-CBC暗号化文字列を返却
   ・ivの16進数表記文字列とpayloadの16進数表記文字列を返却
  ・iv, commandを表示
 ・selが2の場合
  ・iv: 入力
  ・command: 入力
  ・run_command(iv, command)
   ・iv: ivをhexデコード
   ・command: commandをhexデコード
   ・command = decrypt(command, iv)
    ・commandのAES-CBC暗号化文字列を返却
   ・command: commandの右側のb'\x00'を削除
   ・command: commandをJSONロード
   ・commandの"act"の値が"echo"の場合
    ・msg: commandの"msg"の値
    ・msgを表示
   ・commandの"act"の値が"flag"の場合
    ・commandの"from"の値が"admin"の場合、フラグを表示
 ・selが3の場合、終了

以下のデータの暗号化データを作成したい。

0123456789abcdef
{"from": "admin"
, "act": "flag"}

以下のようにして暗号化データを作成する。

・適当なデータをrun_commandで復号する。
・復号した結果を2ブロック目の平文とXORしたものを1ブロック目の暗号化データとする。
・1ブロック目の暗号化データをrun_commandで復号する。
・復号した結果を2ブロック目の平文とXORしたものをivとする。

あとはこのデータを指定して、run_commandを実行する。

#!/usr/bin/env python3
import socket
from Crypto.Util.strxor import strxor
from binascii import hexlify, unhexlify

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

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('integral-communication.chal.irisc.tf', 10103))

target = b'{"from": "admin", "act": "flag"}'

iv = '0' * 32
ct1 = '0' * 32
command = ct1
data = recvuntil(s, b'> ')
print(data + '2')
s.sendall(b'2\n')
data = recvuntil(s, b': ')
print(data + iv)
s.sendall(iv.encode() + b'\n')
data = recvuntil(s, b': ')
print(data + command)
s.sendall(command.encode() + b'\n')
data = recvuntil(s, b'\n').rstrip()
print(data)

ct0 = strxor(target[16:], bytes.fromhex(data.split(' ')[-1]))
ct0 = hexlify(ct0).decode()
command = ct0
data = recvuntil(s, b'> ')
print(data + '2')
s.sendall(b'2\n')
data = recvuntil(s, b': ')
print(data + iv)
s.sendall(iv.encode() + b'\n')
data = recvuntil(s, b': ')
print(data + command)
s.sendall(command.encode() + b'\n')
data = recvuntil(s, b'\n').rstrip()
print(data)

iv = strxor(target[:16], bytes.fromhex(data.split(' ')[-1]))
iv = hexlify(iv).decode()
command = ct0 + ct1
data = recvuntil(s, b'> ')
print(data + '2')
s.sendall(b'2\n')
data = recvuntil(s, b': ')
print(data + iv)
s.sendall(iv.encode() + b'\n')
data = recvuntil(s, b': ')
print(data + command)
s.sendall(command.encode() + b'\n')
data = recvuntil(s, b'\n').rstrip()
print(data)

実行結果は以下の通り。

== proof-of-work: disabled ==
---------------------------------------------------------------------------
1. Create command
2. Run command
3. Exit
---------------------------------------------------------------------------
> 2
IV: 00000000000000000000000000000000
Command: 00000000000000000000000000000000
Failed to decode UTF-8: dc79af429e49d2d1a5fad5a6fbf5561e
---------------------------------------------------------------------------
1. Create command
2. Run command
3. Exit
---------------------------------------------------------------------------
> 2
IV: 00000000000000000000000000000000
Command: f0598d23fd3df0eb85d8b3ca9a927463
Failed to decode UTF-8: f56fccf9d16ab2bda21fdd3c07d21c56
---------------------------------------------------------------------------
1. Create command
2. Run command
3. Exit
---------------------------------------------------------------------------
> 2
IV: 8e4daa8bbe079087823dbc586abb7274
Command: f0598d23fd3df0eb85d8b3ca9a92746300000000000000000000000000000000
Congratulations! The flag is: irisctf{cbc_d03s_n07_m34n_1n73gr1ty}
irisctf{cbc_d03s_n07_m34n_1n73gr1ty}

Exit Survey (Welcome)

指定のシークレットコードを入力し、アンケートに答えたら、フラグが表示された。

irisctf{th@nk5_4_pL@y1ng_2024}