Cấu hình CONFIG_DEBUG_PAGEALLOC để phát hiện lỗi bộ nhớ
Bước đầu tiên là kích hoạt cơ chế DEBUG_PAGEALLOC của Kernel. Tham số này biến đổi các trang bộ nhớ đã giải phóng thành các trang lỗi (faulting pages), giúp phát hiện ngay lập tức khi có tiến trình truy cập trái phép vào vùng nhớ đã free (use-after-free).
Trước khi tải kernel mới, bạn cần kiểm tra xem kernel hiện tại có hỗ trợ tùy chọn này chưa. Nếu đang dùng kernel mặc định của distribution, khả năng cao là chưa bật.
grep CONFIG_DEBUG_PAGEALLOC /boot/config-$(uname -r)
Kết quả mong đợi: Nếu thấy dòng CONFIG_DEBUG_PAGEALLOC=y thì bạn đã sẵn sàng. Nếu thấy # CONFIG_DEBUG_PAGEALLOC is not set hoặc không có dòng nào, bạn cần compile lại kernel hoặc tải kernel module có hỗ trợ.
Compile Kernel với DEBUG_PAGEALLOC
Trong môi trường production, việc compile kernel riêng là bắt buộc để có quyền kiểm soát sâu. Dưới đây là quy trình build kernel với các tùy chọn hardening liên quan đến bộ nhớ.
Tạo file cấu hình kernel tùy chỉnh tại /root/kernel-config. Nội dung này bao gồm DEBUG_PAGEALLOC và các tham số liên quan để tăng cường bảo vệ.
cat > /root/kernel-config
Sau đó, thực hiện build kernel. Lưu ý: Bạn cần có nguồn code kernel (linux-source) và các công cụ build (build-essential, kmod).
cd /usr/src/linux
cp /root/kernel-config .config
make olddefconfig
make -j$(nproc) bzImage modules
make modules_install
make install
Kết quả mong đợi: Kernel mới được cài đặt vào /boot và grub được cập nhật. Reboot server để áp dụng.
reboot
Verify CONFIG_DEBUG_PAGEALLOC
Sau khi khởi động lại, xác nhận kernel mới đã chạy và tính năng đã kích hoạt.
grep CONFIG_DEBUG_PAGEALLOC /boot/config-$(uname -r)
Kết quả mong đợi: Dòng đầu ra phải hiển thị CONFIG_DEBUG_PAGEALLOC=y.
dmesg | grep -i "debug pagealloc"
Kết quả mong đợi: Nếu thấy thông báo debug pagealloc: initialized hoặc các log liên quan đến việc map/unmap trang bộ nhớ, chứng tỏ cơ chế đã hoạt động.
Triển khai eBPF để giám sát truy cập bộ nhớ bất thường
Thiết lập môi trường BCC và eBPF
Chúng ta sẽ sử dụng bộ công cụ BCC (BPF Compiler Collection) để viết và chạy các chương trình eBPF. Công cụ này cho phép biên dịch code C thành byte-code eBPF và tải vào kernel.
Cài đặt BCC và các gói phụ thuộc cần thiết cho kernel module eBPF.
apt-get update && apt-get install -y bpfcc-tools linux-headers-$(uname -r) clang llvm
Kiểm tra xem BCC đã cài đặt đúng chưa bằng cách gọi script mẫu.
bpftrace -l /usr/share/bcc/tools/tcplist.bpf.c
Kết quả mong đợi: Không báo lỗi, xuất ra danh sách các hook points có sẵn trong kernel.
Viết script eBPF giám sát Memory Corruption
Mục tiêu: Viết một chương trình eBPF để bắt các cuộc gọi hệ thống liên quan đến quản lý bộ nhớ (mmap, mprotect) và kiểm tra xem có hành vi bất thường nào (ví dụ: cố gắng mapping bộ nhớ vào vùng kernel hoặc sử dụng cờ bảo vệ lạ) không.
Tạo file script eBPF tại /root/memory_guard.bpf.c. Script này sử dụng hook tracepoint:syscalls:sys_enter_mmap để giám sát.
cat > /root/memory_guard.bpf.c 32;
u32 uid = bpf_get_current_uid_gid();
struct event event = {};
event.pid = pid;
event.uid = uid;
event.addr = ctx->addr;
event.length = ctx->length;
event.prot = ctx->prot;
event.flags = ctx->flags;
// Logic phát hiện bất thường:
// 1. Cố gắng map vào vùng địa chỉ thấp (kernel space giả lập)
// 2. Sử dụng PROT_EXEC kết hợp với PROT_READ/WRITE trên vùng stack
// Đây là heuristic đơn giản để demo
if (ctx->addr < 0x1000) {
// Suspicious low address access
event.flags |= 0x80000000; // Set suspicious flag
}
bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, &event, sizeof(event));
return 0;
}
char LICENSE[] SEC("license") = "GPL";
EOF
Biên dịch và tải script vào kernel. Chúng ta sẽ dùng clang để biên dịch và bpftool để load, nhưng để đơn giản hóa quy trình test, ta dùng bpftrace với logic tương tự hoặc biên dịch thành object file.
Biên dịch file C thành file object eBPF:
clang -O2 -target bpf -D__TARGET_ARCH_x86 -c /root/memory_guard.bpf.c -o /root/memory_guard.o
Kết quả mong đợi: Tạo ra file memory_guard.o không có lỗi biên dịch.
Triển khai bộ thu thập dữ liệu (User-space Collector)
eBPF chỉ chạy trong kernel, cần một tiến trình user-space để đọc dữ liệu từ map và xử lý logic "giết process". Ta sẽ viết script Python đơn giản để đọc stream từ eBPF.
Tạo file /root/memory_guard_collector.py.
cat > /root/memory_guard_collector.py
Cấp quyền thực thi cho script Python.
chmod +x /root/memory_guard_collector.py
Chạy script với quyền root để eBPF có thể hook vào kernel.
sudo /root/memory_guard_collector.py
Kết quả mong đợi: Script chạy, in dòng Memory Guard Active. Monitoring syscalls... và đứng chờ (blocking) để đọc sự kiện.
Cơ chế tự động Kill Process khi phát hiện gian lận bộ nhớ
Tích hợp cơ chế Kill với eBPF và Signal
Trong script Python ở trên, chúng ta đã tích hợp logic os.kill(event.pid, signal.SIGKILL). Đây là cách xử lý nhanh nhất để ngăn chặn sự lan rộng của lỗi bộ nhớ (memory corruption) hoặc exploit.
Để đảm bảo an toàn hơn, bạn có thể thêm logic lọc dựa trên uid hoặc executable path để tránh giết nhầm tiến trình quan trọng của hệ thống, nhưng với mục tiêu bảo vệ Database, việc kill ngay lập tức là ưu tiên hàng đầu khi phát hiện dấu hiệu bất thường.
Bạn có thể tùy chỉnh script để log sự kiện vào file trước khi kill, hoặc gửi cảnh báo qua syslog.
cat > /root/memory_guard_collector.py
Cập nhật lại script và chạy lại để áp dụng logic log mới.
sudo /root/memory_guard_collector.py
Test cơ chế bảo vệ (Simulation)
Để kiểm tra xem hệ thống có hoạt động đúng không, ta sẽ tạo một tiến trình giả mạo cố gắng truy cập bộ nhớ ở địa chỉ thấp (0x0 hoặc vùng gần 0), kích hoạt logic trong BPF.
Tạo file test C đơn giản để cố gắng mmap địa chỉ 0 (thường bị chặn bởi kernel, nhưng nếu kernel cấu hình sai hoặc có lỗ hổng, eBPF sẽ bắt được).
cat > /root/test_mem_inject.c
Biên dịch test program.
gcc -o /root/test_mem_inject /root/test_mem_inject.c
Chạy test program trong terminal khác (hoặc background) khi script Python đang chạy.
/root/test_mem_inject &
Kiểm tra log của script Python hoặc syslog để xem có phát hiện và kill không.
tail -f /var/log/syslog | grep MEMORY_CORRUPTION
Kết quả mong đợi:
- Script Python in ra dòng
[MEMORY_CORRUPTION] ... KILLED.
- Process
test_mem_inject bị dừng ngay lập tức (không còn chạy trong ps aux).
- Không có crash của kernel (OOPS/panic).
Verify kết quả bảo vệ
Xác nhận rằng tiến trình đã bị giết và log đã được ghi lại.
ps aux | grep test_mem_inject
Kết quả mong đợi: Không tìm thấy process test_mem_inject (chỉ thấy dòng grep).
dmesg | grep -i "kill"
Kết quả mong đợi: Có thể thấy log từ kernel hoặc syslog về việc process bị kill do signal SIGKILL.
journalctl -u rsyslog | grep -i "MEMORY_CORRUPTION"
Kết quả mong đợi: Thấy dòng log chi tiết về PID và địa chỉ bộ nhớ đã bị chặn.
Điều hướng series:
Mục lục: Series: Triển khai Database an toàn với Linux Kernel Hardening và eBPF
« Phần 5: Xây dựng chính sách eBPF để chặn các cuộc tấn công SQL Injection
Phần 7: Tối ưu hóa hiệu năng và xử lý sự cố sau khi hardening »