Mổ xẻ lỗi UAF với C++ và môn Reverse Engineering, chứng kiến impact chiếm quyền!

thính dê

New Member
07/04/2026
0
1 bài viết
Mổ xẻ lỗi UAF với C++ và môn Reverse Engineering, chứng kiến impact chiếm quyền!
Heap Ring3 UAF RE Virtual Vprt

Note: Bài viết được thực hiện bởi sinh viên mới học môn RE, nên sai xót chắc chắn có thể xảy ra, nếu nhìn ra lỗi trong logic của mình thì xin hãy nói cho mình biết với nhé !

Lỗi UAF là gì và tại sao cho đến bây giờ một số CVE mới vẫn cứ réo tên nó ? Impact mà UAF để lại nghe rất oai nhưng để hiểu được nó thì rất khoai TT

Luồng flow mà UAF sinh ra trên Heap:
Coder dùng syntax “new” tạo ra 1 object/biến động => Sau khi dùng xong muốn xoá nó thì chỉ xoá = “delete” nhưng quên gán nó về [null] => Attacker lợi dụng.
IMG_1174.jpeg

Đúng như các bạn vừa đọc và do mình đang học viết driver nên bắt đầu học về các con trỏ phức tạp này , đây là luồng flow mà bài blog này ra đời:

Thắc mắc về syntax * và & => Học được về Heap => Học về bufferoverflow trên Heap => Lòi ra khái niệm UAF và Virtual => Kết hợp UAF + Virtual => Chứng kiến impact chiếm luồng thực thi chương trình!

Thật sự mà nói thì lỗi này khá nhức đầu nhưng tin mình đi nếu bạn đã làm lab UAF và chứng kiến từng giọt nước chảy ra thì sẽ thấy rất pheee ><, chúng ta sẽ đi từ từ nhất có thể :

SYNTAX EXPLAINT
Đầu tiên ta hãy nói về đoạn code này:
Screenshot 2026-04-08 071329.png

Syntax [*] và Syntax [&] là gì =)))) Nếu bạn lú thì đừng buồn, lần đầu mình học cũng khá khổ sở vì chả hiểu nó dùng để làm gì . Thì để cho dễ hiểu * chính là ta đang truy xuất và đọc/ghi (Read/Write) giá trị bên trong thằng b, còn & chính là địa chỉ chính xác của thằng a bên dưới mã máy như 0x0000.

Dễ hiểu hơn thì [*] giống như ta đang chỉ tay vào nội thất bên trong 1 căn nhà (int b) và [&] chính là ta đang chỉ tay vào địa chỉ nhà (int a) như kiểu: số 100 nguyễn huệ q1.​

Oke tiếp theo ta sẽ khám phá lệnh new,delete và virtual

New ở đây tạm thời là ta đang ra lệnh OS cấp phát 1 vùng nhớ heap cho 1 object/biến động!

Delete ở đây là khi ta dùng xong ngôi nhà New , ta ra lệnh cho OS xoá đi ngôi nhà đó!

Còn Virtual thì nó theo hướng đối tượng -> Bảng con trỏ hàm (Vtable), khi ta dùng virtual cho 1 hàm nó sẽ tạo ngay 1 con trỏ vptr (virtual pointer) nhét vào đầu địa chỉ và UAF là ta ghi đè con trỏ đó thành địa chỉ shell code, khi gọi vào nó sẽ nhảy vào shell code.

UAF (use after free) EXPLAINT

Giờ hãy tìm hiểu chi tiết tại sao quên gán nó về null thì lại bị lợi dụng bởi attacker?

Khi dùng lệnh delete cho biến/ đối tượng new, nhưng thật sự bên dưới, hệ điều hành hoàn toàn không xoá sạch cái biến đó như cái tên delete! Why? Vì C++ cần chúng ta phải tự tay xoá = việc gán null. Vì bạn hãy thử nghĩ trong 1 app đã phải chạy quá nhiều chức năng cồng kềnh.

