Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống
1
/ 14 trang
THÔNG TIN TÀI LIỆU
Thông tin cơ bản
Định dạng
Số trang
14
Dung lượng
1,37 MB
Nội dung
Windows Shell Code và Ứng dụng khai thác lỗi tràn bộ đệm Windows Shell Code và Ứng dụng khai thác lỗi tràn bộ đệm – Phần 1 Hôm nay tôi xin chia sẻ tới các bạn loạt các bài viết về chủ đề “Windows shellcode và ứng dụng khai thác lỗi tràn bộ đệm”. Bài viết của tôi sẽ bao gồm các thành phần sau đây: I. Khái niệm II. Shellcode cơ bản 1. System Calls & Shellcode 2. API & Shellcode 3. Lấy địa chỉ hàm trong Shellcode 4. Mã hóa shellcode III. Shellcode nâng cao 1. Ý tưởng 2. Tìm kiếm địa chỉ Kernel32.dll 3. Process Enviroment Block 4. Structured Exception Handling 5. TOPSTACK 6. Lấy địa chỉ hàm bên trong Kernel32.dll 7. Tối ưu độ dài của Shellcode IV. Các shellcode phổ biến 1. MessageBox Shellcode Version 1 2. MessageBox Shellcode Version 2 Hi vọng các bạn sẽ thu được những kiến thức hữu ích qua các bài viết này. Những kiến thức mà tôi đã tích lũy được trong suốt 8 năm nghiên cứu về an toàn thông tin, khai thác lỗ hổng phần mềm. 1. Khái niệm Khai thác lỗi tràn bộ đệm cho phép kẻ tấn công chiếm quyền điều khiển chương trình bị lỗi hay chính xác là có thể điều khiển con trỏ lệnh của chương trình đó. Như vậy, sau khi đã thực hiện việc khai thác và chiếm được quyền điều khiển con trỏ lệnh, kẻ tấn công cần phải thực thi thực hiện đoạn chương trình nào đó cho phép chiếm quyền điều khiển hệ thống. Đoạn chương trình này chính là Shellcode. Shellcode là một đoạn mã máy nhỏ cho phép thực hiện chỉ một nhiệm vụ nào đó bên trong một chương trình bị khai thác. Nhiệm vụ đó có thể đơn giản là đưa ra thông báo (MessageBox), có thể là tạo User, cài đặt trojan… Bài viết này sẽ hướng dẫn cách viết một shellcode từ đơn giản đến nâng cao. Bài viết yêu cầu người đọc phải biết khái niệm về các ngôn ngữ Assembly32 cho bộ vi xử lý 8086 của intel, lập trình API cho Windows 9x và Windows NT. Một số công cụ yêu cầu : • NASM32 & MakeShell • OllyDbg • Hiew • Depends (một công cụ trong bộ Visual Studio 6.0) • MSDN 2. Shellcode cơ bản System Calls & Shellcode System calls (SysCall) hay hàm gọi hệ thống là một kỹ thuật cho phép chương trình user-mode gọi một thủ tục của Kernel cho phép thao tác vào ra trực tiếp với hệ thống. SysCall có thể hiểu là một cổng trung gian giữa User-mode và Kernel-mode. Để hiểu về SysCall, ta có thể nhìn hình dưới đây : Hình 1 : Hoạt động của System Calls SysCall làm nhiệm vụ trung gian nhận lời gọi API từ ứng dụng User-mode, tìm kiếm chỉ số hàm Kernel trong System Call Table, thực thi hàm đó rồi trả lại kết quả cho ứng dụng. Việc nhận lời gọi API từ ứng dụng thực ra là nó nhận được ngắt từ ứng dụng, SysCall gọi trình phục vụ ngắt thực hiện chuyển đổi dữ liệu của API (tham số, cờ, thanh ghi…) từ User Stack sang Kernel Stack. Trong Windows, SysCall cho phép quản lý bộ nhớ, vào ra dữ liệu, quản lý thư mục, quản lý tiến trình, … Tuy nhiên, đa phần API có giao tiếp SysCall đều là các hàm Undocument. Ngắt được sử dụng để yêu cầu SysCall : • Windows 2000 : “int 0x2e” • Windows XP : SYSENTER Như vậy, Dựa vào cơ chế của System Call, ta có thể viết Shellcode bằng cách gọi hàm bởi chỉ số của hàm bên trong bảng System Call Table. Đoạn asm dưới đây thực hiện kết thúc tiến trình bằng cách gọi API ntTerminalProcess với số SysCall của nó: MOV EAX, 0x000000bb ; 0xbb là số SysCall hàm ntTerminalProcess LEA EDX, [ESP+4] ; EDX lưu tham số của hàm INT 0x2E ; Gọi Syscall thực hiện hàm Với số lượng SysCall tương đối trong Windows, ta có thể thực hiện viết Shellcode chỉ thông qua SysCall. Tuy nhiên, Việc viết shellcode như vậy có cả ưu điểm và hạn chế: Ưu điểm: • Không cần sử dụng của hàm API, cụ thế là không cần xác định địa chỉ của các hàm API. • Có thể sử dụng để vượt qua cơ chế bảo vệ chống tấn công tràn bộ đệm ở user-mode. Nhược điểm: • Giới hạn khả năng làm việc của Shellcode vì số lượng SysCall là có hạn (trong Windows khoảng hơn 1000) • Vì các hàm socket API không được Windows hỗ trợ giao tiếp SysCall nên Shellcode sử dụng SysCall không có khả năng làm việc với mạng. 2. API & Shellcode Windows API là thư viện các hàm cho phép phát triển các ứng dụng khác nhau trên nền hệ điều hành của Microsoft. Shellcode cũng coi như là một ứng dụng trên nền Windows, do đó nó cũng có thể được viết thông qua các hàm API. Khác với viết Shellcode bằng SysCall, Shellcode viết bằng hàm API có khả năng rất đa dạng, đặc biệt nó có thể làm việc với các hàm socket API. Với ưu điểm trên, hầu hết các Shellcode được sử dụng cho các cuộc tấn công ngày nay đều được viết bằng các hàm API và thực hiên các nhiệm vụ như connect back,download, send mail, cài đặt trojan … Tuy nhiên, cũng giống như SysCall, địa chỉ của các hàm API bị thay đổi cho mỗi phiên bản khác nhau của Windows nên một Shellcode có địa chỉ API cố định sẽ chỉ có thể thực thi ở đúng phiên bản Windows. Trước tiên, chúng ta sẽ viết một đoạn Shellcode đơn giản, chỉ sử dụng địa chỉ cố định của hàm API. Công cụ sử dụng : • Depends (Công cụ trong bộ VS6.0). • Text Editor (EditPlus, NotePad…) • NASM&MakeShell Trước tiên, ta biết các ứng dụng khi thực thi đều phải nạp thư viện Kernel32.dll vào không gian bộ nhớ. Do đó, Shellcode đầu tiên này sẽ sử dụng 2 hàm API trong Kernel32.dll với nhiệm vụ gọi “notepad” bắt hàm “Winexec()”, sau đó kết thúc chương trình bằng hàm “ExitProcess()”. Sử dụng Depends để xác định địa chỉ hàm “Winexec()” cho Windows XP SP2 ta nhận được địa chỉ cơ sở của Kernel32.dll (0x7C800000) và địa chỉ tương đối của hàm (0x6114D). Như vậy, địa chỉ tuyệt đối Shellcode sử dụng là 0x7C86114D. Tương tự như vậy ta có địa chỉ hàm “ExitProcess()” là 0x7C81CAA2. Hình 2 : Sử dụng Depends xác định địa chỉ hàm Biên dịch đoạn asm sau ra file nhị phân rồi chuyển sang Shellcode : nasmw.exe -s -fbin file.asm makeshell.exe file Kết quả là đoạn Shellcode có thể thực hiện trên Windows XP SP2: \ xBB\x4D\x11\x86\x7C\xE9\x0E\x00\x00\x00\x58\x68\x05\x00\x00\x00\ x50\xFF\xD3\xE9\x0D\x00\x00\x00\xE8\xED\xFF\xFF\xFF\x6E\x6F\x74\ x65\x70\x61\x64\x00\x68\x01\x00\x00\x00\xBB\xA2\xCA\x81\x7C\xFF\ xD3 Như vậy, ta đã có được đoạn Shellcode thực hiện một nhiệm vụ nhỏ. Tuy nhiên, vì chỉ sử dụng các hàm trong Kernel32.dll nên khả năng của Shellcode rất hạn chế vì thế ta cần phải nạp thêm các thư viện. Trong bài viết tới tôi sẽ tiếp tục với việc “Lấy địa chỉ hàm trong Shellcode” và “Mã hóa shellcode“. #LƯU Ý: Các ví dụ trên đều được thực hiện trên Windows XP. Windows Shell Code và Ứng dụng khai thác lỗi tràn bộ đệm – Phần 2 Chào các bạn, tiếp tục chủ đề về “Windows Shell Code và Ứng dụng khai thác lỗi tràn bộ đệm”. Hôm nay chúng ta tiếp tục giải quyết các vấn đề liên quan tới việc lấy địa chỉ hàm, nạp thêm thư viện và việc mã hóa shellcode. Nếu bạn nào chưa đọc bài viết trước của tôi, bạn có thể tham khảo tại đây. Lấy địa chỉ hàm trong Shellcode Để đa dạng hóa khả năng của Shellcode, ta sẽ nạp thêm các thư viện khác như User32.dll… bằng 2 hàm API là LoadLibrary & GetProcAddress Nạp thư viện bằng hàm LoadLibrary: HINSTANCE LoadLibrary ( LPCTSTR lpLibFileName); Lấy địa chỉ hàm bằng hàm GetProcAddress: FARPROC GetProcAddress( HMODULE hModule, LPCWSTR lpProcName); Như vậy, ta sẽ phải biết địa chỉ của hàm LoadLibrary & GetProcAddress bên trong thư viện Kernel32.dll. Đoạn asm dưới đây sẽ thực hiện gọi MessageBox từ thư viện User32.dll: 4. Mã hóa shellcode Nhắc lại một chút về tấn công tràn bộ đệm (Buffer Overflow), ta biết lỗi tràn bộ đệm về cơ bản là do lập trình cẩu thả, không kiểm soát các giá biên của dữ liệu. Do vậy, khi ta sử dụng các hàm ghi dữ liệu vào vùng nhớ như printf(), strcpy()… vùng nhớ sẽ bị tràn. Kẻ tấn công dựa vào các hàm này để đưa mã độc vào các vùng nhớ đó. Tuy nhiên, các hàm trên đưa dữ liệu vào vùng nhớ dưới dạng chuỗi ký tự (string), nghĩa là tất cả các ký tự NULL hay 0x00 đều là điểm ngắt của chuỗi ký tự và Shellcode chứa ký tự 0x00 sẽ không được đưa vào vùng nhớ. Như vậy, mã hóa Shellcode là cần thiết để loại bỏ các giá trị không mong muốn mà vẫn cho phép được thực thi bình thường. Bài viết này sử dụng phương phép XOR đoạn mã nhị phân với một giá trị cụ thể (ví dụ như 0x02). Dưới đây là đoạn mã nhị phân chưa được XOR và đoạn mã nhị phân sau khi XOR : \x55\x89\xe5\x57\x56\x53\xe8\x00\x00\x00\x00\x5b… \x57\x8b\xe7\x55\x54\x51\xea\x02\x02\x02\x02\x59… Mã hóa tất nhiên sẽ phải có cơ chế giải mã, đoạn mã sau sẽ thực hiện giải mã đoạn Shellcode trên. Đoạn Shellcode sau đây bao gồm cả mã hóa và giải mã sẽ không còn các giá trị không mong muốn (0x00) : \ xeb\x0d\x5e\x31\xc9\xb1\x66\x80\x36\x02\x46\xe2\xfa\xeb\x05\xe8\xee \xff\xff\xff\x57\x8b\xe7\x55\x54\x51\xea\x02\x02\x02\x02\x59… Phương pháp mã hóa trên có hạn chế là phải có cơ chế giải mã tương ứng. Ngoài phương pháp trên đây còn nhiều phương pháp khác để tránh cho Shellcode gặp những giá trị đặc biệt, không mong muốn : • Viết Shellcode bằng ký tự. • Unicode Shellcode. Trong các bài tới tôi sẽ tiếp tục với các chủ đề còn lại bao gồm: III. Shellcode nâng cao 1. Ý tưởng 2. Tìm kiếm địa chỉ Kernel32.dll 3. Process Enviroment Block 4. Structured Exception Handling 5. TOPSTACK 6. Lấy địa chỉ hàm bên trong Kernel32.dll 7. Tối ưu độ dài của Shellcode IV. Các shellcode phổ biến 1. MessageBox Shellcode Version 1 2. MessageBox Shellcode Version 2 Windows Shellcode và ứng dụng khai thác lỗi tràn bộ đệm – Phần 3 Tiếp tục với chủ đề “Windows Shellcode và ứng dụng khai thác lỗi tràn bộ đệm” hôm nay chúng ta sẽ tiếp tục với “Shellcode nâng cao”. Bạn có thể theo dõi lại các bài viết tại đây: Windows Shellcode và ứng dụng khai thác lỗi tràn bộ đệm – Phần 1 Windows Shellcode và ứng dụng khai thác lỗi tràn bộ đệm – Phần 2 Ý tưởng Phần trên ta đã trình bày các bước để viết một Shellcode đơn giản. Tuy nhiên Shellcode trên vẫn còn rất hạn chế, đặc biệt là nó không có khả năng làm việc trên các phiên bản khác nhau của Windows. Một Shellcode hoàn thiện phải có đầy đủ các chức năng sau: • Phải có khả năng đặt vào vùng nhớ có khả năng thực thi. • Phải ngắn nhất có thế. • Phải loại bỏ được tất cả các giá trị đặc biệt. • Không bị phụ thuộc vào phiên bản hệ điều hành. Tìm kiếm địa chỉ Kernel32.dll Để Shellcode không phụ thuộc vào phiên bản của hệ điều hành Windows, ta phải có cơ chế cho phép tìm kiếm địa chỉ hàm bên trong Shellcode. Kỹ thuật đặt ra gồm 2 bước, thứ nhất là tìm kiếm địa chỉ thư viện Kernel32.dll, thứ hai là tìm kiếm địa chỉ hai hàm API LoadLibrary() và GetProcAddress() trong thư viện Kernel32.dll. Từ đó ta dễ dàng xác định địa chỉ của các hàm API khác của Windows. Sau đây sẽ là 3 phương pháp cho phép tìm kiếm địa chỉ thư viện Kernel32.dll. a. Process Enviroment Block Process Enviroment Block (PEB) là một cơ chế quản lý một số thông tin về tiến trình như process heap, threadlocal storage, danh sách module đã nạp… thông qua một cấu trúc là cấu trúc PEB. May mắn thay, tất cả các thông tin về module đã nạp (bao gồm cả địa chỉ cơ sở) được lưu trữ trong cấu trúc PEB này và địa chỉ của cấu trúc này lại không đổi, không phụ thuộc phiên bản của Windows. Sơ đồ sau sẽ mô tả thuật toán tìm ra được địa chỉ của thư viện Kernel32.dll: Hình 3 : Tìm kiếm địa chỉ Kernel32.dll qua cấu trúc PEB Dựa vào sơ đồ địa chỉ và các cấu trúc ở hình 3, ta sẽ viết đoạn mã asm cho phép lấy địa chỉ của Kernel32.dll từng bước như sau. b. Structured Exception Handling Structured Exception Handling (SEH) là một cấu trúc điều khiển ngoại lệ, cho phép xử lý một số tình huống bất thường xảy ra khi thực thi chương trình. Cấu trúc này đặt trên Stack (hình 4) gồm một trường con trỏ tới entry SEH tiếp theo và một trường trỏ tới hàm xử lý ngoại lệ. Điều đặc biệt mà ta muốn biết đến là cấu trúc cuối cùng được chỉ định có con trỏ 0xFFFFFFFF và hàm xử lý ngoại lệ này mặc định là một hàm bên trong Kernel32.dll. Từ địa chỉ của hàm này đi ngược lên ta sẽ tìm thấy địa chỉ cơ sở của Kernel32.dll. Phương pháp này có thể vẫn không tìm thấy địa chỉ Kernel32.dll vì cấu trúc SEH có thể bị chương trình ứng dụng thay đổi. Tuy nhiên, điều này hiếm khi xảy ra nên kỹ thuật trên có thể sử dụng khá ổn định và cho phép chạy trên cả Windows 9x. Trong cấu trúc của file DLL (hay file PE), khoảng cách về địa chỉ giữa các sections là 1000h và mỗi địa chỉ tương ứng với 4-byte nên DLL xác định biên cho các sections là 64KB. Do vậy, từ một hàm trong thư viện, ta đi ngược n lần với bước nhảy 1000h cộng với Offset của hàm trong Section chứa nó cho đến khi gặp chữ ký “MZ” thì đó chính là địa chỉ cơ sở của Kernel32.dll. c. TOPSTACK Phương pháp thứ 3 sử dụng Stack và SEH, nhưng hạn chế của phương pháp này là không áp dụng được cho Windows 9x [...]... đệm Tôi hi vọng đã mang lại những kiến thức hữu ích khi nghiên cứu về shellcode và lỗi tràn bộ đệm Bạn có thể theo dõi các bài viết trước tại đây: Windows Shellcode và ứng dụng khai thác lỗi tràn bộ đệm – Phần 1 Windows Shellcode và ứng dụng khai thác lỗi tràn bộ đệm – Phần 2 Windows Shellcode và ứng dụng khai thác lỗi tràn bộ đệm – Phần 3 Lấy địa chỉ hàm bên trong Kernel32.dll Ta đã có địa chỉ của... theo Stack và SEH Dựa vào hình 5, ta thấy kỹ thuật trên đơn giản là đưa con trỏ tới SEH mặc định trong Stack, lấy hàm xử lý ngoại lệ bên trong Kernel32.dll, từ đó việc lấy địa chỉ cơ sở của thư viện trên giống phương pháp SEH Windows Shellcode và ứng dụng khai thác lỗi tràn bộ đệm – Phần cuối Hôm nay tôi sẽ kết thúc loạt bài viết về chủ đề Windows ShellCode và ứng dụng khai thác lỗi tràn bộ đệm Tôi... điều kiện để viết được một Shellcode không phụ thuộc vào phiên bản của hệ điều hành Trong phần cuối là mã nguồn asm cho phép thực hiện gọi một MessageBoxA trong User32.dll bằng các phương pháp đã nêu Tối ưu độ dài của Shellcode Với phương pháp tìm kiếm địa chỉ đã nêu trên, Shellcode đã có thể thực thi mà không phụ thuộc vào phiên bản của hệ điều hành Nhưng nếu Shellcode sử dụng nhiều hàm API để thực... điều hành Nhưng nếu Shellcode sử dụng nhiều hàm API để thực hiện nhiệm vụ phức tạp thì bộ nhớ lưu trữ tên của các hàm sử dụng sẽ rất lớn Để giảm kích thước của Shellcode ta sẽ phải giảm kích thước lưu trữ tên các hàm cần tìm Điều này còn cho phép ta không cần sử dụng GetProcAddress() để lấy địa chỉ hàm Kỹ thuật được sử dụng là ta sẽ băm (hash) chuỗi tên hàm trong 4-byte bằng thuật toán sau trong C : Đoạn... khớp sẽ dừng lại nếu khớp kết quả băm, từ đó tìm ra địa chỉ hàm LoadLibrary Bên dưới là mã asm của chương trình gọi MessageBox đã cải tiến của version1 bằng các hàm băm, nên shellcode kết quả có kích thước cho nhỏ hơn Các shellcode phổ biến MessageBox1 MessageBox2 ... chứa thông tin về các hàm (symbols) xuất Kỹ thuật sử dụng là truy nhập vào bảng chứa tên các hàm, so sánh tên từng hàm với hàm tên hàm muốn tìm Từ đó xác định địa chỉ tuyệt đối của hàm Đoạn asm dưới đây cho phép tìm kiếm địa chỉ hàm “GetProcAddress()” Như vậy, ta đã xác định được địa chỉ của hàm GetProcAddress(), địa chỉ cơ sở của Kernel32.dll Dựa vào 2 thông tin trên, ta dễ dàng xác định địa chỉ hàm . về shellcode và lỗi tràn bộ đệm. Bạn có thể theo dõi các bài viết trước tại đây: Windows Shellcode và ứng dụng khai thác lỗi tràn bộ đệm – Phần 1 Windows Shellcode và ứng dụng khai thác lỗi tràn. Windows Shell Code và Ứng dụng khai thác lỗi tràn bộ đệm Windows Shell Code và Ứng dụng khai thác lỗi tràn bộ đệm – Phần 1 Hôm nay tôi xin chia sẻ tới các bạn loạt các bài viết về chủ đề Windows. pháp SEH. Windows Shellcode và ứng dụng khai thác lỗi tràn bộ đệm – Phần cuối Hôm nay tôi sẽ kết thúc loạt bài viết về chủ đề Windows ShellCode và ứng dụng khai thác lỗi tràn bộ đệm . Tôi hi