Kernel Pool & Kernel Pool Exploitation

Thảo luận trong 'Exploitation' bắt đầu bởi holoha, 18/11/14, 11:11 AM.

  1. holoha

    holoha Moderator

    Tham gia: 23/12/13, 11:12 AM
    Bài viết: 16
    Đã được thích: 6
    Điểm thành tích:
    18
    Giới thiệu:

    Bài viết này nhằm mục đích hệ thống lại nghiên cứu đã thực hiện về Kernel Pool Memory và các hướng có thể sử dụng để khai thác nó.
    Nội dung bài viết được chia làm hai phần chính

    • Phần 1: Giới thiệu về Kernel Pool
      • Giới thiệu tổng quan
      • Cấu trúc cơ bản
      • Cấp phát và giải phóng bộ nhớ
    • Phần 2: Cách khai thác Kernel Pool Overflow
    Phần này sẽ giới thiệu một số cách thức có thể sử dụng để khai thác Kernel Pool trên Windows 7 và các phiên bản trở về trước.
    I. Giới thiệu về Kernel Pool

    1. Giới thiệu tổng quan

    Kernel Pool là một vùng nhớ thuộc Kernel mode. Bộ nhớ này được tổ chức thành các page, mỗi page thường có dung lượng 4KB. Windows sẽ chia nhỏ các page này thành các blocks (từ 8B trở lên) để cấp phát cho khi cần thiết.
    Kernel Pool được sử dụng để cấp phát động cho tất cả kernel module và driver.
    Giống như bộ nhớ Heap ở User mode, Pool ở Kernel mode cũng cần một cấu trúc quản lý gọi là “pool descriptor” (tương tự “heap descriptor”) để kiểm tra số lượng vùng nhớ được cấp phát trong một page, số page sử dụng và các thông tin khác. Nó giúp cho việc quản lý và tái sử dụng các vùng nhớ. Ngoài ra nó cũng có cấu trúc chưa các vùng nhớ trống tương tự heap là “ListHead” (hay “FreeList”) và “LookAside list”
    2. Cấu trúc cơ bản

    Như đã nói ở trên mỗi Pool có một cấu trúc quản lý gọi là Pool Descriptor, cấu trúc đó có một số điểm cần lưu ý như sau:
    typedef struct _POOL_DESCRIPTOR
    {

    LONG32 TotalBigPages;
    LONG32 TotalPages;
    VOID** PendingFrees;
    struct _LIST_ENTRY ListHeads[512];
    } POOL_DESCRIPTOR, *PPOOL_DESCRIPTOR;
    Trong đó:

    • TotalBigPages: là số lượng page lớn sử dụng (Page có kích thước lớn hơn 4KB)
    • TotalPages: Số lượng page đang được sử dụng
    • PendingFrees trỏ tới một danh sách liên kết đơn các vùng nhớ đang đợi được giải phóng
    • ListHead: trỏ tới một mảng các danh sách liên kết đôi các vùng nhớ còn trống có thể được sử dụng để cấp phát
    ListHead:
    [​IMG]
    Như đã nói ở trên thì ListHead (hay FreeList) là một mảng (trên thực tế danh sách 0 không sử dụng) danh sách liên kết đôi chứa thông tin về các vùng nhớ có cùng kích thước các vùng nhớ này đều còn trống (hoặc đã được giải phóng) và có thể được cấp phát.
    ListHead được sắp xếp theo kích thước từ 8B đến 4080B. Các vùng nhớ free được xác định trong danh sách thông qua “blocksize”
    BlockSize = (NumberOfByte + 0xF)/8
    Mỗi vùng nhớ này lại có 8 byte header,
    ngoài ra khi một vùng nhớ được giải phóng và đưa vào ListHead sẽ có 8byte chứa FLINK ( trỏ tới vùng nhớ ở trước) và BLINK (trỏ tới vùng nhớ sau nó)

    LookAside List

    Ngoài ra Kernel cũng sử dụng Lookaside Lists để cấp phát và giải phóng nhanh các vùng nhớ nhỏ. Đây là một danh sách liên đơn và BlockSize lớn nhất mà LookAside List có thể cấp phát là 32 (256B). Tuy nhiên danh sách này chỉ dùng để cấp phát cho các vùng nhớ có độ lớn chính xác bằng kích thước của các vùng nhớ được sử dụng.

    [​IMG]
    3. Cấp phát bộ nhớ

    Đầu tiên sẽ thực hiện kiểm tra vùng nhớ cần cấp phát có lớn hơn 4KB không nếu có thực hiện cấp phát page mới với kích thước lớn (BigPage) nếu không thực hiện cấp phát vùng nhớ nhỏ theo trình tự
    · Nếu BlockSize < 32: kiểm tra trong LookAside List
    · Nếu LookAside List không có vùng nhớ phù hợp hoặc BlockSize >=32 kiểm tra trong ListHead.
    · Nếu vùng nhớ phù hợp gần nhất lớn hơn cần thiết sẽ thực hịện chia nhỏ vùng nhớ lớn và thực hiện cấp phát
    [​IMG]
    · Nếu không có vùng nhớ nào phù hợp thực hiện mở rộng pool bằng cách thêm page và thực hiện lại.
    Từ phiên bản Windows 7 trở đi cung cấp thêm một cơ chế bảo vệ mới là “Safe Unlinking” khi sử dụng một vùng nhớ trong FreeList. Cơ chế này được mô tả như sau:
    · Khi một vùng nhớ cần được unlink, kiểm tra con trỏ BLINK của vùng nhớ tiếp theo có trỏ tới vùng nhớ này không
    · Sau đó kiểm tra con trỏ FLINK của vùng nhớ trước đó có trỏ tới vùng nhớ này không
    · Nếu thỏa mãn cả hai điều kiện thì sẽ thực hiện unlink sau đó sửa lại con trỏ FLINK của vùng nhớ trước và BLINK của vùng nhớ tiếp theo kết thúc quá trình Unlinking

    [​IMG]
    Nếu vùng nhớ được cấp phát lớn hơn kích thước được yêu cầu, phần còn thừa sẽ được trả vè cho ListHead.
    [​IMG]
    Cơ chế cấp phát khi đó sẽ là:

    • Nếu vùng nhớ hòan tòan còn trống sẽ cấp phát ở đầu vùng nhớ, vùng nhớ còn lại sẽ được cắt ra và trả về cho ListHead
    • Sau đó với các lần cấp phát tiếp theo sẽ cấp phát ở cuối vùng nhớ để hạn chế phân mảnh vùng nhớ
    4. Giải phóng bộ nhớ

    Khi một vùng nhớ được giải phóng nó sẽ thực hiện như sau:
    · Giải phóng vào LookAside List với các vùng nhớ nhỏ hơn 256B và có kích thước chính xác phù hợp (bội của 8B)
    · Giải phóng vào ListHeads với các vùng nhớ nhỏ hơn 4080B vào vùng nhớ có blocksize tương ứng
    · Kết hợp các vùng nhớ trống liên tiếp để hạn chế phân mảnh
    [​IMG]


    II. Cách khai thác Kernel Pool Overflow

    Như đã nói ở trên các blocks được chia nhỏ và cấp phát trên cùng một page. Do đó cần phải đảm bảo mỗi driver chỉ sử dụng không gian nó được cấp phát. Nếu DriverA sử dụng nhiều hơn những gì nó được cấp phát thì nó sẽ ghi đè lên vùng nhớ của DriverB bên cạnh và làm hỏng dữ liệu của vùng này. Hiện tượng này gọi là Buffer Overflow. Sau đó DriverB sẽ sử dụng vùng nhớ bị ghi đè này và sử dụng những thông tin sai lệch và làm hỏng quá trình hoạt động của hệ thống và có thể gây là lỗi màn hình xanh.
    1. ListEntry Overwrite

    Với Windows Vista và các phiên bản nhỏ hơn không có sự kiểm tra cấu trúc của các con trỏ Flink và Blink trong quá trình sử dụng do đó có thể thực hienẹ ghi đè ListEntry để thực hiện khai thác
    [​IMG]
    Tuy nhiên từ phiên bản Windows 7 đã bổ sung thêm một cơ chế đó là “Safe Unlinking” để tránh việc ghi đè ListEntry. Tuy nhiên vẫn còn những hướng khác đẻ thực hiện việc khai thác Kernel Pool thông qua Buffer OverFlow
    2. LookAside Pointer Overwrite

    LookAside List vốn là một mảng các danh sách liên kết đơn lưu thông tin các vùng nhớ có thể cấp phát. Mỗi Entry chỉ có một con trỏ next entry và không bị ảnh hưởng bởi cơ chế Safe Unlinking. Do đó với việc ghi đè con trỏ này có thể khiến Kernel Pool Allocator trả về một điạ chỉ bị điều khiển.
    [​IMG]
    Đầu tiên attacker sẽ làm tràn Pool và ghi đè con trỏ NEXT bằng một địa chỉ có thể điều khiển được.
    Sau đó khi một vùng nhớ trong LookAside List[1] được cấp phát thì con trỏ NEXT sẽ trỏ vào vùng nhớ bị điều khiển.
    3. PendingFrees Pointer Overwrite

    Các vùng nhớ chờ được giải phóng sẽ được lưu trong một danh sách và tương tự như với LookAsideList thì đây cũng là một danh sách liên kết đơn không có cấu trúc kiểm tra như đối với danh sách liên kết đôi. Do đó với việc ghi đè lên con trỏ next pointer có thể điều khiển được vùng nhớ cần thiết
    [​IMG]
    Đầu tiên attacker sẽ làm tràn Pool và ghi đè con trỏ NEXT và ghi đè bằng một địa chỉ có thể điều khiển được.
    Sau đó khi một vùng nhớ được giải phóng thì con trỏ NEXT sẽ trỏ vào vùng nhớ bị điều khiển. con trỏ next của vùng nhớ giả mạo này thường được đặt là Null để kết thúc việc giải phóng tránh việc có nhiều vùng nhớ đẩy lên trước vùng nhớ giả mạo này. Kết quả là khi vùng nhớ này được cấp phát chương trình sẽ bị khai thác.

    Tham chiếu tài liệu


    • Real World Kernel Pool Exploitation – Kostya Kortchinsky
    • Data-only Pwning Microsoft Windows Kernel – Nikita Tarakanov
    • Kernel Pool Exploitation on Windows 7 – Tarjei Mandt
    • Dịch giả: kamaitachi
     
    Last edited by a moderator: 25/11/14, 03:11 PM
    Mời các bạn tham gia Group WhiteHat để thảo luận và cập nhật tin tức an ninh mạng hàng ngày.
    Lưu ý từ WhiteHat: Kiến thức an ninh mạng để phòng chống, không làm điều xấu. Luật pháp liên quan
    tmnt53 thích bài này.