Query Details

Multiple IP Entity AWS Cloud Trail

Query

// This query assumes a feed of threat indicators is ingested/synchronized periodically, and each synchronization ingests new indicators and only old indicators that have been modified.
// Active threat indicators in Sentinel are renovated as ThreatIntelligenceIndicator events every ~12 days.
let query_frequency = 1h;
let query_period = 14d;
let query_wait = 0h;
let table_query_lookback = 14d;
let _TIBenignProperty =
    _GetWatchlist('ID-TIBenignProperty')
    | where Notes has_any ("[SourceIPAddress]")
    | project IndicatorId, BenignProperty
;
let _TIExcludedSources = toscalar(
    _GetWatchlist('Activity-ExpectedSignificantActivity')
    | where Activity == "ThreatIndicatorSource"
    | summarize make_list(Auxiliar)
    );
let _TITableMatch = (table_start: datetime, table_end: datetime, only_new_ti: boolean, ti_start: datetime = datetime(null)) {
    // Scheduled Analytics rules have a query period limit of 14d
    let _Indicators =// materialize(
        ThreatIntelligenceIndicator
        | where TimeGenerated > ago(query_period)
        // Take the earliest TimeGenerated and the latest column info
        | summarize hint.strategy=shuffle
            minTimeGenerated = min(TimeGenerated),
            arg_max(TimeGenerated, Active, Description, ActivityGroupNames, IndicatorId, ThreatType, DomainName, Url, ExpirationDateTime, ConfidenceScore, SourceSystem, Tags, AdditionalInformation, ExternalIndicatorId, NetworkIP, NetworkSourceIP, NetworkDestinationIP, EmailSourceIpAddress)
            by IndicatorId
        // Remove inactive or expired indicators
        | where not(not(Active) or ExpirationDateTime < now())
        // Pick indicators that contain the desired entity type
        | mv-expand IPAddress = pack_array(NetworkIP, NetworkSourceIP, NetworkDestinationIP, EmailSourceIpAddress) to typeof(string)
        | where isnotempty(IPAddress)
        | extend TI_IPAddress = IPAddress
        // Remove indicators from specific sources
        | where not(AdditionalInformation has_any (_TIExcludedSources) or Description has_any (_TIExcludedSources))
        // Remove excluded indicators with benign properties
        | join kind=leftanti _TIBenignProperty on IndicatorId, $left.IPAddress == $right.BenignProperty
        // Deduplicate indicators by IPAddress column, equivalent to using join kind=innerunique afterwards
        | summarize hint.strategy=shuffle
            minTimeGenerated = min(minTimeGenerated),
            take_any(*)
            by IPAddress
        // If we want only new indicators, remove indicators received previously
        | where not(only_new_ti and minTimeGenerated < ti_start)
    //)
    ;
    //let _IndicatorsLength = toscalar(_Indicators | summarize count());
    //let _IndicatorsPrefilter = toscalar(
    //    _Indicators
    //    | extend AuxiliarField = tostring(extract(@"([0-9A-Za-f]+)[\.\:]", 1, IPAddress))
    //    | summarize make_set_if(AuxiliarField, isnotempty(AuxiliarField), 10000)
    //);
    //let _IndicatorsPrefilterLength = array_length(_IndicatorsPrefilter);
    let _TableEvents =
        AWSCloudTrail
        | where ingestion_time() between (table_start .. table_end)
        // Filter events that may contain indicators
        | where isnotempty(SourceIpAddress) and (isnotempty(parse_ipv4(SourceIpAddress)) or isnotempty(parse_ipv6(SourceIpAddress)))
        //| where not(_IndicatorsPrefilterLength < 10000 and not(SourceIpAddress has_any (_IndicatorsPrefilter))) // "has_any" limit 10000
        //| where not(_IndicatorsLength < 1000000 and not(SourceIpAddress in (toscalar(_Indicators | summarize make_list(TI_IPAddress))))) // "in" limit 1.000.000
        | extend IPAddress = SourceIpAddress
        | project-rename AWSCloudTrail_TimeGenerated = TimeGenerated
    ;
    _Indicators
    | join kind=inner hint.strategy=shuffle _TableEvents on IPAddress
    // Take only a single event by key columns
    //| summarize hint.strategy=shuffle take_any(*) by IPAddress, UserIdentityAccountId
    | project
        AWSCloudTrail_TimeGenerated,
        Description,
        ActivityGroupNames,
        IndicatorId,
        ThreatType,
        DomainName,
        Url,
        ExpirationDateTime,
        ConfidenceScore,
        SourceSystem,
        Tags,
        AdditionalInformation,
        TI_IPAddress,
        NetworkIP,
        NetworkSourceIP,
        NetworkDestinationIP,
        EmailSourceIpAddress,
        EventTypeName,
        EventSource,
        EventName,
        ManagementEvent,
        ReadOnly,
        UserIdentityType,
        UserIdentityAccountId,
        UserIdentityArn,
        UserIdentityUserName,
        SourceIpAddress,
        UserAgent,
        SessionMfaAuthenticated,
        ErrorCode,
        ErrorMessage,
        Resources,
        RequestParameters,
        ResponseElements,
        AdditionalEventData,
        AwsEventId
};
union// isfuzzy=true
    // Match      current table events                                all indicators available
    _TITableMatch(ago(query_frequency + query_wait), ago(query_wait), false),
    // Match      past table events                                                          new indicators since last query execution
    _TITableMatch(ago(table_query_lookback + query_wait), ago(query_frequency + query_wait), true, ago(query_frequency))
