# 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.
