以下の内容はhttps://yocchin.hatenablog.com/entry/2025/05/19/074731より取得しました。


BYUCTF 2025 Writeup

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

Sanity Check (Misc)

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

byuctf{bee_why_you_see_tee_eff}

Hash Psycho (Misc)

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

・FLAG: フラグ
・ADMIN = User('admin', 1337)
 ・ADMIN.username = 'admin'
 ・ADMIN.id = 1337
・name: 入力
・nameを表示
・id_: 入力
・id_が数値文字だけになっていない場合、終了する。
・id_: id_を数値型にする。
・idが1337の場合、終了する。
・YOURUSER = User(name, id_)
・choice: 数値入力
・choiceが1の場合
 ・終了する。
・choceが2の場合
 ・YOURUSERのidのhashとADMINのidのhashが一致している場合、フラグを出力

pythonのhash関数で1337以外の数値で、1337になるものを指定できればフラグが得られる。まずhash関数をいくつか試してみる。

>>> hash(999999999999999999)
999999999999999999
>>> hash(9999999999999999999)
776627963145224195
>>> hash(99999999999999999999)
848750603811160106
>>> hash(9999999999999999998)
776627963145224194
>>> hash(9999999999999999997)
776627963145224193

一定の値を超えると、順に大きくなっていると推測できる。1337の次に、hashの結果が1337になる値は以下のように算出できる。

>>> 9999999999999999997 - 776627963145224193 + 1337
9223372036854777141

実際に確認してみる。

>>> hash(9223372036854777141)
1337

正しいことがわかったので、この値をidとして入力してみる。

$ nc psycho.chal.cyberjousting.com 1353
Welcome to onboarding! I'm Jacob from HR, and I'm here to make your experience as seamless as possible joining the company
Go ahead and tell me your name:
hoge
Welcome to the company, hoge
We also give you a user id, but in an attempt to make this company feel like home, we've decided to give you a choice in that, too. Go ahead and choose that now:
9223372036854777141
Okay, you're all set! Just head into your office. The admin's is right next door, but you can just ignore that
*You realize you have freedom of choice. Choose a door*
      1) your office
      2) the admin's office

2
byuctf{wh0_kn3w_h4sh_w4snt_h4sh}
byuctf{wh0_kn3w_h4sh_w4snt_h4sh}

Skywim (OSINT)

Skyrimで次の添付の写真が撮られた場所の名前を答える問題。

画像検索すると、完全一致で以下のページが見つかる。

https://www.reddit.com/r/skyrim/comments/1kojocp/wheres_this_place/

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

Should be on the mountain just by dragon bridge overlook (small forsworn camp)
byuctf{Dragon_Bridge_Overlook}

u (Rev)

関数名が別のもので定義されていて、難読化されている。元の関数名に置き換え直し、読みやすくコードを変換する。
まず、最初の関数名を単純に置換する。

chr=[12838,1089,16029,13761,1276,14790,2091,17199,2223,2925,17901,3159,18135,18837,3135,19071,4095,19773,4797,4085,20007,5733,20709,17005,2601,9620,3192,9724,3127,8125];
u,U=3,256;
dict=input();
ʉ=list(set([pow(u,abs,U) for abs in(range(U))]))[u:len(chr)+u];
list=zip;
dict=[ord(abs) for abs in(dict)];
assert(len(chr)==len(dict));
assert(all([abs*input==print for abs,input,print in(list(ʉ,dict,chr))]));

chrがdictなど変数名として利用している箇所はわかりにくいので、さらに読み換え、整形する。

nums = [12838, 1089, 16029, 13761, 1276, 14790, 2091, 17199, 2223, 2925, 17901, 3159, 18135, 18837, 3135, 19071, 4095, 19773, 4797, 4085, 20007, 5733, 20709, 17005, 2601, 9620, 3192, 9724, 3127, 8125];
u, U = 3, 256;
inp = input();
xs = list(set([pow(3, i, 256) for i in range(256)]))[3:len(nums) + 3];
ys = [ord(c) for c in inp];
assert(len(nums) == len(ys));
assert(all([x * y == z for x, y, z in zip(xs, ys, nums)]));

xs, numsは算出できるので、逆算してysを算出し、文字にする。

#!/usr/bin/env python3
nums = [12838, 1089, 16029, 13761, 1276, 14790, 2091, 17199, 2223, 2925, 17901, 3159, 18135, 18837, 3135, 19071, 4095, 19773, 4797, 4085, 20007, 5733, 20709, 17005, 2601, 9620, 3192, 9724, 3127, 8125]
xs = list(set([pow(3, i, 256) for i in range(256)]))[3:len(nums) + 3]

flag = ''
for x, z in zip(xs, nums):
    assert z % x == 0
    y = z // x
    flag += chr(y)
