Enumeration with NetExec and WinRM
What this page covers
Section titled “What this page covers”After obtaining initial credentials on an HTB Windows / Active Directory machine, this guide walks through what to check with NetExec and where to investigate after connecting through WinRM.
It covers:
- Initial SMB, LDAP, and WinRM checks with
netexec/nxc. - How to assess shares, users, groups, computers, and WinRM candidates.
- Manual enumeration of the user, host, files, credentials, domain, and shares after connecting through WinRM.
- Using winPEAS to catch gaps in manual enumeration.
Related pages cover individual mechanisms such as AD ACLs and Kerberos delegation in more detail.
- Active Directory Access Control and BloodHound Edges
- Kerberos (Active Directory Authentication)
- Kerberos Delegation and SPN Abuse
- NTLM Relay and Coercion
- Local Windows Credentials
Set the initial values
Section titled “Set the initial values”First, establish name resolution for the target, domain, and DC.
<dc-ip> <dc-fqdn> <domain> <hostname>Example:
10.129.13.247 DC01.checkpoint.htb checkpoint.htb DC01The following command examples use these values.
DC='<dc-fqdn-or-ip>'DC_FQDN='<dc-fqdn>'DOMAIN='<domain>'REALM='<DOMAIN uppercase>'USER='<user>'PASS='<password>'HASH='<nt-hash>'For Kerberos, prefer an FQDN over an IP address. Even if /etc/hosts resolves the name, obtaining a TGT or TGS may fail if MIT Kerberos has the wrong realm mapping.
What to learn from a port scan
Section titled “What to learn from a port scan”Before using netexec, read the Nmap output for clues about the environment.
| Observation | Interpretation |
|---|---|
53, 88, 389, 445, 464, 3268, 9389 | Evidence that the host is a Domain Controller (DC). |
Domain: <domain> on 389 / 3268 | LDAP / Global Catalog has revealed the domain name. |
5985/tcp open | WinRM is worth trying once you have valid credentials. The port alone does not imply authorization. |
smb2-security-mode: signing required | Prioritize authenticated SMB/LDAP/Kerberos enumeration instead of SMB relay as the main path. |
Large clock-skew | Can cause failures in Kerberos, BloodHound/RustHound, and TGS requests. |
LDAP signing / channel binding / NTLM:False | Clues about LDAP authentication, relay feasibility, and whether Kerberos is required. |
If Kerberos returns KRB_AP_ERR_SKEW, align the attack machine’s clock with the target. HTB notes include use of ntpdate <dc-fqdn> and faketime.
sudo ntpdate "$DC_FQDN"
FAKETIME_DONT_FAKE_MONOTONIC=1 faketime '+7 hours' <kerberos-command>What to check first with NetExec
Section titled “What to check first with NetExec”SMB / LDAP / WinRM reachability
Section titled “SMB / LDAP / WinRM reachability”After obtaining initial credentials, examine authentication and authorization separately for each protocol.
netexec smb "$DC" -d "$DOMAIN" -u "$USER" -p "$PASS"netexec ldap "$DC" -d "$DOMAIN" -u "$USER" -p "$PASS"netexec winrm "$DC" -d "$DOMAIN" -u "$USER" -p "$PASS"Distinguish these results:
| Result | Meaning |
|---|---|
SMB + | The credentials were accepted by SMB. This does not necessarily mean a share is readable. |
LDAP + | The credentials can be used for authenticated AD object enumeration. This does not imply WinRM access. |
WinRM + / Pwn3d! | A candidate for a WinRM shell. Obtain a prompt with evil-winrm to confirm it. |
WinRM - | Does not necessarily mean the password is wrong. Suspect insufficient authorization, such as lack of Remote Management Users membership, or an authentication mechanism mismatch. |
STATUS_NOT_SUPPORTED with NTLM:False | NTLM is probably disabled or unavailable. Switch to Kerberos. |
No Kerberos credentials available | A ccache selection problem, not a password problem. |
In Puppy, Pirate, and Checkpoint, SMB/LDAP succeeded but the initial user could not use WinRM. In Administrator, the initial user could use WinRM too. Check separately that port 5985 is open, the credentials are valid, and the account is authorized for WinRM.
When using a Kerberos cache
Section titled “When using a Kerberos cache”If NTLM is unavailable, the account belongs to Protected Users, or you want to use Kerberos explicitly, obtain a TGT and use the cache.
getTGT.py "$DOMAIN/$USER:$PASS" -dc-ip '<dc-ip>'export KRB5CCNAME="$USER.ccache"
netexec smb "$DC_FQDN" --use-kcache --shares --rid-brutenetexec ldap "$DC_FQDN" --use-kcache --users --groups --computersnetexec winrm "$DC_FQDN" --use-kcacheWinRM / WSMan uses a TGS for the HTTP/<host> service principal (SPN). A TGT shows that the user authenticated to the KDC; it is not the ticket presented directly to the WinRM server. For KRB_AP_ERR_MODIFIED, check the hostname, SPN, realm, and ccache.
Check krb5.conf and tickets
Section titled “Check krb5.conf and tickets”Adding an FQDN to /etc/hosts alone may not tell MIT Kerberos that <dc-fqdn> belongs to the correct realm. If needed, create a target-specific krb5.conf and specify it with KRB5_CONFIG.
For temporary use, put it under /tmp.
cat > /tmp/krb5-example.conf <<'EOF'[libdefaults] default_realm = EXAMPLE.HTB dns_lookup_realm = false dns_lookup_kdc = false rdns = false udp_preference_limit = 1
[realms] EXAMPLE.HTB = { kdc = dc01.example.htb admin_server = dc01.example.htb }
[domain_realm] .example.htb = EXAMPLE.HTB example.htb = EXAMPLE.HTB dc01.example.htb = EXAMPLE.HTBEOF
export KRB5_CONFIG=/tmp/krb5-example.confObtain a TGT and explicitly select the ccache to use.
getTGT.py 'example.htb/<user>:<password>' -dc-ip '<dc-ip>'export KRB5CCNAME='<user>.ccache'klistCheck that you can obtain a TGS for the service principal you will actually use, not just a TGT.
KRB5_TRACE=/dev/stderr kvno "ldap/dc01.example.htb"KRB5_TRACE=/dev/stderr kvno "cifs/dc01.example.htb"KRB5_TRACE=/dev/stderr kvno "http/dc01.example.htb"klist| SPN | Main use |
|---|---|
ldap/<dc-fqdn> | LDAP queries, BloodHound / RustHound, and ADSI tools. |
cifs/<dc-fqdn> | SMB shares and netexec smb --use-kcache. |
http/<dc-fqdn> | WinRM / WSMan and Evil-WinRM Kerberos. |
If a TGS request resolves to another realm or cannot find the KDC, check [domain_realm], default_realm, and which file KRB5_CONFIG references.
For a persistent setup, place each target’s configuration under ~/.config/krb5/.
mkdir -p ~/.config/krb5 ~/.ccache
cat > ~/.config/krb5/example.htb.conf <<'EOF'[libdefaults] default_realm = EXAMPLE.HTB dns_lookup_realm = false dns_lookup_kdc = false rdns = false udp_preference_limit = 1
[realms] EXAMPLE.HTB = { kdc = dc01.example.htb admin_server = dc01.example.htb }
[domain_realm] .example.htb = EXAMPLE.HTB example.htb = EXAMPLE.HTB dc01.example.htb = EXAMPLE.HTBEOFAliases in ~/.zshrc or ~/.bashrc make it easy to switch Kerberos configuration and ccaches for each box.
alias krb_example='export KRB5_CONFIG=$HOME/.config/krb5/example.htb.conf; export KRB5CCNAME=$HOME/.ccache/example.htb.ccache; klist'alias krb_off='unset KRB5_CONFIG KRB5CCNAME'Reload the profile.
source ~/.zshrc# Orsource ~/.bashrcUsage:
krb_example
# If kinit is available, create the TGT directly at the KRB5CCNAME locationkinit '<user>@EXAMPLE.HTB'klistWith Impacket’s getTGT.py, move the resulting ccache to the location referenced by the alias.
getTGT.py 'example.htb/<user>:<password>' -dc-ip '<dc-ip>'mv '<user>.ccache' "$HOME/.ccache/example.htb.ccache"krb_example
netexec ldap dc01.example.htb --use-kcache --users --groupsnetexec smb dc01.example.htb --use-kcache --sharesevil-winrm -i dc01.example.htb -r example.htb --ccache "$HOME/.ccache/example.htb.ccache"
krb_offDo not put passwords in the profile. Store only the switches for KRB5_CONFIG and KRB5CCNAME.
When using an NT hash
Section titled “When using an NT hash”When you obtain an NT hash, test it with both SMB and WinRM first.
netexec smb "$DC" -d "$DOMAIN" -u "$USER" -H "$HASH" --sharesnetexec winrm "$DC" -d "$DOMAIN" -u "$USER" -H "$HASH"
evil-winrm-py -i "$DC" -u "$USER" -H "$HASH"Quote machine accounts and gMSAs so the shell does not expand the trailing $.
evil-winrm-py -i "$DC" -u 'gMSA_ADCS_prod$' -H '<nt-hash>'When authenticating with a local account
Section titled “When authenticating with a local account”Use --local-auth only when testing a standalone host or local administrator credentials. Do not confuse it with normal domain authentication on a Domain Controller.
netexec smb '<host>' -u '<local-user>' -p '<password>' --local-authnetexec winrm '<host>' -u '<local-user>' -p '<password>' --local-authBasic initial enumeration
Section titled “Basic initial enumeration”SMB shares and RIDs
Section titled “SMB shares and RIDs”netexec smb "$DC" -d "$DOMAIN" -u "$USER" -p "$PASS" --shares --rid-bruteREAD on IPC$, NETLOGON, and SYSVOL is a normal baseline for a domain user. Prioritize shares with business-related names such as DEV, Logs, IT, VMBackups, DevDrop, or Finance.
smbclient "//$DC_FQDN/<share>" -U "$DOMAIN/$USER%$PASS" -m SMB3With an NT hash:
smbclient "//$DC_FQDN/<share>" \ -U "$DOMAIN/$USER%$HASH" \ --pw-nt-hash -m SMB3Seeing a share name does not mean you can read it. In Puppy, DEV was visible initially, but READ became available only after changing group membership. In Checkpoint, VMBackups was visible but unreadable to the initial user; it became useful after retrying as svc_deploy, a member of BackupAccess.
Once inside an SMB share, specify paths relative to the share root. When connected to //dc/IT, the remote path is already equivalent to C:\IT; specifying C:\IT\... may produce NT_STATUS_OBJECT_NAME_INVALID.
Inspect users, groups, and computers with LDAP
Section titled “Inspect users, groups, and computers with LDAP”netexec ldap "$DC" -d "$DOMAIN" -u "$USER" -p "$PASS" \ --users --groups --computersStart with:
| Object | Points to inspect |
|---|---|
| users | svc_*, *_adm, job titles, descriptions, password hints, and disabled status. |
| groups | Remote Management Users, business groups, and Backup/Recovery/IT/WSUS/Certificate-related groups. |
| computers | Servers besides the DC, pre-created computer accounts, and machine accounts. |
| gMSA | msDS-GroupMSAMembership and membership in Remote Management Users. |
| descriptions | Passwords, operational notes, owners, and clues about share names. |
Narrow down candidate users with an LDAP query.
netexec ldap "$DC" -d "$DOMAIN" -u "$USER" -p "$PASS" \ --query "(sAMAccountName=<target-user>)" \ "sAMAccountName memberOf description userAccountControl servicePrincipalName"Membership in Remote Management Users indicates a WinRM candidate; it does not prove that a password or hash is valid. A name such as BackupAccess does not necessarily imply SeBackupPrivilege either. In Checkpoint, svc_deploy belonged to BackupAccess, but whoami /all showed no SeBackupPrivilege; its actual benefit was READ on the VMBackups share.
Kerberoasting and AS-REP Roasting candidates
Section titled “Kerberoasting and AS-REP Roasting candidates”Trying AS-REP roasting or Kerberoasting with initial credentials can be worthwhile, but finding no candidates is common.
netexec ldap "$DC" -d "$DOMAIN" -u "$USER" -p "$PASS" --asreproast asrep.hashesnetexec ldap "$DC" -d "$DOMAIN" -u "$USER" -p "$PASS" --kerberoasting kerberoast.hashesOr with Impacket:
GetNPUsers.py "$DOMAIN/$USER:$PASS" -dc-ip '<dc-ip>' -request -outputfile asrep.hashesGetUserSPNs.py "$DOMAIN/$USER:$PASS" -dc-ip '<dc-ip>' -request -outputfile kerberoast.hashesTargeted Kerberoasting is more than enumeration: it requires an ACL that permits temporarily changing the target’s servicePrincipalName. See the AD ACL guide for details.
Read relationships with BloodHound / RustHound
Section titled “Read relationships with BloodHound / RustHound”rusthound-ce -d "$DOMAIN" -f "$DC_FQDN" -u "$USER" -p "$PASS" -c All --zipTreat BloodHound as a map of LDAP attributes, group memberships, ACLs, SPNs, and delegation, not merely a tool for clicking the shortest path.
Separate these elements when reviewing a finding:
- Principal: Who holds the right.
- Object: Which user, group, computer, OU, or domain the right applies to.
- Edge: What
GenericAll,GenericWrite,WriteOwner,AddMember,WriteSPN, and similar edges mean. - Actual operation: Password reset, adding a group member or SPN, adding shadow credentials, and so on.
- Readback: Whether LDAP / SMB / WinRM confirms the change afterward.
If BloodHound fails after falling back to LDAPS, the root cause may be Kerberos clock skew or realm resolution rather than LDAPS itself. In Checkpoint, clock skew caused Kerberos failure, fallback to NTLM LDAP, rejection by LDAP signing, and then an LDAPS reset.
Log in with WinRM
Section titled “Log in with WinRM”Connection methods
Section titled “Connection methods”Password:
evil-winrm -i "$DC_FQDN" -u "$USER" -p "$PASS"Kerberos:
export KRB5CCNAME="$USER.ccache"evil-winrm -i "$DC_FQDN" -r "$DOMAIN" --ccache "$USER.ccache"NT hash:
evil-winrm-py -i "$DC_FQDN" -u "$USER" -H "$HASH"After connecting, record the user and host shown in the prompt. Do not stop at a successful netexec winrm result; confirm the actual remote PowerShell prompt.
Separate failure causes by layer
Section titled “Separate failure causes by layer”| Symptom | First things to suspect |
|---|---|
SMB/LDAP is +, WinRM is - | Missing WinRM authorization, lack of Remote Management Users membership, or a WinRM authentication mechanism mismatch. |
No Kerberos credentials available | Missing KRB5CCNAME or --ccache selection. |
KRB_AP_ERR_SKEW | Time difference between the attack box and DC. |
KRB_AP_ERR_MODIFIED | SPN, hostname, realm, or service ticket mismatch. For WinRM, consider HTTP/<host>. |
STATUS_NOT_SUPPORTED | NTLM unavailable. Use a Kerberos cache. |
Enumeration after connecting through WinRM
Section titled “Enumeration after connecting through WinRM”User identity and token
Section titled “User identity and token”First, establish the current security context.
whoamiwhoami /upnwhoami /allwhoami /groupswhoami /privhostnamePoints to examine:
| Observation | Interpretation |
|---|---|
BUILTIN\Remote Management Users | Explains why WinRM works; it does not itself grant administrator privileges. |
SeImpersonatePrivilege / SeAssignPrimaryTokenPrivilege | Potential token abuse paths. |
SeBackupPrivilege / SeRestorePrivilege | Potential backup reads past NTFS ACLs, or hive / NTDS acquisition. |
SeDebugPrivilege | Potential process memory / credential access. |
Only privileges such as SeMachineAccountPrivilege, SeChangeNotifyPrivilege, and SeIncreaseWorkingSetPrivilege | Prioritize files, shares, AD, and credentials rather than immediate local privilege escalation. |
| Custom group | Check whether it grants operational access to shares, applications, certificates, WSUS, backups, or similar resources. |
Always retain the whoami /all output. In Puppy’s final session, I did not record it, making it difficult to explain the final account’s exact groups and privileges afterward.
Basic host information
Section titled “Basic host information”systeminfoipconfig /allroute printnetstat -anoServices and tasks:
Get-CimInstance Win32_Service | Select-Object Name,StartName,State,PathName
Get-ScheduledTask | Select-Object TaskName,TaskPath,StateProcesses:
Get-Process -ErrorAction SilentlyContinue | Select-Object ProcessName,Id,PathThe aim here is to find run accounts, paths, writable directories, configuration files, and credentials rather than memorize everything.
Narrow filesystem searches from the root
Section titled “Narrow filesystem searches from the root”Do not recurse through all of C:\ immediately. First inspect the root directory.
Get-ChildItem -LiteralPath 'C:\' -ForceThen investigate only the promising candidates.
$targets = @( 'C:\Backups', 'C:\inetpub', 'C:\IT', 'C:\Logs', 'C:\Shares', 'C:\StorageReports', 'C:\ProgramData') | Where-Object { Test-Path $_ }
if ($targets) { Get-ChildItem -LiteralPath $targets -Force -ErrorAction SilentlyContinue Get-ChildItem -LiteralPath $targets -Recurse -Force -ErrorAction SilentlyContinue}Names to prioritize:
| Name | Significance |
|---|---|
Backups, VMBackups, NightlyBackup | Possible configuration, old databases, snapshots, memory dumps, or NTDS / hives. |
inetpub, wwwroot, app directory | Web configuration, LDAP bind passwords, or connection strings. |
IT, Dev, DevDrop, Logs | Operational shares, deployment artifacts, scripts, or leaked credentials. |
StorageReports, WSUSTemp, vendor directory | Traces of nonstandard operations. |
| User profile archive | Possible DPAPI data, SSH keys, browser data, or Credential Manager data. |
Limit the search scope before searching.
if ($targets) { Get-ChildItem -LiteralPath $targets -Recurse -Force -ErrorAction SilentlyContinue | Where-Object { -not $_.PSIsContainer } | Where-Object { $_.Length -lt 5MB } | Select-String -Pattern 'password|passwd|pwd|secret|token|credential|ldap|bind|backup|winrm' ` -ErrorAction SilentlyContinue}Also limit searches for flags or known filenames.
Get-ChildItem -LiteralPath 'C:\Users' -Filter 'root.txt' -Recurse -Force -ErrorAction SilentlyContinueUser profiles and history
Section titled “User profiles and history”Get-ChildItem -LiteralPath 'C:\Users' -Force
$userDirs = @( "$env:USERPROFILE\Desktop", "$env:USERPROFILE\Documents", "$env:USERPROFILE\Downloads") | Where-Object { Test-Path $_ }
if ($userDirs) { Get-ChildItem -LiteralPath $userDirs -Force -ErrorAction SilentlyContinue}PowerShell history:
Get-Content (Get-PSReadLineOption).HistorySavePath -ErrorAction SilentlyContinueIn Evil-WinRM, ls is an alias for Get-ChildItem and type is an alias for Get-Content. Rewrite commands you intend to reuse with their full names and -LiteralPath.
Get-Content -LiteralPath 'C:\Users\<user>\Desktop\user.txt'Credential Manager and DPAPI
Section titled “Credential Manager and DPAPI”First, check credentials saved by the current user.
cmdkey /list
Get-ChildItem "$env:APPDATA\Microsoft\Credentials" ` -Force -ErrorAction SilentlyContinue
Get-ChildItem "$env:LOCALAPPDATA\Microsoft\Credentials" ` -Force -ErrorAction SilentlyContinue
Get-ChildItem "$env:APPDATA\Microsoft\Protect" ` -Recurse -Force -ErrorAction SilentlyContinueIf other users’ profiles or archived profiles are readable, look for the following set:
AppData\Roaming\Microsoft\CredentialsAppData\Local\Microsoft\CredentialsAppData\Roaming\Microsoft\Protect\<SID>AppData\Local\Microsoft\VaultDPAPI requires more than the Credential Blob.
Credential Blob | | Referenced Master Key GUID vProtect\<SID>\<MasterKeyGUID> | | Decrypt using the user password + SID, or a domain backup key, etc. vdecrypted masterkey | vDecrypt the Credential BlobIn Puppy, winPEAS showed the relationship between Credentials and Protect\<SID>, and the Master Key was decrypted with the user’s password and SID. In Voleur, both Credentials and Protect\<SID> were recovered from an archived profile, and the SID was confirmed from a deleted AD object.
Inspect domain information from the host
Section titled “Inspect domain information from the host”After connecting through WinRM, you can still verify AD facts through LDAP, the AD module, or ADSI.
[System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
$root = [ADSI]'LDAP://RootDSE'$root.defaultNamingContextCurrent user’s group memberships:
$searcher = [adsisearcher]"(sAMAccountName=$env:USERNAME)"$result = $searcher.FindOne()$result.Properties.memberofFind users:
$searcher = New-Object DirectoryServices.DirectorySearcher$searcher.Filter = '(objectClass=user)'$searcher.PageSize = 1000$searcher.PropertiesToLoad.AddRange(@( 'samaccountname', 'distinguishedname', 'memberof', 'useraccountcontrol', 'description', 'serviceprincipalname'))
$searcher.FindAll() | ForEach-Object { $_.Properties }If the AD module is available:
Import-Module ActiveDirectory
Get-ADUser '<user>' -Properties MemberOf,Description,ServicePrincipalName,UserAccountControl | Select-Object SamAccountName,Enabled,Description,MemberOf,ServicePrincipalName,UserAccountControl
Get-ADPrincipalGroupMembership '<user>' | Select-Object Name,DistinguishedNameEdges shown by BloodHound ultimately correspond to facts such as LDAP attributes, ACEs, group memberships, SPNs, and delegation. Do not rely on the graph alone; corroborate it with readable attributes and the results of actual operations.
Recheck shares
Section titled “Recheck shares”After obtaining a new user, hash, or group membership, enumerate SMB shares again from the attack host.
netexec smb "$DC_FQDN" -d "$DOMAIN" -u "$USER" -p "$PASS" --sharesnetexec smb "$DC_FQDN" -d "$DOMAIN" -u "$USER" -H "$HASH" --sharesFrom inside the WinRM target:
$dc = '<dc-fqdn>'net view "\\$dc"net useShare ACLs behave differently with the current credentials. A share that denied access to the initial user may be readable with another user, group, or hash.
Use winPEAS to check for gaps
Section titled “Use winPEAS to check for gaps”Use winPEAS as a baseline for spotting gaps in manual enumeration, rather than as a way to find the answer from the outset.
.\winPEASx64.exe *> C:\Windows\Temp\winpeas.txtCompare these observations:
| Observation | Manual check | winPEAS check | Next decision |
|---|---|---|---|
| Nonstandard directories at the root | C:\Backups, C:\IT, etc. | Any additional candidates? | Limit further investigation to relevant paths. |
| Credential Manager / DPAPI | Credentials, Protect\<SID> | Relationship between Master Key and Blob | Check whether the material needed to decrypt them is available. |
| PowerShell history | History path | Any missed entries? | Check for leaked commands or passwords. |
| Services / scheduled tasks | Service path, run account | Candidate writable paths | Check execution context and ACLs. |
| Writable path | Manual ACL / file write | Additional candidates | Check whether the path is executed. |
| Token privileges | whoami /all | Potential privileges | Do not confuse share access with token privileges. |
Living off the Land (LOTL) is not necessarily undetectable. PowerShell command lines, Script Block Logging, AMSI, rapid large-scale file enumeration, WMI/CIM enumeration, access to credential directories, and processes under the WinRM provider host may all be monitored. Focus on what you read and how widely you searched, rather than whether you used a large tool or manual commands.
Obtain the same facts through multiple layers
Section titled “Obtain the same facts through multiple layers”It helps to verify a fact at three layers: a dedicated tool, standard OS facilities, and LDAP / APIs.
Example: a user’s group memberships.
NetExec:
netexec ldap "$DC" -d "$DOMAIN" -u "$USER" -p "$PASS" \ --query "(sAMAccountName=<user>)" "sAMAccountName memberOf"Standard Windows tools:
whoami /groupsAD module:
Get-ADPrincipalGroupMembership '<user>' | Select-Object Name,DistinguishedNameADSI:
$searcher = [adsisearcher]'(sAMAccountName=<user>)'$searcher.FindOne().Properties.memberofBloodHound, bloodyAD, and Impacket are powerful, but underneath they use LDAP, Kerberos, SMB, RPC, WinRM, and Windows APIs. Relate the facts you want to observe to the protocols and APIs used, rather than focusing on tool names.
| Goal | Common tools | Alternative route |
|---|---|---|
| Enumerate SMB shares | netexec smb --shares | smbclient, net view |
| LDAP users / groups | netexec ldap --users --groups | ADSI, Get-ADUser, ldapsearch |
| Kerberos TGT/TGS | getTGT.py, GetUserSPNs.py | kinit, kvno, Rubeus |
| AD graph | BloodHound / RustHound | LDAP query, AD module, ADSI |
| Change LDAP attributes | bloodyAD | Set-ADUser, ADSI, ldapmodify |
| WinRM shell | Evil-WinRM | PowerShell Remoting, Metasploit WinRM module |
Observations from writeups
Section titled “Observations from writeups”| Box | Observation | General lesson |
|---|---|---|
| Puppy | Port 5985 was open, but Remote Management Users membership and the Kerberos ccache had to be checked separately. An LDAP bind password in C:\Backups led to another credential through DPAPI. | Separate WinRM authentication from authorization. After gaining a foothold, search nonstandard root directories, backups, and DPAPI data within a narrow scope. |
| Checkpoint | svc_deploy belonged to Remote Management Users and BackupAccess. It lacked SeBackupPrivilege; READ access to the VMBackups share was the main path. | Do not mistake a group name for a privilege. Use whoami /all and re-enumerate shares to determine what it means. |
| Pirate | The initial pentest account could use LDAP/SMB but not WinRM. A gMSA belonged to Remote Management Users and could use WinRM with its hash. | Use machine accounts, gMSAs, and group memberships to find WinRM candidates. |
| Certified | LDAP and BloodHound confirmed that management_svc belonged to Remote Management Users; its hash then worked with WinRM. | Remote Management Users identifies a candidate; confirm it by obtaining a shell. |
| Administrator | WinRM returned Pwn3d! for the initial user Olivia. Kerberos clock skew also became a problem later. | Initial credentials can sometimes work for WinRM. Record the identity and groups when access succeeds. |
| Logging | The Logs share, Remote Management Users, Emergency Recovery, and msa_health$ were visible early on. After connecting with WinRM, whoami /groups confirmed authorization. | Share and group names offer clues about operational design. A machine account may also be a WinRM candidate. |
| Voleur | NTLM was unavailable and returned STATUS_NOT_SUPPORTED. A Kerberos cache enabled SMB/LDAP enumeration, leading to DPAPI data in an archived profile. | Do not confuse an incorrect password with an unsupported authentication method. A profile archive can lead to DPAPI data. |
Short checklist
Section titled “Short checklist”After obtaining initial credentials:
- Check
smb,ldap, andwinrmseparately. - Run
--shares --rid-bruteand--users --groups --computers. - Identify
Remote Management Users, custom groups, service accounts, machine accounts, and gMSAs. - Check SMB signing, LDAP signing, NTLM availability, and clock skew.
- Use BloodHound/RustHound as a map of relationships, then verify findings with actual operations and fresh reads.
After connecting through WinRM:
- Save
whoami /alloutput first. - Look for nonstandard directories directly under
C:\, then recurse only into promising candidates. - Check user profiles, PowerShell history, Credential Manager, DPAPI, backups, and app configuration.
- Recheck SMB shares and WinRM whenever you obtain new credentials, hashes, or group memberships.
- Use winPEAS to check for gaps in manual enumeration.

