Detection rules › Elastic

AWS Discovery API Calls from VPN ASN for the First Time by Identity

Status
production
Severity
high
Time window
6m
Group by
cloud.account.id, source.as.number, user.name
Author
Elastic
Source
github.com/elastic/detection-rules

Flags the first time a given IAM principal invokes a narrow set of high-signal discovery APIs (credential check, account and IAM enumeration, bucket and compute inventory, logging introspection) from a source IP whose autonomous system number (ASN) matches a curated set commonly associated with consumer VPN brands, VPN-heavy hosting, and provider networks referenced in public reporting on TeamPCP activity (for example 31173 Services AB AS39351 and Oy Crea Nova Hosting Solution Ltd). Broad List*/Describe* patterns are intentionally omitted to reduce noise. Hosting ASNs are heavily dual-use; validate source.as.number in your data and extend event.action only when your baseline allows it.

Known false positives

  • Administrators, developers, CI runners, and SaaS egress often exit through Datacamp, M247, Vultr, Linode, or brand-name VPN ASNs. Expect more noise on hosting ASNs than on VPN-only registrations. Exclude approved principals, accounts, CIDRs, or ASNs after review. GeoIP and ASN enrichment gaps (`source.as.number` unset) will skip events entirely. Maintain the ASN list with local intelligence (for example RIPE, BGPView, or peeringdb).

MITRE ATT&CK coverage

Telemetry coverage

PlatformRecord / event type
AWSCloudTrail event ListFoundationModels: Returns a list of Amazon Bedrock foundation models available for use, including their providers, modalities, and inference types.
AWSCloudTrail event DescribeTrails: Returns metadata about one or more CloudTrail trails in the current region, including their ARNs, S3 destinations, and configuration settings.
AWSCloudTrail event LookupEvents: Searches CloudTrail management event records in the current region for events matching specified attribute filters such as event name, resource type, or username.
AWSCloudTrail event ListTables: Returns a paginated list of DynamoDB table names in the current AWS account and region.
AWSCloudTrail event DescribeInstances: Returns detailed information about one or more EC2 instances, including their state, type, network interfaces, and associated metadata.
AWSCloudTrail event DescribeRegions: Returns the AWS regions that are enabled for the caller's account, or all regions that are available to EC2.
AWSCloudTrail event DescribeSecurityGroups: Returns information about one or more EC2 security groups, including their inbound and outbound rules.
AWSCloudTrail event DescribeVpcs: Returns information about one or more VPCs in the account, including their CIDR blocks, state, and associated attributes.
AWSCloudTrail event GetAccountSummary: Retrieves a summary of IAM entity usage and quotas for the current AWS account.
AWSCloudTrail event ListAccessKeys: Returns metadata about the access keys associated with a specified IAM user, including key IDs, status, and creation dates.
AWSCloudTrail event ListAccountAliases: Lists the alias associated with the current AWS account, if any.
AWSCloudTrail event ListGroups: Returns a paginated list of IAM groups in the current AWS account.
AWSCloudTrail event ListMFADevices: Returns a list of MFA devices associated with an IAM user, or all virtual MFA devices in the account when no user is specified.
AWSCloudTrail event ListRoles: Returns a paginated list of IAM roles in the current AWS account, optionally filtered by path prefix.
AWSCloudTrail event ListUsers: Returns a paginated list of IAM users in the current AWS account, optionally filtered by path prefix.
AWSCloudTrail event ListAliases: Returns a paginated list of KMS key aliases in the account and region, including system-defined and user-defined aliases.
AWSCloudTrail event ListKeys: Returns a paginated list of KMS key IDs and ARNs for all customer-managed and AWS-managed keys in the current account and region.
AWSCloudTrail event ListFunctions: Returns a paginated list of Lambda functions in the current account and region, with optional filtering by runtime or layer.
AWSCloudTrail event ListAliases: Returns a list of aliases for a Lambda function.
AWSCloudTrail event DescribeDBInstances: Returns information about provisioned RDS DB instances, including their configuration, status, and endpoint details.
AWSCloudTrail event DescribeDBSnapshots: Returns information about DB snapshots for a specified DB instance or all snapshots accessible to the account.
AWSCloudTrail event ListBuckets: Lists all S3 buckets owned by the authenticated requester.
AWSCloudTrail event GetCallerIdentity: Returns details about the IAM user or role whose credentials are used to call the operation.

Rules detecting the same action

These rules filter on the same operation.

Rule body

[metadata]
creation_date = "2026/04/03"
integration = ["aws"]
maturity = "production"
updated_date = "2026/07/15"

