Query Details

Security Event Suspicious AD Attributes Accessed From Unexpected Source

Query

let query_frequency = 1h;
let query_period = 14d;
let object_threshold = 5;
let _Dictionary =
    _GetWatchlist("Value-WindowsEventsDictionary")
    | project Value = tostring(Value), Translation
;
let _ExpectedAccounts = toscalar(
    _GetWatchlist("Activity-ExpectedSignificantActivity")
    | where Activity in ("AccountOperator", "ADObjectAttributeAccess") and not(Notes has "[Group]")
    | summarize make_list(ActorPrincipalName)
);
let _ExpectedGroups = toscalar(
    _GetWatchlist("Activity-ExpectedSignificantActivity")
    | where Activity == "ADObjectAttributeAccess" and Notes has "[Group]"
    | summarize make_list(Auxiliar)
);
let _Identities =
    IdentityInfo
    | where TimeGenerated > ago(query_period) and isnotempty(AccountDomain)
    | summarize arg_max(TimeGenerated, *) by AccountObjectId, AccountSID
    | project
        SubjectUserName = coalesce(extract(@"(?i:CN\=)([^,]+)", 1, OnPremisesDistinguishedName), AccountName),
        GroupMembership = array_sort_asc(GroupMembership)
;
SecurityEvent
| where TimeGenerated > ago(2 * query_frequency)
| where EventID == 4662
    and (AccessMask in ("0x100", "0x10") or AccessList in ("%%7688", "%%7684") or Properties has_any ("%%7688", "%%7684"))
    and not(Account in (_ExpectedAccounts))
    and (Properties has_all (
            "612cb747-c0e8-4f92-9221-fdd5f15b550d", // unixUserPassword
            "6617e4ac-a2f1-43ab-b60c-11fbd1facf05", // ms-PKI-RoamingTimeStamp - Indicates the time of the last synchronization
            "b3f93023-9239-4f7c-b99c-6745d87adbc2", // ms-PKI-DPAPIMasterKeys - Stores the DPAPI Master Keys. These symmetric keys encrypt the private keys and are themselves encrypted.
            "b7ff5a38-0818-42b0-8110-d3d154c97f24", // ms-PKI-Credential-Roaming-Tokens
            "b8dfa744-31dc-4ef1-ac7c-84baf7ef9da7"  // ms-PKI-AccountCredentials - Stores certificates, certificate signing requests, private keys and saved passwords.
            )
        or Properties has_all (
            "612cb747-c0e8-4f92-9221-fdd5f15b550d", // unixUserPassword
            "bf967a9c-0de6-11d0-a285-00aa003049e2"  // Group
            )
        or Properties has_any (
            "7b8b558a-93a5-4af7-adca-c017e67f1057", // ms-DS-Group-Managed-Service-Account
            "ea715d30-8f53-40d0-bd1e-6109186d782c", // ms-FVE-RecoveryInformation
            "43061ac1-c8ad-4ccc-b785-2bfac20fc60a", // ms-FVE-RecoveryPassword
            "800d94d7-b7a1-42a1-b14d-7cae1423d07f", // ms-DS-Allowed-To-Delegate-To
            "3f78c3e5-f79a-46bd-a0b8-9d18116ddc79"  // ms-DS-Allowed-To-Act-On-Behalf-Of-Other-Identity
        ))
| summarize
    StartTime = min(TimeGenerated),
    EndTime = max(TimeGenerated),
    EventCount = count(),
    ObjectNameCount = count_distinct(ObjectName),
    ObjectNamesSample = array_sort_asc(make_set_if(trim(@"[\%\{\}\s]+", ObjectName), isnotempty(ObjectName), 20)),
    SubjectLogonId = make_set_if(SubjectLogonId, isnotempty(SubjectLogonId), 20),
    Properties = make_set_if(Properties, isnotempty(Properties), 50),
    take_any(Activity, OperationType, AccountType, SubjectUserName, AccessList, EventOriginId)
    by Computer, Account, AccessMask, ObjectType
| where ObjectNameCount > object_threshold
| where not(ObjectNameCount < 100 and ObjectType has_any ("bf967aba-0de6-11d0-a285-00aa003049e2", "bf967a86-0de6-11d0-a285-00aa003049e2")) // User Computer
| lookup kind=leftouter _Identities on SubjectUserName
| project-away SubjectUserName
| where not(array_length(_ExpectedGroups) > 0 and GroupMembership has_all (_ExpectedGroups))
| extend AccessList = trim(@"\s+", AccessList)
| lookup kind=leftouter (
    _Dictionary
    | project
        AccessList = Value,
        AccessListTranslated = Translation
    ) on AccessList
