Collection
T1039 — Data from Network Shared Drive
Adversaries may search network shares on computers they have compromised to find files of interest. Sensitive data can be collected from remote systems via shared network drives (host shared directory, network file server, etc.) that are accessible from the current system prior to Exfiltration. Interactive command shells may be in use, and common functionality within [cmd](https://attack.mitre.org/software/S0106) may be used to gather information.
Investigate Data from Network Shared Drive activity in the context of Collection: confirm scope, affected host/identity, and whether it matches expected administrative behaviour before deciding this is benign.
Platforms
Linux, macOS, Windows
Priority / status
high / complete
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-NRAM · isolate
Network Resource Access Mediation
Apply Network Resource Access Mediation to contain the blast radius once this technique is observed.
Tooling: Sentinel, Defender for Endpoint
D3-DNR · deceive
Decoy Network Resource
Deploy Decoy Network Resource to detect and mislead an adversary attempting this technique.
Tooling: Sentinel, Defender for Endpoint
| ATT&CK Technique | D3FEND Technique | Practical SOC Action | Tooling |
|---|---|---|---|
| T1039 Data from Network Shared Drive | Network Resource Access Mediation | Apply Network Resource Access Mediation to contain the blast radius once this technique is observed. | Sentinel, Defender for Endpoint |
| T1039 Data from Network Shared Drive | Decoy Network Resource | Deploy Decoy Network Resource to detect and mislead an adversary attempting this technique. | Sentinel, Defender for Endpoint |
T1039 Data from Network Shared Drive → D3FEND → SOC action
Investigation steps — Microsoft
- project
- order by TimeGenerated desc
- where SenderFromAddress == "<USER_EMAIL>"
- where Timestamp >= ago(7d)
- where DeliveryAction != "Blocked"
- project Timestamp, SenderFromAddress, RecipientEmailAddress, Subject, ThreatTypes
- order by Timestamp desc
- where UserId == "<USER_FROM_ALERT>"
Investigation steps — generic
- Confirm whether the observed data from network shared drive 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: "SharePoint External Sharing Link Surge" -- Replaces the duplicate mass-download logic with a distinct collection signal: users rapidly creating anonymous or external sharing links across SharePoint/OneDrive.
Response actions
| Action | Risk | Automation safe | Approval required |
|---|---|---|---|
| Delete the forwarding rule | Low | No | Yes |
| Revoke all active sessions | Medium | No | Yes |
| Reset password | Medium | No | Yes |
| Notify user's manager and finance team | Low | Yes | No |
| Audit sent items | Low | Yes | No |
| Check for additional inbox rules (delete, mark-as-read, hide | Low | Yes | No |
| Review all emails sent from the account in the past 7 days | Low | Yes | No |
| Alert the customer's finance team to verify any recent payme | Low | No | Yes |
KQL
GEN-CO-001 — SharePoint External Sharing Link Surge
let lookback = 1d;
let shareThreshold = 10;
let officeActivitySource =
union isfuzzy=true OfficeActivity,
(datatable(TimeGenerated:datetime, OfficeWorkload:string, Operation:string, UserId:string, ClientIP:string, Site_Url:string, SourceFileName:string, ObjectId:string, UserAgent:string, TargetUserOrGroupType:string, TargetUserOrGroupName:string)[]);
officeActivitySource
| where TimeGenerated >= ago(lookback)
| where OfficeWorkload in~ ("SharePoint", "OneDrive")
| where Operation in~ ("AnonymousLinkCreated", "SharingSet", "SecureLinkCreated", "AddedToSecureLink", "CompanyLinkCreated")
| extend UserPrincipalName = tolower(tostring(UserId)),
TargetType = tostring(column_ifexists("TargetUserOrGroupType", "")),
TargetName = tostring(column_ifexists("TargetUserOrGroupName", ""))
| where isnotempty(UserPrincipalName)
| summarize
StartTime = min(TimeGenerated),
EndTime = max(TimeGenerated),
ShareActionCount = count(),
UniqueObjects = dcount(ObjectId),
Sites = make_set(Site_Url, 50),
SharedObjects = make_set(ObjectId, 50),
TargetTypes = make_set(TargetType, 20),
TargetNames = make_set(TargetName, 20),
SourceIPs = make_set(ClientIP, 20),
UserAgents = make_set(UserAgent, 10)
by UserPrincipalName
| where ShareActionCount >= shareThreshold or UniqueObjects >= shareThreshold
| extend AccountName = tostring(split(UserPrincipalName, "@")[0]),
UPNSuffix = tostring(split(UserPrincipalName, "@")[1])
| project StartTime, EndTime, UserPrincipalName, AccountName, UPNSuffix,
ShareActionCount, UniqueObjects, Sites, SharedObjects, TargetTypes, TargetNames, SourceIPs, UserAgents
| extend TimeGenerated = EndTime,
timestamp = EndTime,
AccountCustomEntity = UserPrincipalName
| order by ShareActionCount desc, UniqueObjects desc aba0b08c-aace-40c5-a21d-39153023dcaa — Excessive share permissions
let timeframe=1h;
let system_roles = datatable(role:string, system:string) // Link roles to systems.
["DC","dc1.corp.local",
"DC","dc2.corp.local",
"PRINT","printer.corp.local"
];
let share_roles = datatable(role:string, share:string) // Link roles to shares.
["DC", @"\\*\sysvol",
"DC",@"\\*\netlogon",
"PRINT",@"\\*\print$"];
let allowed_system_shares = system_roles // Link systems to shares.
| join kind=inner share_roles on role
| extend system = tolower(system), share = tolower(share)
| project-away role
| summarize allowed_shares = make_set(share) by system;
let monitored_principals=datatable(identifier:string, Group_Name:string) // Define a data-table with groups to monitor.
["AN", "Anonymous Logon", // We accept the 'alias' for these well-known SIDS.
"AU", "Authenticated Users",
"BG","Built-in guests",
"BU","Built-in users",
"DG","Domain guests",
"DU","Domain users",
"WD","Everyone",
"IU","Interactively Logged-on users",
"LG","Local Guest",
"NU","Network logon users",
"513", "Domain Users", // Support matching on the last part of a SID.
"514", "Domain Guests",
"545", "Builtin Users",
"546", "Builtin Guests",
"S-1-5-7", "Anonymous Logon" // For the global SIDS, we accept them as-is.
];
SecurityEvent
| where TimeGenerated >= ago(timeframe)
| where EventID == 5143
| extend EventXML = parse_xml(EventData)
| extend OldSD = tostring(EventXML["EventData"]["Data"][13]["#text"]) // Grab the previous Security Descriptor.
| extend NewSD = tostring(EventXML["EventData"]["Data"][14]["#text"]) // Grab the new Security Descriptor.
| project-away EventXML
| where tostring(OldSD) !~ tostring(NewSD) // Don't bother with unchanged permissions.
| extend system = tolower(Computer), share=tolower(ShareName) // Normalize system and share name for matching with whitelist.
| join kind=leftouter allowed_system_shares on system // Retrieve the allowed shares per system.
| where not(set_has_element(allowed_shares, share)) // Check if the current share is an allowed share.
| project-away system, share, allowed_shares // Get rid of temporary fields.
| extend DACLS = extract_all(@"(D:(?:\((?:[\w\-]*;){5}(?:[\w\-]*)\))*)", tostring(NewSD)) // Grab all instances of D:(DACL), in case there are multiple sets.
| project-away OldSD, NewSD // Get rid of data we no longer need.
| mv-expand DACLS to typeof(string) // In case there are any duplicate/subsequent D: entries (e.g., D:<dacls>S:<sacls>D:<dacls>) split them out to individual D: sets.
| extend DACLS = substring(DACLS,2) // Strip the leading D:.
| extend DACLS = split(DACLS, ")") // Split the sets of DACLS ()() to an array of individual DACLS (). This removes the trailing ) character.
| mv-expand DACLS to typeof(string) // Duplicate the records in such a way that only 1 DACL per record exist. We will aggregate them back later.
| extend DACLS = substring(DACLS, 1) // Also remove the leading ( character.
| where not(isempty(DACLS)) and DACLS startswith "A;" // Remove any empty or non-allow DACLs.
| extend allowed_principal = tostring(split(DACLS,";",5)[0]) // Grab the SID what is affected by this DACL.
| extend allowed_principal = iff(not(allowed_principal startswith "S-" and string_size(allowed_principal) > 15), allowed_principal, split(allowed_principal,"-",countof(allowed_principal,"-"))[0]) // This line takes only the last part (e.g., 513) of a long SID, so you can refer to groups/users without needing to supply the full SID above.
| join kind=inner monitored_principals on $left.allowed_principal == $right.identifier // Join the found groups to the table of groups to be monitored above. Adds the more readable 'group_name).
| project-away allowed_principal, identifier, DACLS
| summarize Authorized_Public_Principals = make_set(Group_Name), take_any(*) by TimeGenerated, SourceComputerId, EventData // Summarize the fields back, making a set of the various group_name values for this record.
| project-away Group_Name
// Begin client-specific filter.
// End client-specific filter. Escalation criteria
- Data from Network Shared Drive 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 out-of-office forwarding to personal email — Verify with user and manager; add approved personal domain to TrustedEmailDomains watchlist with expiry date
- Contractor forwarding work email to corporate account — Document as approved; add contractor's corporate domain to TrustedEmailDomains watchlist
# T1039 - Data from Network Shared Drive
## SOC Recommendation
Investigate Data from Network Shared Drive activity in the context of Collection: 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 |
|---|---|---|
| Network Resource Access Mediation | Isolate | Apply Network Resource Access Mediation to contain the blast radius once this technique is observed. |
| Decoy Network Resource | Deceive | Deploy Decoy Network Resource to detect and mislead an adversary attempting this technique. |
## Investigation Steps
1. project
2. order by TimeGenerated desc
3. where SenderFromAddress == "<USER_EMAIL>"
4. where Timestamp >= ago(7d)
5. where DeliveryAction != "Blocked"
6. project Timestamp, SenderFromAddress, RecipientEmailAddress, Subject, ThreatTypes
7. order by Timestamp desc
8. where UserId == "<USER_FROM_ALERT>"
## 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 |
|---|---|---|---|
| Delete the forwarding rule | Low | No | Yes |
| Revoke all active sessions | Medium | No | Yes |
| Reset password | Medium | No | Yes |
| Notify user's manager and finance team | Low | Yes | No |
| Audit sent items | Low | Yes | No |
| Check for additional inbox rules (delete, mark-as-read, hide | Low | Yes | No |
| Review all emails sent from the account in the past 7 days | Low | Yes | No |
| Alert the customer's finance team to verify any recent payme | Low | No | Yes |
## KQL
```kql
let lookback = 1d;
let shareThreshold = 10;
let officeActivitySource =
union isfuzzy=true OfficeActivity,
(datatable(TimeGenerated:datetime, OfficeWorkload:string, Operation:string, UserId:string, ClientIP:string, Site_Url:string, SourceFileName:string, ObjectId:string, UserAgent:string, TargetUserOrGroupType:string, TargetUserOrGroupName:string)[]);
officeActivitySource
| where TimeGenerated >= ago(lookback)
| where OfficeWorkload in~ ("SharePoint", "OneDrive")
| where Operation in~ ("AnonymousLinkCreated", "SharingSet", "SecureLinkCreated", "AddedToSecureLink", "CompanyLinkCreated")
| extend UserPrincipalName = tolower(tostring(UserId)),
TargetType = tostring(column_ifexists("TargetUserOrGroupType", "")),
TargetName = tostring(column_ifexists("TargetUserOrGroupName", ""))
| where isnotempty(UserPrincipalName)
| summarize
StartTime = min(TimeGenerated),
EndTime = max(TimeGenerated),
ShareActionCount = count(),
UniqueObjects = dcount(ObjectId),
Sites = make_set(Site_Url, 50),
SharedObjects = make_set(ObjectId, 50),
TargetTypes = make_set(TargetType, 20),
TargetNames = make_set(TargetName, 20),
SourceIPs = make_set(ClientIP, 20),
UserAgents = make_set(UserAgent, 10)
by UserPrincipalName
| where ShareActionCount >= shareThreshold or UniqueObjects >= shareThreshold
| extend AccountName = tostring(split(UserPrincipalName, "@")[0]),
UPNSuffix = tostring(split(UserPrincipalName, "@")[1])
| project StartTime, EndTime, UserPrincipalName, AccountName, UPNSuffix,
ShareActionCount, UniqueObjects, Sites, SharedObjects, TargetTypes, TargetNames, SourceIPs, UserAgents
| extend TimeGenerated = EndTime,
timestamp = EndTime,
AccountCustomEntity = UserPrincipalName
| order by ShareActionCount desc, UniqueObjects desc
```
```kql
let timeframe=1h;
let system_roles = datatable(role:string, system:string) // Link roles to systems.
["DC","dc1.corp.local",
"DC","dc2.corp.local",
"PRINT","printer.corp.local"
];
let share_roles = datatable(role:string, share:string) // Link roles to shares.
["DC", @"\\*\sysvol",
"DC",@"\\*\netlogon",
"PRINT",@"\\*\print$"];
let allowed_system_shares = system_roles // Link systems to shares.
| join kind=inner share_roles on role
| extend system = tolower(system), share = tolower(share)
| project-away role
| summarize allowed_shares = make_set(share) by system;
let monitored_principals=datatable(identifier:string, Group_Name:string) // Define a data-table with groups to monitor.
["AN", "Anonymous Logon", // We accept the 'alias' for these well-known SIDS.
"AU", "Authenticated Users",
"BG","Built-in guests",
"BU","Built-in users",
"DG","Domain guests",
"DU","Domain users",
"WD","Everyone",
"IU","Interactively Logged-on users",
"LG","Local Guest",
"NU","Network logon users",
"513", "Domain Users", // Support matching on the last part of a SID.
"514", "Domain Guests",
"545", "Builtin Users",
"546", "Builtin Guests",
"S-1-5-7", "Anonymous Logon" // For the global SIDS, we accept them as-is.
];
SecurityEvent
| where TimeGenerated >= ago(timeframe)
| where EventID == 5143
| extend EventXML = parse_xml(EventData)
| extend OldSD = tostring(EventXML["EventData"]["Data"][13]["#text"]) // Grab the previous Security Descriptor.
| extend NewSD = tostring(EventXML["EventData"]["Data"][14]["#text"]) // Grab the new Security Descriptor.
| project-away EventXML
| where tostring(OldSD) !~ tostring(NewSD) // Don't bother with unchanged permissions.
| extend system = tolower(Computer), share=tolower(ShareName) // Normalize system and share name for matching with whitelist.
| join kind=leftouter allowed_system_shares on system // Retrieve the allowed shares per system.
| where not(set_has_element(allowed_shares, share)) // Check if the current share is an allowed share.
| project-away system, share, allowed_shares // Get rid of temporary fields.
| extend DACLS = extract_all(@"(D:(?:\((?:[\w\-]*;){5}(?:[\w\-]*)\))*)", tostring(NewSD)) // Grab all instances of D:(DACL), in case there are multiple sets.
| project-away OldSD, NewSD // Get rid of data we no longer need.
| mv-expand DACLS to typeof(string) // In case there are any duplicate/subsequent D: entries (e.g., D:<dacls>S:<sacls>D:<dacls>) split them out to individual D: sets.
| extend DACLS = substring(DACLS,2) // Strip the leading D:.
| extend DACLS = split(DACLS, ")") // Split the sets of DACLS ()() to an array of individual DACLS (). This removes the trailing ) character.
| mv-expand DACLS to typeof(string) // Duplicate the records in such a way that only 1 DACL per record exist. We will aggregate them back later.
| extend DACLS = substring(DACLS, 1) // Also remove the leading ( character.
| where not(isempty(DACLS)) and DACLS startswith "A;" // Remove any empty or non-allow DACLs.
| extend allowed_principal = tostring(split(DACLS,";",5)[0]) // Grab the SID what is affected by this DACL.
| extend allowed_principal = iff(not(allowed_principal startswith "S-" and string_size(allowed_principal) > 15), allowed_principal, split(allowed_principal,"-",countof(allowed_principal,"-"))[0]) // This line takes only the last part (e.g., 513) of a long SID, so you can refer to groups/users without needing to supply the full SID above.
| join kind=inner monitored_principals on $left.allowed_principal == $right.identifier // Join the found groups to the table of groups to be monitored above. Adds the more readable 'group_name).
| project-away allowed_principal, identifier, DACLS
| summarize Authorized_Public_Principals = make_set(Group_Name), take_any(*) by TimeGenerated, SourceComputerId, EventData // Summarize the fields back, making a set of the various group_name values for this record.
| project-away Group_Name
// Begin client-specific filter.
// End client-specific filter.
```
## Escalation Criteria
- Data from Network Shared Drive 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 out-of-office forwarding to personal email — Verify with user and manager; add approved personal domain to TrustedEmailDomains watchlist with expiry date
- Contractor forwarding work email to corporate account — Document as approved; add contractor's corporate domain to TrustedEmailDomains watchlist
Generated by SOC Response Atlas by Basyrix.
Want to push this directly to Confluence? Upgrade to Basyrix Pro.
- /api/techniques/T1039.json
- /api/recommendations/T1039.json
- /api/d3fend/T1039.json
- /api/mappings/T1039.json
- /api/confluence/T1039.md
Example curl:
curl https://atlas.basyrix.com/api/recommendations/T1039.json Response:
{
"technique_id": "T1039",
"name": "Data from Network Shared Drive",
"priority": "high",
"status": "complete",
"version": "0.1.0",
"last_reviewed": "2026-07-23",
"generated_by": "SOC Response Atlas by Basyrix",
"tactics": [
"Collection"
],
"platforms": [
"Linux",
"macOS",
"Windows"
],
"summary": "Adversaries may search network shares on computers they have compromised to find files of interest. Sensitive data can be collected from remote systems via shared network drives (host shared directory, network file server, etc.) that are accessible from the current system prior to Exfiltration. Interactive command shells may be in use, and common functionality within [cmd](https://attack.mitre.org/software/S0106) may be used to gather information.",
"soc_recommendation": "Investigate Data from Network Shared Drive activity in the context of Collection: confirm scope, affected host/identity, and whether it matches expected administrative behaviour before deciding this is benign.",
"d3fend_mappings": [
{
"id": "D3-NRAM",
"name": "Network Resource Access Mediation",
"relationship": "isolate",
"practical_action": "Apply Network Resource Access Mediation to contain the blast radius once this technique is observed.",
"tooling": [
"Sentinel",
"Defender for Endpoint"
]
},
{
"id": "D3-DNR",
"name": "Decoy Network Resource",
"relationship": "deceive",
"practical_action": "Deploy Decoy Network Resource to detect and mislead an adversary attempting this technique.",
"tooling": [
"Sentinel",
"Defender for Endpoint"
]
}
],
"investigation_steps": {
"microsoft": [
"project",
"order by TimeGenerated desc",
"where SenderFromAddress == \"<USER_EMAIL>\"",
"where Timestamp >= ago(7d)",
"where DeliveryAction != \"Blocked\"",
"project Timestamp, SenderFromAddress, RecipientEmailAddress, Subject, ThreatTypes",
"order by Timestamp desc",
"where UserId == \"<USER_FROM_ALERT>\""
],
"generic": [
"Confirm whether the observed data from network shared drive 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: \"SharePoint External Sharing Link Surge\" -- Replaces the duplicate mass-download logic with a distinct collection signal: users rapidly creating anonymous or external sharing links across SharePoint/OneDrive."
]
},
"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": "Delete the forwarding rule",
"category": "Containment",
"risk": "Low",
"automation_safe": false,
"approval_required": true,
"tool": "See investigation guide",
"notes": "Exchange Online admin → Mailboxes → [user] → Manage email apps / inbox rules"
},
{
"name": "Revoke all active sessions",
"category": "Containment",
"risk": "Medium",
"automation_safe": false,
"approval_required": true,
"tool": "Entra ID",
"notes": "Entra ID portal → Users → [user] → Revoke sessions"
},
{
"name": "Reset password",
"category": "Containment",
"risk": "Medium",
"automation_safe": false,
"approval_required": true,
"tool": "See investigation guide",
"notes": "and force MFA re-enrolment"
},
{
"name": "Notify user's manager and finance team",
"category": "Containment",
"risk": "Low",
"automation_safe": true,
"approval_required": false,
"tool": "See investigation guide",
"notes": "if the user is in a financial role"
},
{
"name": "Audit sent items",
"category": "Containment",
"risk": "Low",
"automation_safe": true,
"approval_required": false,
"tool": "See investigation guide",
"notes": "check for BEC payment instruction emails sent from the compromised account"
},
{
"name": "Check for additional inbox rules (delete, mark-as-read, hide",
"category": "Eradication",
"risk": "Low",
"automation_safe": true,
"approval_required": false,
"tool": "See investigation guide",
"notes": "Check for additional inbox rules (delete, mark-as-read, hide rules)"
},
{
"name": "Review all emails sent from the account in the past 7 days",
"category": "Eradication",
"risk": "Low",
"automation_safe": true,
"approval_required": false,
"tool": "See investigation guide"
},
{
"name": "Alert the customer's finance team to verify any recent payme",
"category": "Eradication",
"risk": "Low",
"automation_safe": false,
"approval_required": true,
"tool": "See investigation guide",
"notes": "Alert the customer's finance team to verify any recent payment instructions or supplier detail changes"
}
],
"queries": {
"kql": [
{
"name": "GEN-CO-001 — SharePoint External Sharing Link Surge",
"description": "Replaces the duplicate mass-download logic with a distinct collection signal: users rapidly creating anonymous or external sharing links across SharePoint/OneDrive. (Source: Bell Integration baseline detection library.)",
"query": "let lookback = 1d;\nlet shareThreshold = 10;\nlet officeActivitySource =\n union isfuzzy=true OfficeActivity,\n (datatable(TimeGenerated:datetime, OfficeWorkload:string, Operation:string, UserId:string, ClientIP:string, Site_Url:string, SourceFileName:string, ObjectId:string, UserAgent:string, TargetUserOrGroupType:string, TargetUserOrGroupName:string)[]);\nofficeActivitySource\n| where TimeGenerated >= ago(lookback)\n| where OfficeWorkload in~ (\"SharePoint\", \"OneDrive\")\n| where Operation in~ (\"AnonymousLinkCreated\", \"SharingSet\", \"SecureLinkCreated\", \"AddedToSecureLink\", \"CompanyLinkCreated\")\n| extend UserPrincipalName = tolower(tostring(UserId)),\n TargetType = tostring(column_ifexists(\"TargetUserOrGroupType\", \"\")),\n TargetName = tostring(column_ifexists(\"TargetUserOrGroupName\", \"\"))\n| where isnotempty(UserPrincipalName)\n| summarize\n StartTime = min(TimeGenerated),\n EndTime = max(TimeGenerated),\n ShareActionCount = count(),\n UniqueObjects = dcount(ObjectId),\n Sites = make_set(Site_Url, 50),\n SharedObjects = make_set(ObjectId, 50),\n TargetTypes = make_set(TargetType, 20),\n TargetNames = make_set(TargetName, 20),\n SourceIPs = make_set(ClientIP, 20),\n UserAgents = make_set(UserAgent, 10)\n by UserPrincipalName\n| where ShareActionCount >= shareThreshold or UniqueObjects >= shareThreshold\n| extend AccountName = tostring(split(UserPrincipalName, \"@\")[0]),\n UPNSuffix = tostring(split(UserPrincipalName, \"@\")[1])\n| project StartTime, EndTime, UserPrincipalName, AccountName, UPNSuffix,\n ShareActionCount, UniqueObjects, Sites, SharedObjects, TargetTypes, TargetNames, SourceIPs, UserAgents\n| extend TimeGenerated = EndTime,\n timestamp = EndTime,\n AccountCustomEntity = UserPrincipalName\n| order by ShareActionCount desc, UniqueObjects desc"
},
{
"name": "aba0b08c-aace-40c5-a21d-39153023dcaa — Excessive share permissions",
"description": "The query searches for event 5143, which is triggered when a share is created or changed and includes de share permissions. First it checks to see if this is a whitelisted share for the system (e.g. domaincontroller netlogon, printserver print$ etc.). The share permissions are then checked against 'allow' rule (A) for a number of well known overly permissive groups, like all users, guests, authenticated users etc. If these are found, an alert is raised so the share creation may be audited. Note: this rule only checks for changed permissions, to prevent repeat alerts if for example a comment is changed, but the permissions are not altered. (Source: Microsoft's official Azure-Sentinel Detections (MIT licensed).)",
"query": "let timeframe=1h;\nlet system_roles = datatable(role:string, system:string) // Link roles to systems.\n [\"DC\",\"dc1.corp.local\",\n \"DC\",\"dc2.corp.local\",\n \"PRINT\",\"printer.corp.local\"\n ];\nlet share_roles = datatable(role:string, share:string) // Link roles to shares.\n [\"DC\", @\"\\\\*\\sysvol\",\n \"DC\",@\"\\\\*\\netlogon\",\n \"PRINT\",@\"\\\\*\\print$\"];\nlet allowed_system_shares = system_roles // Link systems to shares.\n | join kind=inner share_roles on role\n | extend system = tolower(system), share = tolower(share)\n | project-away role\n | summarize allowed_shares = make_set(share) by system;\nlet monitored_principals=datatable(identifier:string, Group_Name:string) // Define a data-table with groups to monitor.\n [\"AN\", \"Anonymous Logon\", // We accept the 'alias' for these well-known SIDS.\n \"AU\", \"Authenticated Users\",\n \"BG\",\"Built-in guests\",\n \"BU\",\"Built-in users\",\n \"DG\",\"Domain guests\",\n \"DU\",\"Domain users\",\n \"WD\",\"Everyone\",\n \"IU\",\"Interactively Logged-on users\",\n \"LG\",\"Local Guest\",\n \"NU\",\"Network logon users\",\n \"513\", \"Domain Users\", // Support matching on the last part of a SID.\n \"514\", \"Domain Guests\",\n \"545\", \"Builtin Users\",\n \"546\", \"Builtin Guests\",\n \"S-1-5-7\", \"Anonymous Logon\" // For the global SIDS, we accept them as-is.\n ];\nSecurityEvent\n| where TimeGenerated >= ago(timeframe)\n| where EventID == 5143\n| extend EventXML = parse_xml(EventData)\n| extend OldSD = tostring(EventXML[\"EventData\"][\"Data\"][13][\"#text\"]) // Grab the previous Security Descriptor.\n| extend NewSD = tostring(EventXML[\"EventData\"][\"Data\"][14][\"#text\"]) // Grab the new Security Descriptor.\n| project-away EventXML\n| where tostring(OldSD) !~ tostring(NewSD) // Don't bother with unchanged permissions.\n| extend system = tolower(Computer), share=tolower(ShareName) // Normalize system and share name for matching with whitelist.\n| join kind=leftouter allowed_system_shares on system // Retrieve the allowed shares per system.\n| where not(set_has_element(allowed_shares, share)) // Check if the current share is an allowed share.\n| project-away system, share, allowed_shares // Get rid of temporary fields.\n| extend DACLS = extract_all(@\"(D:(?:\\((?:[\\w\\-]*;){5}(?:[\\w\\-]*)\\))*)\", tostring(NewSD)) // Grab all instances of D:(DACL), in case there are multiple sets.\n| project-away OldSD, NewSD // Get rid of data we no longer need.\n| mv-expand DACLS to typeof(string) // In case there are any duplicate/subsequent D: entries (e.g., D:<dacls>S:<sacls>D:<dacls>) split them out to individual D: sets.\n| extend DACLS = substring(DACLS,2) // Strip the leading D:.\n| extend DACLS = split(DACLS, \")\") // Split the sets of DACLS ()() to an array of individual DACLS (). This removes the trailing ) character.\n| mv-expand DACLS to typeof(string) // Duplicate the records in such a way that only 1 DACL per record exist. We will aggregate them back later.\n| extend DACLS = substring(DACLS, 1) // Also remove the leading ( character.\n| where not(isempty(DACLS)) and DACLS startswith \"A;\" // Remove any empty or non-allow DACLs.\n| extend allowed_principal = tostring(split(DACLS,\";\",5)[0]) // Grab the SID what is affected by this DACL.\n| extend allowed_principal = iff(not(allowed_principal startswith \"S-\" and string_size(allowed_principal) > 15), allowed_principal, split(allowed_principal,\"-\",countof(allowed_principal,\"-\"))[0]) // This line takes only the last part (e.g., 513) of a long SID, so you can refer to groups/users without needing to supply the full SID above.\n| join kind=inner monitored_principals on $left.allowed_principal == $right.identifier // Join the found groups to the table of groups to be monitored above. Adds the more readable 'group_name).\n| project-away allowed_principal, identifier, DACLS\n| summarize Authorized_Public_Principals = make_set(Group_Name), take_any(*) by TimeGenerated, SourceComputerId, EventData // Summarize the fields back, making a set of the various group_name values for this record.\n| project-away Group_Name\n// Begin client-specific filter.\n// End client-specific filter."
}
],
"spl": [],
"esql": [
{
"name": "4c59cff1-b78a-41b8-a9f1-4231984d1fb6 — PowerShell Share Enumeration Script",
"description": "(KUERY) Detects PowerShell scripts that use ShareFinder functions (Invoke-ShareFinder/Invoke-ShareFinderThreaded) or Windows share enumeration APIs (shi1_netname/shi1_remark with NetShareEnum/NetApiBufferFree). Attackers use share enumeration to map accessible network shares for collection, lateral movement, or ransomware targeting. (Source: Elastic's official detection-rules repository (Elastic License v2).)",
"query": "event.category:process and host.os.type:windows and\n powershell.file.script_block_text:(\n \"Invoke-ShareFinder\" or\n \"Invoke-ShareFinderThreaded\" or\n (\n \"shi1_netname\" and\n \"shi1_remark\"\n ) or\n (\n \"NetShareEnum\" and\n \"NetApiBufferFree\"\n )\n ) and not user.id : \"S-1-5-18\""
},
{
"name": "61ac3638-40a3-44b2-855a-985636ca985e — PowerShell Suspicious Discovery Related Windows API Functions",
"description": "(KUERY) Detects PowerShell scripts that references native Windows API functions commonly used for discovery of users, groups, shares, sessions, domain trusts, and service security. Attackers use these APIs for situational awareness and targeting prior to lateral movement or collection. (Source: Elastic's official detection-rules repository (Elastic License v2).)",
"query": "event.category:process and host.os.type:windows and\n powershell.file.script_block_text : (\n NetShareEnum or\n NetWkstaUserEnum or\n NetSessionEnum or\n NetLocalGroupEnum or\n NetLocalGroupGetMembers or\n DsGetSiteName or\n DsEnumerateDomainTrusts or\n WTSEnumerateSessionsEx or\n WTSQuerySessionInformation or\n LsaGetLogonSessionData or\n QueryServiceObjectSecurity or\n GetComputerNameEx or\n NetWkstaGetInfo or\n GetUserNameEx or\n NetUserEnum or\n NetUserGetInfo or\n NetGroupEnum or\n NetGroupGetInfo or\n NetGroupGetUsers or\n NetWkstaTransportEnum or\n NetServerGetInfo or\n LsaEnumerateTrustedDomains or\n NetScheduleJobEnum or\n NetUserModalsGet\n ) and\n not powershell.file.script_block_text : (\n (\"DsGetSiteName\" and (\"DiscoverWindowsComputerProperties.ps1\" and \"param($SourceType, $SourceId, $ManagedEntityId, $ComputerIdentity)\")) or\n (\"# Copyright: (c) 2018, Ansible Project\" and \"#Requires -Module Ansible.ModuleUtils.AddType\" and \"#AnsibleRequires -CSharpUtil Ansible.Basic\") or\n (\"Ansible.Windows.Setup\" and \"Ansible.Windows.Setup\" and \"NativeMethods.NetWkstaGetInfo(null, 100, out netBuffer);\")\n ) and\n not file.directory: \"C:\\Program Files (x86)\\Automox\\WDK\\Win32\\WinSession\""
}
]
},
"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": [
"Data from Network Shared Drive 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 out-of-office forwarding to personal email — Verify with user and manager; add approved personal domain to TrustedEmailDomains watchlist with expiry date",
"Contractor forwarding work email to corporate account — Document as approved; add contractor's corporate domain to TrustedEmailDomains watchlist"
],
"confluence": {
"title": "T1039 - Data from Network Shared Drive Response Guidance",
"labels": [
"mitre",
"attack",
"d3fend",
"secops",
"basyrix"
],
"sections": [
"summary",
"d3fend_mappings",
"investigation_steps",
"response_actions",
"queries",
"automation",
"escalation_criteria",
"false_positive_considerations"
]
}
}