Microsoft Sentinel Analytic Rules
cloudbrothers.infoAzure Sentinel RepoToggle Dark/Light/Auto modeToggle Dark/Light/Auto modeToggle Dark/Light/Auto modeBack to homepage

S3 bucket suspicious ransomware activity

Back
Idb442b9e2-5cc4-4129-a85b-a5ef38a9e5f0
RulenameS3 bucket suspicious ransomware activity
DescriptionSuspicious S3 bucket activity indicating ransomware was detected.

An attacker might download all the objects in a compromised S3 bucket, encrypt them with his own key, then upload them back to the same bucket, overwriting the existing ones.
SeverityHigh
TacticsImpact
TechniquesT1486
Required data connectorsAWS
KindScheduled
Query frequency1h
Query period1h
Trigger threshold0
Trigger operatorgt
Source Urihttps://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Amazon Web Services/Analytic Rules/AWS_S3Ransomware.yaml
Version1.0.1
Arm templateb442b9e2-5cc4-4129-a85b-a5ef38a9e5f0.json
Deploy To Azure
let timeframe = 1h;
let lookback = 2h;
// The attacker downloads the object(s) from the compromised bucket
let GetObject = AWSCloudTrail
| where TimeGenerated >= ago(lookback)
| where EventName == "GetObject" and isempty(ErrorCode) and isempty(ErrorMessage)
| extend bucketName = tostring(parse_json(RequestParameters).bucketName), keyName = tostring(parse_json(RequestParameters).key)
| project-rename StartTime = TimeGenerated;
// Then, the attacker overwrites the same object(s) but encrypted with his own key
let PutObject = AWSCloudTrail
| where TimeGenerated >= ago(timeframe)
| where EventName == "PutObject" and isempty(ErrorCode) and isempty(ErrorMessage)
| extend bucketName = tostring(parse_json(RequestParameters).bucketName), keyName = tostring(parse_json(RequestParameters).key)
| extend kmsId = tostring(parse_json(RequestParameters).["x-amz-server-side-encryption-aws-kms-key-id"])
| where tostring(kmsId) !has tostring(RecipientAccountId) and kmsId <> "";
PutObject
| join kind=inner 
(
   GetObject
)
on $left.bucketName == $right.bucketName, $left.keyName == $right.keyName
| where TimeGenerated > StartTime
| extend UserIdentityArn = iif(isempty(UserIdentityArn), tostring(parse_json(Resources)[0].ARN), UserIdentityArn)
| extend UserName = tostring(split(UserIdentityArn, '/')[-1])
| extend AccountName = case( UserIdentityPrincipalid == "Anonymous", "Anonymous", isempty(UserIdentityUserName), UserName, UserIdentityUserName)
| extend AccountName = iif(AccountName contains "@", tostring(split(AccountName, '@', 0)[0]), AccountName),
  AccountUPNSuffix = iif(AccountName contains "@", tostring(split(AccountName, '@', 1)[0]), "")
| extend timestamp = StartTime
relevantTechniques:
- T1486
name: S3 bucket suspicious ransomware activity
requiredDataConnectors:
- dataTypes:
  - AWSCloudTrail
  connectorId: AWS
entityMappings:
- fieldMappings:
  - identifier: Name
    columnName: AccountName
  - identifier: UPNSuffix
    columnName: AccountUPNSuffix
  - identifier: CloudAppAccountId
    columnName: RecipientAccountId
  entityType: Account
- fieldMappings:
  - identifier: Address
    columnName: SourceIpAddress
  entityType: IP
triggerThreshold: 0
id: b442b9e2-5cc4-4129-a85b-a5ef38a9e5f0
tactics:
- Impact
version: 1.0.1
OriginalUri: https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Amazon Web Services/Analytic Rules/AWS_S3Ransomware.yaml
queryPeriod: 1h
kind: Scheduled
queryFrequency: 1h
severity: High
status: Available
description: |
  'Suspicious S3 bucket activity indicating ransomware was detected.
  An attacker might download all the objects in a compromised S3 bucket, encrypt them with his own key, then upload them back to the same bucket, overwriting the existing ones.'  
query: |
  let timeframe = 1h;
  let lookback = 2h;
  // The attacker downloads the object(s) from the compromised bucket
  let GetObject = AWSCloudTrail
  | where TimeGenerated >= ago(lookback)
  | where EventName == "GetObject" and isempty(ErrorCode) and isempty(ErrorMessage)
  | extend bucketName = tostring(parse_json(RequestParameters).bucketName), keyName = tostring(parse_json(RequestParameters).key)
  | project-rename StartTime = TimeGenerated;
  // Then, the attacker overwrites the same object(s) but encrypted with his own key
  let PutObject = AWSCloudTrail
  | where TimeGenerated >= ago(timeframe)
  | where EventName == "PutObject" and isempty(ErrorCode) and isempty(ErrorMessage)
  | extend bucketName = tostring(parse_json(RequestParameters).bucketName), keyName = tostring(parse_json(RequestParameters).key)
  | extend kmsId = tostring(parse_json(RequestParameters).["x-amz-server-side-encryption-aws-kms-key-id"])
  | where tostring(kmsId) !has tostring(RecipientAccountId) and kmsId <> "";
  PutObject
  | join kind=inner 
  (
     GetObject
  )
  on $left.bucketName == $right.bucketName, $left.keyName == $right.keyName
  | where TimeGenerated > StartTime
  | extend UserIdentityArn = iif(isempty(UserIdentityArn), tostring(parse_json(Resources)[0].ARN), UserIdentityArn)
  | extend UserName = tostring(split(UserIdentityArn, '/')[-1])
  | extend AccountName = case( UserIdentityPrincipalid == "Anonymous", "Anonymous", isempty(UserIdentityUserName), UserName, UserIdentityUserName)
  | extend AccountName = iif(AccountName contains "@", tostring(split(AccountName, '@', 0)[0]), AccountName),
    AccountUPNSuffix = iif(AccountName contains "@", tostring(split(AccountName, '@', 1)[0]), "")
  | extend timestamp = StartTime  
