The company posts every employee's name on LinkedIn. Without knowing it, they also post their Active Directory usernames. This is how I get the first foothold in almost every real engagement — without credentials, without prior access.
MITRE ATT&CK: T1589.003 — Gather Victim Identity Information: Employee Names · T1110.003 — Password Spraying · T1078 — Valid Accounts
The standard assumption is that without credentials you can only run a port scan and wait. That is not true. Kerberos gives you more than you think.
Most people treat the unauthenticated phase as a dead zone. Reconnaissance only. But between the public data companies voluntarily expose and the information Kerberos leaks before you authenticate, you can build a valid user list, confirm which accounts have weak configurations, and land the first set of credentials — all before touching a login prompt. This is the exact workflow I follow, in order, on every engagement.
Step 1: Identify the username format
Before you can enumerate anything, you need to know how the domain formats usernames. The answer is almost always sitting in a public inbox.
Start with LinkedIn, the company website, or any breach data you have scoped permission to review. Look for corporate email addresses — support tickets, GitHub commits, conference speaker bios, job postings. One confirmed email is enough.
[email protected] tells you the format is first.last. A single data point propagates across every account in the directory. From that moment, every name you find becomes a candidate username.
Common formats in order of frequency:
first.last— most common in Spanish and European enterprisesflast— first initial + last namef.last— first initial dot last namefirstlast— no separator
When you find multiple email addresses, cross-reference them to confirm the format is consistent. Some organizations use different formats for different departments or acquisitions. Note the exceptions.
Step 2: Build the username list with linkedin2username
With the format confirmed, scrape the company's LinkedIn employee list and convert names directly into candidate usernames.
# linkedin2username — scrapes company LinkedIn → usernames in discovered format
python3 linkedin2username.py -c "Company Name" -n first.last
# Output: 300-500 potential domain usernamesSource: github.com/initstring/linkedin2username
This produces a raw list of candidate usernames derived from publicly listed employees. A company with 500 LinkedIn followers that lists its employees publicly gives you 300 to 500 candidates without any authentication, without any network touch, and without any logs on their infrastructure. The scrape happens entirely on LinkedIn's servers.
The output quality depends on how completely employees list their employer. Technology companies and professional services firms tend to have higher coverage. Industrial or manufacturing companies often have lower coverage — their engineers do not update LinkedIn profiles as frequently.
Step 3: Complement with statistically-likely-usernames
LinkedIn scraping covers only the employees who publicly list the company. For everyone else — the IT staff who set their profiles to private, the finance team that never updated their employer — you need a different source.
# statistically-likely-usernames: real first+last name combinations
git clone https://github.com/insidetrust/statistically-likely-usernames
# Covers names not found in the LinkedIn scrapeThis repository contains name lists derived from census and population data, sorted by statistical frequency. The top 1,000 first names paired with the top 1,000 last names covers a substantial fraction of any workforce. Apply the same username format you identified in Step 1 and merge the output with your LinkedIn-derived list.
Deduplicate the combined list before moving to the next step. You will typically have 800 to 2,000 candidate usernames at this point. Most of them will be wrong. The next step cuts that list down to reality.
Step 4: Validate which users actually exist via Kerbrute (no lockout)
This is where the workflow separates from guesswork.
kerbrute userenum --dc DC-IP -d domain.local users.txt -o valid_users.txtKerbrute sends Kerberos AS-REQ packets to the domain controller for each candidate username. The DC responds differently depending on whether the account exists:
- Account does not exist:
KDC_ERR_C_PRINCIPAL_UNKNOWN - Account exists but pre-auth is required:
KDC_ERR_PREAUTH_REQUIRED - Account exists and pre-auth is disabled: the DC returns a TGT directly — an AS-REP roastable account
The critical property: Kerbrute is distinguishing between "account doesn't exist" and "wrong password" — but it never sends a password. There is no authentication attempt. No failed login event. No account lockout. Windows logs Event ID 4768 for Kerberos ticket requests, but defenders typically alert on repeated failures from the same account, not on KDC_ERR_C_PRINCIPAL_UNKNOWN responses at volume.
This is why you use Kerbrute instead of LDAP for unauthenticated enumeration. LDAP from outside the network requires a bind operation — you have to authenticate before you can query. Kerberos is designed to answer the question "does this principal exist" as part of the normal protocol flow.
Typical result: 800 to 2,000 candidates in, 80 to 150 confirmed valid users out.
Step 5: Check the domain password policy before spraying
Never spray without reading the policy. This is not optional.
nxc smb DC-IP -u '' -p '' --pass-polSMB null sessions are disabled by default on modern Windows, but the command works more often than you expect — particularly in environments with legacy systems or pre-2019 DCs. When it works, it returns the lockout threshold and the observation window.
What you need before touching a single account:
- Lockout threshold — how many failed attempts before lockout
- Observation window — how long before the counter resets
- Lockout duration — how long an account stays locked
If null sessions are blocked, check if the DC exposes the policy via LDAP anonymous bind or via AS-REP responses. Alternatively, check breach data or public filings — some regulated organizations publish their security policy documents.
The conservative rule: one attempt per account per observation window. If the policy is three attempts in 30 minutes, you send one password per account per 30 minutes. Spraying faster than that is how you lock out 200 accounts and end the engagement.
Step 6: Password spraying with seasonal patterns
nxc smb DC-IP -u valid_users.txt -p 'Spring2026!' --continue-on-successSeasonal passwords exist in every environment. Not because users are careless — because rotation policies force frequent changes and nobody memorizes random strings. When you mandate a new password every 90 days, users solve the problem the rational way: they encode the current season and year and increment a special character. It works. It is memorable. And it is predictable.
Patterns that produce hits in real engagements, in rough order of success rate:
Season+Year!—Spring2026!,Autumn2025!,Winter2026!Company+Year!— derived from the organization nameWelcome1!— onboarding default that never got changedPassword1!— still works in 2026, on every engagement
Run --continue-on-success so netexec does not stop on the first hit. In a list of 100 valid users, seasonal passwords typically produce two to five hits. One is enough.
When Kerbrute returned accounts with pre-auth disabled in Step 4, skip spraying those accounts entirely — extract their hashes directly and crack offline. That is the AS-REP roasting path and it does not require a correct password guess. Kerberoasting comes later, once you have authenticated access.
The counterintuitive insight
Kerberos is the key. It tells you whether a user exists without requiring you to authenticate and without triggering lockouts. LDAP does not give you that from outside. Most defenders do not log AS-REQ failures as suspicious — they log failed logins, which this generates zero of.
And seasonal passwords exist in 100% of environments. Without exception. Policy-enforced rotation is exactly what creates them. The more strictly you enforce password rotation, the more predictable the passwords become.
The gap is not in the tools defenders deploy. It is in what they choose to instrument. Kerberos pre-auth failure volume from a single source is detectable. Most organizations are not looking for it.
How ADscan handles this phase
ADscan runs Phase 0 and Phase 1 automatically. The start_unauth command enumerates the domain without credentials — DNS reconnaissance, SMB null session attempts, LDAP anonymous bind, and AS-REP roastable account discovery. Accounts with pre-authentication disabled are surfaced immediately, with the hash extraction step ready to run.
The valid user enumeration, AS-REP check, and initial spray happen in one command instead of six separate tools. The output feeds directly into the attack graph so you see which discovered accounts have paths to Domain Admin before you decide where to focus.
Running 2+ AD engagements per year? Request PRO beta access — free for 90 days.
Detection
Kerbrute user enumeration: Event ID 4768 with result code 0x6 (KDC_ERR_C_PRINCIPAL_UNKNOWN) — detectable if you specifically monitor for Kerberos failures against non-existent accounts at volume from a single source IP. Most SIEM rules alert on Event ID 4625 (failed logon), which this technique generates zero of.
Password spraying: Event ID 4625 with multiple accounts from the same source IP. A rate-limited spray — one attempt per account per observation window — is difficult to distinguish from normal background failure noise at most organizations. The signal is the pattern: many different accounts, same source, same password, evenly spaced in time.
linkedin2username: No detection at the network level. It scrapes a public website. LinkedIn's own abuse detection may flag the session, but there is nothing to detect on the target organization's infrastructure.
Prevention
Disable pre-authentication exemptions. Every account with DONT_REQ_PREAUTH set is exploitable without a password guess. Audit and remediate. There is almost never a legitimate technical reason for this setting in modern environments.
Enforce pre-authentication on all accounts. This eliminates the AS-REP roasting path and forces Kerbrute enumeration to rely solely on the KDC_ERR_C_PRINCIPAL_UNKNOWN / KDC_ERR_PREAUTH_REQUIRED distinction — still exploitable for enumeration, but the hash extraction shortcut disappears.
Monitor Kerberos pre-auth failures in bulk from single sources. Build a SIEM rule for high volume of 4768 events with result code 0x6 from a single source IP within a short window. Tune the threshold based on your normal authentication patterns.
Use longer observation windows in lockout policy. Counterintuitively, a 60-minute observation window makes spraying harder than a 30-minute window — the attacker can only send one attempt per account per hour instead of per half-hour. It does not prevent spraying, but it extends the time to enumerate the password space.
Audit employee exposure on LinkedIn. You cannot prevent employees from listing their employer, but you can understand what an attacker sees. Run linkedin2username against your own organization quarterly and review the output.
For the full methodology from initial access through domain compromise, see the Active Directory pentesting guide.