Back to all posts

STH-Mini-Web-CTF-2025 — Write Up

CTFHacking
STH Mini Web CTF 2025
https://web1.ctf.p7z.pw

🎯 Target

https://web1.ctf.p7z.pw

⚡ เป้าหมาย

  • ทำการโจมตีเว็บโจทย์การแข่งขัน เพื่อหาข้อความลับ ที่เรียกว่า Flag โดย Flag จะมีรูปแบบ เช่น STH1{cff940beed74db5e1c7c63007223a6e6}
  • เข้าสู่ระบบเป็นสิทธิ์ผู้ดูแลระบบ (Flag 1)
  • ทำการพิมพ์เงินออกจากระบบ (Flag 2)

🚩 Flag 1: เข้าสู่ระบบเป็นสิทธิ์ผู้ดูแลระบบ

STH Flag 1
https://web1.ctf.p7z.pw

หลังจากเข้าเว็บมาก็จะเห็นหน้า login

ปัญหา: ซึ่งเราไม่มี username, password แล้วเราจะ login ได้ยังไง?

📊 ขั้นตอนการแก้ไข

1️⃣ รวบรวมข้อมูล (Information Gathering)

เริ่มจากลอง inspect หน้าเว็บดูเผื่อจะเจออะไร

STH Flag 1
https://web1.ctf.p7z.pw

เมื่อลองดูใน Source code เราจะเห็น comment ของ credentials อยู่:

  • username
  • password

ลองเอา username และ password ที่ได้มา login ดู และ ติ๊ก Remember Me ด้วย

STH Flag 1
https://web1.ctf.p7z.pw

หลังจาก login แล้ว เราจะเห็นว่ามีข้อมูลเกี่ยวกับการ login ของเราขึ้นมา:

  • Username: "test"
  • Role: "user"
STH Flag 1
https://web1.ctf.p7z.pw

แต่ใน Flag นี้เราจะต้อง login ด้วย Admin user แล้วเราจะสามารถ login ด้วย Admin ได้ยังไง?

2️⃣ ค้นหาข้อมูลเพิ่มเติม

ลอง inspect หน้าเว็บดูอีกรอบเผื่อจะมีอะไรซ่อนอยู่อีก

STH Flag 1
https://web1.ctf.p7z.pw

เมื่อลองส่องๆดูเราจะเห็นไฟล์ javascript "script.js" และเราเห็นว่าในไฟล์นี้มี function อยู่ 2 function:

debugFetchUserTest()

STH Flag 1
https://web1.ctf.p7z.pw

function นี้จะทำการ fetch data จาก:

api.php?action=get_userinfo&user=test

ลองยิง API ไปที่ endpoint นี้ดู:

https://web1.ctf.p7z.pw/api.php?action=get_userinfo&user=test

ผลลัพธ์ที่ได้:

STH Flag 1
https://web1.ctf.p7z.pw

แสดงว่า function นี้เป็น function ที่ทำหน้าที่ดึงข้อมูลของ user นั้นๆ

debugFetchAllUsers()

STH Flag 1
https://web1.ctf.p7z.pw

function นี้จะทำการ fetch data จาก:

api.php?action=get_alluser

ลองยิง API ไปที่ endpoint นี้ดู:

https://web1.ctf.p7z.pw/api.php?action=get_alluser

ผลลัพธ์ที่ได้:

STH Flag 1
https://web1.ctf.p7z.pw

แสดงว่า function นี้เป็น function ที่ทำหน้าที่ fetch user ทั้งหมดในระบบ

3️⃣ ค้นหาข้อมูล Admin

จากผลลัพธ์ของ function debugFetchAllUsers()

เราจะเห็นว่า มี user อยู่อีก 1 user ซึ่งอาจจะเป็น admin user

เราจะลองเอา username นี้ไปยิง API เพื่อขอข้อมูลของ user ดู:

https://web1.ctf.p7z.pw/api.php?action=get_userinfo&user=admin-uat

ผลลัพธ์ที่ได้:

STH Flag 1
https://web1.ctf.p7z.pw

ดูจากข้อมูลนี้เราจะเห็น remember_me_token ซึ่งอาจจะเป็น token ที่ใช้ sign JWT token ของ admin-uat

แสดงว่าเราอาจจะใช้ token นี้มา sign token เพื่อ login เป็น admin-uat ได้

4️⃣ หา JWT Secret Key

แต่เราไม่มี JWT secret key สำหรับใช้ sign token แล้วเราจะหามันได้ยังไง?

คำตอบก็คือ bruteforce ยังไงหล่ะ!

ก่อนอื่นเราต้องไปเอา JWT token ของเรามาก่อน ซึ่งจะอยู่ใน cookies:

STH Flag 1
https://web1.ctf.p7z.pw

และเราจะใช้ hashcat สำหรับ brutefoce เพื่อหา JWT secret key:

hashcat -a 0 -m 16500 "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b2tlbiI6ImI4MTk0M2JhLWQxYzUtNDk1YS04NDI3LTQ3MTFjMzkyNTZiZiJ9.Rlk_a69lx16hNhwn4nBfRxhiMGmEDoPIcxfr1_7JdH8" /usr/share/wordlists/rockyou.txt
STH Flag 1
https://web1.ctf.p7z.pw

