.:: Buffer Overflow ::.

[Printer Friendly]
ผู้เขียน/โดย : เกริก ภิรมย์โสภา (Krerk Piromsopa)
เขียนเมื่อ/ปรับปรุง : 2007-11-24 15:47:24
มีผู้เยี่ยมชมทั้งสิ้น : 7316

เนื้อหาในส่วนนี้ รวบรวมขึ้นเพื่อประกอบความเข้าใจเรื่อง Buffer Overflow และ Buffer-Overflow Attacks เริ่มต้นด้วยความหมายของ Buffer-Overflow จากนั้นเราจะวิเคราะห์ต่อไปว่าปัญหาง่ายๆ นี้ นำไปสู่ความเสียหายอื่นๆ อันเป็นพื้นฐานของ Computer Viruses หรือ Computer Worms ได้อย่างไร จากนั้นเราจะวิเคราะห์ปัญหา และลองศึกษาวิธีการป้องกันดู นอกจาก Buffer-Overflow แล้ว ยังมีปัญหาอื่นๆ คล้ายคลังกันเช่น Integer Overflow หรือ การใช้ printf ในภาษา C แบบไม่ถูกวิธี อย่างไรก็ตามเราจะไม่กล่าวถึงรายละเอียดปัญหาย่อยๆ ที่คล้ายคลังกันในที่นี้ เมื่ออ่านจบเนื้อหาในหน้านี้แล้วผู้เขียนหวังเป็นอย่างยิ่งว่า ผู้อ่านจะมีความเข้าใจในเรื่อง Buffer-Overflow และพัฒนา skill ในการเขียนโปรแกรมมากขึ้น

Buffer Overflow คืออะไร

หลายคนพยายามนิยามหรือให้ความหมายอย่างเป็นทางการของ Buffer Overflow จากการรวบรวมข้อมูลจากหลายๆ แห่ง แล้ว สรุปใจความได้สั้นๆ ว่า

Buffer Overflow เกิดขี้นเมื่อมีการเขียน(อ้างอิง)ข้อมูลเกินขอบเขตที่กำหนด ผลที่เกิดขึ้นคือข้อมูลที่เขียนล้นเข้าไปทับข้อมูลอื่นที่อยู่ในระบบ

ผลที่ตามมาจากการ Overflow ดังกล่าวอาจจะเป็นไปได้ตั้งแต่ทำให้โปรแกรมประมวลผลข้อมูลผิดพลาด, Segmentation False หรืออาจจะร้ายแรงถึง เปลี่ยนการทำงานของระบบให้ประมวลผล Code ใดๆ ก็ได้ตามที่ Attackers ต้องการ

Buffer-Overflow Attacks สร้างความเสียหายอะไรได้บ้าง

เพื่อความสะดวกในการอธิบาย จะขอยกตัวอย่างประกอบนะที่นี้โดยเริ่มจากตัวอย่างแบบง่าย ไปจนถึงการ Attack ที่ซับซ้อนยิ่งขึ้น โดยเริ่มต้นที่ตัวอย่าง Buffer Overflow แบบง่ายๆ ก่อน แล้วค่อยๆ ยากขึ้นไปจนถึง Buffer-Overflow Attacks ที่ซัพซ้อน (สามารถผ่านระบบป้องกัน Buffer Overflow บน Software ที่ใช้กันทั่วไป)

ตัวอย่างที่ 1 Buffer-Overflow Attacks แบบเบื้องต้น

ในตัวอย่างแรกนี้ (รูปที่ 1) เป็นตัวอย่างโปรแกรมภาษา C ง่ายๆ ที่เมื่อถูก Overflow จะทำให้การทำงานบางอย่างผิดไป ในที่นี้ คือตัวแปร age ซึ่งเป็นตัวแปรเก็บตัวเลขจะถูก Overflow จนค่าที่ได้เปลี่ยนไป ดังในตัวอย่างจะพบว่าอายุเปลี่ยนจาก 15 เป็น 49 (ทั้งที่ไม่ได้ทำการกำหนดค่าตัวแปร age เป็น 49)

