Bước 1 - Phân tích
- Đầu tiên, chúng ta kiểm tra
checksec
củ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
printf
củ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ị
0x55ddf1fb926d
là đị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ế
ASLR
nê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ếASLR
với đoạn code sau:
|
|
Bước 2: Leak địa chỉ __printf từ libc thông qua printf()
|
|
- Chúng ta sử dụng
%s
thay vì%p
bởi vì%s
sẽ đọ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@plt
thô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 đó%7s
sẽ 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ị\x00
vì 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_hook
bằng%6n
và 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_addr
vàone_gadget
bằ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_hook
vớ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