Integer Overflow Trong Kernel:Từ Lỗi Số Học Đến Leo Thang Đặc Quyền

Integer Overflow Trong Kernel: Từ Lỗi Số Học Đến Leo Thang Đặc Quyền
Kernel Security Integer Overflow Privilege Escalation Học thuật

Integer Overflow Trong Kernel:
Từ Lỗi Số Học Đến Leo Thang Đặc Quyền

Một phép cộng đơn giản tràn số có thể phá vỡ toàn bộ mô hình bảo mật của hệ điều hành. Bài viết này phân tích học thuật cơ chế integer overflow trong kernel, cách nó kéo theo lỗ hổng use-after-free, và con đường từ đó đến leo thang đặc quyền.

📅 12 tháng 6, 2026
Đọc ~18 phút
🎓 Nâng cao
🔬 Nghiên cứu học thuật

Integer Overflow Là Gì?

Integer overflow xảy ra khi kết quả của một phép tính số học vượt quá phạm vi biểu diễn của kiểu dữ liệu. Không có ngoại lệ được ném ra, không có cảnh báo — con số đơn giản “quay vòng” trở lại giá trị nhỏ (hoặc lớn với signed) theo cơ chế modular arithmetic.

Trong C/C++, overflow của kiểu unsigned là hành vi được định nghĩa (wrap-around theo modulo 2ⁿ), nhưng overflow của kiểu signedundefined behavior — về mặt lý thuyết trình biên dịch có thể làm bất cứ điều gì.

uint32_t có phạm vi: 0 → 4,294,967,295 (2³² − 1)

4,294,967,295 + 1 = 0 (wrap-around)

uint32_t x = 0xFFFFFFFF; → x + 1 = 0x00000000

Đây là nền tảng của mọi cuộc tấn công dựa trên integer overflow trong kernel.

Signed vs Unsigned: Tại Sao Quan Trọng

Unsigned Wrap-Around

Kiểu unsigned wrap-around là nguồn gốc của hầu hết các lỗ hổng kernel. Khi một reference counter (bộ đếm tham chiếu) được lưu dưới dạng uint32_t, kẻ tấn công chỉ cần tăng nó đủ 2³² lần để về 0 — khiến kernel nghĩ object không còn được tham chiếu và giải phóng bộ nhớ.

refcount_overflow.c C
/* Ví dụ minh họa về reference counter dễ bị overflow */
struct kernel_object {
    uint32_t  cr_ref;      /* Bộ đếm tham chiếu 32-bit */
    void     *data;
    /* ... các trường khác ... */
};

/* Tăng reference — không kiểm tra overflow */
void obj_retain(struct kernel_object *obj) {
    obj->cr_ref++;   /* Dễ bị tràn nếu gọi 2^32 lần */
}

/* Giảm reference — nếu về 0, giải phóng bộ nhớ */
void obj_release(struct kernel_object *obj) {
    if (--obj->cr_ref == 0) {
        kfree(obj);   /* GIẢI PHÓNG — nhưng dangling ptr vẫn tồn tại! */
    }
}

Signed Integer Overflow: Undefined Behavior

Với kiểu signed, overflow dẫn đến undefined behavior theo chuẩn C. Các trình biên dịch tối ưu hóa tích cực (như GCC với -O2) có thể loại bỏ hoàn toàn kiểm tra overflow dựa trên giả định rằng signed overflow không xảy ra — đây là nguồn gốc của nhiều lỗ hổng bảo mật nghiêm trọng trong code kernel.

Integer Overflow Trong Không Gian Kernel

Trong nhân hệ điều hành, integer overflow đặc biệt nguy hiểm vì hai lý do: (1) kernel chạy ở ring 0 với đặc quyền tối cao, và (2) các đối tượng kernel thường được chia sẻ giữa nhiều tiến trình và được theo dõi bằng reference counting.

Reference Counting Trong Kernel

Hầu hết kernel hiện đại sử dụng reference counting để quản lý vòng đời đối tượng. Khi reference count về 0, đối tượng bị giải phóng. Vấn đề: nếu counter bị tràn số về 0 trước thời điểm thực sự không còn ai dùng, đối tượng bị free quá sớm trong khi vẫn còn pointer trỏ đến nó.

⚠️

Vì sao phải gọi syscall ~2³² lần? Với uint32_t, cần đúng 4,294,967,296 lần increment để về 0. Ở tốc độ xử lý syscall hiện đại, điều này mất khoảng 40–55 phút trên phần cứng tiêu dùng — một thời gian đáng kể nhưng hoàn toàn khả thi trong thực tế.

Overflow → Use-After-Free: Chuỗi Khai Thác

