Once upon a time there was a WebSocket

This is the story from one of our recent penetration testing engagements. Still, the story is a familiar one for those who are testing newer web applications that use one of the multitudes of evolving web app platforms built on a poorly understood technology stack. In this case, we ran into a WebSocket-based application that was thought to be relatively secure; however, the use of web sockets in the application was misunderstood, resulting in a significant set of authentication and authorization flaws. This also serves as yet another testament to the fact that automated vulnerability scanners (i.e. SAST and DAST tools) are no match for a seasoned penetration tester.

What is a WebSocket?

Before we continue our story, it is important to understand a little bit about WebSockets. Unlike a typical HTTP request/response interaction in which a browser requests data from the server, a WebSocket opens a persistent bi-directional communication (i.e. full-duplex) path between the server and the browser. It is similar in behavior to a Unix-style socket but is its own protocol, described in RFC 6455 at the same time the HTML 5 specification was proposed.

As long as the socket remains open, the browser and server can push messages to each other. This is particularly useful for applications with long-term connections where efficient asynchronous updates are ideal. A typical live chat program is an example of an application that would benefit from using WebSockets.

The Test

Back to our story: we were testing an application that was meant to display confidential content to authorized users. Our primary goal was to simply determine if there was any way for an unauthenticated or unauthorized user to access the confidential content.

While mapping out the application’s functionality and looking through our proxy tool (Burp Suite Pro) we noticed that the WebSockets history tab was being populated. This is not an unusual occurrence. Many web applications make use of sockets for a variety of asynchronous communication purposes that are often innocuous. What struck us as a little odd was the fact that the WebSocket was being established before our test user was fully authorized to access the application.  In this particular case, the application had been modified to require a Multi-Factor Authentication (MFA) solution. Users who did not authenticate through MFA were supposed to be blocked from the application and presented with a message informing them to use the MFA solution before proceeding.

What we discovered was that the design of the application was to return the confidential content through the WebSocket, based on the ID of the user’s first layer of authentication (i.e. before the MFA message). What this means is the user was being presented with a message informing them to use MFA before proceeding and simultaneously receiving the confidential content through the WebSocket. The content was not yet being displayed to the user, but it was being stored temporarily in the browser.

Upon closer examination of the WebSocket traffic, we realized there was no piece of identifying information sent from the browser to the Socket except the user’s ID, which we all know is not a reliable secret. At this stage, red flags were going up as we considered that this behavior appeared to also include an authorization flaw. But, WebSockets are much trickier to replay than HTTP requests.  First you have to establish the connection. Then you have to work out the internal protocol since it is possible to send arbitrary data across the socket. To accomplish this, we wrote a quick Proof of Concept web page that took a user ID as input. Some custom JavaScript on the page attempted to create a WebSocket, push the specified user ID through the socket, and listened for incoming messages from the server.

This was a gray box test so we had at least two valid user accounts to work with.  We were able to verify that by supplying different user IDs, even over the same WebSocket, we would receive both users’ confidential content through the socket. This proved our theory. In this application it was possible to establish a WebSocket without authenticating and then retrieve any user’s confidential content given only the user’s ID.

At this stage in a test, every penetration tester is tempted with the thought “I could totally script this!”; meaning, we could have generated a script that would iterate over many user IDs and pull back all the confidential content. It is tempting, but not necessary.  However, a different consideration was necessary (i.e. there is one more finding in here). It turns out that WebSockets are not bound by the Same Origin Policy. There is no Origin header sent through the socket, which means any malicious web page with the correct JavaScript on it could open the socket and have the confidential content pushed back to the malicious web page. This may not seem practical since we can’t take advantage of any cookies. However, consider the possibility of this being an application on the company’s internal network. Now an attacker has a legitimate exfiltration attack vector. Since the user’s browser establishes the WebSocket to the internal application, any data received over that socket can then leave the internal network through whatever means the attacker chooses to program into the JavaScript on their own webpage. This is done without breaking context (i.e. it is not a standard XSS attack), so a company’s pattern-based defenses are less likely to detect it.

Takeaways

There are several things to be learned from this story:

Lesson # 1: Always Check for and Analyze WebSockets

WebSockets are often a poorly understood technology, especially among security practitioners. You can use Burp Suite or ZAP or even your browser’s developer tools to examine WebSocket traffic. This is something that should be done during penetration tests with every application, especially since your SAST and DAST tools are likely to miss it. For example, in the above case we know that our client had access to SAST and DAST tools, yet this issue was unknown to them.

Lesson # 2: WebSockets Don’t Do Cookies

Though it may be possible for a cookie to be returned on the same request that initiates a WebSocket, that cookie doesn’t mean anything to the WebSocket protocol.  Your browser will go through its protocol change, thus establishing the WebSocket connection. At this point the server doesn’t receive any session cookies to validate on that connection. If you find an application that is using WebSockets, it is important that you examine the socket traffic to determine if any alternate forms of authentication and authorization are taking place. If you don’t see any in the WebSocket communication, chances are there isn’t any.

Lesson #3: Same Origin Policy is not for WebSockets 

The Same Origin Policy, and by corollary Cross Origin Resource Sharing (CORS) policies do not affect WebSockets. WebSockets are a different protocol from HTTP altogether. It is assumed that the server will perform any necessary validations. It is important to consider this while testing them. Many solutions make assumptions that WebSockets are obeying all the same rules as HTTP traffic, but this isn’t the case.

Lesson #4: Perform Web Application Penetration Testing

We aren’t aware of any scanners that would have detected the critical authentication and authorization flaws we found. They were relatively easy for us to find because we knew where to look and because we understood the context. Though automated SAST and DAST tools are useful for automating more regular testing, they can’t replace a penetration tester. It is important that any applications that deal with confidential data undergo gray box or white box (i.e. not black box) testing, as this helps ensure thorough testing of authentication and authorization mechanisms.

In Conclusion

To be clear, the moral of this story isn’t that every WebSocket is a vulnerability. We have encountered many applications that use WebSockets either for non-confidential purposes or in a way that includes proper authentication and authorization controls. However, when there are WebSocket vulnerabilities, they are likely to be rated high or critical. We therefore believe organizations should be extra cautious of WebSockets and understand that they are often misunderstood and not implemented with proper security controls in place.


*** This is a Security Bloggers Network syndicated blog from Professionally Evil Insights authored by Jason Gillam. Read the original post at: https://blog.secureideas.com/2020/03/once-upon-a-time-there-was-a-websocket.html