[rule]
author = ["Elastic"]
description = """
Flags the first time a given IAM principal invokes a narrow set of high-signal discovery APIs (credential check,
account and IAM enumeration, bucket and compute inventory, logging introspection) from a source IP whose autonomous
system number (ASN) matches a curated set commonly associated with consumer VPN brands, VPN-heavy hosting, and provider
networks referenced in public reporting on TeamPCP activity (for example 31173 Services AB AS39351 and Oy Crea Nova
Hosting Solution Ltd). Broad `List*`/`Describe*` patterns are intentionally omitted to reduce noise. Hosting ASNs are
heavily dual-use; validate `source.as.number` in your data and extend `event.action` only when your baseline allows it.
"""
false_positives = [
    """
    Administrators, developers, CI runners, and SaaS egress often exit through Datacamp, M247, Vultr, Linode, or
    brand-name VPN ASNs. Expect more noise on hosting ASNs than on VPN-only registrations. Exclude approved principals,
    accounts, CIDRs, or ASNs after review. GeoIP and ASN enrichment gaps (`source.as.number` unset) will skip events
    entirely. Maintain the ASN list with local intelligence (for example RIPE, BGPView, or peeringdb).
    """,
]
from = "now-6m"
index = ["filebeat-*", "logs-aws.cloudtrail-*"]
language = "kuery"
license = "Elastic License v2"
name = "AWS Discovery API Calls from VPN ASN for the First Time by Identity"
note = """## Triage and analysis

### Investigating AWS Discovery API Calls from VPN ASN for the First Time by Identity

This rule applies a [New Terms](https://www.elastic.co/guide/en/security/current/rules-ui-create.html#create-new-terms-rule) condition on **`source.as.number`** and **`aws.cloudtrail.user_identity.arn`**. It fires the first time a specific principal is observed calling discovery-like APIs from an IP geolocated to one of the ASNs in the rule query (within the 10-day history window).

**High-signal `event.action` values** (explicit allowlist in the rule query): `GetCallerIdentity`; IAM `ListUsers`,
`ListRoles`, `ListAccessKeys`, `GetAccountSummary`, `ListAccountAliases`, `ListGroups`, `ListMFADevices`; S3 `ListBuckets`;
EC2 `DescribeInstances`, `DescribeRegions`, `DescribeVpcs`, `DescribeSecurityGroups`; Lambda `ListFunctions`; RDS
`DescribeDBInstances`, `DescribeDBSnapshots`; DynamoDB `ListTables`; KMS `ListKeys`, `ListAliases`; CloudTrail
`DescribeTrails`, `LookupEvents`; Bedrock `ListFoundationModels`. Clone the rule to add actions (for example ELB or Secrets Manager) if needed.

**Curated VPN-oriented ASNs (verify locally)** — examples this rule matches (subject to registry and enrichment updates):

| ASN | Commonly associated operator (reference only) |
|-----|-----------------------------------------------|
| 216025 | Mullvad VPN AB |
| 57138 | Mullvad supporting infrastructure |
| 207137 | Tefincom S.A. (NordVPN-related) |
| 212238 | Nord / Nord Security class VPN egress in many datasets |
| 199218 | ProtonVPN |
| 209103 | Proton AG (VPN; confirm in your enrichment source) |
| 209854 | Surfshark Ltd. |
| 141039, 147049 | Packet-style VPN/colocation pools often tied to large VPN footprints |
| 53314 | ExpressVPN-related registration in some registries (often small; validate) |
| 60068 | Datacamp Limited — CDN/hosting; used by several VPN brands and many legitimate workloads (**high dual-use**) |
| 9009 | M247 Ltd — colocation and connectivity; common VPN/proxy exit (**high dual-use**) |
| 20473 | Choopa / Vultr (The Constant Company) — VPS; frequent VPN exit and automation (**high dual-use**) |
| 63949 | Linode LLC (Akamai cloud) — VPS; VPN exits and dev workloads (**dual-use**) |
| 39351 | 31173 Services AB (Sweden) — colocation/hosting; cited in TeamPCP-related reporting (**dual-use**). Not the same as **AS31173** (unrelated Ukrainian ISP). |
| 51765 | Oy Crea Nova Hosting Solution Ltd (Finland) — hosting; cited in TeamPCP-related reporting (**dual-use**) |
| 204187 | Oy Crea Nova Hosting Solution Ltd — related network under the same operator (**dual-use**) |
| 208172 | Proton AG — additional VPN egress network (same Proton operator as 209103 / ProtonVPN) |
| 9002 | RETN Limited — pan-European/global transit backbone; carries VPN/proxy egress plus heavy legitimate traffic (**high dual-use**) |
| 49981 | WorldStream B.V. (Netherlands) — dedicated-server/hosting; VPN exits and many legitimate workloads (**dual-use**) |

Other ASNs sometimes seen for VPN or reseller egress (not in this rule by default) include **16276** (OVH), **14061**
(DigitalOcean), **24940** (Hetzner), **51167** (Contabo), and **49453** (Global Layer). Add them only if your baseline
shows manageable false-positive volume.

#### Possible investigation steps

- Confirm `aws.cloudtrail.user_identity.arn`, `aws.cloudtrail.user_identity.type`, and `aws.cloudtrail.user_identity.access_key_id`.
- Review `event.action` and `event.provider` in the alert; several distinct allowlisted actions from the same session suggest broader enumeration.
- Compare `source.ip`, `source.as.organization.name`, and `source.as.number` against your asset inventory and approved remote-access patterns.
- Hunt ±30 minutes for privilege changes, data access (`GetObject`, snapshot sharing), or credential operations.

### False positive analysis

- First-time legitimate VPN or hosting egress per identity produces a single alert per ASN until the term ages out of the window.
- **Datacamp (60068), M247 (9009), and Vultr (20473)** are especially noisy; consider dropping them locally if alerts exceed capacity.
- **31173 Services AB (39351)** and **Crea Nova (51765, 204187)** are legitimate hosting providers; only escalation-worthy when paired with unexpected identities or follow-on impact.

### Response and remediation

- If unexpected, rotate keys, revoke sessions, and tighten IAM; add exceptions only after documented approval.

### Additional information

- [CloudTrail userIdentity](https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudtrail-event-reference-user-identity.html)
- [BGP / ASN lookup](https://bgp.tools/) (third-party) for validating AS registrations
"""
references = [
    "https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudtrail-event-reference.html",
    "https://attack.mitre.org/techniques/T1526/",
    "https://github.com/bountyyfi/bad-asn-list/blob/main/all.txt"
]
risk_score = 73
rule_id = "e3c7a891-4b2d-4e8c-a1f0-9d8e7c6b5a4d"
severity = "high"
tags = [
    "Domain: Cloud",
    "Domain: Identity",
    "Data Source: AWS",
    "Data Source: Amazon Web Services",
    "Data Source: AWS CloudTrail",
    "Use Case: Threat Detection",
    "Tactic: Discovery",
    "Resources: Investigation Guide",
]
timestamp_override = "event.ingested"
type = "new_terms"