Giờ mà C++ phải tự tay đi xoá sạch sẽ những biến delete không dùng nữa => bên dưới mã máy nó phải tự tay tạo vòng lặp và đi tìm khổ sở các biến delete để gán null => tốn thêm kha khá chu kỳ => giảm hiệu năng app!!!

Vậy nên sau khi delete mà không gán null, dữ liệu cũ của 1 biến hoặc thậm chí là địa chỉ và sự sống của biến đó hoàn toàn tồn tại! Bạn hoàn toàn có thể test đơn giản như bên dưới:
1775608732092.png

Như bạn có thể thấy ta tạo ra int* arr và gán giá trị là 5, sau đó ta ngây thơ dùng delete xóa biến arr và tắt máy đi ngủ. Nhưng như các bạn thấy nếu ta gọi nó ra 1 lần nữa nó hoàn toàn sống lại và in ra màn hình (Giá trị đang chứa sau đó có thể là rác/các quy định ngầm của OS)

=> Khái niệm UAF ra đời với từ tiếng anh USE AFTER FREE (dùng sau khi giải phóng)

EXPLOIT UAF ON x32dbg
1775609238150.png

Trong exploit này có 1 hàm ta cần chú ý đó là virtual. Vì có từ khóa virtual, Trình biên dịch C++ đã âm thầm nhét một biến con trỏ tàng hình vào ngay vị trí đầu tiên (offset 0x0) của Object SinhVien. Người ta gọi nó là vptr (Virtual Pointer).​
  • vptr này trỏ tới một mảng có sẵn trong bộ nhớ gọi là VTable (Virtual Table).​
  • Trong VTable chứa địa chỉ thật của hàm HanhDong().
Để cho dễ hiểu mình sẽ nói theo cách nghĩ của mình là khi có từ khóa virtual. Giống như virtual ám chỉ 1 người lớn, vptr - Virtual Pointer (ám chỉ người virtual đó có 1 cái ví tên vptr) và bên trong ví vptr lại chứa địa chỉ nơi ở chính xác của người lớn virtual. Việc mà ta khéo léo thực hiện là mở ví vptr - Virtual Pointer ra và ghi đè vào đó địa chỉ nhà ở của HACKER => Yes sir!
Screenshot 2026-04-08 075936.png

Oke giờ hãy bình tĩnh ta cần ghi chép để dễ theo dõi các con số hex nhảy múa:

Địa chỉ mã độc: 0x00EE1F60
1775610064007.png

1775610155707.png

Đầu tiên ta tạo 1 fake_vtable, gán giá trị = địa chỉ shellcode 0x00EE1F60
Screenshot 2026-04-08 080305.png

Tiếp theo ta tạo ra lỗ hỗng bằng new và delete nhưng thiếu việc gán null !!!
1775610304476.png

Tiếp theo ta lại khai báo prt_Kedo , ta chỉ khai báo new int[1]. Nhưng sự thật bên dưới ta đã vô tình để cho biến mới này và biến ptr_NanNhan ở trên. Trỏ vào cùng 1 địa chỉ. Why? Vì như đã nói ở trên không gán null nên ngay địa chỉ và sự sống của ptr_NanNhan vẫn đang lơ lửng và thằng prt_Kedo bên dưới là dùng chung 1 ví tiền với ptr_NanNhan (nghe lú nhưng đó là sự thật TT)
Screenshot 2026-04-08 080727.png

*ptr_Kedo = (int)fake_vtable;

Với câu lệnh này ta đang cho gán giá trị (địa chỉ mã độc shell code) vào prt_Kedo. Sự thật rằng tại địa chỉ prt_Kedo ptr_NanNhan lúc này đều đã bị ghi đè hoàn toàn = địa chỉ shell code!
1775610840181.png

1775610963423.png


1775611017958.png
 
Chỉnh sửa lần cuối bởi người điều hành:
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
Thẻ
malware reverse engineering uaf
Bên trên