Detection rules › YARA-L

Hunt for Non-Anonymous Office 365 file downloads

Severity
low
Type
hunt
Time window
5m
Match by
ip
Author
Google Cloud Security
Source
github.com/chronicle/detection-rules

Detects file downloads using O365 or Graph Activity logs, not including anonymous file links

MITRE ATT&CK coverage

Telemetry coverage

PlatformRecord / event type
Microsoft 365FileDownloaded

Rules detecting the same action

These rules filter on the same operation.

Rule body

// Copyright 2025 Google LLC. Licensed under Apache-2.0.

 rule o365_file_download {

  meta:
    author = "Google Cloud Security"
    description = "Detects file downloads using O365 or Graph Activity logs, not including anonymous file links"
    rule_id = "mr_0dfb4338-4b4c-4af1-82da-fd5221d611a0"
    rule_name = "Hunt for Non-Anonymous Office 365 file downloads"
    assumption = "Because file downloads occur all the time, additional criteria to narrow this rule is expected. Areas to filter include user agent, specific users, IPs, applications and folders or items in the directory structure."
    tactic = "TA0010"
    technique = "T1048.002"
    type = "hunt"
    platform = "azure"
    data_source = "o365, ms graph activity logs"
    severity = "Low"
    priority = "Low"

  events:
    (
        $file.metadata.event_type = "USER_RESOURCE_UPDATE_CONTENT" and
        $file.metadata.product_event_type = "FileDownloaded" and
        $file.metadata.product_name = "Office 365" and
        $file.metadata.vendor_name = "Microsoft" and
        //This could be modified to specify downloading only thorugh specific applications
        //$file.target.application = "OneDrive" and
        //Add folder or docs of interest to monitor for downloads like this - focus in example is pdf in R&D folder - could also use a list
        //re.regex($file.src.url, `^https://.*sharepoint.com/sites/.*/R&D/.*\.pdf$`) nocase and
        NOT (
            $file.principal.user.userid = /^urn:spo:anon#/ or
            $file.principal.user.userid = "anonymous"
        )
    )
    or
    (
        $file.metadata.event_type = "NETWORK_HTTP" and
        $file.metadata.product_event_type = "Microsoft Graph Activity" and
        $file.network.http.method = "GET" and
        $file.network.http.response_code = 302 //and
        //Could modify this to focus on a specific UA string or a UA strings not commonly used in environment
        //$file.network.http.user_agent = /PowerShell/ nocase
        //Could tighten to specify drives or items using an example like this
        //re.regex($file.target.url, `^https://graph.microsoft.com/.*/drives/.*/content$`) nocase
    )
    $file.principal.ip = $ip

  match:
    $ip over 5m

  outcome:
    $risk_score = 35
    $event_count = count_distinct($file.metadata.id)
    $referral_url = array_distinct($file.network.http.referral_url)
    $user_agent = array_distinct($file.network.http.user_agent)
    $principal_application = array_distinct($file.principal.application)
    $principal_ip = array_distinct($file.principal.ip)
    $target_application = array_distinct($file.target.application)
    //$principal_user_email_address = array_distinct(principal.user.email_addresses)
    $principal_user_userid = array_distinct($file.principal.user.userid)
    $src_file_full_path = array_distinct($file.src.file.full_path)
    $src_url = array_distinct($file.src.url)
    $session = array_distinct($file.network.session_id)
    $location = array_distinct($file.principal.location.name)
    $target_resource_guid = array_distinct($file.target.resource.product_object_id)
    $target_url = array_distinct($file.target.url)

  condition:
    $file
}

YARA-L rule structure

Condition

Fires when at least one $file event in the 5m window.

Events

$file USER_RESOURCE_UPDATE_CONTENT

  • metadata.product_event_type = "FileDownloaded"
  • principal.user.userid matches "^urn:spo:anon#"
  • principal.user.userid = "anonymous"
  • metadata.product_event_type = "Microsoft Graph Activity"
  • network.http.method = "GET"
  • network.http.response_code = "302"

Match and correlation

Match key
$ip (principal.ip)
Within
5m

Outcome

Outcome variables add context to a detection. When the rule generates an alert, $risk_score is displayed with it.

FieldExpression
risk_score35
event_countcount_distinct($file.metadata.id)
referral_urlarray_distinct($file.network.http.referral_url)
user_agentarray_distinct($file.network.http.user_agent)
principal_applicationarray_distinct($file.principal.application)
principal_iparray_distinct($file.principal.ip)
target_applicationarray_distinct($file.target.application)
principal_user_useridarray_distinct($file.principal.user.userid)
src_file_full_patharray_distinct($file.src.file.full_path)
src_urlarray_distinct($file.src.url)
sessionarray_distinct($file.network.session_id)
locationarray_distinct($file.principal.location.name)
target_resource_guidarray_distinct($file.target.resource.product_object_id)
target_urlarray_distinct($file.target.url)