query = '''
event.dataset: "aws.cloudtrail"
    and aws.cloudtrail.user_identity.arn:(* and not *AWSServiceRoleForConfig*)
    and not aws.cloudtrail.user_identity.type: "AWSService"
    and event.provider: (
        "sts.amazonaws.com" or
        "iam.amazonaws.com" or
        "s3.amazonaws.com" or
        "ec2.amazonaws.com" or
        "lambda.amazonaws.com" or
        "rds.amazonaws.com" or
        "dynamodb.amazonaws.com" or
        "kms.amazonaws.com" or
        "cloudtrail.amazonaws.com" or
        "bedrock.amazonaws.com"
    )
    and event.action: (
        "GetCallerIdentity" or
        "ListUsers" or
        "ListRoles" or
        "ListAccessKeys" or
        "GetAccountSummary" or
        "ListAccountAliases" or
        "ListGroups" or
        "ListMFADevices" or
        "ListBuckets" or
        "DescribeInstances" or
        "DescribeRegions" or
        "DescribeVpcs" or
        "DescribeSecurityGroups" or
        "ListFunctions" or
        "DescribeDBInstances" or
        "DescribeDBSnapshots" or
        "ListTables" or
        "ListKeys" or
        "ListAliases" or
        "DescribeTrails" or
        "LookupEvents" or
        "ListFoundationModels"
    )
    and source.as.number: (
        216025 or
        57138 or
        207137 or
        212238 or
        199218 or
        209103 or
        209854 or
        141039 or
        147049 or
        53314 or
        60068 or
        9009 or
        20473 or
        63949 or
        39351 or
        51765 or
        204187 or 
        29066 or 
        206092 or
        208172 or
        9002 or
        49981
    )
'''