หลังจาก brutefoce เสร็จ เราก็จะได้คำตอบว่า JWT secret key คือ:

STH Flag 1
https://web1.ctf.p7z.pw

5️⃣ สร้าง Token เพื่อ Login เป็น Admin

ตอนนี้เราได้ทั้ง JWT secret key และ token แล้ว

ต่อไปคือการเอาทั้ง 2 อย่างมารวมกัน ก็คือ การ sign token นั่นเอง

เราจะใช้ Python สำหรับ sign token:

import jwt
print(jwt.encode({ "token": "73eb7063-f8c3-4e50-bea2-07c05681aa92"}, '"bobcats"', algorithm="HS256"))

ผลลัพธ์ที่ได้:

STH Flag 1
https://web1.ctf.p7z.pw

6️⃣ Login เป็น Admin

หลังจาก ได้ JWT token มาแล้ว เราจะเอา token ไปแก้ ใน cookies ของเรา:

STH Flag 1
https://web1.ctf.p7z.pw

แล้วลอง refresh browser ดู ถ้า user ยังไม่เปลี่ยนให้ ปิด browser แล้วเปิดใหม่ มันอาจจะติด cache อยู่ เราก็จะสามารถ login ด้วย admin-uat ได้แล้ว

STH Flag 1
https://web1.ctf.p7z.pw

เราจะลองเข้าไปที่หน้า admin.php ตามที่ถูก comment ไว้ใน script.js

STH Flag 1
https://web1.ctf.p7z.pw

เราจะลองเข้าไปที่หน้า admin.php ตามที่ถูก comment ไว้ใน script.js

STH Flag 1
https://web1.ctf.p7z.pw

หลังจากเข้ามาที่หน้า admin.php แล้ว ก็ลอง inspect ดูอีกรอบ

STH Flag 1
https://web1.ctf.p7z.pw

จะเห็น Flag ที่ 1 ถูก comment อยู่ใน source code 🎉


🚩 Flag 2: ทำการพิมพ์เงินออกจากระบบ

จากการ inspect หน้า admin.php เราจะเห็น โค้ดอะไรบางอย่างถูก comment อยู่

STH Flag 2
https://web1.ctf.p7z.pw

📊 วิเคราะห์โค้ด

ลองแกะๆการทำงานดูจะเห็นว่า โค้ดนี้คือโค้ดสำหรับ validate input และแสดง Flag ออกมา

Logic ของการทำงาน คือ:

  1. validateNumber()

    • Regular Expression /^[0-9]+$/m เช็คว่า input เป็นตัวเลขหรือไม่
    • ⚠️ ข้อสังเกต: Regex นี้ใช้ /m ซึ่งจะตรวจสอบแค่บรรทัดเดียวเท่านั้น
  2. strpos($amount, 'STH')

    • เช็คว่า Input มีคำว่า STH อยู่หรือไม่

💡 แนวทางการ Bypass

การที่เราจะ bypass validator นี้ไปได้:

  • Input ต้องเป็นตัวเลขทั้งหมด และ
  • มี STH อยู่ด้วย

ระบบถึงจะ return Flag มาให้เรา

แล้วเราจะทำยังไงหล่ะ??

ถ้าจำได้ Regex ที่ validate input มันตรวจสอบแค่บรรทัดเดียว ดังนั้นเราก็สามารถ input ข้อมูล 2 บรรทัดได้!

เช่นแบบนี้:

12345
STH
  • บรรทัดแรกจะถูกตรวจสอบว่าถูกต้องด้วย Regex
  • บรรทัดที่ 2 มีคำว่า STH

ทำให้ logic ของระบบเป็น True && True และเราก็จะได้ Flag

🛠️ เครื่องมือและการโจมตี

เราจะใช้ Burp Suite สำหรับยิง API แทนการพิมพ์ข้อมูลใน form ของหน้าเว็บ

STH Flag 2
https://web1.ctf.p7z.pw

เพราะหน้าเว็บใช้ input type="number" ทำให้เราไม่สามารถใส่ข้อความใน input ได้

ยิง API ด้วย Burp Suite

ส่ง payload:

amount=123%0ASTH&denomination=USD

โดย:

  • 12345 คือ ตัวเลขที่เราต้องการให้ผ่าน regex
  • %0A คือ \n แบบเข้ารหัส (URL encoding)
  • STH คือ สิ่งที่เราต้องการแทรกเข้าไปใน payload เพื่อให้ได้ flag
STH Flag 2
https://web1.ctf.p7z.pw

🎯 ผลลัพธ์

หลังจากยิง API ไป เราก็จะเห็น Flag ที่ 2 ใน Response:

STH Flag 2
https://web1.ctf.p7z.pw

🏆 สรุป

ทีนี้เราก็จะได้ Flag ครบทั้ง 2 Flag แล้ว! 🎉🎉🎉

  1. Flag 1: ได้มาจากการปลอมตัวเป็น admin ด้วยการจัดการกับ JWT token
  2. Flag 2: ได้มาจากการ bypass validation โดยใช้ newline character