Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

How to analyze and infiltrate WebSocket and Socket.io

2025-01-16 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Network Security >

Share

Shulou(Shulou.com)06/01 Report--

This article is about how to analyze and penetrate WebSocket and Socket.io. Xiaobian thinks it is very practical, so share it with you to learn. I hope you can gain something after reading this article. Let's not say much. Let's take a look at it together with Xiaobian.

About Websocket

WebSocket is a technology that allows browsers and servers to establish a single TCP connection and then communicate asynchronously with each other in full duplex. WebSockets are popular for web applications because they allow real-time updates without the browser having to send hundreds of new HTTP polling requests to the background. This is bad for testers because support for WebSocket tools is not as widespread as HTTP and sometimes more complex.

In addition to BurpSuite, there are a number of other tools available to handle WebSockets. However, after testing, they are not ideal.

Zed Attack Proxy (ZAP)

Pappy Proxy

Man-in-the-Middle Proxy (mitmproxy)

WebSocket/Socket.io (WSSiP)

I'll focus on socket.io, a popular JavaScript WebSockets library. How popular is it on GitHub? There are already over 41.4 stars.

On NPM, it ranks second and third in WebSocket.

In addition, great projects like OWASP Juice-Shop also use socket.io library, so this article will use websocket.io for demonstration.

https://github.com/bkimminich/juice-shop/search? utf8=%E2%9C%93&q=socket.io&type=

In this article, we assume that you are already familiar with testing Web applications with BurpSuite, everything covered can be done in its community version. Needless to say, let's get started.

If we visit Juice-Shop in a browser, we can quickly view WebSocket traffic in the background. You can also find it in BurpSuite at Proxy-> WebSockets History.

HTTP needs to always send request/response pairs due to the stateless nature of the protocol, whereas WebSocket is a stateful protocol. This means that you can get any number of outgoing "requests" and any number of incoming "responses" from the server. Because the underlying connection is TCP that stays open, clients and servers can send messages at any time without waiting for each other. That's why WebSocket history differs from the HTTP history you're used to looking at.

In this interface, you can see single-byte messages sent and received. However, when the application performs something interesting, you can see messages with a larger load.

BurpSuite has the ability to test WebSockets, which you can intercept and modify in real time, but WebSockets do not have Repeater, Scanner or Intruder capabilities. By default, to enable WebSocket Interception in BurpSuite, you only need to turn on Main Interception.

This way, you can retrieve intercepted WebSocket messages in the same way as HTTP. They can also be edited in the intercept window.

Edited messages can be viewed in the Web Sockets History tab.

Downgrading WebSocket to HTTP Method 1: Using Socket.io's HTTP fallback mechanism

A very strange point is that sometimes you can see messages similar to those in Websocket history in HTTP history. Recall that these interesting WebSocket messages need to solve scoreboard-related problems. The following figure shows the same response from the server, but this time in HTTP history. This shows that socket.io can send messages via WebSocket or HTTP.

Some of the parameter values passed in the observed requests were "websockets" and some were "polling." Presumably, HTTP is allowed to prevent Web Sockets from becoming unsupported or blocked in applications.

The socket.io documentation explains how "polling" and "websockets" are the two default transport options. It also shows how to disable polling by specifying WebSockets as the only transport. I think the reverse is also true, I can specify polling as the only transport mechanism.

https://socket.io/docs/client-api/#with-WebSocket-transport-only

By searching the socket.io.js source code, I found the following:

this.transports=n.transports||["polling","WebSocket"]

This line of code sets an internal variable called transports to the value passed in, or the default ["polling","websocket"] if the value passed in is false/empty. This fits well with our assumptions about polling and WebSocket default transmissions. Now change these defaults by setting match and replace rules under Proxy->Options in Burp and see what happens.

It worked! After adding the rule, refresh the page (you need to enable Burp's built-in rule "Require non-cached response" or perform a forced refresh) and the data is no longer communicated through WebSockets. That's a lot of progress, but what if the application you're using already offers a transport option that overrides our new defaults? In this case, we can modify the matching and substitution rules. The following rules should apply to different versions of the socket.io library, ignoring any transport specified by the application developer.

Here are the strings to use, and be sure to set them to regular expression matching:

this\.transports=.*?\. transports\|\|\["polling","websocket"]this.transports=["polling"] Method 2: Aborting Websocket Upgrade

Method 1 can only be used with socket.io and may be extended to other client libraries. However, the following approach should be more generic because it targets the WebSockets protocol itself.

After analysis, I found that WebSockets communicate over HTTP first in order to negotiate with the server and "upgrade" to WebSockets. The important parts are:

1) The client sends an upgrade request via some WebSocket specific header.

The server response status code is 101 Switching Protocols and WebSocket header.

3) The communication is converted to WebSocket, and HTTP is no longer used for this particular session.

Section 4.1 of the WebSockets RFC document provides various information on how to interrupt this workflow, and the following is an excerpt from https://tools.ietf.org/html/rfc6455#section-4.1 with an opinion attached.

1. If the status code received from the server is not 101, the client responds to HTTP[RFC2616]. In particular, the client may perform authentication when receiving a 401 status code; the server may redirect the client with a 3xx status code (but the client does not need to follow it), etc. Otherwise, follow the steps below.

