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

Conversation

nstarke
Copy link
Contributor

@nstarke nstarke commented Dec 7, 2017

"ua-parser-js" is an npm module for parsing browser
user-agent strings. Vulnerable version of this module
have a problematic regular expression that can be exploited
to cause the entire application processing thread to "pause"
as it tries to apply the regular expression to the input.
This is problematic for single-threaded application environments
such as nodejs. The end result is a denial of service
condition for vulnerable applications, where no further
requests can be processed.

Verification

List the steps needed to make sure this thing works

  • Create a new directory for test application
  • copy below example server into test application directory as server.js
  • run npm i express to install express in the test application directory
  • 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
  • to test non-vulnerable versions of the module, run npm i ua-parser-js to install the latest version of ua-parser-js.
  • once all dependencies are installed, run the server with node server.js
  • Open up a new terminal
  • Start msfconsole
  • use auxiliary/dos/http/ua_parser_js_redos
  • run
  • In vulnerable installations, Module should have positive output and the test application should accept no further requests.
  • In non-vulnerable installations, module should have negative output and the test application should accept further requests.

Test 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!'))

"ua-parser-js" is an npm module for parsing browser
user-agent strings.  Vulnerable version of this module
have a problematic regular expression that can be exploited
to cause the entire application processing thread to "pause"
as it tries to apply the regular expression to the input.
This is problematic for single-threaded application environments
such as nodejs.  The end result is a denial of service
condition for vulnerable applications, where no further
requests can be processed.
@wvu
Copy link
Contributor

wvu commented Dec 7, 2017

He's back!

@h00die
Copy link
Contributor

h00die commented Dec 11, 2017

do you mind putting some docs together for this module? Template is here

@h00die
Copy link
Contributor

h00die commented Dec 11, 2017

Was this reported to the project? I dont see any obvious issues or PRs over there.

super(
'Name' => 'ua-parser-js npm module - Regular Expression Denial of Service',
'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.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you multi-line this please


def initialize
super(
'Name' => 'ua-parser-js npm module - Regular Expression Denial of Service',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we shorten this, maybe DoS or REDoS? Maybe also drop the - before the REDoS part as well?

},
'References' =>
[
['CWE', '400'],
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As per comment in PR, was the project notified of the issue? That would give something to point back to.


def test_service_unresponsive
begin
print_status("Testing for service unresponsiveness.")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

trade " for '

})

if res.nil?
print_good("Service not responding.")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no need for interpolation

print_error("Service responded with a valid HTTP Response; ReDoS attack failed.")
end
rescue ::Rex::ConnectionRefused
print_error("An unknown error occurred.")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no need for interpolation

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.")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no need for interpolation


def test_service
begin
print_status("Testing Service to make sure it is working.")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no need for interpolation

})

if !res.nil? && (res.code == 200 || res.code == 404)
print_status("Test request successful, attempting to send payload")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no need for interpolation

end

def run
if !test_service
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe we prefer unless instead of if !

@h00die h00die self-assigned this Dec 11, 2017
A few things were changed as per the PR comments:
1) The module title was reworded
2) The module description was multi-lined
3) Negative logic was rewritten to use 'unless'
4) Strings which did not require interpolation were rewritten
5) Documentation markdown was added.
@nstarke
Copy link
Contributor Author

nstarke commented Dec 11, 2017

This issue was indeed reported to the project, but it was done privately. I added a link to the commit in the references section.

@h00die
Copy link
Contributor

h00die commented Dec 13, 2017

image

@h00die
Copy link
Contributor

h00die commented Dec 13, 2017

Against the test app on ubuntu 16.04 with the vuln code:

Good

msf auxiliary(dos/http/ua_parser_js_redos) > run

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

success.

not vuln

msf auxiliary(dos/http/ua_parser_js_redos) > run

