Object Writeup - HackTheBox
Object is a Windows Domain Controller running Jenkins with open self-registration. The chain runs: Jenkins remote build trigger for arbitrary command execution, offline decryption of a Jenkins-stored credential to get a WinRM shell as oliver, then three consecutive ACL abuses discovered via BloodHound: ForceChangePassword onto smith, GenericWrite onto maria abused via logon script injection to exfiltrate a spreadsheet with credentials, and WriteOwner on Domain Admins to add maria directly.
Reconnaissance
Port scan
PORT STATE SERVICE VERSION 80/tcp open http Microsoft IIS httpd 10.0 |_http-title: Mega Engines 5985/tcp open http Microsoft HTTPAPI httpd 2.0 8080/tcp open http Jetty 9.4.43.v20210629 | http-robots.txt: 1 disallowed entry |_/
IIS on 80 served a static page with nothing useful. WinRM on 5985. Jetty on 8080 with a robots.txt disallowing everything strongly implies Jenkins. Directory brute force on 8080 confirms it:
login [Status: 200, Size: 2120] signup [Status: 200, Size: 7937] git [Status: 302, Size: 0] cli [Status: 302, Size: 0]
Jenkins. Self-registration is enabled at /signup.
Initial Access
Jenkins remote build trigger to RCE
Register a throwaway account via /signup, then create a new job. Under Configure:
Build Triggers: enable "Trigger builds remotely", set token
test.Build: add "Execute Windows batch command", start with
whoami.
Generate an API token under the account's user configuration. This token is the credential for triggering builds remotely.
Trigger the build:
Started by remote host $ATTACKER_IP Running as SYSTEM Building in workspace C:\Users\oliver\AppData\Local\Jenkins\.jenkins\workspace\test
Read the console output at /job/test/1/console:
object\oliver Finished: SUCCESS
The "Running as SYSTEM" line reflects the Jenkins master process identity. The actual OS execution context of the build step is object\oliver. Arbitrary Windows batch command execution is confirmed. Rather than immediately popping a reverse shell, the build step was used as an enumeration primitive by editing the command and re-triggering the job each time.
Jenkins credential extraction
The Jenkins home directory layout exposes credential material. The admin user's config.xml held a stored credential:
<com.cloudbees.plugins.credentials.impl.UsernamePasswordCredentialsImpl>
<username>oliver</username>
<password>{AQAAABAAAAAQqU+m+mC6ZnLa0+yaanj2eBSbTk+h4P5omjKdwV17vcA=}</password>
</com.cloudbees.plugins.credentials.impl.UsernamePasswordCredentialsImpl>
The encrypted blob needs master.key and hudson.util.Secret to decrypt. Dump master.key directly:
hudson.util.Secret is binary, so base64-encode it before reading:
Decode both locally and run offline decryption with jenkins_offline_decrypt.py from https://github.com/gquere/pwn_jenkins:
$OLIVER_PASSWORD
WinRM as oliver
Shell as oliver. Check what else is running internally before moving further:
TCP 0.0.0.0:88 LISTENING TCP 0.0.0.0:389 LISTENING TCP 0.0.0.0:445 LISTENING TCP 0.0.0.0:636 LISTENING TCP 0.0.0.0:3268 LISTENING TCP 0.0.0.0:9389 LISTENING <snip - ephemeral RPC ports and IPv6/loopback DNS>
Kerberos, LDAP, Global Catalog, and AD Web Services are all listening locally but were not exposed in the external scan. This host is the domain controller for object.local. Other user profiles (smith, maria, Administrator) are visible under C:\Users. This is an AD enumeration job for BloodHound.
BloodHound and ACL Abuse Chain
Collect AD data
SharpHound Enumeration Completed at 9:07 AM on 7/31/2026! Happy Graphing!
oliver to smith via ForceChangePassword
BloodHound shows oliver holds ForceChangePassword over smith. Reset smith's password with PowerView:
Smith is a member of Remote Management Users, so the reset immediately grants WinRM access. The next BloodHound analysis run as smith shows GenericWrite over maria.
smith to maria via GenericWrite - logon script injection
GenericWrite allows writing arbitrary attributes to the target object including servicePrincipalName and scriptPath. A targeted Kerberoast was attempted first: a fake SPN was set on maria and a TGS was requested, but every ticket came back with UNKNOWN in the username field rather than maria, making the hash uncrackable. That path was dropped.
scriptPath sets the logon script executed when maria authenticates. Outbound connections beyond ICMP were firewalled, confirmed with a ping callback:
IP $TARGET_IP > $ATTACKER_IP: ICMP echo request, id 1, seq 1104
Script execution on maria's logon cycle confirmed. Since a reverse shell was not viable, write command output to a file in C:\programdata and read it back through the existing WinRM session:
Directory: C:\users\maria\desktop
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 10/26/2021 8:13 AM 6144 Engines.xls
Engines.xls on maria's desktop is worth pulling. Use the logon script to copy it to C:\programdata, then download it:
Engines.xls credential extraction
The spreadsheet holds engine inventory data with a "Chamber Username" and "Chamber Password" column. Three rows for maria with three different passwords. Save them to a file and spray WinRM:
WINRM $TARGET_IP [-] object.local\maria:$MARIA_PASSWORD_1 WINRM $TARGET_IP [-] object.local\maria:$MARIA_PASSWORD_2 WINRM $TARGET_IP [+] object.local\maria:$MARIA_PASSWORD (Pwn3d!)
(Pwn3d!) means maria also has local admin rights on the box.
Privilege Escalation
WriteOwner on Domain Admins
BloodHound shows maria holds WriteOwner over the Domain Admins group. The path to Domain Admin is three steps: take ownership of the group object, grant maria full control ACL over it, then add maria as a member.
Confirm membership:
Global Group memberships *Domain Admins *Domain Users The command completed successfully.
The existing WinRM session still holds the old Kerberos token. Open a fresh session to pick up the new group membership:
Group Name SID ============================================= ============================================= BUILTIN\Administrators S-1-5-32-544 OBJECT\Domain Admins S-1-5-21-4088429403-1159899800-2753317549-512 Mandatory Label\High Mandatory Level S-1-16-12288
Domain Admin with a High Mandatory Level token. Root reached.