Tổng Hợp Các Câu Hỏi Phỏng Vấn Về Khai Thác Các Lỗ Hổng Trong Quá Trình Phát Triển - Exploit Development (Phần 1)

I. Describe what buffer overflow is and how you would test for it?

Mô tả lỗi tràn bộ đệm và làm sao để có thể phát hiện ?

1.1 Một số định nghĩa:

Để tránh gây bức rứt cũng như bối rối khi đọc bài này, các bạn nên hiểu qua một vài định nghĩa dưới đây.

  • Exploit: là phần mềm được thiết kế để khai thác một vấn đề hay lỗi nào đó mà một chương trình khác đang gặp phải. Các chương trình hay đoạn mã này sẽ cho phép chúng ta chạy một đoạn mã tùy ý mà cho phép chúng ta làm điều chúng ta muốn.

  • Buffer: (bộ đệm) là một khối bộ nhớ lưu trữ các thực thể cùng kiểu dữ liệu.

  • Heap: là vùng nhớ dùng để lưu trữ biến được cấp phát động thông qua malloc/free/calloc,…

  • Stack: là vùng nhớ để lưu trữ các biến nằm trong hàm hoặc các đối số (argument) khi gọi hàm.

  • SFP: stack frame pointer, con trỏ chứa địa chỉ bắt đầu của stack

  • RET: địa chỉ trả về (RETurn address). Đây là nơi mà hàm được gọi, và hệ thống lưu lại nơi mà nó được gọi, nên khi thực hiện xong hàm, thì nó sẽ trở về đoạn code mà nó được gọi là tiếp tục thực hiện các lệnh sau đó. Vậy giả sử, nếu chúng ta chỉ cần thay đổi địa chỉ trả về này thành địa chỉ của nơi mà chứa code thực thi thì… 😎😎

1.2.Vậy Buffer overflow là cái gì?

  • Trong các lĩnh vực an ninh máy tính và lập trình, lỗi tràn bộ nhớ đệm hay gọi tắt là lỗi tràn bộ đệm (buffer overflow) là một lỗi lập trình có thể gây ra một ngoại lệ truy nhập bộ nhớ máy tính và làm cho chương trình bị kết thúc, hoặc khi người dùng có ý phá hoại, họ có thể lợi dụng lỗi này để phá vỡ an ninh hệ thống.

  • Có thể hiểu một cách đơn giản giống như chúng ta đổ một chai nước 500ml vào một cốc nước chỉ chưa được 400ml. Thì điều gì sẽ xảy ra? Chắc chắn ai cũng biết là nó sẽ bị TRÀN (overflow) sau đó lan ra khắp nơi và thế nào chúng ta mất đi 100ml nước.

  • Cũng giống như cốc nước và chai nước kia, buffer cũng có giới hạn, nên khi chúng ta đổ quá nhiều dữ liệu vào buffer và vượt quá giới hạn cho phép thì sẽ gây ra hiện tượng tràn bộ đệm (buffer overflow). Từ đây, chúng ta có thể xem ví dụ để dễ hiểu hơn:

    int main() { //đây là nơi chương trình C bắt đầu
         char coc[400]; //đây là biến cốc nước của chúng ta, và nó chỉ chứa được 400ml
         char chainuoc[500]; //còn đây là chai nước, chỉ chứa được 500ml
         memset(chainuoc,0x41,500); // rót 500ml nước vào chai nước
         strcpy(coc, chainuoc); //hàm này tương đương đổ nước trong chai vào cốc và 
         // kết quả như ta thấy là: TRÀN
         return 0 //trả về 0 sau đó kết thúc
    } //chương trình kết thúc
    
  • Bây giờ thì dễ hiểu hơn rồi chứ nhỉ? Thì trong khi bị tràn, SFP và RET sẽ bị ghi đè và tràn ngập bởi "Nước!"

  • Nghĩa là, bây giờ địa chỉ trả về của chúng ta nó sẽ như thế này đây: 0x41414141. Khi hàm main thực hiện xong là thoát, thì thay vì thoát chương trình, nhưng CPU thấy có địa chỉ trả về, và nó tưởng rằng chương trình vẫn còn lệnh thực hiện. Sau đó CPU sẽ nhảy tới địa chỉ 0x41414141 và thực thi tiếp câu lệnh ở đâu. Tuy nhiên, nó sẽ ngã ngửa ngay sau đó vì ngoài vùng thực thi và sinh ra lỗi.

