@@ -4,181 +4,123 @@ tags:
44 - fruit
55created : 2025-10-05
66---
7- ![ ] ( https://cdn-images-1.medium.com/max/800/0*quSIvw2fwHBmbBJa )
8-
9- Target : [ https://web1.ctf.p7z.pw ] ( https://web1.ctf.p7z.pw )
10-
11- - ทำการโจมตีเว็บโจทย์การแข่งขัน เพื่อหาข้อความลับ ที่เรียกว่า Flag โดย Flag จะมีรูปแบบ เช่น STH1{cff940beed74db5e1c7c63007223a6e6}
12- - เข้าสู่ระบบเป็นสิทธิ์ผู้ดูแลระบบ (Flag 1)
13- - ทำการพิมพ์เงินออกจากระบบ (Flag 2)
7+ > [ !abstract] ** Target Information**
8+ >
9+ > - ** Target URL:** [ https://web1.ctf.p7z.pw ] ( https://web1.ctf.p7z.pw/ )
10+ >
11+ > - ** Objective:** ค้นหา Flag ทั้งหมด 2 ตัว (Admin Access & Logic Bypass)
12+ >
13+ > - ** Flag Format:** ` STH1{...} `
14+ >
1415
1516---
1617
17- ## มาเริ่มกันที่ Flag 1 : เข้าสู่ระบบเป็นสิทธิ์ผู้ดูแลระบบ
18-
19- ![ ] ( https://cdn-images-1.medium.com/max/800/1*MIA-GGdTygl9TsJOnUpJFg.png )
20-
21- หลังจากเข้าเว็ปมาก็จะเห็นหน้า login ซึ่งเราไม่มี username, password แล้วเราจะ login ได้ยังไง ?
22-
23- ขั้นตอนแรกเรามารวบรวมหาข้อมูลให้มากที่สุดก่อน (Information Gathering) เริ่มจากลอง inspect หน้าเว็ปดูเผื่อจะเจออะไร
24-
25- ![ ] ( https://cdn-images-1.medium.com/max/800/1*dqASwHKhA-0YkurFc0i-1w.png )
26-
27- เมื่อลองดูใน Source code เราจะเห็น comment ของ credentials อยู่ คือ username และ password ลองเอา username และ password ที่ได้มา login ดู และ ติ๊ก Remember Me ด้วย
28-
29- ![ ] ( https://cdn-images-1.medium.com/max/800/1*qBCMf8ulCnAjzEl_uYdSWg.png )
30-
31- หลังจาก login แล้ว เราจะเห็นว่ามีข้อมูลเกี่ยวกับการ login ของเราขึ้นมา คือ username “test”, Role “user”
32-
33- ![ ] ( https://cdn-images-1.medium.com/max/800/1*V9pCf388lfTMN3p6CzXecA.png )
34-
35- แต่ใน Flag นี้เราจะต้อง login ด้วย Admin user แล้วเราจะสามารถ login ด้วย Admin ได้ยังไง ? ลอง inspect หน้าเว็บดูอีกรอบเผื่อจะมีอะไรซ่อนอยู่อีก
36-
37- ![ ] ( https://cdn-images-1.medium.com/max/800/1*tPAnEqOqZCeNaFMxgSg_BQ.png )
38-
39- เมื่อลองส่องๆดูเราจะเห็นไฟล์ javascript “script.js” และเราเห็นว่าในไฟล์นี้มี function อยู่ 2 function คือ
40-
41- ** debugFetchUserTest()** - function นี้จะทำการ fetch data จาก ** api.php?action=get_userinfo&user=test**
42-
43- ![ ] ( https://cdn-images-1.medium.com/max/800/1*XEHSmAeddFaafO57Y1d9Ng.png )
18+ ## Flag 1: Privilege Escalation (เข้าสู่ระบบสิทธิ์ Admin)
4419
20+ ### 1. Information Gathering & Login
4521
46- ลองยิง api ไปที่ endpoint นี้ดู ` https://web1.ctf.p7z.pw/api.php?action=get_userinfo&user=test `
22+ จากการสำรวจหน้าแรก เราพบช่องโหว่พื้นฐานจากการตรวจสอบ Source Code (Inspect Element)
4723
48- ผลลัพธ์ที่ได้ :
24+ - ** พบ Credentials หลุด:** มี Username/Password ของ User ทดสอบถูก Comment ไว้
25+
26+ - ** Action:** ทำการ Login ด้วยสิทธิ์ ` test ` และติ๊ก ** "Remember Me"** เพื่อสร้าง JWT Cookie
27+
28+ ### 2. API Enumeration (การไล่หาข้อมูลผ่าน API)
4929
50- ![ ] ( https://cdn-images-1.medium.com/max/800/1*RjiFslSA3qieWE09l66fAQ.png )
30+ เมื่อตรวจสอบไฟล์ ` script.js ` พบฟังก์ชัน Debug ที่น่าสนใจ 2 ตัว:
5131
52- แสดงว่า function นี้เป็น function ที่ทำหน้าที่ดึงข้อมูลของ user นั้นๆ ทีนี้ลองไปดูอีก function ว่ามันทำอะไรได้บ้าง ?
32+ 1 . ` debugFetchUserTest() ` → ดึงข้อมูล User ` test `
33+
34+ 2 . ` debugFetchAllUsers() ` → ดึงรายชื่อ User ทั้งหมดในระบบ
35+
5336
54- ** debugFetchAllUsers()** - function นี้จะทำการ fetch data จาก ** api.php?action=get_alluser**
37+ > [ !bug] ** Exploit Point**
38+ >
39+ > จากการเรียก ` api.php?action=get_alluser ` พบ User ชื่อ ` admin-uat `
40+ >
41+ > เมื่อเจาะจงไปที่ ` api.php?action=get_userinfo&user=admin-uat ` เราได้รับ ** remember_me_token** ของ Admin มา!
5542
56- ![ ] ( https://cdn-images-1.medium.com/max/800/1*ztm59x3xqb5ipCb0fzgnHA.png )
43+ ### 3. JWT Secret Brute-forcing
5744
58- ลองยิง api ไปที่ endpoint นี้ดู ` https://web1.ctf.p7z.pw/api.php?action=get_alluser `
45+ เรามี Token แต่ไม่สามารถปลอมแปลงสิทธิ์ได้ถ้าไม่มี ** Secret Key ** เราจึงต้องใช้ ` Hashcat ` เพื่อถอดรหัสหา Key จาก JWT เดิมของเรา:
5946
60- ผลลัพธ์ที่ได้ :
61-
62- ![ ] ( https://cdn-images-1.medium.com/max/800/1*2nzCK4WD1WhV1tRgjVCayQ.png )
63-
64- แสดงว่า function นี้เป็น function ที่ทำหน้าที่ fetch user ทั้งหมดในระบบ
65-
66- จากผลลัพธ์ของ function ** debugFetchAllUsers()** เราจะเห็นว่า มี user อยู่อีก 1 user ซึ่งอาจจะเป็น admin user เราจะลองเอา username นี้ไปยิง api เพื่อขอข้อมูลของ user ดู
67-
68- ` https://web1.ctf.p7z.pw/api.php?action=get_userinfo&user=admin-uat `
69-
70- ผลลัพธ์ที่ได้ :
71-
72- ![ ] ( https://cdn-images-1.medium.com/max/800/1*iKnZgCthfsCiMGCq0L4vuw.png )
73-
74- ดูจากข้อมูลนี้เราจะเห็น remember_me_token ซึ่งอาจจะเป็น token ที่ใช้ sign jwt token ของ admin-uat แสดงว่าเราอาจจะใช้ token นี้มา sign token เพื่อ login เป็น admin-uat ได้
75-
76- แต่เราไม่มี jwt secret key สำหรับใช้ sign token แล้วเราจะหามันได้ยังไง ? คำตอบก็คือ ** bruteforce** ยังไงหล่ะ
77- ก่อนอื่นเราต้องไปเอา Jwt token ของเรามาก่อน ซึ่งจะอยู่ใน cookies
78-
79- ![ ] ( https://cdn-images-1.medium.com/max/800/1*pFCpW3nPwieQsrVl37GpHw.png )
80-
81- และเราจะใช้ hashcat สำหรับ brutefoce เพื่อหา Jwt secret key
47+ Bash
8248
8349```
84- hashcat -a 0 -m 16500 "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b2tlbiI6ImI4MTk0M2JhLWQxYzUtNDk1YS04NDI3LTQ3MTFjMzkyNTZiZiJ9.Rlk_a69lx16hNhwn4nBfRxhiMGmEDoPIcxfr1_7JdH8" /usr/share/wordlists/rockyou.txt
50+ # ใช้ Mode 16500 สำหรับ JWT (HS256)
51+ hashcat -a 0 -m 16500 "<YOUR_JWT_HERE>" /usr/share/wordlists/rockyou.txt
8552```
8653
87- ![ ] ( https://cdn-images-1.medium.com/max/800/1*iEY26e5EwWmYuby84foSmQ.png )
88-
89- หลังจาก brutefoce เสร็จ เราก็จะได้คำตอบว่า jwt secret key คือ
54+ - ** Result Found:** Secret Key คือ ` "bobcats" `
55+
9056
91- ![ ] ( https://cdn-images-1.medium.com/max/800/1*VzCLOwQjpO5cNY5NcnFChQ.png )
57+ ### 4. Forging Admin Token
9258
93- ตอนนี้เราได้ทั้ง jwt secret key และ token แล้ว
59+ ใช้ Python ในการสร้าง JWT ใหม่โดยใช้ Token ของ ` admin-uat ` ที่เราแอบดูมาจาก API:
9460
95- ต่อไปคือการเอาทั้ง 2 อย่างมารวมกัน ก็คือ การ sign token นั่นเอง
96-
97- เราจะใช้ python สำหรับ sign token
61+ Python
9862
63+ ```
9964import jwt
100- print(jwt.encode({ "token": "73eb7063-f8c3-4e50-bea2-07c05681aa92"}, '"bobcats"', algorithm="HS256"))
101-
102- ![ ] ( https://cdn-images-1.medium.com/max/800/1*woGgxRc0ghPoRvdEphHaYw.png )
103-
104- หลังจาก ได้ jwt token มาแล้ว เราจะเอา token ไปแก้ ใน cookies ของเรา
105-
106- ![ ] ( https://cdn-images-1.medium.com/max/800/1*7rK6Jf0o0ti3dL4g8D4RaQ.png )
107-
108- แล้วลอง refresh browser ดู ถ้า user ยังไม่เปลี่ยนให้ ปิด browser แล้วเปิดใหม่ มันอาจจะติด cache อยู่
109-
110- เราก็จะสามารถ login ด้วย admin-uat ได้แล้ว
111-
112- ![ ] ( https://cdn-images-1.medium.com/max/800/1*9_H-AIoIbyZnD0UjLBhKMA.png )
113-
114- เราจะลองเข้าไปที่หน้า admin.php ตามที่ถูก comment ไว้ใน script.js
115-
116- ![ ] ( https://cdn-images-1.medium.com/max/800/1*foch7j9mazC6tgDgHq_6cw.png )
117-
118- ![ ] ( https://cdn-images-1.medium.com/max/800/1*kzY9YBqNPKoSNH8dzf-VpQ.png )
119-
120- หลังจากเข้ามาที่หน้า admin.php แล้ว ก็ลอง inspect ดูอีกรอบ
121-
122- ![ ] ( https://cdn-images-1.medium.com/max/800/1*tNyPQK5p_8Gh_X5UFiGHZg.png )
65+ # Sign token ใหม่ด้วย Secret ที่ Brute-force ได้
66+ payload = { "token": "73eb7063-f8c3-4e50-bea2-07c05681aa92" }
67+ print(jwt.encode(payload, "bobcats", algorithm="HS256"))
68+ ```
12369
124- จะเห็น Flag ที่ 1 ถูก comment อยู่ใน source code
70+ ** Step สุดท้าย: ** นำ JWT ที่ได้ไปแทนที่ใน Cookies ของ Browser แล้ว Refresh หน้าเว็บเพื่อเข้าสู่สิทธิ์ Admin และไปที่หน้า ` admin.php ` เพื่อรับ Flag ที่ 1 ใน Source Code
12571
12672---
12773
128- ## มาต่อกันที่ Flag ที่ 2
129-
130- Flag 2 : ทำการพิมพ์เงินออกจากระบบ
131-
132- จากการ inspect หน้า admin.php เราจะเห็น โค้ดอะไรบางอย่างถูก comment อยู่
133-
134- ![ ] ( https://cdn-images-1.medium.com/max/800/1*lTNt3qPI3dQjNBTJtgbTBw.png )
74+ ## Flag 2: Logic Bypass (พิมพ์เงินออกจากระบบ)
13575
136- ลองแกะๆการทำงานดูจะเห็นว่า
76+ ### 1. Vulnerability Analysis
13777
138- โค้ดนี้คือโค้ดสำหรับ validate input และแสดง Flag ออกมา
78+ ในหน้า ` admin.php ` มีโค้ดตรวจสอบความปลอดภัย (Validation) ที่ถูก Comment ไว้ ซึ่งมีช่องโหว่ที่ตัว Regex:
13979
140- logic ของการทำงาน คือ
80+ PHP
14181
142- ** validateNumber()**
143-
144- - Regular Expression ` /^[0-9]+$/m ` เช็คว่า input เป็นตัวเลขหรือไม่
145- - ข้อสังเกตคือ Regx นี้ใช้ /m ซึ่งจะตรวจสอบแค่บรรทัดเดียวเท่านั้น
146-
147- ** strpos($amount, ‘STH’)**
148-
149- - เช็คว่า Input มีคำว่า STH อยู่หรือไม่
150-
151- การที่เราจะ bypass validator นี้ไปได้ คือ input ต้องเป็นตัวเลขทั้งหมด และ มี STH อยู่ด้วย ระบบถึงจะ return Flag มาให้เรา
152-
153- แล้วเราจะทำยังไงหล่ะ ??
154-
155- ถ้าจำได้ Regx ที่ validate input มันตรวจสอบแค่บรรทัดเดียว ดังนั้นเราก็สามารถ input ข้อมูล 2 บรรทัดได้สิ
156-
157- เช่นแบบนี้
158-
159- 12345
160- STH
161-
162- เพราะบรรทัดแรกจะถูกตรวจสอบว่าถูกต้องด้วย Regx แต่บรรทัดที่ 2 มีคำว่า STH ทำให้ logic ของระบบเป็น True && True และเราก็จะได้ Flag
82+ ```
83+ // Regex: /^[0-9]+$/m <-- มีจุดอ่อนที่ตัวแก้ไข /m (Multiline)
84+ if (preg_match('/^[0-9]+$/m', $amount) && strpos($amount, 'STH')) {
85+ echo $flag2;
86+ }
87+ ```
16388
164- เราจะใช้ burpsuit สำหรับยิง API แทนการพิมพ์ข้อมูลใน form ของหน้าเว็ป
89+ > [ !warning] ** The Flaw**
90+ >
91+ > - ` ^...$ /m ` จะตรวจสอบเงื่อนไขแบบ ** บรรทัดต่อบรรทัด**
92+ >
93+ > - ถ้าบรรทัดแรกเป็นตัวเลข (ผ่าน Regex) และมีบรรทัดอื่นที่มีคำว่า "STH" (ผ่าน strpos) เงื่อนไขจะเป็นจริงทันที
94+ >
16595
166- ![ ] ( https://cdn-images-1.medium.com/max/800/1*KXxreu45N0Z6cwvrVBpF8g.png )
96+ ### 2. Exploitation (Newline Injection )
16797
168- เพราะหน้าเว็ปใช้ input type number ทำให้เราไม่สามารถใส่ ข้อความใน input ได้
98+ เราไม่สามารถใส่ข้อความผ่านหน้าเว็บได้เพราะ Input ถูก Lock เป็น ` type=" number" ` จึงต้องใช้ ** Burp Suite ** ในการยิง Request โดยตรง
16999
170- ลองยิง API ด้วย burpsuit โดยส่ง payload ** amount=123%0ASTH&denomination=USD **
100+ ** Payload ที่ใช้: **
171101
172- ` 12345 ` คือ ตัวเลขที่เราต้องการให้ผ่าน regx
102+ HTTP
173103
174- ` %0A ` คือ \n แบบเข้ารหัส (url encoding)
104+ ```
105+ POST /api.php HTTP/1.1
106+ ...
107+ amount=123%0ASTH&denomination=USD
108+ ```
175109
176- ` STH ` คือ สิ่งที่เราต้องการแทรกเข้าไปใน payload เพื่อให้ได้ flag
110+ - ` 123 ` : ผ่านเงื่อนไขบรรทัดแรกว่าเป็นตัวเลข
111+
112+ - ` %0A ` : คือ Newline (` \n ` ) เพื่อขึ้นบรรทัดใหม่
113+
114+ - ` STH ` : บรรทัดที่สองทำให้ฟังก์ชัน ` strpos ` ตรวจเจอคำที่ต้องการ
115+
177116
178- ![ ] ( https://cdn-images-1.medium.com/max/800/1*YwS2Ho3DH7ueHwzh5CyXyg.png )
117+ ** Result: ** ระบบส่ง Flag ที่ 2 กลับมาให้ใน Response Body ทันที!
179118
180- หลังจากยิง API ไป เราก็จะเห็น Flag ที่ 2 ใน Response
119+ ---
181120
182- ![ ] ( https://cdn-images-1.medium.com/max/800/1*kg2kjMK5Hs_AQ_PgV1FwnA.png )
121+ ## Summary
183122
184- ทีนี้เราก็จะได้ Flag ครบทั้ง 2 Flag แล้ว เย้ๆๆๆ
123+ | ** Flag** | ** Vulnerability Type** | ** Tool Used** |
124+ | ---| ---| ---|
125+ | ** Flag 1** | Information Disclosure & Weak JWT Secret| Hashcat, Python, DevTools|
126+ | ** Flag 2** | Regex Multiline Bypass (Newline Injection)| Burp Suite|
0 commit comments