Discovery
T1087 — Account Discovery
Adversaries may attempt to get a listing of valid accounts, usernames, or email addresses on a system or within a compromised environment. This information can help adversaries determine which accounts exist, which can aid in follow-on behavior such as brute-forcing, spear-phishing attacks, or account takeovers (e.g., [Valid Accounts](https://attack.mitre.org/techniques/T1078))...
Investigate Account Discovery activity in the context of Discovery: confirm scope, affected host/identity, and whether it matches expected administrative behaviour before deciding this is benign.
Platforms
ESXi, IaaS, Identity Provider, Linux, macOS, Office Suite, SaaS, Windows
Priority / status
high / draft
Evidence to collect
- Host or resource affected
- Account or identity involved
- Timestamp of the activity
- Related process, file, or network artifact
- Any preceding or follow-on alerts
D3-LAM · detect
Local Account Monitoring
Monitor for Local Account Monitoring indicators relevant to this technique.
Tooling: Entra ID
D3-AA · harden
Agent Authentication
Apply Agent Authentication to reduce this technique's viability before an incident occurs.
Tooling: Defender for Endpoint
D3-AL · evict
Account Locking
Use Account Locking to remove the adversary's foothold once this technique is confirmed.
Tooling: Entra ID
D3-UAP · isolate
User Account Permissions
Apply User Account Permissions to contain the blast radius once this technique is observed.
Tooling: Entra ID
| ATT&CK Technique | D3FEND Technique | Practical SOC Action | Tooling |
|---|---|---|---|
| T1087 Account Discovery | Local Account Monitoring | Monitor for Local Account Monitoring indicators relevant to this technique. | Entra ID |
| T1087 Account Discovery | Agent Authentication | Apply Agent Authentication to reduce this technique's viability before an incident occurs. | Defender for Endpoint |
| T1087 Account Discovery | Account Locking | Use Account Locking to remove the adversary's foothold once this technique is confirmed. | Entra ID |
| T1087 Account Discovery | User Account Permissions | Apply User Account Permissions to contain the blast radius once this technique is observed. | Entra ID |
T1087 Account Discovery → D3FEND → SOC action
Investigation steps — Microsoft
- Review Defender for Endpoint / Defender XDR alerts and timeline for the affected host or identity.
- Check Sentinel analytics rules and incidents correlated with this technique.
- Review Entra ID sign-in and audit logs if the technique involves an identity or cloud resource.
Investigation steps — generic
- Confirm whether the observed account discovery activity matches expected administrative or application behaviour.
- Identify the host, account, or resource where the activity occurred and its business criticality.
- Check for related alerts before and after this activity to reconstruct the broader intrusion timeline.
- Real detection reference: "AD Reconnaissance Tooling Execution" -- Flags execution of common Active Directory enumeration tools (AdFind, dsquery, BloodHound/SharpHound, PowerView) used for domain account and trust discovery.
Response actions
| Action | Risk | Automation safe | Approval required |
|---|---|---|---|
| Contain the affected host or account | Medium | No | Yes |
| Collect and preserve evidence | Low | Yes | No |
KQL
GEN-DI-001 — AD Reconnaissance Tooling Execution
let ReconTools = dynamic([
"adfind.exe",
"dsquery.exe",
"sharphound.exe",
"bloodhound.exe"
]);
let ReconCommandPatterns = dynamic([
"Get-DomainUser",
"Get-DomainGroup",
"Get-DomainTrust",
"Get-NetUser",
"Get-NetGroup",
"Invoke-BloodHound"
]);
DeviceProcessEvents
| where TimeGenerated >= ago(1d)
| where FileName in~ (ReconTools) or ProcessCommandLine has_any (ReconCommandPatterns)
| project
TimeGenerated,
DeviceName,
DeviceId,
AccountName,
FileName,
ProcessCommandLine,
InitiatingProcessFileName,
InitiatingProcessCommandLine
| extend timestamp = TimeGenerated,
HostCustomEntity = DeviceName,
AccountCustomEntity = AccountName
| order by TimeGenerated desc 25c86f99-0a91-4b7f-88f3-599a008e5ab8 — API - Account Takeover
let firstRec = FortyTwoCrunchAPIProtection
| where TimeGenerated >= ago(5m)
| project-away NonBlockingMode, SourcePort, DestinationPort, Query, ApiId, RequestHeader, ResponseHeader, Errors, EventType, Uuid
| where (UriPath has "/api/register" or UriPath has "/api/reset") and Status == 403;
let recCount = iff((toscalar(firstRec | count) >= 20), 1, 0);
firstRec | take recCount c63ae777-d5e0-4113-8c9a-c2c9d3d09fcd — Probable AdFind Recon Tool Usage
let args = dynamic(["objectcategory","domainlist","dcmodes","adinfo","trustdmp","computers_pwdnotreqd","Domain Admins", "objectcategory=person", "objectcategory=computer", "objectcategory=*","dclist"]);
let parentProcesses = dynamic(["pwsh.exe","powershell.exe","cmd.exe"]);
DeviceProcessEvents
//looks for execution from a shell
| where InitiatingProcessFileName in~ (parentProcesses)
// main filter
| where FileName =~ "AdFind.exe" or SHA256 == "c92c158d7c37fea795114fa6491fe5f145ad2f8c08776b18ae79db811e8e36a3"
// AdFind common Flags to check for from various threat actor TTPs
or ProcessCommandLine has_any (args)
| extend HostName = split(DeviceName, '.', 0)[0], DnsDomain = strcat_array(array_slice(split(DeviceName, '.'), 1, -1), '.'), FileHashAlgorithm = "SHA256" Escalation criteria
- Account Discovery activity observed on a privileged account or critical system.
- Activity follows or precedes other suspicious behaviour in the same investigation.
- Automated triage cannot confidently rule out malicious intent.
False positive considerations
- Legitimate administrative or maintenance activity matching this pattern.
- Approved security testing or red team exercise.
- Known benign software producing similar telemetry.
# T1087 - Account Discovery
## SOC Recommendation
Investigate Account Discovery activity in the context of Discovery: confirm scope, affected host/identity, and whether it matches expected administrative behaviour before deciding this is benign.
## D3FEND Mappings
| D3FEND Technique | Relationship | Practical SOC Action |
|---|---|---|
| Local Account Monitoring | Detect | Monitor for Local Account Monitoring indicators relevant to this technique. |
| Agent Authentication | Harden | Apply Agent Authentication to reduce this technique's viability before an incident occurs. |
| Account Locking | Evict | Use Account Locking to remove the adversary's foothold once this technique is confirmed. |
| User Account Permissions | Isolate | Apply User Account Permissions to contain the blast radius once this technique is observed. |
## Investigation Steps
1. Review Defender for Endpoint / Defender XDR alerts and timeline for the affected host or identity.
2. Check Sentinel analytics rules and incidents correlated with this technique.
3. Review Entra ID sign-in and audit logs if the technique involves an identity or cloud resource.
## Evidence to Collect
- Host or resource affected
- Account or identity involved
- Timestamp of the activity
- Related process, file, or network artifact
- Any preceding or follow-on alerts
## Response Actions
| Action | Risk | Automation Safe | Approval Required |
|---|---|---|---|
| Contain the affected host or account | Medium | No | Yes |
| Collect and preserve evidence | Low | Yes | No |
## KQL
```kql
let ReconTools = dynamic([
"adfind.exe",
"dsquery.exe",
"sharphound.exe",
"bloodhound.exe"
]);
let ReconCommandPatterns = dynamic([
"Get-DomainUser",
"Get-DomainGroup",
"Get-DomainTrust",
"Get-NetUser",
"Get-NetGroup",
"Invoke-BloodHound"
]);
DeviceProcessEvents
| where TimeGenerated >= ago(1d)
| where FileName in~ (ReconTools) or ProcessCommandLine has_any (ReconCommandPatterns)
| project
TimeGenerated,
DeviceName,
DeviceId,
AccountName,
FileName,
ProcessCommandLine,
InitiatingProcessFileName,
InitiatingProcessCommandLine
| extend timestamp = TimeGenerated,
HostCustomEntity = DeviceName,
AccountCustomEntity = AccountName
| order by TimeGenerated desc
```
```kql
let firstRec = FortyTwoCrunchAPIProtection
| where TimeGenerated >= ago(5m)
| project-away NonBlockingMode, SourcePort, DestinationPort, Query, ApiId, RequestHeader, ResponseHeader, Errors, EventType, Uuid
| where (UriPath has "/api/register" or UriPath has "/api/reset") and Status == 403;
let recCount = iff((toscalar(firstRec | count) >= 20), 1, 0);
firstRec | take recCount
```
```kql
let args = dynamic(["objectcategory","domainlist","dcmodes","adinfo","trustdmp","computers_pwdnotreqd","Domain Admins", "objectcategory=person", "objectcategory=computer", "objectcategory=*","dclist"]);
let parentProcesses = dynamic(["pwsh.exe","powershell.exe","cmd.exe"]);
DeviceProcessEvents
//looks for execution from a shell
| where InitiatingProcessFileName in~ (parentProcesses)
// main filter
| where FileName =~ "AdFind.exe" or SHA256 == "c92c158d7c37fea795114fa6491fe5f145ad2f8c08776b18ae79db811e8e36a3"
// AdFind common Flags to check for from various threat actor TTPs
or ProcessCommandLine has_any (args)
| extend HostName = split(DeviceName, '.', 0)[0], DnsDomain = strcat_array(array_slice(split(DeviceName, '.'), 1, -1), '.'), FileHashAlgorithm = "SHA256"
```
## Escalation Criteria
- Account Discovery activity observed on a privileged account or critical system.
- Activity follows or precedes other suspicious behaviour in the same investigation.
- Automated triage cannot confidently rule out malicious intent.
## False Positive Considerations
- Legitimate administrative or maintenance activity matching this pattern.
- Approved security testing or red team exercise.
- Known benign software producing similar telemetry.
Generated by SOC Response Atlas by Basyrix.
Want to push this directly to Confluence? Upgrade to Basyrix Pro.
- /api/techniques/T1087.json
- /api/recommendations/T1087.json
- /api/d3fend/T1087.json
- /api/mappings/T1087.json
- /api/confluence/T1087.md
Example curl:
curl https://atlas.basyrix.com/api/recommendations/T1087.json Response:
{
"technique_id": "T1087",
"name": "Account Discovery",
"priority": "high",
"status": "draft",
"version": "0.1.0",
"last_reviewed": "2026-07-23",
"generated_by": "SOC Response Atlas by Basyrix",
"tactics": [
"Discovery"
],
"platforms": [
"ESXi",
"IaaS",
"Identity Provider",
"Linux",
"macOS",
"Office Suite",
"SaaS",
"Windows"
],
"summary": "Adversaries may attempt to get a listing of valid accounts, usernames, or email addresses on a system or within a compromised environment. This information can help adversaries determine which accounts exist, which can aid in follow-on behavior such as brute-forcing, spear-phishing attacks, or account takeovers (e.g., [Valid Accounts](https://attack.mitre.org/techniques/T1078))...",
"soc_recommendation": "Investigate Account Discovery activity in the context of Discovery: confirm scope, affected host/identity, and whether it matches expected administrative behaviour before deciding this is benign.",
"d3fend_mappings": [
{
"id": "D3-LAM",
"name": "Local Account Monitoring",
"relationship": "detect",
"practical_action": "Monitor for Local Account Monitoring indicators relevant to this technique.",
"tooling": [
"Entra ID"
]
},
{
"id": "D3-AA",
"name": "Agent Authentication",
"relationship": "harden",
"practical_action": "Apply Agent Authentication to reduce this technique's viability before an incident occurs.",
"tooling": [
"Defender for Endpoint"
]
},
{
"id": "D3-AL",
"name": "Account Locking",
"relationship": "evict",
"practical_action": "Use Account Locking to remove the adversary's foothold once this technique is confirmed.",
"tooling": [
"Entra ID"
]
},
{
"id": "D3-UAP",
"name": "User Account Permissions",
"relationship": "isolate",
"practical_action": "Apply User Account Permissions to contain the blast radius once this technique is observed.",
"tooling": [
"Entra ID"
]
}
],
"investigation_steps": {
"microsoft": [
"Review Defender for Endpoint / Defender XDR alerts and timeline for the affected host or identity.",
"Check Sentinel analytics rules and incidents correlated with this technique.",
"Review Entra ID sign-in and audit logs if the technique involves an identity or cloud resource."
],
"generic": [
"Confirm whether the observed account discovery activity matches expected administrative or application behaviour.",
"Identify the host, account, or resource where the activity occurred and its business criticality.",
"Check for related alerts before and after this activity to reconstruct the broader intrusion timeline.",
"Real detection reference: \"AD Reconnaissance Tooling Execution\" -- Flags execution of common Active Directory enumeration tools (AdFind, dsquery, BloodHound/SharpHound, PowerView) used for domain account and trust discovery."
]
},
"evidence_to_collect": [
"Host or resource affected",
"Account or identity involved",
"Timestamp of the activity",
"Related process, file, or network artifact",
"Any preceding or follow-on alerts"
],
"response_actions": [
{
"name": "Contain the affected host or account",
"category": "Containment",
"risk": "Medium",
"automation_safe": false,
"approval_required": true,
"tool": "Defender for Endpoint / Entra ID",
"notes": "Requires approval -- confirm malicious intent before isolating a host or disabling an account."
},
{
"name": "Collect and preserve evidence",
"category": "Investigation",
"risk": "Low",
"automation_safe": true,
"approval_required": false,
"tool": "Defender for Endpoint",
"notes": "Safe -- read-only evidence collection."
}
],
"queries": {
"kql": [
{
"name": "GEN-DI-001 — AD Reconnaissance Tooling Execution",
"description": "Flags execution of common Active Directory enumeration tools (AdFind, dsquery, BloodHound/SharpHound, PowerView) used for domain account and trust discovery. (Source: Bell Integration baseline detection library, mapped via sub-technique T1087.002.)",
"query": "let ReconTools = dynamic([\n \"adfind.exe\",\n \"dsquery.exe\",\n \"sharphound.exe\",\n \"bloodhound.exe\"\n]);\nlet ReconCommandPatterns = dynamic([\n \"Get-DomainUser\",\n \"Get-DomainGroup\",\n \"Get-DomainTrust\",\n \"Get-NetUser\",\n \"Get-NetGroup\",\n \"Invoke-BloodHound\"\n]);\nDeviceProcessEvents\n| where TimeGenerated >= ago(1d)\n| where FileName in~ (ReconTools) or ProcessCommandLine has_any (ReconCommandPatterns)\n| project\n TimeGenerated,\n DeviceName,\n DeviceId,\n AccountName,\n FileName,\n ProcessCommandLine,\n InitiatingProcessFileName,\n InitiatingProcessCommandLine\n| extend timestamp = TimeGenerated,\n HostCustomEntity = DeviceName,\n AccountCustomEntity = AccountName\n| order by TimeGenerated desc"
},
{
"name": "25c86f99-0a91-4b7f-88f3-599a008e5ab8 — API - Account Takeover",
"description": "42Crunch API protection against account takeover' (Source: Microsoft's official Azure-Sentinel Detections (MIT licensed).)",
"query": "let firstRec = FortyTwoCrunchAPIProtection\n| where TimeGenerated >= ago(5m) \n| project-away NonBlockingMode, SourcePort, DestinationPort, Query, ApiId, RequestHeader, ResponseHeader, Errors, EventType, Uuid\n| where (UriPath has \"/api/register\" or UriPath has \"/api/reset\") and Status == 403;\nlet recCount = iff((toscalar(firstRec | count) >= 20), 1, 0);\nfirstRec | take recCount"
},
{
"name": "c63ae777-d5e0-4113-8c9a-c2c9d3d09fcd — Probable AdFind Recon Tool Usage",
"description": "This query identifies the host and account that executed AdFind, by hash and filename, in addition to the flags commonly utilized by various threat actors during the reconnaissance phase.' (Source: Microsoft's official Azure-Sentinel Detections (MIT licensed), mapped via sub-technique T1087.002.)",
"query": "let args = dynamic([\"objectcategory\",\"domainlist\",\"dcmodes\",\"adinfo\",\"trustdmp\",\"computers_pwdnotreqd\",\"Domain Admins\", \"objectcategory=person\", \"objectcategory=computer\", \"objectcategory=*\",\"dclist\"]);\nlet parentProcesses = dynamic([\"pwsh.exe\",\"powershell.exe\",\"cmd.exe\"]);\nDeviceProcessEvents\n//looks for execution from a shell\n| where InitiatingProcessFileName in~ (parentProcesses)\n// main filter\n| where FileName =~ \"AdFind.exe\" or SHA256 == \"c92c158d7c37fea795114fa6491fe5f145ad2f8c08776b18ae79db811e8e36a3\"\n // AdFind common Flags to check for from various threat actor TTPs\n or ProcessCommandLine has_any (args)\n| extend HostName = split(DeviceName, '.', 0)[0], DnsDomain = strcat_array(array_slice(split(DeviceName, '.'), 1, -1), '.'), FileHashAlgorithm = \"SHA256\""
}
],
"spl": [],
"esql": [
{
"name": "80aa6cca-b343-457b-877e-5877cd71a1f8 — Azure AD Graph Potential Enumeration (ROADrecon)",
"description": "(ESQL) Detects an Azure AD Graph (graph.windows.net) burst from a user-agent identifying as \"aiohttp\" (the default HTTP library used by ROADrecon's \"gather\" command) where a single calling identity issues many requests in a short window. ROADrecon walks every interesting directory object type via aiohttp, producing a large volume of requests from one user / source IP / UA triple... (Source: Elastic's official detection-rules repository (Elastic License v2).)",
"query": "from logs-azure.aadgraphactivitylogs-* metadata _id, _version, _index\n\n| where data_stream.dataset == \"azure.aadgraphactivitylogs\"\n and to_lower(user_agent.original) like \"*aiohttp*\"\n\n| eval Esql.target_endpoints = case(\n url.path like \"*/eligibleRoleAssignments*\", \"eligibleRoleAssignments\",\n url.path like \"*/roleAssignments*\", \"roleAssignments\",\n url.path like \"*/users*\", \"users\",\n url.path like \"*/groups*\", \"groups\",\n url.path like \"*/servicePrincipals*\", \"servicePrincipals\",\n url.path like \"*/applications*\", \"applications\",\n url.path like \"*/devices*\", \"devices\",\n url.path like \"*/directoryRoles*\", \"directoryRoles\",\n url.path like \"*/roleDefinitions*\", \"roleDefinitions\",\n url.path like \"*/administrativeUnits*\", \"administrativeUnits\",\n url.path like \"*/contacts*\", \"contacts\",\n url.path like \"*/oauth2PermissionGrants*\", \"oauth2PermissionGrants\",\n url.path like \"*/authorizationPolicy*\", \"authorizationPolicy\",\n url.path like \"*/settings*\", \"settings\",\n url.path like \"*/policies*\", \"policies\",\n url.path like \"*/tenantDetails*\", \"tenantDetails\",\n \"other\"\n )\n| where Esql.target_endpoints != \"other\"\n\n| eval Esql.time_window = date_trunc(1 minutes, @timestamp)\n\n| stats\n Esql.request_count = count(*),\n Esql.distinct_endpoints = count_distinct(Esql.target_endpoints),\n Esql.api_versions = values(azure.aadgraphactivitylogs.properties.api_version),\n Esql.app_ids = values(azure.aadgraphactivitylogs.properties.app_id),\n Esql.user_agent = values(user_agent.original),\n Esql.http_methods = values(http.request.method),\n Esql.status_codes = values(http.response.status_code),\n Esql.source_ips = values(source.ip),\n Esql.source_asn_orgs = values(source.`as`.organization.name),\n Esql.source_countries = values(source.geo.country_name),\n Esql.actor_types = values(azure.aadgraphactivitylogs.properties.actor_type),\n Esql.client_auth_methods = values(azure.aadgraphactivitylogs.properties.client_auth_method),\n Esql.session_ids = values(azure.aadgraphactivitylogs.properties.session_id),\n Esql.sign_in_activity_ids = values(azure.aadgraphactivitylogs.properties.sign_in_activity_id),\n Esql.scopes = values(azure.aadgraphactivitylogs.properties.scopes),\n Esql.first_seen = min(@timestamp),\n Esql.last_seen = max(@timestamp)\n by\n user.id,\n azure.tenant_id,\n Esql.time_window\n\n| where Esql.distinct_endpoints >= 5\n\n| keep\n user.id,\n azure.tenant_id,\n Esql.*"
},
{
"name": "3aec394d-ed2a-4f3e-8ed3-4a2adea39f05 — Azure AD Graph Access with Suspicious User-Agent",
"description": "(ESQL) Identifies Azure AD Graph (graph.windows.net) requests originating from user-agent strings associated with offensive tooling, scripting libraries, or generic HTTP clients. First-party Microsoft components calling AAD Graph identify with specific user agents such as \"Microsoft Azure Graph Client Library\", \"Microsoft ADO.NET Data Services\", or \"Microsoft.OData.Client\"... (Source: Elastic's official detection-rules repository (Elastic License v2).)",
"query": "from logs-azure.aadgraphactivitylogs-* metadata _id, _version, _index\n\n| where data_stream.dataset == \"azure.aadgraphactivitylogs\"\n and azure.aadgraphactivitylogs.properties.actor_type == \"User\"\n and user_agent.original is not null\n| eval Esql.ua_lower = to_lower(user_agent.original)\n| where Esql.ua_lower like \"*fasthttp*\"\n or Esql.ua_lower like \"*aiohttp*\"\n or Esql.ua_lower like \"*hound*\"\n or Esql.ua_lower like \"*aadinternals*\"\n or Esql.ua_lower like \"*go-http-client*\"\n or Esql.ua_lower like \"python*\"\n or Esql.ua_lower like \"*curl/*\"\n or Esql.ua_lower like \"*okhttp*\"\n or Esql.ua_lower like \"*axios*\"\n or Esql.ua_lower like \"*node-fetch*\"\n or Esql.ua_lower like \"*go-resty*\"\n or Esql.ua_lower like \"*bav2ropc*\"\n or Esql.ua_lower like \"*undici*\"\n| keep\n _id,\n _version,\n _index,\n @timestamp,\n user.id,\n source.ip,\n source.as.organization.name,\n user_agent.original,\n azure.aadgraphactivitylogs.properties.app_id,\n azure.aadgraphactivitylogs.properties.api_version,\n url.path,\n http.response.status_code,\n azure.tenant_id,\n Esql.ua_lower"
}
]
},
"automation": {
"safe": [
"Add recommendation as Sentinel incident comment.",
"Run enrichment queries.",
"Create ServiceNow SecOps task."
],
"approval_required": [
"Contain or disable the affected host/account.",
"Any change to production configuration."
]
},
"escalation_criteria": [
"Account Discovery activity observed on a privileged account or critical system.",
"Activity follows or precedes other suspicious behaviour in the same investigation.",
"Automated triage cannot confidently rule out malicious intent."
],
"false_positive_considerations": [
"Legitimate administrative or maintenance activity matching this pattern.",
"Approved security testing or red team exercise.",
"Known benign software producing similar telemetry."
],
"confluence": {
"title": "T1087 - Account Discovery Response Guidance",
"labels": [
"mitre",
"attack",
"d3fend",
"secops",
"basyrix"
],
"sections": [
"summary",
"d3fend_mappings",
"investigation_steps",
"response_actions",
"queries",
"automation",
"escalation_criteria",
"false_positive_considerations"
]
}
}