print(flag)
byuctf{uuuuuuu_uuuu_uuu_34845}

Are You Looking Me Up? (Forensics)

添付のログから、最も多くのDNSリクエストを受信して​​いるDNSサーバを読み取る問題。
グレコードのDNSリクエストのカラム数は23あり、最後から2番目のカラムがポート番号53、最後から4番目がDNSサーバのIPになっていることが推測できる。あとはその条件を満たすログで、同一IPのDNSサーバへのアクセスの数をIPアドレスごとに算出し、最も多いものを答える。

#!/usr/bin/env python3
with open('logs.txt', 'rb') as f:
    lines = f.read().splitlines()

ips = {}
for line in lines:
    clms = line.split(b',')
    clm_count = len(clms)
    if clm_count == 23:
        dst_port = int(clms[-2])
        if dst_port == 53:
            ip = clms[-4]
            if ip not in ips:
                ips[ip] = 1
            else:
                ips[ip] +=1

max_ip = b''
max_count = 0
for ip, count in ips.items():
    if count > max_count:
        max_ip = ip
        max_count = count

flag = 'byuctf{%s}' % max_ip.decode()
print(flag)
byuctf{172.16.0.1}

Mine Over Matter (Forensics)

添付のログから、暗号通貨のマイニングを実行している2つのホストのIPアドレスを分析する問題。
内部から外部に向けてTCP/443接続しているものがあるので、抽出してみる。該当するレコードは、以下のようになっている。

2025-05-06T14:49:32+00:00 164,,,75a2b136446ad166a85f3150b40b7d1e,vtnet0,match,pass,in,4,0x0,,128,9674,0,DF,6,tcp,52,172.16.0.10,4.149.227.78,65308,443,0,S,3208345317,,64240,,mss;nop;wscale;nop;nop;sackOK

最後から8番目のカラムがポート番号443、最後から11番目が通信元、最後から10番目が通信先になっていることがわかる。通信先が"172."から始まらないもので、通信が多い送信元がマイニングのホストであると推測し、そのIPを2つ答える。

#!/usr/bin/env python3
with open('logs.txt', 'rb') as f:
    lines = f.read().splitlines()

ips = {}
for line in lines:
    clms = line.split(b',')
    clm_count = len(clms)
    if clm_count == 23:
        dst_port = int(clms[-2])
        if dst_port == 53:
            ip = clms[-4]
            if ip not in ips:
                ips[ip] = 1
            else:
                ips[ip] +=1

max_ip = b''
max_count = 0
for ip, count in ips.items():
    if count > max_count:
        max_ip = ip
        max_count = count

flag = 'byuctf{%s}' % max_ip.decode()
print(flag)
byuctf{172.16.0.5,172.16.0.10}

Wimdows 3 (Forensics)

ovaが添付されているので、その環境で攻撃者が作成したアカウントが追加されたグループ名を答える問題。
ovaを解凍後、vmdkからWindows\System32\winevt\LogsのSecurity.evtxをエクスポートし、イベントビューアで見てみる。
イベントID 4720を見てみると、いろんなアカウントがプライマリグループID 513で作成されている。またイベントID 4632で見てみると、作成されているユーザにUsersのグループに割り当てられていることがわかる。他に割り当てられているグループにはRemote Desktop Usersがあった。

byuctf{Remote Desktop Users}

Many Primes (Crypto)

Multi-Prime RSAの問題。構成する素数のビット数は小さいので、nをprimefacで素因数分解する。

