Pwn | ASEAN Open CTF 2025
by ShoIsMyName • Aug 2025
หนีห่าวมา ว่อเจี้ยวโช วันนี้จะมาแสดงวิธีทำโจทย์หมวด pwn สไตย์ผมกันนะครับ เพื่อไม่เป็นการเสียนาฬิกา ไปดูกันเลยยยยย

มาเริ่มกันที่ข้อแรก กับข้อ EZ_PWN

โดยเปิดมาก็เจอไฟล์ zip ให้เราโหลด บวกกับต้องเล่นผ่าน netcat แค่คิดก็เหนื่อยแล้ววว เลยขอเปิดที่ netcat นี่แหละ อยากรู้ว่าต้องเล่นอะไรร

เห็นขนาดนี้ก็ภาวนาในใจขอให้เอาไฟล์เข้า ghidra แล้วเจอ password พร้อมกับโหลดไฟล์ และใน zip ก็จะมีแค่นี้

Let move to ghidra!!

โดยเริ่ม step แรกของเรากันก่อน จะเริ่มจาก main()

function นี้มีการ call function 2 อันคือ check_password() และ triggerland() เราลองไปดู check_password() กันดีกว่า

เราเจอ password 2 อัน “security123” & “P@ss0wrd”


เมื่อลองใส่ password ทั้ง 2 ก็จะเห็นว่าเราควรใช้ “security123” เพราะเหมือนมันสามารถให้เราเข้า access อะไรบางอย่างของมันได้ ซึ่งนั่นก็คือ triggerland() นั่นเอง!!

แต่คำถามคือแล้ว function ที่มี flag มันอยู่ไหน ก็เลยไปเจอว่ามันอยู่ที่ onepiece()

ประเด็นมันอยู่ว่า ตอนนี้เราใส่ password แล้วมาถึง triggerland() แต่ในนี้มันไม่มีอะไรที่จะ call function onepiece() ได้เลย แล้วเราจะทำยังไง
วิธีที่เราจะใช้ก็คือ buffer overflow ไงล่ะะะ!!!
เป้าหมายของเราคือพา program ของเรากระโดดไปเรียก onepiece() ที่มี flag ซ่อนไว้!!
แต่ก่อนที่จะเริ่มโจมตี เรามาทำความเข้าใจเกี่ยวกับโปรแกรมนี้กันก่อน เจ้า triggerland() เนี่ย มันจะรับ input buffer ได้ 64 bytes ซึ่งถ้าจะ buffer overflow มันเนี่ย แน่นอนเราจะใส่ input ที่เกิน buffer ของมัน ปัญหาคือ แล้วต้องทำแบบไหนให้มันกระโดดไปที่ onepiece() กันล่ะ
เอางี้ เรามาลงลึกตรงจุดนี้กันดีกว่า

เวลา function ใน C ทำงานบน x86–64 compiler จะใช้ calling convention ที่สร้าง “stack frame” ของ function ขึ้นมา พอ triggerland() เริ่มทำงาน คำสั่ง assembly แรกๆ โครงสร้างมันจะเป็นแบบนี้

ตรง push rbp จะเก็บค่า RBP ของฟังก์ชันที่เรียกมา (caller) ไว้บน stack เรียกว่า saved RBP
stack ของ triggerland() จะประมาณนี้

เวลา get(local_48) มันจะเขียนทับ local_48 ก่อน ถ้าเกิน 64 bytes ก็จะทับ saved RBP (8 bytes ถัดมา) แล้วถ้าเกินอีก 8 bytes ก็จะทับ return address ซึ่งจุดนี้คือจุดที่เราจะใส่ address ของ onepiece()
เพราะฉะนั้น offset รวมจากบัฟเฟอร์ไปจนถึง return address = 64 + 8 = 72 bytes
โดยรวม input ที่เราจะส่งก็จะประมาณนี้

สิ่งที่เราต้องการตอนนี้คือ address ของ onepiece() นั่นก็คือ 21011196 หรือ 0x21011196

เมื่อเราได้มาแล้วก็เขียน payload code กันนน!!
( printf 'security123\n'; python3 -c 'import sys,struct; sys.stdout.buffer.write(b"A"*72+struct.pack("<Q",0x21011196)+b"\n")' ) | nc 203.154.91.221 5225Let’s exploit!!

แนะนำว่าใช้ code ดีกว่าพิมพ์เอง เพราะจะติดเรื่อง “inability to send raw bytes via standard input”
ขอบคุณที่อ่านจนจบครับบ See u next illusion~