Integer overflow trong reference counter là cầu nối điển hình giữa lỗi số học đơn giản và lỗ hổng use-after-free. Chuỗi từ overflow đến UAF diễn ra như sau:

⬛ Chuỗi khai thác: Integer Overflow → UAF → Privilege Escalation
Bước 1 — Trigger Overflow
Gọi syscall liên tục (ví dụ: kqueueex) để tăng cr_ref đủ 2³² lần. Counter quay về 0 trong khi đối tượng vẫn còn được tham chiếu.
Bước 2 — Premature Free
Khi counter về 0, kernel gọi kfree() giải phóng đối tượng. Nhưng các con trỏ đến nó vẫn còn tồn tại trong nhiều cấu trúc dữ liệu kernel → dangling pointers.
Bước 3 — Heap Grooming
Khai thác phân bổ đối tượng mới cùng kích thước để chiếm đúng chunk vừa được giải phóng. Dữ liệu của attacker giờ nằm ở địa chỉ mà dangling pointer trỏ đến.
Bước 4 — Build Primitives
Dùng chunk kiểm soát để xây dựng kernel read/write primitive — khả năng đọc/ghi tùy ý vào bộ nhớ kernel thông qua pipe hoặc IPv6 socket.
Bước 5 — Privilege Escalation
Với kernel R/W primitive, ghi đè các trường bảo mật của tiến trình (credentials, ucred) để leo thang lên root, hoặc vô hiệu hóa cơ chế bảo mật trực tiếp.

Xây Dựng Read/Write Primitives

Sau khi có dangling pointer, bước tiếp theo là biến nó thành arbitrary read/write primitive — khả năng đọc/ghi bộ nhớ kernel tại bất kỳ địa chỉ nào. Đây là bước quan trọng nhất trong chuỗi khai thác.

Pipe-Based Read/Write

Kỹ thuật phổ biến: tạo một pipe và kiểm soát các trường pipe_buf hoặc iov_base của scatter-gather structure. Bằng cách ghi địa chỉ tùy ý vào các trường này, mọi thao tác read()/write() trên pipe đều đọc/ghi bộ nhớ kernel tại địa chỉ đó.

pipe_primitive_concept.c (pseudocode học thuật) C
/* Ý tưởng: Kiểm soát iov_base để tạo primitive đọc kernel */
struct iovec fake_iov = {
    .iov_base = (void *)KERNEL_TARGET_ADDR, /* địa chỉ kernel muốn đọc */
    .iov_len  = 8
};

/* Nếu attacker kiểm soát con trỏ iov qua UAF, thì: */
char buf[8];
readv(pipe_fd, &fake_iov, 1);   /* đọc 8 bytes từ KERNEL_TARGET_ADDR */
/* → buf chứa nội dung bộ nhớ kernel */

IPv6 Socket Primitives

Kỹ thuật khác được sử dụng rộng rãi là kiểm soát các buffer của IPv6 socket. Cấu trúc sockbuf hoặc các trường liên quan trong mbuf (BSD) / sk_buff (Linux) chứa con trỏ đến dữ liệu — nếu attacker kiểm soát được các con trỏ này, họ có primitive đọc/ghi kernel.

📖

Nghiên cứu liên quan: Kỹ thuật pipe-based kernel R/W được mô tả chi tiết trong bài báo “ret2dir: Rethinking Kernel Isolation” (USENIX Security 2014) và nhiều write-up CTF về kernel pwning. Pipe primitive là phổ biến vì pipe là đối tượng kernel đơn giản nhưng linh hoạt.

Leo Thang Đặc Quyền

Với kernel arbitrary write primitive, kẻ tấn công có thể thực hiện leo thang đặc quyền theo nhiều hướng:

Ghi đè ucred/credentials

Mỗi tiến trình trong Unix/Linux có cấu trúc ucred (hoặc cred trên Linux) lưu UID, GID và các đặc quyền. Bằng cách ghi đè UID = 0, tiến trình trở thành root mà không cần bất kỳ kiểm tra nào.

cred_overwrite_concept.c (học thuật) C
/* Tìm địa chỉ của cấu trúc credentials của tiến trình hiện tại */
uint64_t cred_addr = kernel_read64(current_task + OFFSET_CRED);

/* Ghi UID = 0 (root) */
kernel_write32(cred_addr + OFFSET_UID, 0);   /* uid  → 0 */
kernel_write32(cred_addr + OFFSET_GID, 0);   /* gid  → 0 */
kernel_write32(cred_addr + OFFSET_EUID, 0);  /* euid → 0 */
/* Tiến trình bây giờ chạy với đặc quyền root */

GPU PM4 DMA Writes vào .data Kernel

