Buffer Overflow cho "dummies" P.1

Buffer Overflow for kids - Part 1
By Bob of dtors.net


1. Giới thiệu:
Trước tiên là bài viết này sẽ không chỉ bạn cách viết mã khai thác nào cả. Mà ở đây sẽ nói cho bạn hiểu lỗi buffer overflow là gì (tràn bộ đệm), có bao nhiêu loại tràn bộ đệm, làm sao có thể phát hiện ra lỗi đó và tìm hiểu một chút về cách khai thác lỗi này.
Bạn không cần phải biết C và Assembly (ASM) để có thể hiểu toàn bộ bài viết.
Mục tiêu của bài viết này là giúp cho ai muốn hiểu sâu hơn về lỗi buffer overflow. Và nếu như bạn muốn tìm lỗi của một phần mềm, hay viết mã khai thác (exploit code) thì đây là một trong những nơi đầu tiên mà bạn phải bước chân đến.
Hy vọng các bạn sẽ học được điều gì đó mới mẻ ở bài viết này.

2. Một số định nghĩa:
Để tránh gây bức rứt cũng như bối rối khi đọc bài này, các bạn nên hiểu qua một vài định nghĩa dưới đây.
exploit: là phần mềm được thiết kế để khai thác một vấn đề hay lỗi nào đó mà một chương trình khác đang gặp phải. Các chương trình hay đoạn mã này sẽ cho phép chúng ta chạy một đoạn mã tùy ý mà cho phép chúng ta làm điều chúng ta muốn.
buffer: (bộ đệm) là một khối bộ nhớ lưu trữ các thực thể cùng kiểu dữ liệu.
heap: là vùng nhớ dùng để lưu trữ biến được cấp phát động thông qua malloc/free/calloc,…
stack: là vùng nhớ để lưu trữ các biến nằm trong hàm hoặc các đối số (argument) khi gọi hàm.
SFP: stack frame pointer, con trỏ chứa địa chỉ bắt đầu của stack
RET: địa chỉ trả về (RETurn address). Đây là nơi mà hàm được gọi, và hệ thống lưu lại nơi mà nó được gọi, nên khi thực hiện xong hàm, thì nó sẽ trở về đoạn code mà nó được gọi là tiếp tục thực hiện các lệnh sau đó. Vậy giả sử, nếu chúng ta chỉ cần thay đổi đỉa chỉ trả về này thành địa chỉ của nơi mà chứa code thực thi thì… cười một cách nguy hiểm

3. Vậy Buffer overflow là cái quái gì?
Tưởng tượng nhé, khi chúng ta rót một chai bia 500ml vào một ly bia chỉ chứa được 400ml thì thế nào nhỉ? Tất nhiên, ai cũng biết là nó sẽ bị TRÀN (overflow) sau đó lan ra khắp nơi và thế nào chúng ta mất đi 100ml bia yêu quý.
Cũng giống như ly bia và chai bia kia, buffer cũng có giới hạn, nên khi chúng ta đổ quá nhiều dữ liệu vào buffer và vượt quá giới hạn cho phép thì sẽ gây ra hiện tượng tràn bộ đệm (buffer overflow). Từ đây, chúng ta có thể thực thi vài dòng lệnh “đáng yêu” NẾU con trỏ bộ nhớ đã được ghi đè.
Có vẻ cũng hơi khó hiểu nhỉ, không sao, cùng xem ví dụ nào!

/*
 * beer.c
 */
int main() { //đây là nơi chương trình C bắt đầu
     char lybia[400]; //đây là biến ly bia của chúng ta, và nó chỉ chứa được 400ml
     char chaibia[500]; //còn đây là chai bia, chỉ chứa được 500ml
     memset(chaibia,0x41,500); // rót 500ml bia vào chai bia
     strcpy(lybia, chaibia); //hàm này tương đương đổ bia trong chai vào ly bia và 
     // kết quả như ta thấy là: TRÀN
     return 0 //trả về 0 sau đó kết thúc
} //chương trình kết thúc

Bây giờ thì dễ hiểu hơn rồi chứ nhỉ? Thì trong khi bị tràn, SFP và RET sẽ bị ghi đè và tràn ngập bởi "BIAAAAA!"
Nghĩa là, bây giờ địa chỉ trả về của chúng ta nó sẽ như thế này đây: 0x41414141. Khi hàm main thực hiện xong là thoát, thì thay vì thoát chương trình, nhưng CPU thấy có địa chỉ trả về, và nó tưởng rằng chương trình vẫn còn lệnh thực hiện. Sau đó CPU sẽ bay tới địa chỉ 0x41414141 và thực thi tiếp câu lệnh ở đâu. Tuy nhiên, nó sẽ hú lỗi ngay do ngoài vùng thực thi. Còn vì sao hú lỗi thì tôi sẽ giải thích sau.

