Khai thác lỗi format string (phần II)

thanhlong

Moderator
gemgem
Tham gia
18/03/2025
Bài viết
64
Được Like
0
Coin
2,477
Points
355
Ở bài trước – “khai thác lỗi format string (phần I)”, mình đã giới thiệu những nguyên liệu cơ bản để thực hiện khai thác được study case “echoServer”. Hôm nay mình sẽ hoàn thiện khai thác nó. Tóm tắt lại, mình có:

%[imath]s, %[/imath]p để leak dữ liệu

%$n để ghi đè địa chỉ tùy ý.

Bảng GOT chứa các con trỏ hàm, trỏ tới libc

Trong quá trình khai thác, mình sẽ sử dụng công cụ pwntools. Đây là công cụ rất mạnh mẽ hỗ trợ exploit. Để cài đặt, các bạn làm theo hướng dẫn tại: http://docs.pwntools.com/en/stable/install.html

format-string-png.14312

Các file đính kèm (echoServer, exploit_echoServer,…): format_string.zip

Kế hoạch khai thác sẽ như sau:

Leak dữ liệu từ con trỏ hàm GOT[fgets] trong GOT table. Do nó chứa địa chỉ của hàm fgets trong libc, nên mình sẽ lấy địa chỉ của libc.

Tính ra địa chỉ của libc từ dữ liệu leak được. Sau đó tìm địa chỉ của hàm system.

Ghi đè con trỏ hàm GOT[printf] bằng địa chỉ hàm system. Như vậy, khi chương trình gọi printf(buf) thì lại là thực hiện system(buf). Ta chỉ cần nhập buf = “/bin/sh” là shell được gọi lên, và ta đã khai thác được chương trình.

1. Leak dữ liệu từ con trỏ hàm GOT[fgets]

Bằng IDA, ta lấy được địa chỉ con trỏ hàm GOT[fgets] là 0x0804A004.

1-png.14313


Tìm offset của buf khi mà thực hiện gọi hàm printf. Đặt breakpoin tại call printf@plt, lấy địa chỉ của buf – argument0, và của esp, từ đó tính ra offset = (bufAddress – esp)/4.

2-png.14314


Như hình trên ta có esp = 0xffffd420, bufAddress = 0xffffd43c => offsetBuf = 7.

Dùng %[imath]s, ta sẽ lấy được nội dung của các con trỏ nằm trên stack. Ví dụ, khi gọi printf(“%10[/imath]s”), và tại offset 10 (dword thứ 10) là 0x08048000, thì nội dung tại địa chỉ 0x08048000 sẽ được in ra. Ta cần in ra nội dung của GOT[fgets] = 0x0804A004, thì ta phải có giá trị này trên stack. Làm sao để có được vậy? Vì buf nằm trên stack, nên ta sẽ nhập buf = “…x04xA0x04x08…” là được. Nhưng các giá trị “x04”, “x08” không nhập được bằng tay, nên giờ ta sẽ sử dụng công cụ pwntools.

Vì offsetBuf = 7, nên ta nhập vào “x04xA0x04x08%7$s”.

Mã nguồn exploit_echoServer.py:

3-png.14315


Kết quả chạy exploit_echoServer.py:

4-png.14316


Dữ liệu leak được từ con trỏ GOT[fgets] là “xa0lZxf7xe0x87zx7…”, ở đó 4byte “xa0lZf7” chính là địa chỉ hàm fgets trong libc. Vậy là ta đã leak ra được địa chỉ fgets.

Chú ý là địa chỉ này không cố định ở mỗi lần chạy chương trình:

5-png.14317


Nên mỗi lần chạy, ta sẽ tính ra được địa chỉ libc khác nhau.

2. Tính địa chỉ của hàm system và xâu “/bin/sh” trong libc

Với nhiều bài khai thác ta đều cần có libc. Để lấy được libc trong máy mình, ta dùng ldd:

6-png.14318


Đoạn code tiếp theo trong exploit_echoServer cho phép ta tính ra được địa chỉ của system, và xâu “/bin/sh” trong libc:

7-png.14319


Output có được:

8-png.14320


3. Ghi đè địa chỉ hàm system

Ta thực hiện ghi đè địa chỉ hàm system vào GOT[‘printf’] - 0x0804A004. Giả sử địa chỉ hàm system là 0xf7619310, thì ta cần ghi:

“x10” vào byte 0x0804A004.

“x93” vào byte 0x0804A005.

“x61” vào byte 0x0804A006.

“xf7” vào byte 0x0804A007.

Như bài trước giới thiệu, ta sẽ sử dụng đến %n. Chú ý là thay vì dùng %n (ghi đè 4 byte), ta sẽ sử dụng %hhn để ghi từng byte một. Để ghi đè được “x10” vào byte 0x0804A004, ta cần payload truyền cho printf như sau:

“x04xA0x04x08%12c%7$hhn”

Ta thấy, giống như “x04xA0x04x08%7[imath]s”, “%7[/imath]hhn” tương ứng với offset thứ 7 trên stack, và nó trùng đúng với địa chỉ buf. Buf được gán “x04xA0x04x08…” nên tại offset thứ 7 đó là địa chỉ 0x0804A004. Như vậy, số ký tự được in ra sẽ được lưu vào địa chỉ này. Xâu “x04xA0x04x08%12c” sẽ in ra 16 ký tự = 0x10, nên 0x10 sẽ được ghi vào địa chỉ 0x0804A004.

Còn để ghi tiếp “x93” vào byte 0x0804A005, ta có payload:

“x04xA0x04x08x05xA0x04x08%8c%7[imath]hhn%131c%8[/imath] hhn”

%8$hhn sẽ tương ứng với offset thứ 8 trên stack, là dữ liệu tại buf+4 = “…x05xA0x04x08…”. Xâu “x04xA0x04x08x05xA0x04x08%8c” in ra 0x10 ký tự => 0x0804A004 được ghi 0x10, phần sau “%131c” ghi thêm 131 ký tự nữa, mà 131+0x10 = 0x93, nên 0x0804A005 được ghi 0x93.

Qua các ví dụ trên, mình đã minh họa ý tưởng để có thể ghi đè được dữ liệu vào. Do địa chỉ system thay đổi sau mỗi lần gọi chương trình, nên ta cần lập trình để tính ra được payload cần thiết.

9-png.14321


Output thu được:

10-png.14322


Và giờ, khi ta truyền dữ liệu này cho echoServer, thì chương trình bị khai thác, và tựa như đang thực hiện chức năng sau:

while(1) {

fgets(buf, 256, stdin);

system(buf);

}

Như vậy, khi nhập ls, cat flag.txt,… thì chương trình thực hiện đúng các lệnh ấy như đang trong một shell. Hay ta nhập /bin/sh thì chương trình gọi hẳn một cái shell lên, và ta có thể thực hiện mọi lệnh như đang trong một phiên làm việc shell, chẳng hạn dùng cd để di chuyển giữa các thư mục:

11-png.14323


12-png.14324


Mình xin kết thúc bài exploit formatstring tại đây. Có gì thắc mắc các bạn cứ hỏi tự nhiên nhé.
 
Top Bottom