FalconForce unknown stable kql

AWS Instance Profile Credentials Used from Unexpected IP

This query searches for API calls made by credentials originating from an instance profile. It creates a summary of the external IP addresses used for these calls. When the same instance is observed making calls from multiple IP addresses, this is considered suspicious and the rule triggers.

View Source

Detection Logic

let timeframe = 2*1d;
let RuleId = "0240";
let DedupFields = dynamic(["TimeGenerated"]);
let AgentAPIs = dynamic(["AcknowledgeMessage","DeleteMessage","FailMessage","GetEndpoint","GetMessages","SendReply","CreateControlChannel","CreateDataChannel","OpenControlChannel","OpenDataChannel","DescribeDocumentParameters","DescribeInstanceProperties","GetCalendar","GetManifest","ListInstanceAssociations","PutCalendar","PutConfigurePackageResult","RegisterManagedInstance","RequestManagedInstanceRoleToken","UpdateInstanceAssociationStatus","UpdateInstanceInformation","UpdateManagedInstancePublicKey"]);
let InstanceAPICalls=(
    AWSCloudTrail
| where ingestion_time() >= ago(timeframe)
| where EventName !in~ (AgentAPIs) // Exclude SSM Agent API calls
| where UserIdentityPrincipalid contains_cs ":i-"
| parse UserIdentityPrincipalid with * ":i-" InstanceId
| where not(ipv4_is_match(SourceIpAddress, "198.18.0.0/15")) // AWS interconnect.
| where not(ipv4_is_in_range(SourceIpAddress, "18.202.0.0/15") or ipv4_is_in_range(SourceIpAddress, "34.240.0.0/12") or ipv4_is_in_range(SourceIpAddress, "52.16.0.0/15")) // AWS IP ranges.
| where not(SourceIpAddress =~ "AWS Internal")
| where not(ipv4_is_private(SourceIpAddress))
    // Begin environment-specific filter.
    // End environment-specific filter.
);
let InstancesFromMultipleIPs=(
    InstanceAPICalls
| summarize IPCount=dcount(SourceIpAddress) by InstanceId
| where IPCount > 1
);
InstanceAPICalls
// Find calls that originate from an instance which has multiple known IPs.
| lookup kind=inner InstancesFromMultipleIPs on InstanceId
// Find the first event issued by the Source IP that made the least number of calls since that is likely to be
// a request issued by the attacker.
| summarize arg_min(TimeGenerated, *), EventCount=count(), EventNames=make_set(EventName) by InstanceId, SourceIpAddress
| summarize arg_min(EventCount,*), ObservedIps=make_set(SourceIpAddress),RequestCountByIp=make_bag(bag_pack(SourceIpAddress, EventCount)),EventsByIp=make_bag(bag_pack(SourceIpAddress, EventNames)) by InstanceId
// Begin environment-specific filter.
// End environment-specific filter.
// Begin de-duplication logic.
| extend DedupFieldValues=pack_all()
| mv-apply e=DedupFields to typeof(string) on (
    extend DedupValue=DedupFieldValues[tostring(e)]
| order by e // Sorting is required to ensure make_list is deterministic.
| summarize DedupValues=make_list(DedupValue)
)
| extend DedupEntity=strcat_array(DedupValues, "
| ")
| project-away DedupFieldValues, DedupValues
| join kind=leftanti (
    SecurityAlert
| where AlertName has RuleId and ProviderName has "ASI"
| where TimeGenerated >= ago(timeframe)
| extend DedupEntity = tostring(parse_json(tostring(parse_json(ExtendedProperties)["Custom Details"])).DedupEntity[0])
| project DedupEntity
) on DedupEntity
// End de-duplication logic.

Field Validations

Loading…

Comments (0)

Loading comments...