บทความนี้ผมจะนำช่องโหว่ของโปรแกรม Easy RM to MP3 Converter 2.7.3.700 มาวิเคราะห์ให้อ่านกันครับ
เป็นช่องโหว่ที่ถูกประกาศเมื่อวันที่ 10 Dec 2009
link: http://www.exploit-db.com/exploits/10374
หลังจากวิเคราะห์ดูแล้ว ผมรู้สึกว่ามันไม่ยากจนเกินไป เลยนำมาเขียนเป็นบทความให้ทุกท่านได้อ่านกัน
ผมปรับเปลี่ยนนิดหน่อย เพราะจาก code ที่ประกาศไว้ เป็น code ที่ใช้บน windows xp sp2
แต่ผมจะวิเคราะห์บน windows xp without service pack เลยปรับ code นิดหน่อยครับ
สำหรับคนที่อยากอ่าน
- ควรมีความรู้พื้นฐานเกี่ยวกับภาษา assembly
- ควรรู้จัก stack และ heap
- ควรเข้าใจพื้นฐานการเก็บข้อมูลลง stack
ปล.
- ผมใช้ windows xp without service pack ในการวิเคราะห์
- address บน memory ของแต่ละเครื่องอาจจะแตกต่างกันไป
- ในบทความนี้ผมใช้เลขฐาน 16 ทั้งหมด
[hide=40]
Author: Gen0TypE
Tool: Ollydbg 1.10
OS: Windows XP without service pack
Target: Easy RM to MP3 Converter 2.7.3.700
โปรแกรม Easy RM to MP3 Converter เป็นโปรแกรมที่ใช้ convert ไฟล์ RM ไปเป็น mp3 ทั่วไป
ซึ่งสามารถอ่านไฟล์ playlist แล้ว convert ไฟล์ทั้งหมดตาม playlist ได้
ปัญหาของโปรแกรมนี้มันเกิดใน function ที่ใช้ parsing ไฟล์ playlist (.m3u) ครับ ซึ่งเจ้า function นี้มันจะอ่านไฟล์ .m3u ขึ้นมา แล้วดึงข้อมูลออกมาว่ามีเพลงชื่ออะไรบ้าง ผมขออนุญาตให้ดู
การทำงานใน function parsing เลยละกันนะครับ
รูปที่ 1
จากรูปที่ 1 เป็นตำแหน่งที่แรกใน function parsing เลย ถ้าดูที่ ESP จะเห็นว่า return address จะอยู่ใน stack ตำแหน่งที่ 0x000FF750 และที่คำสั่งแรกของ function จะเห็นว่ามีการเก็บค่า 0x8918 ไว้ที่ EAX ซึ่งเป็นการเตรียมการเพื่อจอง stack ขนาด 0x8918 ครับ โดย function ที่ใช้จัดการกับ stack ก็คือคำสั่งถัดมาที่ตำแหน่ง 0x0041E2B5
ถ้าเรา step into เข้าไปดู function ที่ตำแหน่ง 0x0041E2B5 ซึ่งเป็น function สำหรับจอง stack ให้ array ก็จะเห็นดังรูปที่ 2 ครับ
รูปที่ 2
ที่ตำแหน่ง 0x00437786 จะเห็นว่ามีการนำค่า 0x000FF750 ไปใส่ที่ ECX แต่... เอ๊ะ คุ้นๆ มั้ยเอ่ย 0x000FF750 เหมือนพึ่งผ่านตามาเมื่อกี้.... ใช่เลยครับมันคือตำแหน่งล่างสุดของ stack สำหรับ function parsing ซึ่งเก็บค่า return address ไว้นั่นเอง ถ้าสังเกตดีๆ จะเห็นว่ามีการลบ EAX และ ECX ออกทีละ 0x1000 จนกว่าค่า EAX จะน้อยกว่า 0x1000 เมื่อ EAX น้อยกว่า 0x1000 แล้ว ก็จะลบ ECX ออกเท่ากับค่าที่เก็บใน EAX ถ้ายังจำกันได้ EAX เก็บค่า 0x8918 ไว้ ส่วน ECX เก็บค่าล่างสุดของ stack เพราะฉะนั้นเดาไม่ยากว่า function นี้ใช้สำหรับจอง stack ให้ array ขนาด 0x8918 นั่นเอง เมื่อจบการทำงานของ function จอง array นี้ เราก็จะ return กลับไปที function parsing เหมือนเดิม เมื่อเราไล่ debug ลงมาเรื่อยๆ จะพบกับ code ดังรูปที่ 3
รูปที่ 3
ในส่วนนี้จะเป็นการเคลียร์ค่าใน stack ที่จองไปให้เป็น 0 โดยจะเคลียร์ค่าเป็นส่วนๆ ไป ซึ่งแต่ละส่วนก็จะเคลียร์ค่าเป็นจำนวนครั้งเท่ากับที่เก็บไว้ใน ECX และแต่ละครั้งจะเคลียร์ค่าครั้งละ 4 ไบต์ จากรูปที่ 3 จะเห็นว่ามี 3 ส่วน ขนาด 0x8C3*4, 0x880*4 และ 0x880*4 (จะเห็นว่ามีการคูณด้วย 4 เนื่องจากว่าการเคลียร์ค่าแต่ละครั้ง จะกระทำทีละ 4 ไบต์) แต่พระเอกของเราจะอยู่ที่ตำแหน่ง 0x0041E311 ซึ่งจากรูปที่ 3 จะเห็นว่าเป็นตำแหน่ง 0x000F9150 (เจ้า array ตัวนี้แหละครับ ที่ทำให้เกิด buffer overflow)
รูปที่ 4
เมื่อ debug ต่อมาเรื่อยๆ จะพบกับคำสั่งที่ตำแหน่ง 0x0041E368 ดังรูปที่ 4 ซึ่งเป็น function ที่อ่าน content ของไฟล์ m3u ทั้งหมดมาเก็บไว้ใน heap เพื่อเตรียมแยกชื่อเพลงออกเป็นเพลงๆ ไป (ในจุดนี้ผมขออนุญาตไม่ step into เข้าไปละกันนะครับ มันค่อนข้างยาวเกินความจำเป็น) หลังจากคำสั่ง call EAX แล้ว เมื่อไล่ลงมาเรื่อยๆ จะเจอคำสั่งดังรูปที่ 5
รูปที่ 5
คำสั่งที่ตำแหน่ง 0x0041E3F0 จะอ่านค่าจากใน heap ที่เก็บ content ของไฟล์ .m3u ไว้ แล้วแยกชื่อเพลงออกมาทีละ 1 เพลง แล้วนำชื่อเพลงนั้นมาเก็บใน array ที่จองไว้ตั้งแต่ตอนแรก ซึ่ง array ที่เก็บชื่อเพลงนี้ คือ array ที่ตำแหน่ง 0x000F9150 นั่นเอง ถ้ายังจำกันได้เจ้า array ที่ตำแหน่งนี้ถูกเคลียร์ค่าเป็น 0 ไว้ โดยเคลียร์ไว้ขนาด 0x2200(0x880*4) ไบต์ นั่นก็คือผู้เขียนโปรแกรมคิดไว้ ว่าขนาดสูงสุดของชื่อไฟล์จะอยู่ที่ 0x2200 ไบต์นั่นเอง เมื่อลอง step into เข้าไปดูข้างใน function ที่ตำแหน่ง 0x0041E3F0 จะเห็น code ดังรูปที่ 6
รูปที่ 6
จุดสำคัญจะอยู่ที่ตำแหน่ง 0x10008D93 ซึ่งจะเห็นว่ามีการ copy ค่าจาก heap ที่ตำแหน่ง 0x02B61490 ไปไว้ที่ array ตำแหน่ง 0x000F9150 โดย copy ไปทั้ง 0x664C(0x1993*4) ไบต์ !!!! (จำนวนครั้งที่จะ copy ถูกเก็บไว้ใน ECX ด้วยคำสั่งในตำแหน่ง 0x10008D78 ซึ่งแต่ละครั้งจะ copy ทีละ 4 ไบต์) จาก code ในรูปที่ 6 จะเห็นว่าก่อนถึงคำสั่งที่ตำแหน่ง 0x10008D93 ไม่มีการตรวจสอบค่า ECX ก่อนเลย ว่ามีขนาดน้อยกว่า 0x2200 หรือไม่ มาคำนวณเลขเล่นกัน
0x000F9150 + 0x664C = 0x000FF79C !!!!!
เห็นอะไรกันรึป่าวเอ่ย.... จากการคำนวณข้างบนจะเห็นว่าถ้าชื่อไฟล์มีขนาด 0x664C ไบต์ ตำแหน่งที่เก็บชื่อไฟล์บน stack จะเริ่มที่ 0x000F9150 และสิ้นสุดที่ 0x000FF79C ซึ่ง return address ของเราอยู่ที่ตำแหน่งไหนจำกันได้มั้ยเอ่ย... ปิ๊งป่อง อยู่ที่ตำแหน่ง 0x000FF750 นั่นเอง จะเห็นว่าถ้าเราใส่ชื่อไฟล์ลงไปในไฟล์ .m3u ให้มีขนาดยาวมากๆ จะมีโอกาสที่ชื่อไฟล์ที่เราใส่ไปนั้นจะไปทับค่า return address ซึ่งเป็นสาเหตุให้เกิดการโจมตีที่เรียกว่า stack overflow ได้ครับ
รูปที่ 7
จากรูปที่ 7 จะเห็นว่ามันเขียนทับตำแหน่ง 0x000FF750 ให้ชี้ไปที่ตำแหน่งอื่น เมื่อจบการทำงานของ function parsing โปรแกรมนี้ก็จะวิ่งไปทำงานยังตำแหน่งที่ผมกำหนดได้ครับ
ตัวอย่างการเปิดไฟล์ .m3u ที่ถูกสร้างขึ้นเพื่อโจมตีช่องโหว่ stack overflow ของโปรแกรม Easy RM to MP3 Converter 2.7.3.700
รูปที่ 8
ผลลัพธ์จากการเปิดไฟล์ .m3u เป็นดังรูปที่ 9
รูปที่ 9
code สำหรับสร้างไฟล์ .m3u ที่ใช้โจมตีช่องโหว่ครับ
[code]
$buf = "\x41" x 26109;
$ret = "\x33\x19\xED\x77";
$nop = "\x90" x 20;
#
# This is my shit. you can modify this shit to suit yourself.
# Gen0TypE
#
$shellcode = "\xEB\x02\xEB\x05\xE8\xF9\xFF\xFF\xFF\x5B\x8B\xC3".