สวัสดีครับผู้อ่านทุกท่าน ทีม MAYASEVEN มีโอกาสได้เข้าร่วมแข่งขันงาน TCSD CTF เจอโจทย์ข้อ Hello World #2 ข้อนี้ค่อนข้างน่าสนใจในเรื่องการ bypass custom stack canary และผมคิดว่าผู้อ่านน่าจะได้เรียนรู้เพิ่มเติมจากโจทย์ข้อนี้ทั้งในแง่มุมของ reverse engineering และ buffer overflow exploitation ผมเลยเขียนบทความนี้เพื่อช่วยเผยแพร่ความรู้เทคนิคขั้นสูงให้ Thai cybersecurity community ครับ
มาเริ่มกันเลย
ผมจะสรุปพื้นฐานความรู้ที่จำเป็นสั้นๆ ในการทำความเข้าใจบทความนี้ก่อน
ช่องโหว่ buffer overflow คืออะไร ? เอาให้เข้าใจง่ายๆคือในภาษา native พวก c/c++ เราต้องจัดการ memory/buffer เอง เช่น เราประกาศตัวแปร array ขนาด 16 ตัว แต่เราใส่ค่าเข้าไป 50 ตัว มันก็เกินที่เราจองไว้ 16 ตัว เกินแล้วไปไหน ? เกินแล้วมันก็ไปทับ memory ส่วนอื่นที่อยู่ใกล้ๆกันนั้นแหละ จอบอ จบ.
Stack Canary คืออะไร ? มันคือค่าๆหนึ่งที่ใส่ไว้ใน stack เพื่อเช็คว่าถ้าค่านี้เปลี่ยน แสดงว่าเกิด buffer overflow ขึ้นมาทับค่าที่ใส่ไว้ แล้วเขาใส่ค่านั้นไว้ที่ไหน ? ดูรูปประกอบด้านล่าง
คำถามต่อมาคือ buffer overflow แล้ว hacker ทำอะไรได้ ? ในเคสนี้คือ buffer overflow แล้วต้องการให้ค่า overflow นั้นไปทับ return address เพื่อให้เวลาจบ function นั้นๆ มัน return ไป address ที่ hacker ต้องการ เช่น address ที่ shellcode หรือ malicious code อยู่ หรือ address function อื่นในโปรแกรมนั้นๆ ตามที่ hacker ต้องการ โดยสรุปก็คือ hacker สามารถ hijack flow การทำงานของโปรแกรมได้จากช่องโหว่ buffer overflow นั้นเอง
ความรู้พื้นฐานพร้อมแล้ว มาเริ่มทำโจทย์กัน
โจทย์ให้ไฟล์ชื่อว่า canary เป็นไฟล์ execuable บน linux x86 32-bit
ผมลองรันโปรแกรมเล่นดูได้ผลตามรูปได้ล่าง
โปรแกรมมีการรับ input 2 ครั้ง ครั้งที่ 1 คือ username ครั้งที่ 2 คือ password
เราเอาโปรแกรมมา disassembly ด้วย IDA เหมือนเดิมได้ ได้ผลตามรูปด้านล่าง
รูปด้านบนคือ main function จะเห็นว่ามีการ call function login เป็น function ที่เราสนใจ ก่อนที่เราจะเข้าไปดูใน function login ตาผมเหลือบ ไปเห็น function list เจอ functions ที่น่าสนใจอยู่ 4 functions ตามรูปด้านล่าง
เราลองไปดูที่ function secret กันก่อนเพราะชื่อน่าสนใจ
ทาด๊าาาา เรียบร้อย ถ้าเราสามารถ call secret function นี้ได้คือได้ flag จอบอ จบ
กลับมาดูที่ function login กันต่อ
ในเบื้องต้นโปรแกรมมีการรับ input ด้วย scanf() 2 รอบ
รอบที่ 1 username ใส่ค่าที่รับมาไว้ที่ตัวแปร s
รอบที่ 2 password ใส่ค่าไว้ที่ตัวแปร s1
สังเกตให้ดีในการใช้ scanf() รอบแรกมีการกำหนด limit ว่าจะรับ input แค่ 63 ตัว ถ้าใส่ input เกินกว่านั้น โปรแกรมก็จะรับ input มาทำงานแค่ 63 ตัว ทำให้ไม่เกินช่องโหว่ buffer overflow แต่ scanf() รอบที่ 2 ไม่ได้ limit จำนวน input ที่รับเข้ามา ทำให้เกิดช่องโหว่ buffer overflow ที่ตัวแปร s1 ซึ่งจากช่องโหว่นี้เราสามารถ hijack flow การทำงานของโปรแกรมให้ไป call secret function เพื่อให้มันแสดง flag ออกมา แม้ว่าในโปรแกรมนี้จะไม่มีการ call secret function เลยก็ตาม เราสามารถ exploit ช่องโหว่นี้แล้วกระโดดเข้าไปทำงาน function นั้นได้เลย
แต่ !!!
ถ้าเรายังจำได้ จากต้นบทความนี้ เราต้องทำ buffer overflow เพื่อให้ input มันล้นไปทับ return address ในที่นี้ function ที่มีช่องโหว่คือ function login ซึ่งเราสามารถใส่ input ในตัวแปร s1 จนมันล้นไปทับตำแหน่ง return address ได้ โดยโปรแกรมนี้จะ return ออกจาก function login กลับไปทำงาน function main ใน code บรรทัดที่ 25 ตามรูปด้านบน โดยเราจะ exploit ให้มัน return ไปทำงานที่ secret function แทน ก็เป็นอันเสร็จภารกิจ แต่มีอุปสรรคคือถ้าโปรแกรมมันหลุดเข้า if บรรทัดที่ 14 หรือ 20 มันจะ call exit(1) แล้วจบโปรแกรมก่อนที่มันจะมาถึงบรรทัดที่ 25 แล้ว return ไป secret function นะซิ
เนื่องจากบทความนี้ค่อนข้างซับซ้อนผมเลยตัดสินใจทำเป็นวิดีโอมามาอธิบายครับ
Download: https://drive.google.com/file/d/1bgiCgajNVbqV9_v1EAOj74Cdw3e0pMPN
Sha1: 51bc26ee740d28f88047ace9844f165ccba9a285 canary.zip
ปล. บทความนี้ค่อนข้างจะเป็นเทคนิคขั้นสูง ถ้าอ่านบทความนี้แล้วไม่เข้าใจ สามารถอ่านบทความ buffer overflow พื้นฐานตาม link ด้านล่างเพิ่มเติมได้เลยครับ
https://mayaseven.com/?s=buffer+overflow