กลับมาแล้วกับโจทย์ Weekly Challenge ของ Bugforge.io นะครับ สัปดาห์ก่อนทีมไม่ได้เขียนลงเพราะโจทย์มันง่ายเกินไป ไม่สนุก แต่สัปดาห์นี้ค่อนข้างน่าสนใจกว่าก็เลยเอามาเขียน writeup ครับ

สำหรับโจทย์สัปดาห์นี้จะเป็นเว็บ FurHire เว็บไซต์จ้างงาน/สมัครงานที่เราคุ้นเคย โดย Hint ของโจทย์นี้คือ Tokens are Fun! ดีเลยครับ ทีมชอบโจทย์แนวนี้ มีอะไรให้เล่นเยอะดี


เพื่อทดสอบว่า Token ของเว็บนี้มันมีรูปแบบอย่างไร ทีมจะสมัครสมาชิกในฐานะของคนจ้างงาน (Recruiter) ทั้งสองบัญชี คือ teammyinside และ recruit ครับ


ทีนี้เราจะเอา request จาก /api/my-jobs ของแต่ละอันมาเทียบกันครับ รูปบนเป็นของ teammyinside และรูปล่างเป็นของ recruit จะเห็นว่าสิ่งที่คล้ายคลึงกันคือ refresh token ที่จะเหมือนกันทั้งชุด ต่างแค่จุดเดียวคือตัวอักษรตัวท้ายครับ
- teammyinside: refresh_token=xpxyigcywhqxgsidrbufxstutxbwpfwu
- recruit: refresh_token=xpxyigcywhqxgsidrbufxstutxbwpfwv


และเพื่อพิสูจน์ว่า refresh token มันเปลี่ยนที่ตัวอักษรตัวสุดท้ายจริง เดี๋ยวจะลองล็อกอินบัญชี teammyinside ใหม่ แล้วจับ request มาดู ซึ่งพบว่าก็ได้ refresh_token=xpxyigcywhqxgsidrbufxstutxbwpfww แสดงว่ามันเรียงตัวอักษรไปเรื่อย ๆ จริง ๆ ครับ

ทีนี้ แล้วเราจะเอา refresh token ไปทำอะไรได้บ้าง? เราจะไปดูที่ page source กันก่อนเผื่อมันมีข้อมูลเรื่องของการเรียกใช้ api เกี่ยวกับ refresh token ซึ่งเราเจอว่ามันมี app.js ครับ เดี๋ยวลองเปิดดูกัน

ใน app.js ให้ข้อมูลว่า ถ้า refresh token เราสามารถเรียกใช้ /api/refresh ส่งแบบ POST request พร้อม content-type แบบ json ส่วนใน json body ให้ระบุ username ของบัญชีที่จะขอให้ทำการ refresh token

ทีมก็เลยเอา /api/my-jobs ของ teammyinside อันล่าสุดมาปรับแต่งเพื่อเรียกใช้ /api/refresh ก็จะได้ตามภาพเลยครับ

ดังนั้น เราจะมาลองเรียกของ refresh token ของแอดมินกันบ้าง โดยเปลี่ยน username เป็น admin แล้วอย่าลืมให้เปลี่ยน JWT token ใน Authorization และ refresh token อันใหม่ที่ได้มาจาก response ก่อนหน้านี้ด้วยนะครับ
แต่พอลองเรียกโดยใช้ refresh_token=xpxyigcywhqxgsidrbufxstutxbwpfww งั้นเราลองปรับมาเป็น refresh_token=xpxyigcywhqxgsidrbufxstutxbwpfwx ก็ยังเรียกไม่ได้อีก เอ๊ะ! ทำยังไงดีนะ?


อ๋อ เรามี Burp pro งั้นเราก็ใช้ Intruder เลยสิ ทีมจึงนำ request ล่าสุดใส่ลงใน intruder เลือกตำแหน่งที่จะเปลี่ยนคืออักษรตัวสุดท้ายของ refresh token จากนั้น payload type เลือกเป็น brute forcer กำหนดเซตเป็นตัวอักษรอังกฤษพิมพ์เล็ก และ length กำหนดไว้แค่ 1 ครับ จากนั้นเริ่มยิงเลย

ผลลัพธ์คือเราได้ข้อมูล token ของ admin มาแล้วครับ ใน refresh_token=xpxyigcywhqxgsidrbufxstutxbwpfwy และ refresh_token=xpxyigcywhqxgsidrbufxstutxbwpfwt ซึ่งเราจะใช้ refresh_token=xpxyigcywhqxgsidrbufxstutxbwpfwy ละกัน

กลับไปที่หน้า dashboard ของ teammyinside ให้เปิด Inspect ขึ้นมา แล้วไปที่ Application แก้ไขค่า JWT ใน LocalStorage และแก้ไข refresh_token ใน Cookies ครับ


พอแก้แล้วลองรีเฟรชหน้า dashboard ปรากฏว่าเข้าใช้ไม่ได้ เพราะสงวนให้เฉพาะ recruiter เท่านั้น

แต่ไม่เป็นไรครับ โจทย์แบบนี้เราเดาได้อยู่แล้วว่าหน้าแอดมินมันต้องเป็น /admin ทีมก็เปลี่ยนจาก dashboard เป็น admin เท่านี้ก็เข้าได้แล้วครับ

และ Flag ของโจทย์ข้อนี้คือ bug{LnOftQLACuNu0YYP9TpTLa6peyTWuoPp}
สนใจแนวนี้ฝากกดติดตามด้วยนะครับ หรือติดตามได้ในช่องทางเหล่านี้