$ python3 -m primefac 84957339878841249042661992015318775914153057891067549723671982778975957526984361748735670333426792012519248099265537115315835602057882318543112781196438953840653279029957533208045448649312183873062132255928109150470090982541502003013666939934181416473581073990221728448342533549843710383544743220364127453679761909368982493089369740815066106196444062558446312836612656666434089655206650558129894180991572670565328419648196602807190683488653617068325238542193033423978598565191919683033996144778397817249870630736098769732268174866389943010980427234356604782502318622853423388492983406635687647680770869588481426436811952986075832678887752706507835771052708232550208582584262345437818916577580806059662945066377375408508814381061305598911972399165132279018674832071994673661875185328115121586302490349253912699566783660070599552395205352917554104371784905394477629626963439266490743240318198729049054547013391348516066097465180775402810975266096525722415778248668765855332776864126033830969325570615444114396786906102536716873598804164196155179833683285635189223166238933318740720918827120989416013091102543382987278707392961157272937695585450287755023671116911370689655292160336521776501052329465384315585357461493649222793172462113102463
84957339878841249042661992015318775914153057891067549723671982778975957526984361748735670333426792012519248099265537115315835602057882318543112781196438953840653279029957533208045448649312183873062132255928109150470090982541502003013666939934181416473581073990221728448342533549843710383544743220364127453679761909368982493089369740815066106196444062558446312836612656666434089655206650558129894180991572670565328419648196602807190683488653617068325238542193033423978598565191919683033996144778397817249870630736098769732268174866389943010980427234356604782502318622853423388492983406635687647680770869588481426436811952986075832678887752706507835771052708232550208582584262345437818916577580806059662945066377375408508814381061305598911972399165132279018674832071994673661875185328115121586302490349253912699566783660070599552395205352917554104371784905394477629626963439266490743240318198729049054547013391348516066097465180775402810975266096525722415778248668765855332776864126033830969325570615444114396786906102536716873598804164196155179833683285635189223166238933318740720918827120989416013091102543382987278707392961157272937695585450287755023671116911370689655292160336521776501052329465384315585357461493649222793172462113102463: 523 523 523 523 547 547 547 547 653 653 653 673 673 673 673 683 683 683 683 773 773 3259 6221 1171 6211 1787 1201 1201 3643 1423 2903 1171 2903 1009 8209 1187 1657 1049 1193 1423 6211 1283 1201 5507 3259 1049 1193 1171 11971 2903 1009 1637 1193 19381 1361 1697 1657 2699 2287 1487 2213 11971 6653 12653 3259 1009 1637 2699 1697 1607 1637 3527 1697 2287 2251 1187 1187 1637 1423 2251 1697 3313 1487 1697 6469 1423 1283 1487 6599 1709 2213 2699 5441 1607 2099 3359 5081 3313 10169 1901 3527 3643 20681 10301 5437 2621 3803 8581 10301 2287 3593 3527 5081 3947 2251 1607 21193 1709 3803 7207 11971 5507 13619 5437 11971 5441 15329 7207 3359 2971 3947 3593 2621 2213 5081 1361 3643 1361 20681 3089 3089 2251 5437 6599 2621 13963 13619 8719 9181 6469 3643 2971 9181 6653 1901 8623 6653 13619 13963 6653 12653 6599 2621 2621 4801 6599 1901 1901 9187 8623 23167 15233 6829 8623 23167 11279 26759 6599 5081 54773 12959 21193 2971 17959 12653 12893 2971 15329 40559 45989 6829 16447 6469 8209 6829 7207 6829 16447 9181 6469 8719 4801 6221 8209 8623 9187 7207 12893 4801 45989 9349 17959 8581 7207 19381 26119 22171 6221 12653 16447 9349 18461 19381 16447 31267 9187 12893 8209 9349 15329 11279 11279 54773 13381 26759 12893 11971 12959 11093 26119 11279 9349 9187 26759 17959 15329 15329 16447 9349 11093 31267 15473 15473 23167 45989 23167 15473 29333 22171 62129 8581 15473 13381 13381 29333 33617 26119 23167 40559 26119 17959 29333 47939 51239 45989 45989 62129 40559 17959 18461 47939 18461 47939 33617 40559 62129 62129 51239 40559 51239 55843 55843 62129 11093 26119 11279 18461 2971 18461 3643 33617 1607 2099 3947 12653 6221 2099 2287 8581 10169 15233 5507 1787 12893 1009 1901 1709 1487 1487 1759 1187 2251 6221 1709 1283 1193 1187 1759 5081 3313

あとはこのことを元に同じ値のべき乗があることに注意しphiを算出し、通常通り復号する。

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