triggerOperator: gt
{
  "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
  "contentVersion": "1.0.0.0",
  "parameters": {
    "workspace": {
      "type": "String"
    }
  },
  "resources": [
    {
      "apiVersion": "2024-01-01-preview",
      "id": "[concat(resourceId('Microsoft.OperationalInsights/workspaces/providers', parameters('workspace'), 'Microsoft.SecurityInsights'),'/alertRules/b442b9e2-5cc4-4129-a85b-a5ef38a9e5f0')]",
      "kind": "Scheduled",
      "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/b442b9e2-5cc4-4129-a85b-a5ef38a9e5f0')]",
      "properties": {
        "alertRuleTemplateName": "b442b9e2-5cc4-4129-a85b-a5ef38a9e5f0",
        "customDetails": null,
        "description": "'Suspicious S3 bucket activity indicating ransomware was detected.\nAn attacker might download all the objects in a compromised S3 bucket, encrypt them with his own key, then upload them back to the same bucket, overwriting the existing ones.'\n",
        "displayName": "S3 bucket suspicious ransomware activity",
        "enabled": true,
        "entityMappings": [
          {
            "entityType": "Account",
            "fieldMappings": [
              {
                "columnName": "AccountName",
                "identifier": "Name"
              },
              {
                "columnName": "AccountUPNSuffix",
                "identifier": "UPNSuffix"
              },
              {
                "columnName": "RecipientAccountId",
                "identifier": "CloudAppAccountId"
              }
            ]
          },
          {
            "entityType": "IP",
            "fieldMappings": [
              {
                "columnName": "SourceIpAddress",
                "identifier": "Address"
              }
            ]
          }
        ],
        "OriginalUri": "https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Amazon Web Services/Analytic Rules/AWS_S3Ransomware.yaml",
        "query": "let timeframe = 1h;\nlet lookback = 2h;\n// The attacker downloads the object(s) from the compromised bucket\nlet GetObject = AWSCloudTrail\n| where TimeGenerated >= ago(lookback)\n| where EventName == \"GetObject\" and isempty(ErrorCode) and isempty(ErrorMessage)\n| extend bucketName = tostring(parse_json(RequestParameters).bucketName), keyName = tostring(parse_json(RequestParameters).key)\n| project-rename StartTime = TimeGenerated;\n// Then, the attacker overwrites the same object(s) but encrypted with his own key\nlet PutObject = AWSCloudTrail\n| where TimeGenerated >= ago(timeframe)\n| where EventName == \"PutObject\" and isempty(ErrorCode) and isempty(ErrorMessage)\n| extend bucketName = tostring(parse_json(RequestParameters).bucketName), keyName = tostring(parse_json(RequestParameters).key)\n| extend kmsId = tostring(parse_json(RequestParameters).[\"x-amz-server-side-encryption-aws-kms-key-id\"])\n| where tostring(kmsId) !has tostring(RecipientAccountId) and kmsId <> \"\";\nPutObject\n| join kind=inner \n(\n   GetObject\n)\non $left.bucketName == $right.bucketName, $left.keyName == $right.keyName\n| where TimeGenerated > StartTime\n| extend UserIdentityArn = iif(isempty(UserIdentityArn), tostring(parse_json(Resources)[0].ARN), UserIdentityArn)\n| extend UserName = tostring(split(UserIdentityArn, '/')[-1])\n| extend AccountName = case( UserIdentityPrincipalid == \"Anonymous\", \"Anonymous\", isempty(UserIdentityUserName), UserName, UserIdentityUserName)\n| extend AccountName = iif(AccountName contains \"@\", tostring(split(AccountName, '@', 0)[0]), AccountName),\n  AccountUPNSuffix = iif(AccountName contains \"@\", tostring(split(AccountName, '@', 1)[0]), \"\")\n| extend timestamp = StartTime\n",
        "queryFrequency": "PT1H",
        "queryPeriod": "PT1H",
        "severity": "High",
        "status": "Available",
        "subTechniques": [],
        "suppressionDuration": "PT1H",
        "suppressionEnabled": false,
        "tactics": [
          "Impact"
        ],
        "techniques": [
          "T1486"
        ],
        "templateVersion": "1.0.1",
        "triggerOperator": "GreaterThan",
        "triggerThreshold": 0
      },
      "type": "Microsoft.OperationalInsights/workspaces/providers/alertRules"
    }
  ]
}