1.3. Làm sao để phát hiện ra chúng

  • Đa phần các chương trình bị lỗi tràn bộ đệm là sử dụng C hoặc C++ ngôn ngữ cho phép ta tự do quản lý bộ nhớ. Và chỉ cần sai sót của người lập trinh không để ý rất có thể sẽ dẫn đến lỗi tràn bộ đệm.
  • Các tốt nhất để phát hiện lỗi tràn bộ đệm là đọc mà nguồn và tìm kiếm lỗi. Nhưng nếu dự án quá lớn với lượng mã nguồn quá lớn thì ta sẽ không thể làm cách thủ công mà chỉ có thể sử dụng các công cụ kiểm thử để kiểm tra. Ví dụ như gdb (GNU Debugger), IDA,...

II. Describe what SEH is and how you exploit it?

Mô tả SEH là gì và cách khai thác nó?

2.1. SEH là gì?

  • SEH là viết tắt của Structured Exception Handling là một cơ chế để xử lý cả ngoại lệ của phần cứng và phần mềm. Do đó, mã của bạn sẽ xử lý ngoại lệ phần cứng và phần mềm giống hệt nhau. SEH cho phép bạn có toàn quyền kiểm soát việc xử lý các ngoại lệ, cung cấp hỗ trợ cho trình gỡ lỗi và có thể sử dụng trên tất cả các ngôn ngữ lập trình và máy tính.

    try-except-statement :
    __try compound-statement __except ( expression ) compound-statement
    
    try-finally-statement :
    __try compound-statement __finally compound-statement
    

2.2. Các thức khai thác

  • Trước khi nói về các khai thác chúng ta cần tìm hiểu xem SEH hoạt động như thế nào:

    • Các trình xử lý ngoại lệ sẽ được liên kết với nhau
    • Chúng tạo thành một chuỗi danh sách được liên kết trên ngăn xếp và có vị trí tương đối gần với đáy của Stack
    • Khi xảy ra ngoại lệ, Windows sẽ truy xuất phần đầu của chuỗi SEH truyền thông qua danh sách và cố gắng tìm trình xử lý phù hợp để đóng ứng dụng đúng cách.
  • Cách Khai thác cơ bản đối với SEH:

    • Khi ngoại lệ được kích hoạt, luồng chương trình chuyển đến trình xử lý SE Handler
    • Tất cả những gì chúng ta cần chỉ là đặt một số mã để chuyển sang payload của mình
    • Việc tạo một ngoại lệ thứ hai làm cho ứng dụng đi đến con trỏ SEH tiếp theo
    • Vì con trỏ SEH tiếp theo nằm trước trình xử lý SE Handler, chúng ta có thể ghi đè lên SEH tiếp theo
    • Vì shellcode nằm sau Handler, chúng ta có thể lừa SE Handler để thực thi POP Hướng dẫn POP RET để địa chỉ cho SEH tiếp theo sẽ được đặt trong EIP, do đó thực thi mã trong SEH tiếp theo
    • Về cơ bản, mã sẽ nhảy qua một số byte và thực thi shellcode

    III. How would you bypass SafeSEH?

Làm thế nào để có thể bỏ qua SafeSEH?

