[Under construction] Buffer Overflow Demo
Từ Crash Đến Exploitation
Lưu ý:
Trong bài này mình sẽ demo cho mọi người quy trình từ việc phát hiện một lỗ hổng trong software đến khai thác lỗ hổng và get shell.
Những công cụ dung trong bài:
- Immunity Debugger
- SLMail 32-bit (target software)
- Kali Linux
Pre-game: đây là set up
- SLMail service chạy trên Windows 7
- Port: 110
- Attach SLMail vào debugger. SLMail sẽ bị debugger pause lại
]- Click play
- SLMail sẽ tiếp tục chạy
Fuzzing:đây là quá trình chúng ta tìm lỗ hổng của software bằng cách input nhiều kiểu data khác nhau. Nếu input nào làm crash nghĩa là software có lỗ hổng ở đó mà không có doạn code nào handle exception.
Ở đây SLMail được chọn là targeted software với lỗ hổng nằm ở PASS command. Trên thực tế khi làm research về các lỗ hổng security, các bạn sẽ phải thử rất nhiều, và phải nắm bắt các kỹ năng của một software tester với các tools thích hợp.
]Để bắt đầu, chúng ta test thử POP3 (port 110) trên target bằng một vài câu lệnh netcat đơn giản
Ok, mọi thứ ổn. Giờ chúng ta viết một Python script nhỏ để tự động tăng kích thước của input cho PASS command. Như ở ví dụ trên, input của PASS command là bob.
Trong script trên, chúng ta thay input bob bằng một loạt các chữ A với kích thước của buffer tằng lên 200 bytes cho mỗi lần chạy (1 char = 1 byte).
Để cho tiện theo dõi, mình chạy putty trên windows 7. Nhưng nguyên lý vẫn không thay đổi: Kali liên tục gửi input cho PASS command đến SLMail. Sau khi buffer size đạt 2700 bytes thì SLMail crash, chương trình dừng lại, không chạy nữa.
Vậy chuyện gì đã xảy ra? Nếu đế ý kỹ, chúng ta sẽ thấy giá trị đang lưu trữ trong EIP đã bị ghi đè. EIP là register trong processor lưu trữ địa chỉ của instruction tiếp theo (các bạn nên tìm hiểu về assembly và kiến trúc máy tính để hiểu thêm). Hiện tại EIP lưu trữ địa chỉ 41414141, thực chất chỉ là AAAA nếu ta dịch từ hex sang ACSII (0x41 = 65). Nó hoàn toàn là một địa chỉ vô nghĩa, hay nếu có thì nó là của một program khác trên stack. Đó chính là lý do mà SLMail không thể chạy tiếp được, vì processor không biết instruction tiếp theo là gì. Lúc này, OS kernel sẽ chuyển qua kernel mode và nhảy vào để dọn dẹp.
Như vậy, chúng ta có thể thấy được buffer nào đó của SLMail được cung cấp không quá 2700 bytes. Để kiểm chứng lại những gì vừa phân tích, chúng ta viết một Python script khác gửi đúng 2700 bytes cho PASS command để xem có thực sự crash được program nay hay không.
Cùng một setup, lần này chúng ta chỉ gửi 1 gói tin duy nhất với 2700 bytes đến PASS command, và một lần nữa SLMail lại crash.
]Đến đây, chúng ta có thể xác định bất cứ input nào từ 2700 bytes trở lên gửi đến SLMail đều có thể làm crash.
Thêm một điểm lưu ý nữa, hiện tại chúng ta có thể ghi đè lên EIP register, nghĩa là chúng ta có cách để bắt chương trình chạy đến bất cứ vị trí nào chúng ta muốn và bắt đầu thực hiện instruction tại vị trí đó. Diễn đạt một cách dễ hiểu hơn: nếu chúng ta có thể chèn được một payload vào buffer, và bảo EIP địa chỉ của payload chúng ta chèn vào, processor sẽ theo chỉ thị của EIP mà chạy đến payload và run nó. Đó sẽ là mục tiêu tiếp theo.
Control EIP: Trong phần này, chúng ta sẽ tìm cách để ghi một địa chỉ định sẵn vào EIP. Ở trên, chúng ta đã tìm ra cách để ghi đè lên giá trị lưu trong EIP. Tuy nhiên, vấn đề đặt ra là trong 2700 chữ A đó thì chỗ nào mới chính xác là chỗ bắt đầu được ghi vào EIP. Hay nói cách khác, chúng ta phải xác định offset của EIP (cũng có thể hiểu nôm na như index trong array, nhưng ko hoàn toàn chính xác!).
Để làm được việc đó, thay vì gửi 2700 chữ A thì ta có thể gửi 2700 ký tự hoàn toàn khác nhau. Như vậy khi xem xét giá trị của EIP sau khi SLMail crash, chúng ta có thể xác định được offset của EIP trong 2700 ký tự đó.
Chúng ta dùng tool trong metasploit-framework để generate characters.
Sau đó copy kết quả và thế vào 2700 chữ A trong scipt.
Tiếp tục chạy script. Chúng ta thu được kết quả như sau.
Lúc này, trong EIP đã có một giá trị mới. Dùng một tool khác trong metasploit-framework để tìm kiếm giá trị đó và cho ra offset.
Kết quả là 2606. Lý giải: vì metasploit generate 2700 ký tự đó nên metasploit biết rõ ký tự nào ở vị trí nào.
Đến đây, chúng ta có thể kết luận rằng ký tự thứ 2607 sẽ là ký tự đầu tiên được ghi vào EIP (offset = 2606 vì index bắt đầu từ 0). Để kiểm chứng kết luận trên, chúng ta lại chỉnh Python script lần nữa. Lần này chúng ta sẽ gửi cho SLMail 2606 chữ A, 4 chữ B và 90 chữ C. Theo giả định trên thì 4 chữ B sẽ được ghi vào EIP (đúng 4 bytes vì đây là 32-bit) và các chữ C sẽ được ghi
tiếp sau đó.
Chạy script, và kết quả đúng như dự đoán. EIP giờ lưu giá trị 42424242 (0x42 = 66).
Có một điểm lưu ý ở đây. Sau khi chạy script, ta thấy ESP (Extended Stack Pointer) đang chỉ địa chỉ mà chúng ta ghi các chữ C vào.
Điều đó có nghĩa là nếu thay vì các chữ C, chúng ta chèn payload dưới dạng HEX/BINARY code thì stack pointer sẽ chỉ ngay vào đó. Và chúng ta đã control được EIP. Kết hợp lại, nếu chúng ta tìm ra cách ghi giá trị của ESP vào EIP và ESP thì lại trỏ vào payload chúng ta chèn trong input, chúng ta có thể bắt system chạy payload đó.
[Con tiep, chua viet xong. Se update sau]
[FONT="][/FONT]
Lưu ý:
- Bài viết này được biên dịch dựa trên tài liệu về Buffer Overflow training của Offensive Security
- Vì thời lượng của bài viết có hạn nên không thể giải thích hết các khái niệm nêu ra trong bài, người đọc phải tìm hiểu thêm
Trong bài này mình sẽ demo cho mọi người quy trình từ việc phát hiện một lỗ hổng trong software đến khai thác lỗ hổng và get shell.
Những công cụ dung trong bài:
- Immunity Debugger
- SLMail 32-bit (target software)
- Kali Linux
Pre-game: đây là set up
- SLMail service chạy trên Windows 7
- Port: 110
- Attach SLMail vào debugger. SLMail sẽ bị debugger pause lại
]- Click play
- SLMail sẽ tiếp tục chạy
Fuzzing:đây là quá trình chúng ta tìm lỗ hổng của software bằng cách input nhiều kiểu data khác nhau. Nếu input nào làm crash nghĩa là software có lỗ hổng ở đó mà không có doạn code nào handle exception.
Ở đây SLMail được chọn là targeted software với lỗ hổng nằm ở PASS command. Trên thực tế khi làm research về các lỗ hổng security, các bạn sẽ phải thử rất nhiều, và phải nắm bắt các kỹ năng của một software tester với các tools thích hợp.
]Để bắt đầu, chúng ta test thử POP3 (port 110) trên target bằng một vài câu lệnh netcat đơn giản
Ok, mọi thứ ổn. Giờ chúng ta viết một Python script nhỏ để tự động tăng kích thước của input cho PASS command. Như ở ví dụ trên, input của PASS command là bob.
Trong script trên, chúng ta thay input bob bằng một loạt các chữ A với kích thước của buffer tằng lên 200 bytes cho mỗi lần chạy (1 char = 1 byte).
Để cho tiện theo dõi, mình chạy putty trên windows 7. Nhưng nguyên lý vẫn không thay đổi: Kali liên tục gửi input cho PASS command đến SLMail. Sau khi buffer size đạt 2700 bytes thì SLMail crash, chương trình dừng lại, không chạy nữa.
Vậy chuyện gì đã xảy ra? Nếu đế ý kỹ, chúng ta sẽ thấy giá trị đang lưu trữ trong EIP đã bị ghi đè. EIP là register trong processor lưu trữ địa chỉ của instruction tiếp theo (các bạn nên tìm hiểu về assembly và kiến trúc máy tính để hiểu thêm). Hiện tại EIP lưu trữ địa chỉ 41414141, thực chất chỉ là AAAA nếu ta dịch từ hex sang ACSII (0x41 = 65). Nó hoàn toàn là một địa chỉ vô nghĩa, hay nếu có thì nó là của một program khác trên stack. Đó chính là lý do mà SLMail không thể chạy tiếp được, vì processor không biết instruction tiếp theo là gì. Lúc này, OS kernel sẽ chuyển qua kernel mode và nhảy vào để dọn dẹp.
Như vậy, chúng ta có thể thấy được buffer nào đó của SLMail được cung cấp không quá 2700 bytes. Để kiểm chứng lại những gì vừa phân tích, chúng ta viết một Python script khác gửi đúng 2700 bytes cho PASS command để xem có thực sự crash được program nay hay không.
Cùng một setup, lần này chúng ta chỉ gửi 1 gói tin duy nhất với 2700 bytes đến PASS command, và một lần nữa SLMail lại crash.
]Đến đây, chúng ta có thể xác định bất cứ input nào từ 2700 bytes trở lên gửi đến SLMail đều có thể làm crash.
Thêm một điểm lưu ý nữa, hiện tại chúng ta có thể ghi đè lên EIP register, nghĩa là chúng ta có cách để bắt chương trình chạy đến bất cứ vị trí nào chúng ta muốn và bắt đầu thực hiện instruction tại vị trí đó. Diễn đạt một cách dễ hiểu hơn: nếu chúng ta có thể chèn được một payload vào buffer, và bảo EIP địa chỉ của payload chúng ta chèn vào, processor sẽ theo chỉ thị của EIP mà chạy đến payload và run nó. Đó sẽ là mục tiêu tiếp theo.
Control EIP: Trong phần này, chúng ta sẽ tìm cách để ghi một địa chỉ định sẵn vào EIP. Ở trên, chúng ta đã tìm ra cách để ghi đè lên giá trị lưu trong EIP. Tuy nhiên, vấn đề đặt ra là trong 2700 chữ A đó thì chỗ nào mới chính xác là chỗ bắt đầu được ghi vào EIP. Hay nói cách khác, chúng ta phải xác định offset của EIP (cũng có thể hiểu nôm na như index trong array, nhưng ko hoàn toàn chính xác!).
Để làm được việc đó, thay vì gửi 2700 chữ A thì ta có thể gửi 2700 ký tự hoàn toàn khác nhau. Như vậy khi xem xét giá trị của EIP sau khi SLMail crash, chúng ta có thể xác định được offset của EIP trong 2700 ký tự đó.
Chúng ta dùng tool trong metasploit-framework để generate characters.
Sau đó copy kết quả và thế vào 2700 chữ A trong scipt.
Tiếp tục chạy script. Chúng ta thu được kết quả như sau.
Lúc này, trong EIP đã có một giá trị mới. Dùng một tool khác trong metasploit-framework để tìm kiếm giá trị đó và cho ra offset.
Kết quả là 2606. Lý giải: vì metasploit generate 2700 ký tự đó nên metasploit biết rõ ký tự nào ở vị trí nào.
Đến đây, chúng ta có thể kết luận rằng ký tự thứ 2607 sẽ là ký tự đầu tiên được ghi vào EIP (offset = 2606 vì index bắt đầu từ 0). Để kiểm chứng kết luận trên, chúng ta lại chỉnh Python script lần nữa. Lần này chúng ta sẽ gửi cho SLMail 2606 chữ A, 4 chữ B và 90 chữ C. Theo giả định trên thì 4 chữ B sẽ được ghi vào EIP (đúng 4 bytes vì đây là 32-bit) và các chữ C sẽ được ghi
tiếp sau đó.
Chạy script, và kết quả đúng như dự đoán. EIP giờ lưu giá trị 42424242 (0x42 = 66).
Có một điểm lưu ý ở đây. Sau khi chạy script, ta thấy ESP (Extended Stack Pointer) đang chỉ địa chỉ mà chúng ta ghi các chữ C vào.
Điều đó có nghĩa là nếu thay vì các chữ C, chúng ta chèn payload dưới dạng HEX/BINARY code thì stack pointer sẽ chỉ ngay vào đó. Và chúng ta đã control được EIP. Kết hợp lại, nếu chúng ta tìm ra cách ghi giá trị của ESP vào EIP và ESP thì lại trỏ vào payload chúng ta chèn trong input, chúng ta có thể bắt system chạy payload đó.
[Con tiep, chua viet xong. Se update sau]
[FONT="][/FONT]
Chỉnh sửa lần cuối bởi người điều hành: