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

T

tmnt53

Guest
Khai thác lỗi format string (phần II)
Ở 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

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

Kế hoạch khai thác sẽ như sau:
  1. 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.
  2. Tính ra địa chỉ của libc từ dữ liệu leak được. Sau đó tìm địa chỉ của hàm system.
  3. 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

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

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

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

4.png

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

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

Đ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

Output có được:

8.png

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:
  1. “x10” vào byte 0x0804A004.
  2. “x93” vào byte 0x0804A005.
  3. “x61” vào byte 0x0804A006.
  4. “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

Output thu được:

10.png

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



12.png

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é.
 
Chỉnh sửa lần cuối bởi người điều hành:
Bài viết thật bổ ích.
 
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
Comment
Cho mình xin lại sample với ạ
 
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
Comment
Thẻ
format string lỗi format string
Bên trên