2. If the response is missing an Upgrade header, or if the Upgrade header contains a value that does not match ASCII for "WebSocket," the client must close the WebSocket connection.

3. If the response is missing a Connection header, or if the Connection header contains a value that does not match ASCII for "WebSocket," the client must close the WebSocket connection.

4. If the response is missing the Sec-WebSocket-Accept header, or if the Sec-WebSocket-Accept header value is not a base64 encoded SHA-1 value of the string "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" concatenated by Sec-WebSocket-Key (as a string, not base64 decoded)(ignoring any leading and trailing spaces), the client must close the WebSocket connection.

5. If the response includes a Sec-WebSocket-Extensions header, and the extension required by the header is not present in the client handshake message (the server indicates an extension that is not requested by the client), the client must close the WebSocket connection. (The problem of parsing headers to determine which extensions are requested is discussed in Section 9.1.)

With these "connection must be closed" conditions in mind, I came up with the following set of substitution rules that should cover all five failure conditions:

Once these rules are applied, all WebSocket upgrade requests fail. Since socket.io cannot use HTTP by default, it has achieved the desired effect. Other libraries may behave differently and cause errors in the application you are testing. But our job is to make software do things it shouldn't!

The original response looks like this and causes the client and server to convert to WebSocket for communication.

Instead, the client receives this modified response from the server and closes the WebSocket connection.

One thing I encountered in my tests was that after adding these match and replace rules, the client was very persistent in retrying WebSocket connections and caused a lot of unnecessary traffic in my HTTP history. If you are working with socket.io library, the easiest way is to use method 1 above. If you have different libraries or other situations, you may need to add more rules to make the client server not support WebSockets.

Burp Repeater as Socket.io client

Since we enforce communication over HTTP rather than WebSockets, it's now possible to add custom matches and replace rules that would be applied to traffic already over WebSockets! Next, tools like Repeater, Intruder, and Scanner can be used, and these changes will be specific to the socket.io library. However, there were still two problems:

1. Each request has a session number, and any invalid request causes the server to terminate the session.

2. The body of each request has a calculated field that indicates the length of the message. If this is incorrect, the server will treat it as an invalid request and terminate the session.

Here are a few sample URLs used in applications.

/socket.io/? EIO=3&transport=polling&t=MJJR2dr/socket.io/? EIO=3&transport=polling&t=MJJZbUa&sid=iUTykeQQumxFJgEJAABL

The "sid" parameter in the URL represents a single connection flow to the server. If an invalid message is sent (which is common when trying to crack), the server closes the entire session, after which it must restart a new session.

The body of a given request contains a field that holds the number of bytes of the payload. This is similar to the "Content-Length"HTTP header, except that the value of this field is closer to socket.io. For example, if the payload you want to send is "hello," then the corresponding body will be "5: hello" and the value of the Content-Length header will be 7. where 5 represents the number of letters in the string "hello," and 7 represents the sum of the number of letters in the string "hello" and the number of letters in the string "5:" that socket.io adds to the body. As usual, Burp will update the Content-Length header for us, so we don't have to worry about it. However, I haven't found a good way to automatically calculate and contain payload lengths. Even more disturbing, I noticed that socket.io sends multiple messages in the same HTTP request. Since each message is an encapsulated WebSocket payload, and each message has its own length, it ends up looking like this: "5:hello,4:john,3:doe"(actual syntax may vary, just for demonstration purposes). Any error in calculating the length will be rejected as invalid by the server, and we'll have to start over.

This is an example of a body. This is the response in the Juice-Shop application, and the request is in the same format. Note that "215" here indicates the length of the payload after ":."

215:42["challenge solved",{"key":"zeroStarsChallenge","name":"Zero Stars","challenge":"Zero Stars (Give a devastating zero-star feedback to the store.) ","flag":"e958569c4a12e3b97f38bd05cac3f0e5a1b17142 ","hidden":false}] Macro

The first problem can be solved using the Burp macro. Basically, every time Burp matches when the server rejects a message, the macro will automatically establish a new session and update the original request with a valid "sid." Create a new macro by going to options->Sessions->Macros-> Add.

The URL to establish a new session simply omits the "sid" parameter. For example:

/socket.io/? EIO=3&transport=polling&t=MJJJ4Ku

The server response contains a brand new "sid" value for use.

Next, click the Configure item button and name the parameter sid. Then, select the "Extract from regex group" option and use the regular expression shown below.

"sid"\:"(.*?) "

The configuration window should look like this:

session handling rules

Now that we have a macro, we need a way to trigger it. This is where the Burp session handling rules come in. by

Project options->Sessions->Session Handling Rules->Add

Create a new rule action for Check session is valid:

Configure the new rule as follows:

Configure blackhillsinfosec to set the new rule action as follows: Finally, after completing the new rule action, you need to modify the scope of the rule. Here you can decide where to apply this rule. It is recommended to use it at least for Repeaters so that you can repeat requests manually.

Here's how I configure scope rules. You can be more specific about what you want, but the options below should apply to most situations.

This is a request made without session handling rules:

Here is the same request issued after the session handling rule takes effect:

The above is how to analyze and penetrate WebSocket and Socket.io. Xiaobian believes that some knowledge points may be seen or used in our daily work. I hope you can learn more from this article. For more details, please follow the industry information channel.

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Network Security

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report