#include
int main(char argc,char *argv[]) {
        int age;
        char name[8];
        char tmp[20];
        printf("Enter your age:");
        gets(tmp);
        age=atoi(tmp);
        printf("Enter your name:");
        gets(name);  /* 1 */
        printf("----------- ");
        printf("%s is %d years old " ,name,age);
}
$./a.out
Enter your age:15
Enter your name: Krerk.P01
-----------
Krerk.P01 is 49 years old

รูปที่ 1 ตัวอย่าง Buffer-Overflow Attack แบบเบื้องต้น

**** ค่าที่ได้อาจจะแตกต่างกันไป ขึ้นอยู่กับ Processor, ระบบปฏิบัติการ และ ตัว compiler *****

สิ่งที่เกิดขึ้นคือ คำสั่ง gets(name) (1) ซึ่งรับข้อมูล input ที่มีขนาด 10 ตัว (9 ตัวอักษร "Krerk.P01" และ 1 terminator) มาเก็บไว้ในตัวแปร name แต่ตัวแปร name นั้นมีขนาดเพียง 8 ตัวอักษร ทำให้ข้อมูลที่ได้มาเกิด Overflow ไปทับ age ผลที่ตามมาคือตัวแปร age ซึ่งเดิมเก็บตัวเลข 15 ถูกเปลี่ยนเป็นเก็บตัวอักษร '1' แทน เมื่อ age ถูกอ้างอิงอีกครั้ง ตัวอักษร '1' จึงถูกอ้างอิงเป็นตัวเลขซึ่งมีค่าเป็น 49 (ในที่นี้ถือตามมาตรฐานรหัส ASCII)

ตัวอย่างที่ 2 Stack Smashing

ในตัวอย่างนี้ จะเป็นทำงานที่ซับซ้อนยิ่งขึ้น โดยก่อนอื่นผู้อ่านจะต้องทำความเข้าใจเรื่องของ Stack Frame นิดนึงก่อน ทุกครั้งที่โปรแกรมมีการเรียก sub routine ค่า parameters และ ตำแหน่งการทำงานปัจจุบัน (Return Address) จะถูกเก็บไว้ที่ข้อมูลชั่วคราวคือ Stack (memory เฉพาะ) หาก sub routine นั้นๆ มีตัวแปรที่เป็น local variable โปรแกรมก็จะจองเนื้อที่บน Stack เพื่อใช้สำหรับเก็บตัวแปรดังกล่าว การทำงานลักษณะนี้ ทำให้โปรแกรมสามารถอ้างอิง scope ของ variable ได้ (local variables อ้างอิงจาก stack frame ปัจจุบันเสมอ)

ในรูปที่ 2 ตัว function func ประกอบไปด้วยตัวแปร i, f, ptr, และ buffer โดยในที่นี้ f เป็น function pointer (สำหรับผู้ที่ไม่คุ้นเคย funtion pointer เป็น pointer ที่ใช้สำหรับการ dynamic bind ตัวแปร f ให้เป็น funtion ใดๆ ก็ได้ ในตัวอย่างนี้ เรา bind f ให้เป็น printf) และ ptr เป็น pointer ทั่วไป และ buffer เป็น array of characters ธรรมดา เมื่อ func ถูกเรียกใช้งาน ระบบจะทำการสร้าง stack frame ขึ้นเพื่อจำค่าตำแหน่งปัจจุบัน (ก่อนจะ call subroutine) ที่โปรแกรมจะต้องกลับมาทำงานต่อ เมื่อเข้ามาสู่โปรแกรม ตัว subroutine จะบันทึกค่า frame pointer funtion เดิม และจองเนื้อที่สำหรับ local variables ดังแสดงได้ในด้านขวาของรูปที่ 2

