Detection rules › Splunk

Cloud Compute Instance Created With Previously Unseen Image

Status
production
Severity
low
Group by
All_Changes.Instance_Changes.image_id, All_Changes.user
Author
David Dorsey, Splunk
Source
github.com/splunk/security_content

The following analytic detects the creation of cloud compute instances using previously unseen image IDs. It leverages cloud infrastructure logs to identify new image IDs that have not been observed before. This activity is significant because it may indicate unauthorized or suspicious activity, such as the deployment of malicious payloads or unauthorized access to sensitive information. If confirmed malicious, this could lead to data breaches, unauthorized access, or further compromise of the cloud environment. Immediate investigation is required to determine the legitimacy of the instance creation and to mitigate potential threats.

Rule body splunk

name: Cloud Compute Instance Created With Previously Unseen Image
id: bc24922d-987c-4645-b288-f8c73ec194c4
version: 10
creation_date: '2020-04-29'
modification_date: '2026-05-13'
author: David Dorsey, Splunk
status: production
type: Anomaly
description: The following analytic detects the creation of cloud compute instances using previously unseen image IDs. It leverages cloud infrastructure logs to identify new image IDs that have not been observed before. This activity is significant because it may indicate unauthorized or suspicious activity, such as the deployment of malicious payloads or unauthorized access to sensitive information. If confirmed malicious, this could lead to data breaches, unauthorized access, or further compromise of the cloud environment. Immediate investigation is required to determine the legitimacy of the instance creation and to mitigate potential threats.
data_source:
    - AWS CloudTrail
search: |-
    | tstats count earliest(_time) as firstTime, latest(_time) as lastTime values(All_Changes.object_id) as dest FROM datamodel=Change
      WHERE All_Changes.action=created
      BY All_Changes.Instance_Changes.image_id, All_Changes.user
    | `drop_dm_object_name("All_Changes")`
    | `drop_dm_object_name("Instance_Changes")`
    | where image_id != "unknown"
    | lookup previously_seen_cloud_compute_images image_id as image_id OUTPUT firstTimeSeen, enough_data
    | eventstats max(enough_data) as enough_data
    | where enough_data=1
    | eval firstTimeSeenImage=min(firstTimeSeen)
    | where isnull(firstTimeSeenImage) OR firstTimeSeenImage > relative_time(now(), "-24h@h")
    | table firstTime, user, image_id, count, dest
    | `security_content_ctime(firstTime)`
    | `cloud_compute_instance_created_with_previously_unseen_image_filter`
how_to_implement: You must be ingesting your cloud infrastructure logs from your cloud provider. You should run the baseline search `Previously Seen Cloud Compute Images - Initial` to build the initial table of images observed and times. You must also enable the second baseline search `Previously Seen Cloud Compute Images - Update` to keep this table up to date and to age out old data. You can also provide additional filtering for this search by customizing the `cloud_compute_instance_created_with_previously_unseen_image_filter` macro.
known_false_positives: After a new image is created, the first systems created with that image will cause this alert to fire.  Verify that the image being used was created by a legitimate user.
references: []
drilldown_searches:
    - name: View the detection results for - "$dest$"
      search: '%original_detection_search% | search  dest = "$dest$"'
      earliest_offset: $info_min_time$
      latest_offset: $info_max_time$
    - name: View risk events for the last 7 days for - "$dest$"
      search: '| from datamodel Risk.All_Risk | search normalized_risk_object IN ("$dest$") | stats count min(_time) as firstTime max(_time) as lastTime values(search_name) as "Search Name" values(risk_message) as "Risk Message" values(analyticstories) as "Analytic Stories" values(annotations._all) as "Annotations" values(annotations.mitre_attack.mitre_tactic) as "ATT&CK Tactics" by normalized_risk_object | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`'
      earliest_offset: 7d
      latest_offset: "0"
intermediate_findings:
    entities:
        - field: dest
          type: system
          score: 20
          message: User $user$ is creating an instance $dest$ with an image that has not been previously seen.
        - field: user
          type: user
          score: 20
          message: User $user$ is creating an instance $dest$ with an image that has not been previously seen.
analytic_story:
    - Cloud Cryptomining
asset_type: Cloud Compute Instance
mitre_attack_id: []
product:
    - Splunk Enterprise
    - Splunk Enterprise Security
    - Splunk Cloud
category: cloud
security_domain: threat
baselines:
    - Previously Seen Cloud Compute Images - Update
    - Previously Seen Cloud Compute Images - Initial
tests:
    - name: True Positive Test
      attack_data:
        - data: https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/suspicious_behaviour/abnormally_high_cloud_instances_launched/cloudtrail_behavioural_detections.json
          sourcetype: aws:cloudtrail
          source: aws_cloudtrail
      description: PORTED MANUAL TEST - This search needs the baseline `Previously Seen Cloud Compute Images - Initial` to be run first.
      test_type: experimental

Stages and Predicates

Stage 1: tstats

| tstats count earliest(_time) as firstTime, latest(_time) as lastTime values(All_Changes.object_id) as dest FROM datamodel=Change
  WHERE All_Changes.action=created
  BY All_Changes.Instance_Changes.image_id, All_Changes.user

Stage 2: search

| `drop_dm_object_name("All_Changes")`

Stage 3: search

| `drop_dm_object_name("Instance_Changes")`

Stage 4: where

| where image_id != "unknown"

Stage 5: lookup

| lookup previously_seen_cloud_compute_images image_id as image_id OUTPUT firstTimeSeen, enough_data
Lookup table
previously_seen_cloud_compute_images
Key field
image_id
Output columns
['firstTimeSeen', 'firstTimeSeen'], ['enough_data', 'enough_data']

Stage 6: eventstats

| eventstats max(enough_data) as enough_data

Stage 7: where

| where enough_data=1

Stage 8: eval

| eval firstTimeSeenImage=min(firstTimeSeen)

Stage 9: where

| where isnull(firstTimeSeenImage) OR firstTimeSeenImage > relative_time(now(), "-24h@h")

Stage 10: table

| table firstTime, user, image_id, count, dest

Stage 11: search

| `security_content_ctime(firstTime)`

Stage 12: search

| `cloud_compute_instance_created_with_previously_unseen_image_filter`

Indicators

Each row is a field, operator, and value that the rule matches. The corpus column counts how many other rules in the catalog look for the same combination: high numbers point to widely-used, community-vetted indicators. Blank or 1 shows that the indicator is specific to this rule.

FieldKindValues
All_Changes.actioneq
  • created
enough_dataeq
  • 1
firstTimeSeenImageis_null
  • (no value, null check)
image_idne
  • "unknown"