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:

ShellCodeNangCao3

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.

ShellCodeNangCao1

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.

ShellCodeNangCao4

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.

ShellCodeNangCao2

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..

ShellCodeNangCao5

Hình 5 : Tìm kiếm địa chỉ Kernel32 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.