[rule.investigation_fields]
field_names = [
    "@timestamp",
    "user.name",
    "user_agent.original",
    "source.ip",
    "source.as.number",
    "source.as.organization.name",
    "aws.cloudtrail.user_identity.arn",
    "aws.cloudtrail.user_identity.type",
    "aws.cloudtrail.user_identity.access_key_id",
    "event.provider",
    "event.action",
    "event.outcome",
    "cloud.account.id",
    "cloud.region",
]

[[rule.threat]]
framework = "MITRE ATT&CK"

[[rule.threat.technique]]
id = "T1526"
name = "Cloud Service Discovery"
reference = "https://attack.mitre.org/techniques/T1526/"

[[rule.threat.technique]]
id = "T1580"
name = "Cloud Infrastructure Discovery"
reference = "https://attack.mitre.org/techniques/T1580/"

[rule.threat.tactic]
id = "TA0007"
name = "Discovery"
reference = "https://attack.mitre.org/tactics/TA0007/"

[rule.new_terms]
field = "new_terms_fields"
value = ["source.as.number", "user.name", "cloud.account.id"]
[[rule.new_terms.history_window_start]]
field = "history_window_start"
value = "now-10d"

Stages and Predicates

Stage 1: new_terms

event.dataset: "aws.cloudtrail"
    and aws.cloudtrail.user_identity.arn:(* and not *AWSServiceRoleForConfig*)
    and not aws.cloudtrail.user_identity.type: "AWSService"
    and event.provider: (
        "sts.amazonaws.com" or
        "iam.amazonaws.com" or
        "s3.amazonaws.com" or
        "ec2.amazonaws.com" or
        "lambda.amazonaws.com" or
        "rds.amazonaws.com" or
        "dynamodb.amazonaws.com" or
        "kms.amazonaws.com" or
        "cloudtrail.amazonaws.com" or
        "bedrock.amazonaws.com"
    )
    and event.action: (
        "GetCallerIdentity" or
        "ListUsers" or
        "ListRoles" or
        "ListAccessKeys" or
        "GetAccountSummary" or
        "ListAccountAliases" or
        "ListGroups" or
        "ListMFADevices" or
        "ListBuckets" or
        "DescribeInstances" or
        "DescribeRegions" or
        "DescribeVpcs" or
        "DescribeSecurityGroups" or
        "ListFunctions" or
        "DescribeDBInstances" or
        "DescribeDBSnapshots" or
        "ListTables" or
        "ListKeys" or
        "ListAliases" or
        "DescribeTrails" or
        "LookupEvents" or
        "ListFoundationModels"
    )
    and source.as.number: (
        216025 or
        57138 or
        207137 or
        212238 or
        199218 or
        209103 or
        209854 or
        141039 or
        147049 or
        53314 or
        60068 or
        9009 or
        20473 or
        63949 or
        39351 or
        51765 or
        204187 or 
        29066 or 
        206092 or
        208172 or
        9002 or
        49981
    )

Exclusions

The rule actively suppresses these predicates.

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
aws.cloudtrail.user_identity.arnis_not_null
  • (no value, null check)
field:"aws::userIdentity.arn" kind:is_not_null
event.actionin
  • DescribeDBInstances
  • DescribeDBSnapshots
  • DescribeInstances
  • DescribeRegions
  • DescribeSecurityGroups
  • DescribeTrails
  • DescribeVpcs
  • GetAccountSummary
  • GetCallerIdentity
  • ListAccessKeys
  • ListAccountAliases
  • ListAliases
  • ListBuckets
  • ListFoundationModels
  • ListFunctions
  • ListGroups
  • ListKeys
  • ListMFADevices
  • ListRoles
  • ListTables
  • ListUsers
  • LookupEvents
field:"EventType" kind:in
event.dataseteq
  • aws.cloudtrail
field:"event.dataset" kind:eq value:"aws.cloudtrail"
event.providerin
  • bedrock.amazonaws.com
  • cloudtrail.amazonaws.com
  • dynamodb.amazonaws.com
  • ec2.amazonaws.com
  • iam.amazonaws.com
  • kms.amazonaws.com
  • lambda.amazonaws.com
  • rds.amazonaws.com
  • s3.amazonaws.com
  • sts.amazonaws.com
field:"Provider_Name" kind:in
source.as.numberin
  • 141039
  • 147049
  • 199218
  • 204187
  • 20473
  • 206092
  • 207137
  • 208172
  • 209103
  • 209854
  • 212238
  • 216025
  • 29066
  • 39351
  • 49981
  • 51765
  • 53314
  • 57138
  • 60068
  • 63949
  • 9002
  • 9009
field:"source.as.number" kind:in