with open('output.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])

factors = [523, 523, 523, 523, 547, 547, 547, 547, 653, 653, 653, 673, 673, 673, 673, 683, 683, 683, 683, 773, 773, 3259, 6221, 1171, 6211, 1787, 1201, 1201, 3643, 1423, 2903, 1171, 2903, 1009, 8209, 1187, 1657, 1049, 1193, 1423, 6211, 1283, 1201, 5507, 3259, 1049, 1193, 1171, 11971, 2903, 1009, 1637, 1193, 19381, 1361, 1697, 1657, 2699, 2287, 1487, 2213, 11971, 6653, 12653, 3259, 1009, 1637, 2699, 1697, 1607, 1637, 3527, 1697, 2287, 2251, 1187, 1187, 1637, 1423, 2251, 1697, 3313, 1487, 1697, 6469, 1423, 1283, 1487, 6599, 1709, 2213, 2699, 5441, 1607, 2099, 3359, 5081, 3313, 10169, 1901, 3527, 3643, 20681, 10301, 5437, 2621, 3803, 8581, 10301, 2287, 3593, 3527, 5081, 3947, 2251, 1607, 21193, 1709, 3803, 7207, 11971, 5507, 13619, 5437, 11971, 5441, 15329, 7207, 3359, 2971, 3947, 3593, 2621, 2213, 5081, 1361, 3643, 1361, 20681, 3089, 3089, 2251, 5437, 6599, 2621, 13963, 13619, 8719, 9181, 6469, 3643, 2971, 9181, 6653, 1901, 8623, 6653, 13619, 13963, 6653, 12653, 6599, 2621, 2621, 4801, 6599, 1901, 1901, 9187, 8623, 23167, 15233, 6829, 8623, 23167, 11279, 26759, 6599, 5081, 54773, 12959, 21193, 2971, 17959, 12653, 12893, 2971, 15329, 40559, 45989, 6829, 16447, 6469, 8209, 6829, 7207, 6829, 16447, 9181, 6469, 8719, 4801, 6221, 8209, 8623, 9187, 7207, 12893, 4801, 45989, 9349, 17959, 8581, 7207, 19381, 26119, 22171, 6221, 12653, 16447, 9349, 18461, 19381, 16447, 31267, 9187, 12893, 8209, 9349, 15329, 11279, 11279, 54773, 13381, 26759, 12893, 11971, 12959, 11093, 26119, 11279, 9349, 9187, 26759, 17959, 15329, 15329, 16447, 9349, 11093, 31267, 15473, 15473, 23167, 45989, 23167, 15473, 29333, 22171, 62129, 8581, 15473, 13381, 13381, 29333, 33617, 26119, 23167, 40559, 26119, 17959, 29333, 47939, 51239, 45989, 45989, 62129, 40559, 17959, 18461, 47939, 18461, 47939, 33617, 40559, 62129, 62129, 51239, 40559, 51239, 55843, 55843, 62129, 11093, 26119, 11279, 18461, 2971, 18461, 3643, 33617, 1607, 2099, 3947, 12653, 6221, 2099, 2287, 8581, 10169, 15233, 5507, 1787, 12893, 1009, 1901, 1709, 1487, 1487, 1759, 1187, 2251, 6221, 1709, 1283, 1193, 1187, 1759, 5081, 3313]

ps = {}
for factor in factors:
    if factor not in ps:
        ps[factor] = 1
    else:
        ps[factor] += 1

phi = 1
for p, reps in ps.items():
    phi *= (p - 1) * p ** (reps - 1)

d = inverse(e, phi)
m = pow(c, d, n)
flag = long_to_bytes(m).decode()
print(flag)
byuctf{3ulers_ph1_function_15_v3ry_us3ful_4nd_th15_I5_a_l0ng_fl4g}

PEM (Crypto)

nの平方根にフラグが含まれているらしいので、算出し文字にしてみる。

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

with open('ssh_host_rsa_key.pub', 'r') as f:
    pub_data = f.read()

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

sqrt_n, success = iroot(n, 2)
assert success

msg = long_to_bytes(sqrt_n).decode()
print(msg)
fjagkdflgfkdsjgfdltyugvjcbghjqfsdjvfdhbjfd byuctf{P3M_f0rm4t_1s_k1ng} cmxvblalsfiuqeuipplvdldbnmjzxydhjgfdgppsksjq

この中にフラグが含まれていた。

byuctf{P3M_f0rm4t_1s_k1ng}

Real Smooth (Crypto)

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

・key: 32バイトランダム文字列
・nonce: 8バイトランダム文字列
・cipher: key, nonceを使ったChaCha20暗号オブジェクト
・cipherで'Slide to the left'を暗号化した結果を16進数表記で表示
・cipherで'Slide to the right'を暗号化した結果を16進数表記で表示
・user_in: 入力
・cipher: key, nonceを使ったChaCha20暗号オブジェクト
・decrypted: cipherでuser_inをhexデコードしたものを復号した結果
・decryptedが'Criss cross, criss cross'の場合、フラグを表示

keyとnonceが同じ場合、平文と暗号文のXORは同じになる。'Slide to the left'の暗号と'Slide to the right'の暗号はオブジェクトを継続して使っているので、文字列として結合したものの暗号と考えることができる。
これを踏まえてXORで'Criss cross, criss cross'を暗号化したものを渡せばよい。

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

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

pt1 = b'Slide to the left'
pt2 = b'Slide to the right'
pt = b'Criss cross, criss cross'

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('smooth.chal.cyberjousting.com', 1350))

data = recvuntil(s, b'\n').rstrip()
print(data)
ct1 = bytes.fromhex(data)
data = recvuntil(s, b'\n').rstrip()
print(data)
ct2 = bytes.fromhex(data)

key = strxor(pt1 + pt2, ct1 + ct2)
ct = strxor(pt, key[:len(pt)])
ct_hex = ct.hex()
print(ct_hex)
s.sendall(ct_hex.encode() + b'\n')
data = recvuntil(s, b'\n').rstrip()
print(data)
data = recvuntil(s, b'\n').rstrip()
print(data)