[*] Testing Service to make sure it is working.
[*] Test request successful, attempting to send payload
[*] Sending ReDoS request to 192.168.2.137:3000.
[-] Auxiliary aborted due to failure: unknown: ReDoS request unsuccessful. Received status 200 from 192.168.2.137:3000.
[*] Auxiliary module execution completed

success.

python simplehttpserver

msf auxiliary(dos/http/ua_parser_js_redos) > set rport 8000
rport => 8000
msf auxiliary(dos/http/ua_parser_js_redos) > run

[*] Testing Service to make sure it is working.
[*] Test request successful, attempting to send payload
[*] Sending ReDoS request to 192.168.2.137:8000.
[-] Auxiliary aborted due to failure: unknown: ReDoS request unsuccessful. Received status 200 from 192.168.2.137:8000.
[*] Auxiliary module execution completed

success.

@h00die
Copy link
Contributor

h00die commented Dec 13, 2017

a few quick changes, and this will be good to go tomorrow!

@h00die h00die added docs and removed needs-docs labels Dec 13, 2017
@nstarke
Copy link
Contributor Author

nstarke commented Dec 13, 2017

What changes specifically need to be made at this point? I'm a bit confused. Sorry.

@h00die
Copy link
Contributor

h00die commented Dec 13, 2017

I left 2 comments, one about the spaces before => and the other about slightly rearranging the docs

@nstarke
Copy link
Contributor Author

nstarke commented Dec 13, 2017

I'm sorry, I don't see the two comments you are referring to anywhere in the pull request. What am I doing wrong? Is there something I need to do in order to be able to see your comments?

@h00die
Copy link
Contributor

h00die commented Dec 13, 2017

Strange, I had the same thing happen where one tab showed the comments, and another one didn't. Regardless, I linked to the comments. Shouldn't take but 5min, and I'll land in a few hours!

@nstarke
Copy link
Contributor Author

nstarke commented Dec 13, 2017

I'm really sorry to sound like a broken record, but I still cannot see the comments you have left. The links you provided don't direct me anywhere in the source tab. I've tried two computers locally and had a colleague on the other side of the country try as well, and we have not been able to see your comments. I'm really not sure what is going on at this point. I apologize for the inconvenience.

@h00die
Copy link
Contributor

h00die commented Dec 13, 2017

UGHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH.
my fault.
they're still pending. 2 seconds.

or `getResult` functions will be vulnerable to this module. An example server is provided
below.

```
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lets change this to be more of a 'how to install' to include that example. See https://github.com/rapid7/metasploit-framework/blob/master/documentation/modules/auxiliary/scanner/gopher/gopher_gophermap.md#ubuntu-1604-install as an example. All the info is captured here though, just a little re-arranging

7. Open up a new terminal.
8. Start msfconsole.
9. `use auxiliary/dos/http/ua_parser_js_redos`.
10. `set RHOSTS <IP>`.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rhost not rhosts


def initialize
super(
'Name' => 'ua-parser-js npm module ReDoS',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getting kinda nit picky here but can we tighten up the spaces before the =>? i think 2 or 3 could be removed and everything still spaced out nicely

There were several formatting and layout issues
that are fixed in this commit.  Also changing
`RHOSTS` to `RHOST`.
@h00die h00die merged commit dd5532c into rapid7:master Dec 14, 2017
h00die added a commit that referenced this pull request Dec 14, 2017
@h00die
Copy link
Contributor

h00die commented Dec 14, 2017

had 2 minor edits: 544e4e3 but just ninja patched it to prevent that from being a hangup.

Thanks for the module, everything looked pretty good from the beginning, so easy land!

@h00die
Copy link
Contributor

h00die commented Dec 14, 2017

Release Notes

This PR adds a DoS module against ua_parser_js which is an npm module which has a problematic regex.

@nstarke
Copy link
Contributor Author

nstarke commented Dec 15, 2017

Thanks for landing this. As always, it was a pleasure!

@tdoan-r7 tdoan-r7 added the rn-enhancement release notes enhancement label Dec 20, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
docs module rn-enhancement release notes enhancement
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants