2.1 Các kỹ thuật tấn công phổ biến vào ứng dụng Web
2.1.3 Kỹ thuật tấn công Tràn bộ đệm
Lỗi tràn bộ đệm (Tràn bộ đệm) đƣợc sinh ra do thói quen của ngƣời lập trình mà chủ yếu đối với các chƣơng trình đƣợc viết bằng ngơn ngữ C. Tuy nhiên, ngay trong chính bản thân ngơn ngữ này đã tồn tại nhiều điểm yếu mà kẻ tấn cơng có thể khai thác. Khi ngƣời dùng cần nhập một lƣợng dữ liệu có kích thƣớc lớn, hoặc thực hiện sao chép một mảng sang mảng khác thì chƣơng trình cần thực hiện kiểm tra kích thƣớc dữ liệu để cấp phát bộ nhớ hợp lý nhằm khơng vƣợt q kích thƣớc đã cho phép. Sự tính tốn này cịn đƣợc gọi là ―kiểm tra giới hạn‖. Một số hàm thƣ viện trong ngôn ngữ C không thực hiện việc kiểm tra giới hạn, ví dụ: strcpy(), strcat(), sprinf(). Chính vì lý do này nên khi ngƣời lập trình sử dụng các hàm trên mà quên mất không thực hiện bƣớc kiểm tra giới hạn thì sẽ gây ra lỗi tràn bộ đệm.
Ví dụ:
Func(char*ch){
Char buffer[256]; Strcpy(buffer, ch); }
Với đoạn ví dụ ở trên, kích thƣớc tối đa đƣợc cấp phát cho buffer là 256 byte. Do đó, hệ hiều hành sẽ dành 256 byte vùng nhớ trong bộ nhớ máy tính hay cịn gọi là bộ đệm khi thực hiện chƣơng trình. Trƣờng hợp tràn bộ đệm xảy ra khi chƣơng trình khơng kiểm tra dữ liệu nhập vào và bộ đệm nhận nhiều hơn 256 byte dữ liệu. Do đó, đối với những byte dữ liệu còn dƣ sẽ đƣợc ghi đè lên những dữ liệu đã có của vùng nhớ liền kề. Đây là một ví dụ đơn giản để mơ tả về lỗi tràn bộ đệm
2.1.3.2 Kỹ thuật tấn công tràn bộ đệm
Kỹ thuật tấn cơng tràn bộ đệm có thể thực hiện theo nhiều cách khác nhau dựa trên kiến trúc máy tính, hệ điều ngƣời dùng đang sử dụng, hoặc kiến trúc bộ nhớ máy tính. Ngồi ra trong khi phân loại vùng nhớ thì Stack và Heap là hai vùng nhớ tự động thay đổi khi chạy chƣơng trình, do vậy nên lỗi tràn bộ đệm rất có khả năng sẽ xảy ra tại hai vùng nhớ này. Từ đây, kỹ thuật tấn cơng tràn bộ đệm có thể phân chia thành hai loại: Tràn bộ nhớ trên Stack và tràn bộ nhớ trên Heap.
a) Tràn bộ nhớ trên Stack
Ngăn xếp (Stack) là một cấu trúc dữ liệu bao gồm các đối tƣợng dữ liệu đƣợc sắp xếp tuyến tính, một trong hai đầu đƣợc gọi là đỉnh của ngăn xếp. Ngƣời dùng chỉ có thể truy cập tới đối tƣợng dữ liệu ở đỉnh của ngăn xếp, thêm một đối tƣợng mới vào đỉnh của ngăn xếp và loại đối tƣợng dữ liệu ở đỉnh ra khỏi ngăn xếp. Chúng ta có thể tƣởng tƣợng một chồng sách là một ngăn xếp. Trong chồng sách, các quyển sách đã đƣợc sắp xếp theo thứ tự trên-dƣới, quyển sách nằm trên cùng đƣợc xem là đỉnh của chồng sách. Chúng ta có thể dễ dàng đặt một quyển sách mới lên đỉnh chồng sách và lấy quyển sách ở đỉnh ra khỏi chồng sách. Và nhƣ thế quyển sách đƣợc lấy ra khỏi chồng là quyển sách đƣợc đặt vào chồng sau cùng hay còn gọi là cơ chế LIFO (Last In First Out).
Lỗi tràn bộ nhớ trên Stack xảy ra khi bộ đệm lƣu trữ dữ liệu trong bộ nhớ không kiểm sốt việc ghi giá trị trên nó. Hậu quả của việc này dẫn đến tràn bộ nhớ trên stack và dữ liệu dƣ thừa sẽ đƣợc ghi đè lên địa chỉ trả về của hàm.
Lợi dụng lỗi xảy ra trong vùng nhớ stack mà kẻ tấn công sử dụng lỗi tràn bộ đệm này để thực thi một đoạn mã bất hợp pháp khi chạy chƣơng trình. Kết quả là kẻ tấn cơng có thể chiếm quyền điều khiển tiến trình tùy theo vùng nhớ mà bộ đệm đƣợc đặt tại đó. Ví dụ, dữ liệu tạm thời trong vùng stack sẽ đƣợc ―push‖ vào đỉnh ngăn xếp và đƣợc ―pop‖ để lấy giá trị. Khi thực thi chƣơng trình, bộ nhớ sẽ lƣu các dữ liệu tạm thời của chƣơng trình và nhờ đó trong khoảng thời gian chạy chƣơng trình thì chƣơng trình sẽ truy xuất đến các dữ liệu này.
Sau đây là một ví dụ đơn giản về tràn bộ đệm trong Stack. Thanh ghi EIP sẽ bị tràn với một chuỗi ký tự 0x41414141, đó là các ký tự AAAA trong mã ASCII.
/* stack3.c*/ #include <stdlib.h> #include <stdio.h> #include <string.h> int bof() {
char buffer[7]; /* khởi tạo bộ đệm có kích thƣớc là 8 */ /*sao chép 20 byte ký tự A vào bộ đệm*/
strcpy(buffer,"AAAAAAAAAAAAAAAAAAAA"); return 1;
}
int main(int argc, char **argv) {
bof(); /*call our function*/
/*In ra một thông báo ngắn, việc thực hiện này sẽ không bao giờ đƣợc thực hiện bởi vì tràn bộ đệm*/
printf("Not gonna do it!\n"); return 1;
}
Ngoài ra, địa chỉ trả về đƣợc lƣu trữ trong stack, có nghĩa là địa chỉ của chƣơng trình đang chạy trƣớc khi thực hiện lời gọi làm. Do đó, khi kết thúc lời gọi hàm, vùng nhớ tạm thời sẽ đƣợc lấy ra khỏi stack và trả lại địa chỉ trả về cho stack. Vậy nên, nếu địa chỉ trả về trong stack lại bị ghi đè bởi lỗi tràn bộ đệm do kẻ tấn cơng có chủ đích gây ra thì kẻ tấn cơng có thể thực thi chƣơng trình hoặc đoạn mã bất hợp pháp và gây ra mất an toàn cho hệ thống. [11]
b) Tràn bộ nhớ Heap
Heap là vùng nhớ đƣợc cấp phát động. Vùng nhớ này là vùng nhớ logic riêng biệt với vùng nhớ đƣợc cấp phát cho stack và code. Heap đƣợc tạo ra và giải phóng một cách tự động. Heap thƣờng đƣợc sử dụng bởi vì kích cỡ bộ nhớ cho các chƣơng trình khơng biết trƣớc thời gian kết thúc hoặc lớn hơn so với stack. Heap không chứa địa chỉ trả về nhƣ stack. Vì vậy, kẻ tấn cơng khơng có khả năng ghi đè lên địa chỉ trả về. [10]
Ví dụ cơ bản của tràn heap: /*heap1.c*/
#include<stdio.h> #include<stdlib.h>
Int main(int argc, char *argv[]){ Char *input = malloc (20); Char *output = malloc (20); Strcpy(output, ―normal output‖); Strcpy(input, argv[1]);
Printf(―input at %p: %s\n‖, input, input); Printf(―output at %p: %s\n‖, output, output); Printf(―\n\n%s\n‖, output);
}
Khi tràn heap xảy ra thì kẻ tấn cơng có thể lợi dụng việc này để ghi đè con trỏ hoặc ghi đè hàm con trỏ. [11]
Hình 2.7: Ví dụ mơ tả tràn bộ đệm Heap