Bắt đầu thú vị rồi phải không?

Vâng ở đây tôi sẽ giải thích cho bạn tạo sao lại bị hú lỗi thế kia. khi mà bạn cố tình hay vô ý làm tràn một chương trình đi nữa thì ở hầu hết trong mọi trường hợp thì chuyện này là khó thể xảy ra trừ khi chương trình của bạn chạy ở quyền cao nhất là root và có SUID của bản thân nó. (Chắc ai dùng *nix sẽ biết cái này nhỉ :stuck_out_tongue: ) Do đâu thì đây là một trong những cơ chế bảo vệ của hệ điều hành. Nên đừng mơ mà lộng hành. :))

4. Các loại buffer overflow
Có 2 loại tràn bộ đệm được biết đến rộng rãi. Một cái dựa trên stack và một cái dựa trên heap. Thi trong 2 cái đó, stack lại phổ biến hơn hẳn. Tuy nhiên thì, lỗi này đã cũ và hiện nay đã được fix rồi nhé. Và dựa vào đó mà heap buffer overflow lại trở nên hữu dụng. Mà, heap overflow thì có vẻ như không dễ hiểu cho lắm, nên chúng ta cứ tìm hiểu cơ bản về stack trước đã nhé. Heap thì chúng ta sẽ bàn ở bài khác và lần sau.

Như đã giới về stack ở trước, thì ở đây tôi sẽ cho bạn một ví dụ tại sao mà stack buffer overflow lại có thể xảy ra.
Mà trước tiên chúng ta hãy viết một chương trình để khai thác đã

void bob(){
char beer[10];
gets(beer);
}

Vậy stack hoạt động như thế nào? Hãy tượng tượng tới chồng đĩa CD, đĩa nào bỏ vô cuối cùng thì sẽ là cái lấy ra đầu tiên. Cơ chế này gọi là, Last In First Out. hoặc First In Last Out và cũng là cơ chế mà stack sử dụng để tổ chức dữ liệu.

Trở lại với chương trình của chúng ta, nhiệm vụ của chương trình này là yêu cầu người dùng nhập vào số bia cần bỏ vô chai với chai bia chỉ có 10ml. Nhưng nếu người dùng tham lam, lấy 15ml thì chắn chắn nó sẽ bị tràn, gây ra lỗi huyền thoại chắc ai cũng gặp qua: Segmentation Fault.
Vậy khi chúng ta gọi hàm bob(), “hàm của chúng ta” sẽ làm một chỗ trống cho biến beer[] trên stack.
Imgur
Một ví dụ cơ bản về cách sắp xếp dữ liệu của stack.

Nhiệm vụ của chúng ta bây giờ là ghi đè beer[] với mã độc cho đến khi biến beer[] tràn. Khi bị tràn, chúng ta có thể ghi đè luôn cả địa chỉ trả về bằng địa chỉ của biết beer[]. Thì khi mà hàm bob() thực thi xong, thay vì nó trở về nơi nó cần thực thi tiếp thì, nó sẽ quay lại địa chỉ của biến beer[] và thực thi mớ mã độc trong đó.

Và đây là stack của chúng ta khi làm điều trên:
http://imgur.com/vSEOYLU

Và đến đây thì chỉ hy vọng là bạn vẫn đang còn tỉnh táo sau khi đọc một cái wall of text như thế này. Và nếu đã đi được xa như thế này rồi, hy vọng các bạn đã học được cái gì có ích hoặc ôn lại kiến thức cỏn con.

5. Kết luận:
Gần xong rồi, một chút nữa thôi :slight_smile:
Ở topic này đã đưa cho bạn một vài khái niệm cơ bản về lỗi buffer overflow, cách khai thác nó như thế nào và cách mà stack hoạt động.
Có một cách mà chúng ta có thể bảo vệ khỏi việc này đó là đảm bảo rằng việc sử dụng một hàm bảo mật cho các hàm sử dụng stack (?) và luôn quản lý việc nhập xuất dữ liệu thật chặt chẽ.
Nếu bạn là người dùng Linux thì có thể dùng phần mềm FlawFinder để kiểm tra source của bạn có bị buffer overflow hay không.
Link download: www.packetstormsecurity.org.

Hy vọng các bạn thích bài viết này. Xin cảm ơn.
(Cont.)

5 Likes
83% thành viên diễn đàn không hỏi bài tập, còn bạn thì sao?