Một kỹ thuật tinh vi hơn: dùng lệnh DMA của GPU (PM4 packets trên AMD GCN/RDNA) để ghi vào vùng .data của kernel mà không qua CPU. Điều này giúp vượt qua một số mitigation CPU-level như SMEP/SMAP vì đây là DMA transfer, không phải CPU memory access. Đây là cách một số exploit kích hoạt chế độ debug ẩn của hệ điều hành bằng cách thay đổi các flag trong vùng .data kernel read-only.

Phòng Thủ & Mitigation

7.1 Refcount Hardening

Giải pháp trực tiếp nhất: sử dụng kiểu dữ liệu reference counter có bảo vệ. Linux kernel đã giới thiệu refcount_t thay thế atomic_t cho reference counting — nó phát hiện và xử lý overflow thay vì wrap-around.

refcount_t_linux.c C (Linux Kernel)
/* Dùng refcount_t thay vì atomic_t hoặc uint32_t */
struct my_object {
    refcount_t ref;   /* Bảo vệ chống overflow */
    /* ... */
};

/* refcount_inc() sẽ WARN và bão hòa tại INT_MAX
   thay vì wrap-around về 0 */
refcount_inc(&obj->ref);   /* An toàn */

/* Trên ARM64: sử dụng hardware atomic với overflow detection */

7.2 Integer Sanitizers

SanitizerPhát hiệnOverheadTrường hợp dùng
-fsanitize=integerSigned overflow, shift issues~2–3×CI/CD, fuzzing
UBSAN (Kernel)Undefined behavior toàn diện~1.5–2×Kernel debug build
-fsanitize=unsigned-integer-overflowUnsigned wrap-around~1.2×Security-sensitive code
KASANMemory access lỗi (bao gồm UAF)~2×Kernel development

7.3 Compiler Hardening

  • -fwrapv: Buộc trình biên dịch xử lý signed overflow như two’s-complement wrap-around, loại bỏ undefined behavior.
  • -ftrapv: Thêm trap instruction khi signed overflow xảy ra — phù hợp cho debugging, không dùng production.
  • Control Flow Integrity (CFI): Hạn chế khả năng redirect control flow sau khi đã có kernel write primitive.

7.4 Kiến Trúc Phòng Thủ Chiều Sâu

Không có biện pháp đơn lẻ nào đủ mạnh. Mô hình phòng thủ hiện đại kết hợp nhiều lớp:

  • KASLR: Ngẫu nhiên hóa địa chỉ kernel, buộc attacker phải leak địa chỉ trước.
  • SMEP/SMAP: Ngăn kernel thực thi/đọc từ user-space.
  • PAC (Pointer Authentication): ARM — ký con trỏ bằng cryptographic key, phát hiện giả mạo.
  • MTE (Memory Tagging Extension): ARM v8.5 — tag bộ nhớ, phát hiện UAF tại runtime với overhead thấp (~1%).

Xu hướng ngành: Google và Microsoft đang tích cực viết lại các component nhân hệ điều hành bằng Rust — Android kernel (từ 2021), Windows kernel (thử nghiệm từ 2023). Mục tiêu loại bỏ cả lớp lỗ hổng memory safety, bao gồm integer overflow dẫn đến UAF, tại cấp độ ngôn ngữ.

Kết Luận

Integer overflow trong kernel là ví dụ điển hình về cách một lỗi nhỏ trong lập trình — một phép cộng không được kiểm tra — có thể leo thang thành kiểm soát hoàn toàn hệ thống. Chuỗi từ overflow → premature free → UAF → kernel R/W primitive → privilege escalation thể hiện sự kết hợp tinh tế của nhiều kỹ thuật.

Từ góc độ phòng thủ, không có viên đạn bạc. Sự kết hợp giữa safe types (refcount_t, Rust), hardware mitigation (PAC, MTE, SMEP) và runtime detection (KASAN, UBSAN) tạo nên mô hình phòng thủ chiều sâu hiệu quả. Đặc biệt, việc di chuyển sang ngôn ngữ memory-safe cho các component kernel mới là bước tiến quan trọng nhất trong thập kỷ qua.

📚

Tài liệu tham khảo học thuật: “Integer Overflow to Buffer Overflow” (CERT Secure Coding), “Kernel Exploitation: Theory and Practice” (Black Hat USA), Linux Kernel Documentation — refcount_t API, “Eternal War in Memory” (Szekeres et al., IEEE S&P 2013), CWE-190 (Integer Overflow or Wraparound), “PAX: Kernel Self-Protection via Hardware” (PaX Team).

Để lại một bình luận

Email của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *

0
    0
    Tạo wiki game yêu thích
    Giỏ game trốngTrở lại trang
    Lên đầu trang