Hacking Kiến thức

Bảo mật thần chưởng 1 – Stackoverflow

Stackoverflow

Như các bạn đã biết thì các vụ tấn công đều xuất phát từ các lỗ hổng bảo mật, có thể là do các lập trình viên của chương trình cố tình hay vô tình tạo nên. Series này chúng ta sẽ đến với các lỗ hổng bảo mật được coi là cơ bản, trong bài đầu tiên này sẽ là Stackoverflow aka lỗi tràn bộ nhớ trên stack.

1. Tìm hiểu cấu trúc stack của chương trình

Để bắt đầu reproduce lại lỗ hổng và khai thác nó thì đầu tiên ta cần hiểu stack là gì và nó được sử dụng để làm gì? Nếu bạn muốn tìm hiểu sâu và chi tiết thì hãy đọc ở bài này, còn nói một cách dễ hiểu thì stack giống như là một phân vùng dữ liệu chương trình tạo ra để thực hiện các câu lệnh trong một hàm, có nghĩa là với mỗi hàm mà ta gọi trong chương trình thì sẽ có một stack riêng, sau khi dã thực hiện xong các lệnh trong hàm đó thì con trỏ stack sẽ được trả về vị trí của con trỏ stack trước đó. Nôm na trên lý thuyết là nó trông như thế này:

 

trước khi hàm main gọi hàm con

sau khi hàm main gọi hàm con

sau khi hàm con đã chạy xong và trả flow về hàm main

Hãy cùng tạo một chương trình và debug nó để thấy rõ hơn cách hoạt động của stack.

Chạy câu lệnh:

sudo apt install build-essential vim python3 python3-pip

để tải các chương trình cơ bản cần thiết cho việc viết chương trình, biên dịch và khai thác nó.

Ok, bây giờ hãy viết chương trình và biên dịch nó sang file mà ta chạy được:

Stackoverflow

Sử dụng gdb để debug chương trình:

 

Có thể thấy ở đầu, cuối hàm main và function1 có các instruction:

push rbp

mov rbp, rsp

pop rbp

Đây chính là instruction để thay đổi con trỏ stack, lúc chạy chương trình trong debugger ta cũng có thể thấy:

Trước khi chạy thì RBP (con trỏ cuối stack vẫn nằm ở vị trí của stack cũ)

Sau đó RBP đã được cập nhật lên vị trí mới, vì hàm function1 không thực hiện tính toán gì nên con trỏ đầu stack RSP và con trỏ cuối stack RBP có cùng một địa chỉ, có thể hiểu size stack cho hàm function1 là 0.

2. Tìm và phân tích lỗi

🧠 Khoan đã đọc tiếp, bây giờ hãy thử đọc ở đây và tìm xem chương trình này gặp lỗi ở đâu, và làm thế nào gọi được hàm winner?

 

 

Nếu đã tìm thấy được thì xin chúc mừng, còn nếu chưa thì hãy theo dõi tiếp.

Biên dịch với câu lệnh:

gcc test.c -no-pie -fno-stack-protector

Hmm, ngay từ lúc compile chương trình nó đã thông báo là sử dụng hàm gets, vì sao không an toàn nhỉ? Tại vì hàm gets cho phép người dùng input dữ liệu vào mà không cần kiểm tra độ dài của dữ liệu, vậy nếu một hacker có ý định tấn công thì sẽ nhập nhiều kí tự hơn buffer để gây tràn bộ đệm và thay đổi return pointer để điều khiển flow chương trình chạy những instruction khác.

Đúng như vậy, chương trình đã bị segmentation fault, nghĩa là return pointer (sau khi hàm main kết thúc thì mặc định return pointer trỏ quay về hàm __libc_start_main) bị ghi đè trỏ đến địa chỉ vô nghĩa 0x4141414141414141 (0x41 là kí tự ‘A’ nhưng trong dạng hex), vậy để khai thác và chuyển hướng return pointer đến địa chỉ hàm winner thì sao nhỉ?

Lại debug.

Đặt breakpoint ngay tại chỗ gọi hàm gets để thấy được địa chỉ buffer mà ta nhập input bắt đầu vào từ đâu

Tiếp đến ta tìm địa chỉ chứa return pointer của hàm

Tính khoảng cách giữa 2 địa chỉ này:

Vậy khoảng cách từ chỗ ta nhập input đến return pointer là 40 bytes, giờ hãy xem địa chỉ của hàm winner

Địa chỉ của hàm là 0x401156.

3. Khai thác lỗi

Những thông tin đã có:

+ Khoảng cách từ chỗ nhập input đến return pointer của hàm main là 40 bytes

+ Địa chỉ hàm winner là 0x401156

Giờ hãy chạy lệnh:

pip3 install pwntools

để cài module python giúp ta điều khiển chương trình và gửi input vào chương trình dễ dàng hơn.

Để sử dụng pwntools trong python3 hay viết thử một chương trình thế này rồi chạy với câu lệnh:

python3 <tên chương trình>

Nếu hiện ra thế này có nghĩa là bạn đã cài thành công pwntools

Bây giờ ta sẽ tạo payload và gửi nó vào chương trình:

b’A’*40 là 40 bytes rác để ta có thể đến được địa chỉ của return pointer, p64(0x401156) là địa chỉ hàm winner nhưng được encode lại bởi vì trong hệ thống sử dụng kiến trúc little-endian, có nghĩa là input được truyền ngược chứ không phải theo thứ tự, p64 là để encode theo kiểu little-endian 64-bit.

Bây giờ hãy thử chạy chương trình:

Đã gọi thành công hàm winner và in ra dòng ‘Oops… secret!\n’, vậy là ta đã khai thác thành công lỗ hổng stackoverflow, mình sẽ demo thêm các lỗ hổng cơ bản khác trong những bài tiếp theo.

 

 

 

Visits: 996