Author: SeaWind (J4ckP0t)
Vì đây là vòng khởi động nên có thể năm nay các challenges về pwn có vẻ tương đối dễ thở. Cho nên hơn thua nhau ở các đội khi làm về mảng pwn là việc team nào giải ra nhanh hơn thôi
Đối với mình thì tốc độ chưa bao giờ là lợi thế của mình, thường đối với một challenge dù dễ thì mình cũng cần mất tương đối thời gian mới có thể identify lỗi và exploit nó được.
Thôi tám chuyện vậy là đủ rồi, vào phần writeup thôi.
Đầu tiên ta sẽ tiến hành kiểm tra các mitigations của file challenge này.
Ở đây thì có mitigation của stack và PIE mitigation đã tắt (điều này sẽ có lợi cho lỗi bufferoverflow nên trong chương trình có tồn tại lỗi đó)
Tiếp đến ta sẽ check đến luồng thực thi và cấu trúc chương trình thông qua IDA Pro.
Chương trình sẽ có 3 chức năng chính:
1. Login(): (option=1) Hàm này sẽ tiến hành kiểm tra ta đã login chưa thông qua giá trị của biến old_user và old_passwd (Hàm này dường như không có ích gì trong việc khai thác lỗ hổng của ta)
2. Signup(): (option=2) Hàm này sẽ tiến hành đọc từ input của ta về username và passwd (trừ username = admin) và lưu giá trị vào trong biến old_user và old_passwd (Đây là hàm mà chúng ta sẽ tiến hành khai thác lỗ hỗng ở đây)
3. getflag(): (option=4) Hàm này chính là hàm mà chúng ta hướng đến, hàm này sẽ kiểm tra giá trị của biến old_user có = với "admin" không. Nếu có thì sẽ leak ra flag cho chúng ta.
Ở trong hàm Signup này thì ta thấy ngay được lỗ hổng buffer overflow ở ngay hàm scanf('%s')
khi hàm này sẽ cho ta phép nhập input ở đến khi gặp dấu '\n'
hoặc dấu ' '
(dấu xuống hàng và dấu khoảng trắng) ở trong input của cà username và passwd.
Ban đấu ý tưởng của mình là sử dụng lỗi buffer overflow này để tiến hành ghi đè địa chỉ của RIP ở điều hướng chương trình này sang hẳn hàm open flag trong hàm getflag luôn. Nhưng mà khổ nỗi là địa chỉ của hàm getflag ấy có chứa mã hex của '\n'
nên căn bản là không thể ghi đè đầy đủ địa chỉ được đó.
Và mình đã rất chật vật để làm sao để có thể ghi đè địa chỉ RIP của hàm là địa chỉ của hàm getflag theo ý mình muốn nhưng mã không được.
Cho đến khi mình được gợi ý rằng là đối lúc chúng ta cần phải chơi đúng luật chơi một chút :V
Căn bản là chúng ta vẫn sẽ khai thác lỗi buffer overflow nhưng chúng ta sẽ tiến hành ghi đè giá trị của biến old_user
sẽ là "admin" thông qua việc kết hợp buffer overflow của input nhập passwd + hàm strncpy.
Tại sao mình lại có thể ghi đè được giá trị của biến old_user
ư? Nếu ta để ý kỹ thì input của việc nhập username cũng như passwd của ta được lưu vào 2 biến local của hàm.
Trong đó ta thấy biến src
(chứa input của nhập passwd) nằm trước biến s1
(chứa input của nhập username) của ta cho nên nếu ta lặp đầy biến src
64 bytes + chuỗi string admin
thì ta sẽ ghi đè giá trị trong biến local s1
Sau đó hàm strncpy sẽ lưu giá trị của biến s1
vào trong biến old_user
và src
vào trong biến old_passwd
Giờ thì ta đã thành công truyền được string admin
vào trong biến old_user
rồi. Gọi hàm getflag() và in ra flag thôi.
Đầu tiên ta sẽ tiến hành kiểm tra các mitigations của challenge này.
Oh wow! Tất cả các mitigations đều không được bật. Một mảnh đất đầy màu mỡ cho việc khai thác lỗ hổng
Tiếp đến thì ta sẽ tiến hành xem mã giả và luồng thực thi chương trình thông qua IDA Pro.
Phân tích luồng thực thi của chương trình thì ta thấy ngay rõ ràng ở hàm getflag có lỗ hỗng shellcode execution (do ta thấy mitigation của challenge này đã disable NX và có RWX segments)
Tức là việc ở input thứ 2 (cụ thể là ở hàm read(0,s,0x64)
) thì ta có thể inject shellcode để có thể chiếm được shell của chương trình
Đến đây thì ta cần biết chút ít về lập trình assembly (cụ thể là kiến trúc x86-64) ở để có thể viết được shellcode chiếm shell
Thì ở solution của mình, vì lúc đó trong thời gian thi (áp lực các kiểu) và một phần là do mình lười :V , cho nên mình sẽ sử dụng shellcode có sẵn trên mạng (nguồn mình sẽ để ở đây link)
Vì shellcode này chỉ có 30 bytes length trong khi trong hàm main sẽ kiểm tra xem length của shellcode mà mình nhập vào có lớn hơn 39 (0x27) hay không. Cho nên để lấp đầy phần còn thiếu trong shellcode thì mình sẽ sử dụng lệnh nop (hay còn gọi là kỹ thuật nop sled với chức năng là không làm gì cả :V, nó sẽ tiến hành thực thi lệnh nop để trượt dài cho đến shellcode của mình).