3.1. Định nghĩa:

  • SafeSEH là một cơ chế bảo vệ chuỗi xử lý ngoại lệ bằng cách không để stack bị ghi đè.

  • Như đã giải thích phần trước, SEH là mã bảo vệ được tích hợp trên hệ thống phần mềm để xử lý luồng bất thường của chương trình có thể khiến chương trình hoạt động quá bất thường (sự cố, treo, v.v.). SEH sẽ ngăn EIP (Extended Instruction Pointer - Thanh ghi con trỏ lệnh ) bị ghi đè trực tiếp bởi dữ liệu dư thừa được gửi bằng fuzzer. SEH sẽ kiểm soát hoàn toàn bộ nhớ được sử dụng bởi phần mềm. Sự bảo vệ này chắc chắn buộc tin tặc phải phát triển một kỹ thuật để vượt qua sự bảo vệ SEH. Khi bảo vệ này có thể được kiểm soát, quá trình thực thi trên CPU cũng có thể được kiểm soát giống như Direct Return Exploitation(Khai thác trực tiếp).

  • Kỹ thuật phổ biến nhất để bỏ qua sự bảo vệ này là sử dụng kỹ thuật POP, POP, RETN. Về cơ bản stack trong bộ nhớ là một bộ nhớ có cấu trúc bao gồm virtual file 32 bit. Lệnh POP đầu tiên sẽ đưa layer giá trị trên cùng của virtual file sang register(thanh ghi) khác trên bộ nhớ. Và lệnh POP thứ hai sẽ lấy ra layer thứ hai của stack trên bộ nhớ. Cuối cùng, lệnh thứ ba, RETN sẽ là layer đầu tiên trên stack, vì vậy hệ thống sẽ bắt đầu quá trình thực thi từ vị trí phụ thuộc đó đến địa chỉ bộ nhớ được chỉ ra bởi lệnh RETN.

  • Khi kỹ thuật trên việc kiểm soát quá trình trong CPU quá dễ dàng và tin tặc có thể thao tác theo ý muốn. Và Windows có thêm một biện pháp bảo vệ để ngăn chặn SEH bị thao túng bởi kỹ thuật POP, POP, RETN có tên là "SafeSEH".

3.2. Bỏ qua SafeSEH

  • Trong windows có một công nghệ để bảo vệ SEH được bỏ qua được gọi là SafeSEH. Nói chung, SafeSEH chỉ là một trình liên kết có thể được sử dụng trong quá trình biên dịch program/software trong hệ thống Windows. Khi SafeSEH được sử dụng, ứng dụng sẽ tạo một bảng chứa tất cả địa chỉ bộ nhớ sẽ được sử dụng bởi chính nó và cũng lưu các địa chỉ của SEH trên các module được sử dụng. Điều này có nghĩa là, khi việc khai thác sử dụng lệnh POP POP RETN xảy ra, địa chỉ được sử dụng để đưa SEH đến địa chỉ POP POP RETN sẽ không hoạt động bởi vì địa chỉ không được ghi lại trong bảng được tạo bởi SafeSEH và việc khai thác sẽ thất bại.

  • Ngoài bảo vệ SafeSEH, Windows còn có một cơ chế bảo vệ khác chống lại việc khai thác có thể được tích hợp vào các tệp dll của nó. Tính năng này được gọi là "IMAGE_DLLCHARACTERISTICS_NO_SEH". Nếu một tệp dll sử dụng tính năng đó, nó sẽ ngăn việc sử dụng bất kỳ địa chỉ nào bên trong chính nó được sử dụng làm lệnh để bỏ qua SEH

  • Có một số kỹ thuật để bỏ SafeSEH:

    • Sử dụng các module hoặc tệp không có tính năng SafeSEH và IMAGE_DLLCHARACTERISTICS_NO_SEH được tích hợp. Thông thường thư viện ứng dụng của bên thứ ba không được biên dịch với hai tính này.
    • Sử dụng thư viện sẽ được sử dụng để ghi đè SEH từ bên ngoài ứng dụng có lệnh POP POP RETN bên trong.
    • Sử dụng địa chỉ bộ nhớ ngoài bộ nhớ HEAP.
    • Sử dụng bộ nhớ xử lý tích hợp đã được đăng ký.