Hỏi & Đáp#
- Reflected Cross-Site Scripting (XSS) là gì và nó khác biệt như thế nào so với Stored XSS và DOM-Based XSS?
- Reflected XSS là một lỗ hổng trong đó đoạn mã độc (malicious script) được đính kèm trong một request và ngay lập tức bị phản hồi (reflect) ngược lại trong server response, khiến trình duyệt của nạn nhân thực thi đoạn mã đó. Khác với Stored XSS, mã độc không được lưu trữ vĩnh viễn trên server. Khác với DOM-Based XSS, lỗ hổng này thường xảy ra ở khâu tạo response phía server (server-side response generation) thay vì chỉ xử lý bằng JavaScript ở phía client (client-side).
- Reflected XSS hoạt động như thế nào và các vector tấn công (attack vectors) điển hình là gì?
- Reflected XSS hoạt động khi kẻ tấn công gửi một đường link hoặc request đã được nhúng mã độc JavaScript cho nạn nhân. Nếu ứng dụng đưa (reflect) input đó vào trang web mà không có cơ chế kiểm tra (validation) hoặc mã hóa đầu ra (output encoding) hợp lý, trình duyệt của nạn nhân sẽ thực thi đoạn mã đó. Các vector tấn công phổ biến bao gồm trường tìm kiếm (search fields), tham số URL, thông báo lỗi, form nhập liệu, và các link độc hại gửi qua email, chat, hoặc các trang phishing.
Khai thác Reflected DOM XSS#
1. Mục tiêu Lab#
Mục tiêu của bài lab này là khai thác lỗ hổng Reflected DOM XSS trong tính năng tìm kiếm của blog. Ứng dụng đã phản hồi (reflect) tham số search vào dữ liệu và sau đó dữ liệu này được xử lý bởi JavaScript ở phía client. Đích đến là tạo ra một payload để thực thi alert(1) trên trình duyệt và hoàn thành lab.
2. Truy cập ứng dụng Lab#
Đầu tiên, tôi mở lab PortSwigger Web Security Academy có tên Reflected DOM XSS. Trang chủ hiển thị một chức năng tìm kiếm blog và trạng thái lab ban đầu là Not solved.
Ô tìm kiếm gửi (submit) dữ liệu người dùng thông qua tham số truy vấn (query parameter) search trên URL. Do đó, tham số này được chọn làm điểm chèn (input point) chính để kiểm thử.
3. Kiểm tra mã nguồn HTML#
Tiếp theo, tôi xem source code của trang để hiểu cách chức năng tìm kiếm hoạt động. Mã HTML cho thấy trang tải một file JavaScript bên ngoài và sau đó gọi hàm search():
| |
Điều này cho thấy việc render kết quả tìm kiếm được xử lý bằng JavaScript ở phía client, chứ không chỉ được tạo ra bằng HTML tĩnh phía server.
4. Kiểm tra mã nguồn JavaScript#
Sau đó, tôi mở file JavaScript được sử dụng bởi tính năng tìm kiếm:
| |
Bên trong file, ứng dụng tạo một đối tượng XMLHttpRequest và gửi chuỗi query trên URL hiện tại xuống backend:
| |
Điểm quan trọng nhất là server response được đưa thẳng vào hàm eval():
| |
Đây chính là hành vi gây ra lỗ hổng. Hàm eval() sẽ thực thi (treats) đoạn text phản hồi như một đoạn mã JavaScript. Nếu input do kẻ tấn công kiểm soát được phản hồi vào this.responseText một cách không an toàn, kẻ tấn công có thể thoát khỏi (break out) cấu trúc dữ liệu dự kiến và thực thi JavaScript trên trình duyệt.
Luồng dữ liệu gây ra lỗ hổng (Vulnerable data flow) là:
| |
5. Kiểm thử các ký tự đặc biệt trong tham số Search#
Sau khi xác định được việc sử dụng eval() nguy hiểm, tôi bắt đầu kiểm thử các ký tự đặc biệt trong tham số search để hiểu cách input được reflect và phân tích (parsed).
Đầu tiên, tôi test một giá trị chứa dấu ngoặc kép (double quote) và dấu ngoặc nhọn đóng (closing brace):
| |
Trang web phản hồi lại giá trị trong thông báo kết quả tìm kiếm như sau:
| |
Điều này xác nhận rằng các ký tự như " và } có thể đi vào output tìm kiếm do JavaScript xử lý.
Tiếp theo, tôi test một giá trị chứa dấu gạch chéo ngược (backslash), dấu ngoặc kép, dấu ngoặc nhọn đóng và ký hiệu comment của JavaScript:
| |
Trang vẫn load, và output phản hồi thay đổi thành một giá trị tương tự như:
| |
Kết quả này rất quan trọng vì nó cho thấy chuỗi backslash và ngoặc kép đã tác động đến ngữ cảnh chuỗi (string context) của JavaScript. Từ hành vi này, tôi xác định rằng payload cuối cùng cần thực hiện ba việc:
| |
6. Xây dựng Payload cuối cùng#
Dựa trên các kết quả kiểm thử trước đó, tôi xây dựng payload sau:
| |
Phân tích cấu trúc payload:
| |
Phiên bản URL-encoded của payload là:
| |
Định dạng URL cuối cùng là:
| |
7. Thực thi Payload#
Tôi gửi (submit) payload cuối cùng thông qua tham số search. Khi trang được load, trình duyệt gửi yêu cầu lấy kết quả tìm kiếm, nhận được response chứa giá trị tìm kiếm đã bị reflect, và thực thi response đó thông qua eval().
Vì payload đã thoát thành công khỏi JavaScript string context dự kiến, trình duyệt đã thực thi:
| |
Hộp thoại cảnh báo (alert box) xuất hiện trên trình duyệt, xác nhận việc thực thi JavaScript thành công.
8. Hoàn thành Lab#
Sau khi alert(1) được thực thi thành công, lab phát hiện việc khai thác và chuyển trạng thái sang Solved.
9. Tổng kết#
Bài lab này minh họa một lỗ hổng Reflected DOM XSS gây ra bởi việc xử lý JavaScript phía client không an toàn. Ứng dụng đã reflect tham số search vào server response, và client-side script đã thực thi response đó bằng eval(). Bằng cách phân tích cách các ký tự đặc biệt được phản hồi và chế tạo payload a\"+alert(1)}//, tôi đã có thể thoát khỏi string context của JavaScript, thực thi thành công alert(1) và giải quyết trọn vẹn bài lab.