Chương trình sẽ sử dụng vùng nhớ này đểchứa các biến tạm và các dữ liệu trong quá trình thực thi của chương trình.. Tràn bộ đệm trên heap Head over flow Heap được sử dụng cho việc cấp ph
Trang 1MỤC LỤC
DANH MỤC CÁC HÌNH VẼ
CHƯƠNG 1 TỔNG QUAN VỀ LỖI TRÀN BỘ ĐỆM 1
1.1 Khái niệm bộ đệm 1
1.2 Lỗi tràn bộ đệm 1
1.2.1 Tràn bộ đệm trên heap (Head over flow) 1
1.2.2 Tràn bộ đệm trên stack (Stack over flow) 1
CHƯƠNG 2 MỘT SỐ PHƯƠNG PHÁP KHAI THÁC VÀ PHÒNG CHỐNG LỖI TRÀN BỘ ĐỆM 2
2.1 Một số phương pháp khai thác lỗi tràn bộ đệm 2
2.1.1 Thay đổi giá trị biến cục bộ 2
2.1.2 Ghi đè địa chỉ trả về của hàm 5
2.1.3 Quay về thư viện chuẩn (Return-to-libc) 6
2.1.4 Kỹ thuật ROP (Return Oriented Program) 7
2.2 Các cơ chế bảo vệ, chống khai thác lỗi tràn bộ đệm 11
2.2.1 Buffer Security Check -/GS 11
2.2.2 SafeSEH 13
2.2.3 DEP 13
2.2.4 ASLR 14
CHƯƠNG 3 DEMO KHAI THÁC LỖI TRÀN BỘ ĐỆM 15
3.1 Môi trường thử nghiệm 15
3.2 Kết quả thử nghiệm 18
Trang 2DANH MỤC CÁC HÌNH
Hình 2.1 Vị trí cookie và buf 3
Hình 2.2 Quá trình tràn biến buf và trạng thái cần đạt 4
Hình 2.3 Cách sắp đặt địa chỉ của hàm và các đối số trên stack 6
Hình 2.4 Cách sắp đặt hàm system() và exit() trên stack 7
Hình 2.5 Tìm kiếm gadgets thủ công 9
Hình 2.6 Tìm kiếm các modules được nạp lên bộ nhớ trong chương trình 10
Hình 2.7 Tìm kiếm tất cả gadgets có trong modules kernel32.dll 10
Hình 2.8 Các gadgets trong tập tin rop.txt 10
Hình 2.9 Windows bảo vệ - /GS trên Visul Studio 2008 11
Hình 2.10 Mô tả tùy chọn “/DYNAMICBASE” được thiết lập trên Visual Studio 2013 14Y Hình 3.1 Tiến hành chạy thử file đã biên dịch ra 14
Hình 3.2 Nhập thử vài ký tự 14
Hình 3.3 Tận dụng được thành công lỗi tràn bộ đệm trên head 16
Trang 3CHƯƠNG 1 TỔNG QUAN VỀ LỖI TRÀN BỘ ĐỆM 1.1 Khái niệm bộ đệm
Bộ đệm là một vùng nhớ được hệ điều hành cấp phát sẵn cho chương trìnhkhi chương trình được nạp lên bộ nhớ Chương trình sẽ sử dụng vùng nhớ này đểchứa các biến tạm và các dữ liệu trong quá trình thực thi của chương trình
1.2 Lỗi tràn bộ đệm
Tràn bộ đệm là lỗi xảy ra khi dữ liệu xử lý, thường là dữ liệu đầu vào, dàihơn giới hạn của vùng nhớ đệm được cấp phát để chứa nó Nếu phía sau vùng nhớnày chứa những dữ liệu quan trọng tới quá trình thực thi của chương trình thì dữliệu dư thừa có thể sẽ làm hỏng các dữ liệu quan trọng này
1.2.1 Tràn bộ đệm trên heap (Head over flow)
Heap được sử dụng cho việc cấp phát bộ nhớ động và heap chỉ sử dụng đểlưu trữ dữ liệu, không sử dụng để lưu các giá trị địa chỉ trả về của hàm như stacknên việc khai thác lỗi tràn bộ đệm trên heap khó hơn so với khai thác trên stack.Tuy vậy, vẫn có thể khai thác thành công lỗi tràn bộ đệm trên heap bằng các cáchsau:
Sửa dữ liệu: Kẻ tấn công có thể khai thác lỗ hổng bằng cách ghi đè dữ liệuquan trọng Điều này có thể làm hỏng chương trình hoặc làm thay đổi giá trị có thểđược sử dụng sau này
Sửa đối tượng: Trong ngôn ngữ lập trình như C++, Objective C, các đốitượng được đặt trên heap bao gồm các bảng con trỏ hàm và dữ liệu, do đó có thểthay thế dữ liệu khác hoặc thậm chí thay thế cả các phương thức thể hiện trong lớpđối tượng
Quản lý heap: Tuy việc cài đặt các bộ nhớ heap khác nhau tương đối nhiềunhưng một vài tính chất chung được cài đặt trong hầu hết các thuật toán Về cơbản, khi một lời gọi tới hàm malloc() hoặc một đoạn chương trình cấp phát tương
tự được tạo ra, một số ô nhớ sẽ được lấy từ bộ nhớ heap và được trả về cho ngườidùng
1.2.2 Tràn bộ đệm trên stack (Stack over flow)
Lỗi tràn bộ đệm trên stack xuất hiện khi bộ đệm lưu trữ dữ liệu trong bộ nhớkhông kiểm soát việc ghi giá trị dẫn đến tràn stack và việc tràn stack này làm chocác biến cục bộ và địa chỉ trở về của hàm bị ghi đè
Trong thực tế, các biến cục bộ thường được cấp phát liên tiếp nhau trênstack và khi có một lỗi xảy ra sẽ cho phép các dữ liệu có thể được ghi thêm vào các
Trang 4ô nhớ stack, các dữ liệu này sẽ ghi đè lên các biến cục bộ, thông tin trạng thái củachương trình, các tham số của hàm và địa chỉ trở về của hàm.
CHƯƠNG 2 MỘT SỐ PHƯƠNG PHÁP KHAI THÁC VÀ PHÒNG CHỐNG
LỖI TRÀN BỘ ĐỆM 2.1 Một số phương pháp khai thác lỗi tràn bộ đệm
2.1.1 Thay đổi giá trị biến cục bộ
printf (“You_Win!\n”);
}}
Ví dụ 1 là một ví dụ đầu tiên Để biên dịch những ví dụ tương tự ta sẽ dùng
cú pháp lệnh gcc –o <tên> <tên.c> như sau:
Bạn đọc dễ dàng nhận ra rằng GCC đã cảnh báo về sự nguy hiểm của việc
sử dụng hàm gets Chúng ta bỏ qua cahr báo đó vì vậy chính là hàm sẽ gây ra lỗi
tràn bộ đệm, đối tượng mà chúng ta đang bàn đến trong chương trình này
Để tận dụng lỗi thành công, người tận ụng lõi phải hiểu rõ chương trình hoạtđộng như thế nào Ví dụ 1 nhận một chuỗi từ bộ nhập chuẩn (Stdin) thông qua hàm
gets Nếu giá trị của biến nội bộ cookie là 41424344 thì sẽ in ra bộ cuất chuẩn (Stdout) dòng chữ “You_Win!” Biến cookie đóng vai trò là một dữ liệu quan trọng
trong quá trình hoạt động của chương trình
Trang 5Hình 2.1 Vị trí cookie và bufThông qua việc hiểu cách hoạt động của chương trình, chúng ta thấy rằngchính bản than chương trình đã chưa mã thực hiện tác vụ mong muốn (in ra mànhình dòng chữ “You_win!” Do đó một trong những con đường để dạt được mục
tiêu ấy là gán giá trị của cookie bằng với giá trị 41424344.
Ngoài việc hiểu cách hoạt động của chương trình, người tận dụng lỗi dĩnhiên phải tìm được nơi phát sinh lỗi Chúng ta may mắn được GCC thông báorằng lỗi nằm ở hàm gets Vấn đề giờ đây trở thành làm sao để tận dụng lỗi ở hàmgets để gán giá trị của cookie là 41424344
Hàm gets thực hiện việc nhận một chuỗi từ bộ nhập chuẩn và đưa vào bộđệm Ký tự kết thúc chuỗi (mã ASCII 00) cũng được hàm gets tự động thêm vàocuối Hàm này không kiểm tra kích thước vùng nhớ dùng để chứa dữ liệu nhập chonên sẽ xảy ra tràn bộ đệm nếu như dữ liệu nhập dài hơn kích thước của bộ đệm
Vùng nhớ được truyền vào hàm gets để chứa dữ liệu nhập là biến nội bộ buf.Trên bộ nhớ, cấu trúc của các biến nội bộ của hàm main được xác định như trongHình 2.1 Vì biến cookie được khai báo trước nên biến cookie sẽ được phân phát
bộ nhớ trong ngăn xếp trước, cũng đồng nghĩa với việc biến cookie nằm ở địa chỉcao hơn biến buf, và do đó nằm phía sau buf trong bộ nhớ
Trang 6Hình 2.2 Quá trình tràn biến buf và trạng thái cần đạtNếu như ta nhập vào 6 ký tự “abcdef” thì trạng thái bộ nhớ sẽ như Hình3.2a, 10 ký tự “0123456789abcdef” thì byte đầu tiên của cookie sẽ bị viết đè với
ký tự kết thúc chuỗi, và 14 ký “0123456789abcdefghij” tự thì toàn bộ biến cookie
sẽ bị ta kiểm soát
Để cookie có giá trị 41424344 thì các ô nhớ của biến cookie phải có giá trịlần lượt là 44, 43, 42, 41 theo như quy ước kết thúc nhỏ của bộ vi xử lý Intel x86.Hình 3.2d minh họa trạng thái bộ nhớ cần đạt tới để dòng chữ “You win!” được in
ra màn hình
Trang 7Ví dụ 2:
int main (){
int cookie;
char buf[16];
printf (“&buf:_%p,_&cookie:_%p\n”, buf, &cookie);
if (cookie == 0x0102030405){
printf (“You_Win!\n”);
}}
Như vậy, dữ liệu mà chúng ta cần nhập vào chương trình là 10 ký tự bất kỳ
để lấp đầy biến buf, theo sau bởi 4 ký tự có mã ASCII lần lượt là 44, 43, 42, và 41.Bốn ký tự này chính là D, C, B, và A Hình chụp sau là kết quả khi ta nhập vào 10
ký tự “a” và “DCBA”
Chúng ta đã tận dụng thành công lỗi tràn bộ đệm của chương trình để ghi đèmột biến nội bộ quan trọng Kết quả đạt được ở ví dụ này chủ yếu chính là câu trảlời cho một câu hỏi quan trọng: cần nhập vào dữ liệu gì Ở các ví dụ sau, chúng ta
sẽ gặp những câu hỏi tổng quát tương tự mà bất kỳ quá trình tận dụng lỗi nào cũngphải có câu trả lời
2.1.2 Ghi đè địa chỉ trả về của hàm
Một người dùng thạo kỹ thuật và có ý đồ xấu có thể khai thác các lỗi tràn bộđệm trên stack để thao túng chương trình theo một trong các cách sau:
Ghi đè một biến địa phương nằm gần bộ nhớ đệm trong stack để thay đổihành vi của chương trình nhằm tạo thuận lợi cho kẻ tấn công
Ghi đè địa chỉ trả về trong một khung stack (stack frame) Khi hàm trả về,
thực thi sẽ được tiếp tục tại địa chỉ mà kẻ tấn công đã chỉ rõ, thường là tại một bộđệm chứa dữ liệu vào của người dùng
Nếu không biết địa chỉ của phần dữ liệu người dùng cung cấp, nhưng biếtrằng địa chỉ của nó được lưu trong một thanh ghi, thì có thể ghi đè lên địa chỉ trả
về một giá trị là địa chỉ của một opcode mà opcode này sẽ có tác dụng làm chothực thi nhảy đến phần dữ liệu người dùng
Trang 82.1.3 Quay về thư viện chuẩn (Return-to-libc)
Quay trở về thư viện chuẩn là một kỹ thuật khai thác lỗi phần mềm có thểchống lại được cơ chế DEP, cơ chế này ngăn chặn việc thực thi trên vùng nhớđược đánh dấu là chỉ đọc hoặc ghi
Thay vì ghi đề địa chỉ trả về của hàm thành địa chỉ của shellcode thì kỹ thuậtnày sẽ ghi đè địa chỉ trả về thành địa chỉ của hàm trong bộ thư viện được nạp lên
bộ nhớ
Dữ liệu được đưa vào chương trình có thể kiểm soát vùng nhớ trên stack và
có thể sử dụng nó như các tham số đầu vào của hàm[Error: Reference source notfound]
Hình 2.3 mô tả cách sắp đặt địa chỉ của hàm và các đối số cho hàm đó và địachỉ của hàm tiếp theo trên stack
Hình 2.3 Cách sắp đặt địa chỉ của hàm và các đối số trên stack
Trang 9Trong ví dụ trên, hàm strcpy() sẽ copy giá trị từ tham số của chương trìnhvào biến nhưng không kiểm tra xem dữ liệu này có vượt quá kích thước của biếnlưu trữ buffer hay không, cụ thể ở đây biến buffer lưu trữ 80 byte, nếu dữ liệu đưavào chương trình lớn hơn 80 byte thì lỗi tràn bộ đệm sẽ xảy ra tại hàm strcpy().
Để minh họa việc khai thác chương trình này sử dụng kỹ thuật return-to-libc,
ta sẽ truyền chuỗi “calc” vào hàm system() để gọi chương trình calculator củaWindows, sau đó hàm exit() sẽ được gọi để kết thúc chương trình Hàm system()
và hàm exit() là hai hàm nằm trong bộ thư viện chuẩn và đều được nạp lên bộ nhớtrong các chương trình
Hình 2.4 minh họa cách sắp đặt địa chỉ của hàm system() và exit() trên stack
để có thể thực hiện khai thác lỗi Theo như Hình 2.4, ta sẽ cần 80 byte bất kỳ để có
thể ghi đè biến buffer và 4 byte bất kỳ để ghi đè giá trị EBP, giá trị Addr1 là địa chỉ của hàm system() sẽ ghi đè địa chỉ trả về của hàm main() Addr2 là giá trị tiếp theo trên stack, Addr2 là giá trị trả về sau khi hàm system() kết thúc, nghĩa là khi hàm
system() kết thúc, hàm exit() sẽ được gọi
Addr3 là tham số của hàm system(), giá trị này sẽ trỏ đến vùng nhớ lưu trữ
chuỗi được truyền vào hàm system(), trong trường hợp này nó trỏ đến chuỗi
“calc” Sau khi tham số “calc” được truyền vào, hàm system() sẽ gọi chương trình
Calculator của Windows
Hình 2.4 Cách sắp đặt hàm system() và exit() trên stack
2.1.4 Kỹ thuật ROP (Return Oriented Program)
ROP – Return Oriented Programing: Là một kỹ thuật khai thác lỗi tràn bộđệm khi chương trình được bảo vệ bởi cơ chế DEP, kỹ thuật này cũng gần giốngnhư kỹ thuật quay trở về thư viện chuẩn (return to libc), nhưng thay vì quay trở vềtrực tiếp các lời gọi các hàm của thư viện chuẩn thì ROP sử dụng các đoạn codenhỏ có sẵn trong các module của chương trình hoặc thư viện được nạp lên bộ nhớ
Trang 10và sắp xếp các đoạn code này trên stack theo một thứ tự nhất định để thực hiện
việc khai thác lỗi, các đoạn code nhỏ này được gọi là “Gadgets”
Gadgets là một nhóm nhỏ các tập lệnh được kết thúc bởi lệnh RET Ví dụ
như mov eax, 0x10; ret là một gadgets cho phép thiết lập giá trị thanh ghi eax thành
0x10 sau đó lệnh RET sẽ lấy giá trị tại đỉnh của stack và gán cho thanh ghi con trỏlệnh EIP và chương trình sẽ tiếp tục thực hiện mã lệnh tại địa chỉ mà thanh ghi EIPtrỏ đến Các gadgets này có thể kết hợp với nhau và tạo thành một chuỗi cácgadgets, chuỗi các gadgets này còn được gọi là “ROP chain”
Tìm kiếm các gadgets thủ công
- Sử dụng các chương trình gỡ lỗi như IDA, OllyDbg, Immunity Debugger
để tìm kiếm tất cả các lệnh “retn” hoặc byte “C3” trong chương trình hoặc modulecủa thư viện được sử dụng trong chương trình
- Từ các lệnh “ret” tìm được, thực hiện tìm kiếm một số byte phía trên so vớilệnh “ret” để được các mã lệnh hợp lệ mà được theo sau bởi “ret”
- Thực hiện việc ghi lại tất cả các mã lệnh được theo sau bởi “ret”
Như ta thấy trong Hình 2.5, các gadgets tìm thấy nằm trong khung vẽ hìnhchữ nhật Ta có tất cả 4 gadgets:
0x00401473: #POP EDI #POP ESI #RETN
0x00401474: #POP ESI #RETN
0x00401492: #XOR EAX, EAX #POP EBP #RETN
0x00401494: #POP EBP #RETN
Trang 11Hình 2.5 Tìm kiếm gadgets thủ côngTìm kiếm các gadgets tự động
Có thể thực hiện tìm kiếm tất cả các gadgets trong một chương trình hoặctrong một module của thư viện được sử dụng trong chương trình một cách tự động
bằng việc sử dụng mona.py [Error: Reference source not found] Đây là một
PyCommands của trình gỡ lỗi Immunity Debugger, chương trình này được viết
bằng Python và viết bởi corelanc0de3r.
Chỉ cần copy tập tin mona.py vào thư mục “PyCommands” của thư mục cài
đặt Immunity Debugger là có thể sử dụng
Hình 2.5 mô tả việc sử dụng lệnh “!mona modules” để tìm kiếm tất cả cácmodules được nạp lên bộ nhớ cùng với chương trình “test.exe” trong ImmunityDebugger Như ta thấy có module ntdll.dll, kernel32.dll, msvcr100.dll và chươngtrình test.exe
Hình 2.6 mô tả việc sử dụng lệnh “!mona rop –m kernel32” để tìm kiếm tất cảcác gadgets có trong module kernel32.dll, tất cả các gadgets được lưu vào tập tin
“rop.txt” và tập tin này được lưu trong thư mục cài đặt của Immunity Debugger
Trang 12Hình 2.6 Tìm kiếm các modules được nạp lên bộ nhớ trong chương trình
Hình 2.7 Tìm kiếm tất cả gadgets có trong modules kernel32.dllNội dung tập tin rop.txt được minh họa như trong Hình 2.8
Trang 132.2 Các cơ chế bảo vệ, chống khai thác lỗi tràn bộ đệm
2.2.1 Buffer Security Check -/GS
Chúng ta hãy phân tích cờ / GS của Visual Studio C / C ++ Tùy chọn này sẽ
cố gắng để ngăn chặn chồng BOF dựa vào thời gian chạy thêm một số dòng mãtrong đoạn mở đầu và kết thúc thủ tục / GS thực hiện hai cơ chế để đánh bại cuộctấn công này Thứ nhất một giá trị ngẫu nhiên , được gọi là cookie hoặc canary ,được lưu trữ trên stack , thứ hai là một loại sắp xếp lại biến được thực hiện Saukhi chương trình được khởi động, các cookie được lưu trong phần dữ liệu, sau đó ,nếu cần thiết , trong phần mở thủ tục được di chuyển trên stack giữa các biến cục
bộ và các địa chỉ ret, giá trị chúng ta sẽ bảo vệ Đồ họa stack nhìn chung sẽ giốngnhư sau:
Bây giờ chúng ta hãy xem làm thế nào để kích hoạt hoặc vô hiệu hóa ag flnày trên Visual Studio 08, và những gì xảy ra trong đoạn mở đầu thủ tục:
Hình 2.9 Windows bảo vệ - /GS trên Visul Studio 2008vuln!main: 00411260 55 push ebp 00411261 8bec mov ebp,esp 00411263 83ec4csub esp,4Ch 00411266 a100604100 mov EAX,dword ptr [vuln! security_cookie
Trang 14(00416000)] 0041126b 33c5 xor EAX,ebp 0041126d 8945fc mov dword ptr[ebp4],EAX.
Đây là đoạn mở đầu mới sử dụng cờ / GS , và như bạn có thể thấy, giá trịcủa cookie được lưu trữ trong EAX đăng ký và sau đó nó được XORed với con trỏ
cơ sở và nó được đặt trên stack Từ những dòng mã nó được dọn dẹp rằng ngănxếp sẽ được một cái gì đó giống như Hình vẽ ngăn xếp ở trên (sau khi con trỏkhung hình lưu lại) Bây giờ chúng ta hãy xem phần kết :
0041128b 8b4dfc mov ECX, dword ptr [ebp-4] 0041128e 33cd xor ECX,ebp 00411290 e87ffdffff call vuln!ILT+15( security_check_cookie (00411014)
00411295 8be5 mov esp,ebp 00411297 5d pop ebp
Trước hết , chúng ta nhận cookie từ stack và chúng ta lưu trữ nó trên sổ đăng
ký ECX , thứ hai chúng ta xor nó với EBP và cuối chúng ta gọi để thường xuyênkiểm tra Chúng ta hãy xem làm thế nào kiểm tra này được thực hiện:
vuln! security_check_cookie: 004112b0 3b0d00604100 cmp ECX,dwordptr[vuln! security_cookie(00416000)]004112b67502jne
vuln! security_check_cookie+0xa (004112ba) 004112b8 f3c3 rep ret004112ba e991fdffff jmp vuln!ILT+75( _report_gsfailure) (00411050)
Giá trị trong ECX, cookie trên stack, được so sánh với một thực thể và nếuchúng không bằng nhau nó nhảy vào báo cáo để thoát khỏi quá trình, trên thực tếsau rất nhiều hướng dẫn nó gọi :
vuln! report_gsfailure: 00411800 8bff mov edi,edi 00411802 55 pushebp 00411904 ff1578714100 call dword ptr [vuln!_imp TerminateProcess (00417178)
Do đó, nếu một kẻ tấn công ghi đè bộ nhớ đệm nó sẽ ghi đè lên các tập tincookie là tốt, trên thực tế cookie là giao ngay sau khi con trỏ cơ sở lưu Như vậy ,trong quá trình kiểm tra, báo cáo được gọi và nó sẽ gọi
TerminateProcess 8 Một cơ chế được sử dụng bởi cờ / GS để giảm thiểuloại tấn công này được dựa trên sắp xếp lại biến Ý tưởng rất đơn giản: chúng tamuốn giảm thiểu các hiệu ứng trong suốt bất ngờ tràn bộ đệm, đặc biệt chúng tamuốn tránh việc ghi đè các biến cục bộ và các đối số truyền cho hàm Bất cứ khinào một đối số dễ bị tổn thương do đó có nghĩa là hoặc là một buffer hoặc một contrỏ được tìm thấy nó sẽ được sắp xếp lại trên địa chỉ cao hơn ( nhớ bố trí stack) :cách này, nếu một buffer trên flow xảy ra chúng ta lưu các biến cục bộ của hàm