เมื่อวิเคราะห์โปรแกรมดังกล่าว จะพบว่า หากเกิด overflow ที่ตัวแปร buffer ค่าต่างๆ (อาทิ ptr, f , x, frame pointer, return address) สามารถถูกเปลี่ยนเป็นค่าใดๆ ก็ได้ เมื่อมี input ที่เหมาะสม Buffer-overflow attack แบบแรกๆ ที่พบมักจะแทรก code ที่ต้องการให้โปรแกรม run ลงในตอนต้นของ buffer จากนั้นก็เขียน address ของ buffer ดังกล่าวต่อท้ายไป ผลที่ได้คือ address ทั้งหลายจะถูกชี้กลับมาที่ buffer ซึ่งมี code ที่ hacker ต้องการอยู่ เมื่อ address ดังกล่าวถูกอ้างอิง code เหล่านี้ก็จะถูกประมวลผลโดยปริยาย buffer-overflow attack ในลักษณะนี้ บางครั้งนิยมเรียกว่า stack smashing เนื่องจากข้อมูลขนาดใหญ่ถูกปะลงบนเนื้อที่ Stack อย่างไรก็ตาม บางคนจึงคิดว่าหากเราทำให้ stack ไม่สามารถ execute ได้ (เช่น AMD non-executable area --- NX หรือ patch ที่ Linux kernel บางอัน) น่าจะสามารถหยุด buffer-overflow attack ได้

int func(char **argv) {
    int x;
    int (*f) (const char *, ...);
    char *ptr;
    char buffer[30];
    ptr=buffer;
    f=& printf;
    f("ptr %p - before ",ptr);
    strcpy(ptr,argv[1]);
    f("ptr %p - after ",ptr);
    strcpy(ptr,argv[2]);
}
return address
frame pointer
x
f
ptr
buffer
& buffer
& buffer
& buffer
& buffer
& buffer
Malicious Code

รูปที่ 2 ตัวอย่าง Buffer overflow แบบซับซ้อนยิ่งขึ้น

ในความเป็นจริง การ injected code มิใช่สิ่งสำคัญ เพราะหากเราทราบว่ามี code ที่เราต้องการให้โปรแกรมประมวลผล อยู่ที่ตำแหน่งอันใดอันหนึ่งใน memory (เช่น code จาก share library หรือตัว code ของโปรแกรมเอง) เราก็เพียงเปลี่ยนตำแหน่งของ pointers และ addresses ต่างๆ ให้ชี้ไปยัง code ส่วนนั้น การ attack ในลักษณะนี้ บางคนเรียกว่า arc injection

ตัวอย่างที่ 3 Multi-stage attacks

ในหลายๆ กรณี buffer-overflow attacks จะเกิดขี้นจากการ overflow หลายครั้งต่อกัน โดยทั่วไปการ overflow ครั้งแรกจะทำให้เกิด pointer ชี้ไปยังที่ใดก็ได้ในตัวโปรแกรมนั้นๆ และการ overflow อีกครั้งนึงจะเปลี่ยนค่าที่ pointer ชี้อยู่เป็นค่าที่ต้องการ

ตัวอย่างการ attack ที่พบเช่น Apache mod_SSL SLAPPER Worm ซึ่งการ overflow ครั้งแรก ช่วยให้เกิด pointer ชี้ไปยัง global offset table (jump table ที่ใช้อ้างอิงตัวโปรแกรมหลักกับ share library) ของ funtion free จากนั้น การ overflow ครั้งที่ 2 จะเปลี่ยน entry ดังกล่าวให้มา run remote shell

ลองพิจารณาโปรแกรมในตัวอย่างที่ 2 อีกครั้งหนึ่ง เราจะพบว่ามี strcpy 2 ครั้ง ในกรณีของ Multi-stage attacks นี้ strcpy ครั้งแรก จะoverflow ตัว pointer ptr ให้ชี้ไปยังที่ใดก็ได้ของโปรแกรม สุมมุติว่าชี้ไปที่ jump table ของ printf เมื่อมี strcpy ครั้งที่ 2 เราก็สามารถจะเขียนค่าใดๆ ก็ได้ที่ entry นั้น ผลที่เกิดขึ้นก็คือ เมื่อเรียก printf ครั้งต่อไป ตัวโปรมก็จะไปเรียกทำงานค่าที่ระบุแทนโปรแกรม printf ที่อ้างอิงกับ library