実行結果は以下の通り。

457a8e8a3fcc4ce61d5407988b9c92dceb
9239c4e130dd6b538a08c75751145d4197f2
55648e9d29cc5bfb52531cd18b9385d3ecb275cef73a8e6c
Cha cha real smooth
byuctf{ch4ch4_sl1d3?...n0,ch4ch4_b1tfl1p}
byuctf{ch4ch4_sl1d3?...n0,ch4ch4_b1tfl1p}

Anaken21sec1 (Crypto)

逆算していけばよい。暗号文字列の長さは48で、鍵の長さは11のため、letterBoxesの構成は以下の文字の構成になる。

[[5つの文字の配列]が4、[4つの文字の配列]が7]

このことを元に、resultLettersを復元する。あとはアルファベットを3進数で3要素の数字にしていることを使って復号する。

#!/usr/bin/env python3
import numpy as np

A = np.array([[1, 7, 13, 19, 25, 31],
            [2, 8, 14, 20, 26, 32],
            [3, 9, 15, 21, 27, 33],
            [4, 10, 16, 22, 28, 34],
            [5, 11, 17, 23, 29, 35],
            [6, 12, 18, 24, 30, 36]])
B = np.array([[36, 30, 24, 18, 12, 6],
            [35, 29, 23, 17, 11, 5],
            [34, 28, 22, 16, 10, 4],
            [33, 27, 21, 15, 9, 3],
            [32, 26, 20, 14, 8, 2],
            [31, 25, 19, 13, 7, 1]])
C = np.array([[31, 25, 19, 13, 7, 1],
            [32, 26, 20, 14, 8, 2],
            [33, 27, 21, 15, 9, 3],
            [34, 28, 22, 16, 10, 4],
            [35, 29, 23, 17, 11, 5],
            [36, 30, 24, 18, 12, 6]])
D = np.array([[7, 1, 9, 3, 11, 5],
            [8, 2, 10, 4, 12, 6],
            [19, 13, 21, 15, 23, 17],
            [20, 14, 22, 16, 24, 18],
            [31, 25, 33, 27, 35, 29],
            [32, 26, 34, 28, 36, 30]])
E = np.array([[2, 3, 9, 5, 6, 12],
            [1, 11, 15, 4, 29, 18],
            [7, 13, 14, 10, 16, 17],
            [20, 21, 27, 23, 24, 30],
            [19, 8, 33, 22, 26, 36],
            [25, 31, 32, 28, 34, 35]])
permutes = [A, B, C, D, E]