| extend ObjectType = trim(@"[\%\{\}\s]+", ObjectType)
| lookup kind=leftouter (
    _Dictionary
    | project
        ObjectType = Value,
        ObjectTypeTranslated = Translation
    ) on ObjectType
| mv-expand Properties to typeof(string)
| mv-expand PropertiesKey = split(translate("{}", "", trim(@"\s+", replace_regex(Properties, @"\s+", " "))), " ") to typeof(string)
| lookup kind=leftouter (
    _Dictionary
    | project
        PropertiesKey = Value,
        PropertiesKeyTranslated = Translation
    )
    on PropertiesKey
| summarize
    take_any(*),
    PropertiesTranslated = strcat_array(make_list(iff(isnotempty(PropertiesKeyTranslated), PropertiesKeyTranslated, PropertiesKey)), " | ")
    by EventOriginId
| as hint.materialized=true _Auxiliar
| mv-expand SubjectLogonId to typeof(string)
| lookup kind=leftouter (
    SecurityEvent
    | where TimeGenerated > ago(query_frequency + 1h)
    | where EventID == 4624 and Account in (toscalar(_Auxiliar | summarize make_list(Account)))
    | project Computer, Account, TargetLogonId, IpAddress
    )
    on Computer, Account, $left.SubjectLogonId == $right.TargetLogonId
| summarize
    SubjectLogonIds = make_set(SubjectLogonId),
    IpAddresses = make_set_if(IpAddress, isnotempty(IpAddress)),
    take_any(*)
    by EventOriginId
| summarize
    StartTime = min(StartTime),
    EndTime = max(EndTime),
    Computers = make_set(Computer),
    SubjectLogonIds = make_set(SubjectLogonIds),
    IpAddresses = make_set(IpAddress),
    EventCount = sum(EventCount),
    AccessLists = make_set(AccessList),
    AccessListsTranslated = make_set(AccessListTranslated),
    ObjectTypes = make_set(ObjectType),
    ObjectTypesTranslated = array_sort_asc(make_set(ObjectTypeTranslated)),
    ObjectNameCount = sum(ObjectNameCount),
    ObjectNamesSample = make_set(ObjectNamesSample),
    Properties = make_set(Properties),
    PropertiesTranslated = make_set(PropertiesTranslated),
    take_any(AccountType, Activity, OperationType, GroupMembership)
    by Account
| where EndTime > ago(query_frequency)
| where not(array_length(ObjectTypes) == 1 and ObjectTypes has_any ("bf967aba-0de6-11d0-a285-00aa003049e2", "bf967a86-0de6-11d0-a285-00aa003049e2", "bf967a9c-0de6-11d0-a285-00aa003049e2")) // User Computer Group
| project
    StartTime,
    EndTime,
    Computers,
    Account,
    AccountType,
    SubjectLogonIds,
    IpAddress = tostring(IpAddresses[0]),
    IpAddresses,
    Activity,
    EventCount,
    OperationType,
    AccessLists,
    AccessListsTranslated,
    ObjectTypes,
    ObjectTypesTranslated,
    ObjectNameCount,
    ObjectNameEventCountRatio = round(ObjectNameCount / toreal(EventCount), 2),
    ObjectNamesSample,
    Properties,
    PropertiesTranslated,
    AccountGroupMembership = GroupMembership

Explanation

This KQL (Kusto Query Language) query is designed to analyze security events, specifically looking for unusual or potentially suspicious activities related to Active Directory object access. Here's a simplified breakdown of what the query does:

  1. Setup and Configuration:

    • It defines some parameters like query_frequency, query_period, and object_threshold to control the time frame and thresholds for the analysis.
    • It retrieves data from watchlists to identify expected accounts and groups that are allowed to perform significant activities.
  2. Data Preparation:

    • It gathers identity information from the IdentityInfo table to map user accounts and their group memberships.
    • It creates a dictionary from a watchlist to translate certain event values into more understandable terms.
  3. Event Filtering:

    • The query focuses on security events with EventID 4662, which indicates an operation was performed on an Active Directory object.
    • It filters out events involving expected accounts and checks for specific access properties that might indicate sensitive operations.
  4. Event Aggregation:

    • It summarizes the events by counting occurrences, distinct object names, and other attributes.
    • It filters out events that do not meet certain criteria, such as having too few distinct object names.
  5. Data Enrichment:

    • It enriches the data by looking up additional information about user identities and translating technical codes into human-readable terms using the dictionary.
  6. Final Aggregation and Output:

    • The query aggregates the results by account, summarizing various attributes like computers involved, IP addresses, access lists, object types, and properties.
    • It calculates a ratio of distinct object names to event count to help identify unusual patterns.
    • Finally, it projects the relevant fields for output, providing a concise summary of potentially suspicious activities.

In essence, this query is designed to detect and summarize unusual access patterns to Active Directory objects, helping security analysts identify potential security incidents.