นักวิจัยหลายคนเสนอแนวทางการป้องกัน global offset table โดยให้ทำส่วนดังกล่าวเป็น read only หลังจากที่ได้มีตัวค่าโดย loader เรียบร้อยแล้ว อย่างไรก็การป้องกันดังกล่าว ก็มิได้ป้องกัน function pointer หรือส่วนอื่นๆ แต่อย่างใด

ยิ่งไปกว่านั้น หลักการดังกล่าวยังใช้ attacks โปรแกรมที่มีการป้องกัน Buffer Overflow ด้วยวิธีการที่นำเสนอกันหลายๆ แบบ เช่นการแทรกค่า cannary เพื่อตรวจสอบว่า address มีการเปลี่ยนแปลงหรือไม่ได้อีกด้วย เช่น การเปลี่ยนตัว error handling routine ให้เป็นของผู้บุกรุกเอง เป็นต้น (ดูรายละเอียดเพิ่มเติมได้ที่ Secure Bit 2, และ Buffer Overflow: the Fundamentals และ Secure Bit)

หลักการสำคัญของ Buffer-Overflow

ถึงตรงนี้ บางคนอาจจะตั้งข้อสังเกตุว่า Buffer Overflow ดูเหมือนจะเป็นปัญหาที่เกิดขึ้นจาก ภาษา C ซึ่งไม่มีระบบ Bound Checking แต่ในความเป็นจริงคือ ทุกภาษาในปัจจุบัน มักจะถูกแปลงลงมาเป็นภาษาเครื่อง หรือติดต่อกับระบบปฏิบัติการ ซึ่ง component ต่างๆ มักเขียนในภาษา C (และ assembly) ตัวอย่างที่เห็นได้ชัดเช่น Buffer-overflow attacks ที่พบใน Java, Perl, หรือแม้แต่ .Net

เมื่อวิเคราะห์ถึงประเด็นสำคัญที่ทำให้เกิด Buffer-Overflow Attacks แล้ว เราจะพบว่าปัญหาโดยตรงเกิดจากการที่ Input ซึ่งเป็นข้อมูลที่นอกเหนือความควบคุมของผู้เขียนโปรแกรม เข้ามาทำให้เกิดความเสียหายในระบบ จนทำให้การทำงานของโปรแกรมผิดไป [Howard and LeBlanc] จาก Microsoft กล่าวในหนังสือ Threat Model ว่า “All input is evil until proven otherwise” และเสนอว่า ข้อมูลทุกอันต้องมีการตรวจสอบเมื่อมีการส่งผ่านขอบเขตของโปรแกรม (“Data must be validated as it crosses the boundary between untrusted and trusted environments.”) ซึ่งข้อคิดนี้ มิใช่แนวคิดใหม่ หากแต่ผู้เขียนโปรแกรมทั่วไปมักมิได้คำนึงถึง

Secure Bit 2 ซึ่งเป็นงานวิจัยของผู้เขียนเอง เป็นเทคโนโลยีที่จะฝังหลักการดังกล่าวลงในระบบโดยผู้พัฒนาโปรแกรม ไม่จำเป็นต้องคอยระมัดระวังตรวจสอบ Input ของตัวเองตลอดเวลา (เพราะแม้นักพัฒนาซอฟต์แวร์มืออาชีพหลายคน ยังกล่าวว่ามีบ่อยครั้งที่ไม่สามารถตรวจสอบตัวโปรแกรมได้ครบทุกกรณีในขณะพัฒนาตัวโปรแกรม) รายละเอียดเพิ่มเติมศึกษาได้ที่ Secure Bit 2

บทสรุป