def rev_permute(blockM, count):
    finalBlockM = np.zeros((6, 6))
    for i in range(6):
        for j in range(6):
            index = int(permutes[count][i, j] - 1)
            finalBlockM[index // 6, index % 6] = blockM[i, j]
    return finalBlockM

def rev_add(blockM, count):
    if count == 0:
        for i in range(6):
            for j in range(6):
                if (i + j) % 2 == 0:
                    blockM[i, j] -= 1
    elif count == 1:
        blockM[3:,3:] = blockM[3:,3:] - blockM[:3,:3]
    elif count == 2:
        blockM[:3,:3] = - blockM[3:,3:] + blockM[:3,:3]
    elif count == 3:
        blockM[3:,:3] = blockM[3:,:3] - blockM[:3,3:]
    else:
        blockM[:3,3:] = - blockM[3:,:3] + blockM[:3,3:]

    return np.mod(blockM, 3)

def decrypt(ciphertext, key):
    keyNums = [ord(key[i]) - 97 for i in range(len(key))]
    reducedKeyNums = []
    [reducedKeyNums.append(x) for x in keyNums if x not in reducedKeyNums]
    letterBoxes = [[] for i in reducedKeyNums]

    index = 0
    for i in range(len(reducedKeyNums)):
        nextLowest = reducedKeyNums.index(min(reducedKeyNums))
        reducedKeyNums[nextLowest] = 27
        if nextLowest < 4:
            length = 5
        else:
            length = 4
        letterBoxes[nextLowest] = list(ciphertext[index:index + length])
        index += length

    resultLetters = ''
    for i in range(len(ciphertext) // len(key) + 1):
        for j in range(len(key)):
            if i < len(ciphertext) // len(key) or j < 4:
                resultLetters += letterBoxes[j][i]

    blocks = [resultLetters[i:i+12] for i in range(0, len(resultLetters), 12)]

    plaintext = ''
    for block in blocks:
        blockM = np.zeros((6, 6))
        for i in range(6):
            letter = block[i]
            if letter == '0':
                resultLetterNum = 0
            else:
                resultLetterNum = ord(letter) - 96
            blockM[i, 0] = resultLetterNum // 9
            blockM[i, 1] = (resultLetterNum % 9) // 3
            blockM[i, 2] = resultLetterNum % 3
        for i in range(6):
            letter = block[i + 6]
            if letter == '0':
                resultLetterNum = 0
            else:
                resultLetterNum = ord(letter) - 96
            blockM[i, 3] = resultLetterNum // 9
            blockM[i, 4] = (resultLetterNum % 9) // 3
            blockM[i, 5] = resultLetterNum % 3

        for keyNum in keyNums[::-1]:
            blockM = rev_add(blockM, keyNum % 5)
            blockM = rev_permute(blockM, (keyNum // 5) % 5)

        for i in range(6):
            letterNum = 9 * blockM[0, i] + 3 * blockM[1, i] + blockM[2, i]
            plaintext += chr(int(letterNum) + 96)
        for i in range(6):
            letterNum = 9 * blockM[3, i] + 3 * blockM[4, i] + blockM[5, i]
            plaintext += chr(int(letterNum) + 96)

    return plaintext

key = 'orygwktcjpb'
ciphertext = 'cnpiaytjyzggnnnktjzcvuzjexxkvnrlfzectovhfswyphjt'
plaintext = decrypt(ciphertext, key)

flag = 'byuctf{%s}' % plaintext
print(flag)
byuctf{revisreallythestartingpointformostcategoriesiydk}

Cycles (Crypto)

pow(g, a, p)が1になることからaはp-1の倍数であることがわかる。aのブルートフォースで復号する。

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

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

g = int(params[0].split(' = ')[1])
p = int(params[1].split(' = ')[1])
ct = eval(params[2].split(' = ')[1])
hint = int(params[3].split(' = ')[1])

base = p - 1

i = 1
while True:
    a = base * i
    if len(bin(a)) >= 1050:
        break
    key = long_to_bytes(a)[:16]
    cipher = AES.new(key, AES.MODE_ECB)
    i += 1
    try:
        flag = unpad(cipher.decrypt(ct), AES.block_size).decode()
        print(flag)
        break
    except:
        continue
byuctf{1t_4lw4ys_c0m3s_b4ck_t0_1_21bcd6}

Choose Your RSA (Crypto)

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

・key: ランダム160バイト文字列
・p, q, n, e = [], [], [], []
・0以上3未満のiに対して以下を実行
 ・pに1024+512*iビット素数を追加
 ・qに1024+512*iビット素数を追加
 ・nにp[i]*q[i]を追加
・cipher: keyの先頭16バイトを鍵としてAES ECBモード暗号化オブジェクト
・flagをパディングしてcipherで暗号化して16進数表記で表示
・e: スペース区切りで数値を3つ入力
・e[0]が1より大きいことをチェック
・e[1]がe[0]より大きいことをチェック
・e[2]がe[1]1より大きいことをチェック
・key: keyを数値化したもの
・0以上3未満のiに対して以下を実行
 ・n[i]を表示
 ・pow(key, e[i], n[i])を表示

keyを割り出せば、フラグを復号できる。
keyは、異なるe, nで3個以下のRSA暗号を取得できるので、そのことを使う。
eは同じ数字を指定できれば、Hastad's Broadcast Attackが使えるが、そのままでは使えない。

c[i] = pow(key, e[i], n[i])と置く。
e[0]に2、e[1]に4を指定し、c[0]を2乗することでeの値を揃えることを考える。

c[0] = pow(key, 2, n[0])
c[1] = pow(key, 4, n[1])
        ↓
pow(c[0], 2 ,n[0]) = pow(pow(key, 2), 2, n[0])
c[1] = pow(pow(key, 2), 2, n[1])

Rabin暗号になるが、Hastad's Broadcast Attackは使え、pow(key, 2)を求めることはできる。あとはそれの平方根を割り出せば、keyがわかる。

#!/usr/bin/env python3
import socket
from sympy.ntheory.modular import crt
from gmpy2 import iroot
from Crypto.Cipher import AES
from Crypto.Util.number import long_to_bytes
from Crypto.Util.Padding import unpad

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(('choose.chal.cyberjousting.com', 1348))

data = recvuntil(s, b'\n').rstrip()
print(data)
data = recvuntil(s, b'\n').rstrip()
print(data)
enc_flag = bytes.fromhex(data)
data = recvuntil(s, b'\n').rstrip()
print(data)
e_str = '2 4 8'
print(e_str)
s.sendall(e_str.encode() + b'\n')

n, c = [], []
for i in range(3):
    data = recvuntil(s, b'\n').rstrip()
    print(data)
    n.append(int(data.split('=')[1]))
    data = recvuntil(s, b'\n').rstrip()
    print(data)
    c.append(int(data.split('=')[1]))

ns = [n[0], n[1]]
cs = [pow(c[0], 2 ,n[0]), c[1]]
me, _ = crt(ns, cs)
key, success = iroot(me, 2 * 2)
assert success
for i in range(3):
    assert pow(key, pow(2, i + 1), n[i]) == c[i]

key = long_to_bytes(key)
cipher = AES.new(key[:16], AES.MODE_ECB)
flag = unpad(cipher.decrypt(enc_flag), AES.block_size).decode()
print(flag)

実行結果は以下の通り。

[+] Generating values...
e598be131fb0de8dba924fcce19dc55ff5cc38b57e01d4f95a93ff030d94a309f75afa0d5911f1bd5445fd0aac9ff1f6bdb3a72e13a33e99d13154297aa24904
We will encrypt the key three times, and you can even choose the value of e. Please put your distinct e values in increasing order.
2 4 8
n0=27785687871656941747168927613734316202846791761889154198317553268249628278098139307391085576122592798877435985841125085294656155413527155415121625773262217055639218599846551953998227705937735776073219477061104260211888703052814812187708893119032984264518579862883567352773078589449802059814099391942239448541558134792087656523595815164082313829655489871951500871910367168019912245479916292814951445449681312917759870936109951448637407761892916111197163918367176764355422622008212664331443383466063574840553880125236735991491986541628617072820843561210541536769999220920504832344991330119915221769028325616532788680753
c0=27368407619894733814166018425810353242061274330666279990893276037456977020135991209608416404387908561052315255723570916569699035299758863792571417530732660651849071130969421640024249823638901431455280014024409086283376149703188915803860554246030468475938295806313868657050711405472556041259118570260109581912786660244545600905156625731135136039283192006244360200067294025239340920813341064578149490787010150930248296561972248669828686087088021602391072238338272737809425426302830809899673859986693395595103332582465852855574274739202289594313844193043368183164560393494499857956063675992742566867889161248107835951333
n1=4021911043527180454274473877961416757381581990361726145330106834450045773353342877180503267052046317093757431979523539579697368819495043907824204289495196049901625648929462898682923090928946788714403946947878729480616741379764782699908651030985845802967634125012959639108189243530778573647770712270025390637120427141835824988138154315667015988006349367372648778505010281450348362975749737735227608517374881493862665810294132370966008768397130498531729362327242230485171796561660413888950075549992041668249601518203773626244712634198746623883468551645831988160017689915828537719865352613997775013456740722149069924912016256176674477201865681264115581373351285557820195624996903200135078732865424600411410228714237484748655491103872048888391226825126624161904360491509873454944552405087525452462525414362964070095243199154267479517536999965235475280539033018873043904593099899326168334287513186253546564463835726296397982531549
c1=3461561135721860825016451383294435273337020927259352996690634169214282992853209753763294943753691355820080360479175125389135365367936913961293549103906557006081587277720719958315631460088749118332465013854811826020010470429612477081533386756870949110021115901378543102090072642981610978107240469514006580973534549692411994541845946792598345212554913842969168550968115101420013769845791544218086804747274127189667653137558200318788277422037433748302690548483413891798483126460101473204750963132021372871686620502108871572659412536945813444333702509041206902286809174473888482219294235927043160574623180252874905642601419879697004384570430078559660068436660243128032506127598801325422099985960200829975826289558980844341714442074366893922812024418284427789115841564913412191070995865885632143915782892769705139627775369824978015892464280427662479929495877369169905286831071210451541305831852835598870114039925529388418074462690
n2=830599877420488729968276258701728230157774366042603709811597481910949787759729946213077822147423798297058115870313001043636612926091255504874916415986060239790410075259813735230084315625518890031328180353048854532777235225118614206528502972489899683256327969099780929103158114459659787905509484797139351441372352095357513195777210509156532968098829887408513106145507623229442001797501364881868710808699680548652710077382266133934702057575260615332782912024287695710807013691105052006726062240718778708885209981493701710788870089703551769340554186039699615721992198968258063525757041898881587094447799608273328207150523323587469151115425560635759254716382453311894890462480822328500861564244837213822396176511842641349088216584753857132911055070309189700598426769937669343180270253711297713754738769245423465399351696380576473424300209392081873027324920118903924617103817674097414413507154156768245580416761844907786493173790139393799646635618321312200924530682891103335907881514377190719952852505550161513127983862170933841934778984748099834169644297835279204621866813334961506960607597765305675919452033501294399629414866965112669362783000787258328064966604685303355645031437512814432172940111070050217050073268287771233886623764559
c2=187487286842766733836035258647877203697472876599050875770371643506043468350438959235979942892079355666330595892103291712315222448661720940277551043589065436335068888501087298465922102135442343891515023166072410703226810189460672664150768372819588840834623005495724446610569450287784388216413961043026470281816624568589753357661646805019027600889274525330646165468392942021738521609089751256602501495494061395611604119581951060473462744221488860777353815154782144253331283933529629665948492453095334684200319638305036480535964264846178106310884140930681146919844812008311996509789227734149089929068784628430907709768477561298187628313733558715508912371949720199500955186854254253547085353682832333955058188474342969372465245875954919123994641265239544020641001844074004936713295476956594642203473504311285959421100899003152479856940970581884079344649115676014589953057989219042837669475987804141764146713147950911383168086836207021483952905290266849764115404565595139899335425762513330328161788798040035959279265494475600924240129329865094470089554521155480793154701651570038715423002313133172718004162797118234658245811339265789005646985633263127822026962190919539255448810315725510829238032832128588175414873211979715112098688327101
byuctf{Chin3s3_rema1nd3r_th30r3m_is_Sup3r_H3lpful}
byuctf{Chin3s3_rema1nd3r_th30r3m_is_Sup3r_H3lpful}

Hash Based Cryptography (Crypto)

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

・KEY, FLAG: 不明
・encrypt(KEY, FLAG)を表示
 ・message: FLAGを20バイトパディング(20バイトの倍数の場合パディングなし)
 ・key = gen_otp(KEY, message)
  ・iv = key
  ・otp = b''
  ・messageの長さを20で割った値だけ以下繰り返し
   ・iv: ivのsha1ダイジェスト
   ・otpにivを結合
  ・otpを返却
 ・messageとkeyのXORを16進数表記で返却
・以下繰り返し
 ・user_input: 入力
 ・decrypted = decrypt(KEY, user_input)
  ・message: user_inputをhexデコード
  ・key = gen_otp(KEY, message)
  ・messageとkeyのXORをアンパディングして、16進数表記で返却
 ・decryptedにFLAGや"byuctf"が含まれている場合は、やり直し
 ・decryptedを表示

KEYが固定なので、otpも固定になる。入力する文字列と復号した文字列のXOR鍵をフラグの暗号化文字列とXORすれば、フラグを復号できる。
ただし、情報欠落が起こるため、Padding Oracle Attackを使う。

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

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

def check(s, ct):
    data = recvuntil(s, b'> ')
    print(data + ct)
    s.sendall(ct.encode() + b'\n')
    data = recvuntil(s, b'\n').rstrip()
    print(data)
    return data != 'Padding error'

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('hash.chal.cyberjousting.com', 1351))

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

enc_flag = bytes.fromhex(data)

keys = [b''] * (len(enc_flag) // 20)
for i in range(1, len(enc_flag) // 20):
    key = b''
    for j in range(len(key), 20):
        for code in range(256):
            print('[+] key(hex): %s' % key.hex())

            try_ct = b'A' * (20 - j - 1) + bytes([code]) + strxor(key, bytes([j + 1]) * j)
            if i == 0:
                try_ct = b'A' * 20 + try_ct
            try_ct_hex = try_ct.hex()

            res = check(s, try_ct_hex)
            if res:
                xor_code = (j + 1) ^ code
                key = bytes([xor_code]) + key
                break

    keys[(len(enc_flag) // 20) - 1 - i] = key

flag = strxor(enc_flag, b''.join(keys)).decode()
print(flag)

実行結果は以下の通り。

I just created this encryption system. I think it's pretty cool
Here's the encrypted flag:
36622b59070227bd8f106c96b0333814c5d712ef306284b32a075ad1d635963255fe630cf5606981
        :
        :
[+] key(hex): 1b5e3a73645cd0f64f07a5c96c0c7aa1887fdc
b'Error decrypting'
 > 3e0f4a2e677048c4e25b13b1dd78186eb59c6bc8
Padding error
[+] key(hex): 1b5e3a73645cd0f64f07a5c96c0c7aa1887fdc
b'Error decrypting'
 > 3f0f4a2e677048c4e25b13b1dd78186eb59c6bc8
Padding error
[+] key(hex): 1b5e3a73645cd0f64f07a5c96c0c7aa1887fdc
b'Error decrypting'
 > 400f4a2e677048c4e25b13b1dd78186eb59c6bc8
b''
byuctf{my_k3y_4nd_m3ss4g3_w3r3_th3_s4m3}
byuctf{my_k3y_4nd_m3ss4g3_w3r3_th3_s4m3}

Survey (Misc)

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

byuctf(ggs_thx_4_pl4y1ng}



以上の内容はhttps://yocchin.hatenablog.com/entry/2025/05/19/074731より取得しました。
このページはhttp://font.textar.tv/のウェブフォントを使用してます

不具合報告/要望等はこちらへお願いします。
モバイルやる夫Viewer Ver0.14