| summarize arg_max(AWSCloudTrail_TimeGenerated, *) by IndicatorId, UserIdentityAccountId
| extend
    timestamp = AWSCloudTrail_TimeGenerated,
    IPCustomEntity = TI_IPAddress

Explanation

This query is designed to identify and analyze potential security threats by matching threat indicators with AWS CloudTrail events. Here's a simplified breakdown of what the query does:

  1. Setup and Parameters:

    • The query is set to run every hour (query_frequency = 1h) and looks back over a period of 14 days (query_period = 14d).
    • It uses a wait time of 0 hours (query_wait = 0h) and considers events from the last 14 days (table_query_lookback = 14d).
  2. Exclusions and Filters:

    • It retrieves a list of benign properties and expected significant activities from watchlists to exclude certain indicators and sources from the analysis.
  3. Threat Indicator Processing:

    • The query fetches threat indicators from the ThreatIntelligenceIndicator table, focusing on active and non-expired indicators.
    • It expands these indicators to include various IP address fields and excludes those from specific sources or with benign properties.
    • It deduplicates indicators by IP address and optionally filters out indicators that were received previously.
  4. AWS CloudTrail Event Matching:

    • It retrieves events from the AWSCloudTrail table within specified time frames.
    • Filters these events to include only those with valid source IP addresses.
    • Matches these events with the threat indicators based on IP addresses.
  5. Result Compilation:

    • The query combines results from matching current events with all available indicators and past events with new indicators.
    • It summarizes the results to ensure only the most recent event for each indicator and user account is included.
  6. Output:

    • The final output includes detailed information about the matched threat indicators and corresponding AWS CloudTrail events, such as timestamps, descriptions, threat types, and user identities.

In essence, this query is a comprehensive mechanism to correlate threat intelligence with AWS activity logs, helping to identify potential security threats based on known indicators.

Details

Jose Sebastián Canós profile picture

Jose Sebastián Canós

Released: December 13, 2023

Tables

ThreatIntelligenceIndicatorAWSCloudTrail

Keywords

ThreatIntelligenceIndicatorAWSCloudTrailUserIdentityAccountIdSourceIpAddressIndicatorId

Operators

lethas_anyprojecttoscalarwheresummarizemake_listdatetimematerializeagominarg_maxmv-expandpack_arraytypeofisnotemptyextendjoinleftantitake_anybetweenparse_ipv4parse_ipv6project-renameunion

Actions

GitHub