ในโอกาสต่อไป ผู้เขียนจะนำเสนอแนวทางการป้องกัน buffer overflow แบบต่างๆ ที่นักวิจัยทัวโลกพยายามพัฒนากันออกมา พร้อมกับชี้ให้เห็นข้อดีข้อเสีย โดยสรุปแล้ว buffer overflow เป็นปัญหาพื้นฐานของระบบคอมพิวเตอร์ตั้งแต่เรามีคอมพิวเตอร์จนถึงปัจจุบัน ยิ่งมีคอมพิวเตอร์ต่อไว้กับ internet มากขึ้นเพียงใด ก็มีเป้านิ่งให้ hackers ทั้งหลายได้โจมตีมาขึ้น ประกอบกับแต่เดิมผู้พัฒนาซอฟต์แวร์รายใหญ่ๆ (อาที MS) เน้นขาย funtioncs ใช้งาน และ มิได้ใส่ใจกับปัญหาดังกล่าวมากนัก แม้ปัจจุบันจะมีแนวทางที่เสนอออกมามากมาย แต่ก็ยังไม่มีแนวทางใดที่สมบูรณ์แบบ ทางแก้ปัญหาที่ดีที่สุด อาจจะเป็นพยายามฝึกให้ นักพัฒนาซอฟต์แวร์ทั้งหลายใส่ใจกับโปรแกรมที่ตัวเองเขียนมากขึ้น

อ้างอิง/อ่านเพิ่มเติม


Comment1
เนื้อหาเข้าใจง่าย มีความครอบครุมในหลาย ๆด้าน
From: <choonicha_15< at >hotmail.com>Date: 2005-06-19 15:05:12

Comment2
ดีมากครับ
From:danz <>Date: 2005-11-25 03:50:59

Comment3
:)
From:subss <>Date: 2006-02-18 22:57:48

Comment4
เข้ามาอ่านทีหลัง แต่ก็ขอบคุณสาระที่แปลมาได้อย่างดีครับ
From:sang <>Date: 2006-12-08 14:46:37

Comment5
เอ้อขอโทษจริงๆครับ นี่เป็นเจ้าของงานเขียนเองนี่เองถึงเขียนได้ดีเข้าใจง่ายขนาดนี้
ผมเจอหน้านี้ด้วย search engine นึกว่าเป็นงานแปลครับ
คนไทยเก่งจริงๆ

ขออภัยอย่างสูงครับ
From:คนเมื้อกี้ <>Date: 2006-12-08 14:50:02

Comment6
Krerk.P1 << ผมนับได้ 9 ตัวนะครับ คือตัวอักษรทั้งสิ้น 8 ตัว
และ terminator อีก 1 ตัว

หรือใช้วิธีนับยังไงครับ ขอบคุณครับ
From:4831243XXX <aruเจ.ออกatจีเมล>Date: 2007-11-24 04:02:37

Comment7
ขอบคุณที่แจ้งที่ผิดครับ ผมแก้เป็น Krerk.P01 เพื่อให้ได้ 10 ตามตัวอย่างที่กล่าวแล้วครับ
From:เกริก <>Date: 2007-11-24 15:48:10

Comment8
ขอบคุณมากครับอาจารย์
From:47380xxxxx <>Date: 2007-11-29 04:06:29

Comment9
แหล่งศึกษาเพิ่มเติมเกี่ยวกับ Buffer Overflow ในไทย
d.com
From:cool <>Date: 2008-01-10 12:19:40

Comment10
google.co.th
From:d <>Date: 2008-01-10 12:20:11

Comment11
www.vwin.co.th/document.php?node=34
From:gg <>Date: 2008-01-10 12:21:20

Comment12
คุณเก่งมากคับ ผมอ่านแล้วเข้าใจอย่างง่ายดาย
จากที่สงสัยมานานคับ ขอบคุณมากๆคับ
From:pp <>Date: 2008-02-21 01:34:15

Comment13
ขอบคุณมากครับ อธิบายละเอียดมากเลยครับ
จริงๆผมหาคำว่า ตัวอย่าง overflow ใน Google ก็เจอหน้านี้เลย
From:pysk <morethank< at >gmail.com>Date: 2008-11-06 04:40:52


ความเห็น:

โดย
Email:
** เนื่องจากปัญหา SPAM ความเห็นของคุณจะถูก post เมื่อได้รับการตรวจสอบจาก Administrator **