Bước 1 - Phân tích
- Đầu tiên, chúng ta kiểm tra
checkseccủa tệp tin
|
|
- Dễ dàng thấy hầu hết các cơ chế bảo mật đều được bật ở trên tệp tin này.
- Tựa đề là format, nên ta dễ dàng biết được đây thuộc loại lỗi
format-string
|
|
- Ta mở bằng ghidra:
|
|
|
|
- Lỗi này cho phép chúng ta đọc được dữ liệu trên stack.
- Mở trên gdb, đặt breakpoint tại
printfcủa hànecho. Chạy, nhập input và dùng lệnhx/100gx $rspđể in ra dữ liệu trên stack và quan sát.
|
|
- Chúng ta thấy có 1 vài giá trị có địa chỉ là
0x5555***trên stack. Kiểm tra các giá trị này với lệnhx/i dia_chi
|
|
- Mặt khác, lỗi format-string có thể cho phép chúng ta dump dữ liệu trên stack. Chúng ta sẽ viết 1 đoạn code nhỏ để dump dữ liệu từ stack ra.
|
|
- Tiến hành chạy, ta được kết quả:
|
|
- Tại đây, mình thấy có giá trị
0x55ddf1fb926dlà địa chỉ tại vị tríinit+117, mình sẽ dùng giá trị này để tínhbase address, bypass cơ chế ASLR và giá trị này nó nằm ở bye thứ 37.
|
|
- Do cơ chế
ASLRnên giá trị này sẽ khác ở mỗi lần chạy. Bước thứ nhất, chúng ta sẽ vượt qua được cơ chếASLRvới đoạn code sau:
|
|
Bước 2: Leak địa chỉ __printf từ libc thông qua printf()
|
|
- Chúng ta sử dụng
%sthay vì%pbởi vì%ssẽ đọc địa giá trị bên trong vùng nhớ được truyền vào printf và in ra cho đến khi gặp giá trị null. Điều đó có nghĩa nếu địa chỉ của vùngGOTđược đưa vào thì chúng ta có thể in nó sẽ in ra địa chỉ của libc đang chứa trong nó. Cụ thể chúng ta sẽ thực hiện như sau:
|
|
- Chúng ta lấy địa chỉ
printf@pltthông qua lệnhelf.got["printf"], sau đó gửi payload có dạng"AAAA%7$s" + p64(printf_got_ptl). Số7ở đây là vì địa chỉ của gotp64(printf_got_ptl)năm ở vị trí thứ 7 trên stack, do đó%7ssẽ lấy địa chỉ này ra và in giá trị chứa trong nó cũng chính là địa chỉlibc. - Lệnh
ljust(8, "\x00")được dùng để thêm các gía trị\x00vì cơ chế align
|
|
- Sử dụng libc.blukat.me và địa chỉ của libc vừa có được để kiểm tra phiên bản của libc.
Bước 3 chiếm quyền điều khiển
- Khi kiểm tra với
checksec, chúng ta thấy rằng binary được setup vớiFull RELRO, điều đó có nghĩa chúng ta không thể ghi đè lên GOT, tuy nhiên nội dung của thư viện libc chúng ta vẫn có thể ghi đực. Do đó, mục tiêu sẽ ghi đè lên con trỏ__malloc_hook - Khi chúng ta gửi vào
printf()chuỗi quá lớn,__malloc_hookđược gọi bất cứ khi nàomalloc()được dùng. - Chúng ta gọi
malloc()bằng cách gọiprintf("%100000$c"), lệnh gọi này sẽ cấp phát nhiều byte trên stack và buộc libc phải cấp phát thêm vùng nhớ trên heap thay vì trên stack. - Khi đó, chúng ta có thể ghi đè giá trị của
__malloc_hookbằng%6nvà thayAAAAđầu thành địa chỉ của__malloc_hook. - Chúng ta sử dụng kĩ thuật one_gadget để thực thi
execve("/bin/sh")trong libc.
|
|
- Tiếp theo, chúng ta tính các địa chỉ cần thiết. Với libc khi chưa chạy, chúng ta đọc địa chỉ của
printf:
|
|
Chúng ta thấy rằng khoảng cách từ printf tới base address là 0x64e80, do đó, để tính base address, chúng ta lấy địa chỉ của printf leak được khi thực thi trừ đi 0x64e80.
- Sau khi có được base address, chúng ta tính địa chỉ của
malloc_hook_addrvàone_gadgetbằng cách lấy base address cộng với khoảng cách của các hàm (lấy bằng cách đọc static)
|
|
- Ta sử dụng pwnlib.fmtstr - công cụ khai thác lỗi format string để tính và ghi đè địa chỉ
__malloc_hookvới one gadget và kích hoạt nó.
|
|
Bước 4 - Chạy mã khai thác
- Chạy mã khai thác, ta có được
|
|
- Mã khai thác: exploit.py