PageSourceSearch

https://hamelin.sh/assets/js/b020f723.1648cd0f.js

js hamelin.sh collected 2026-10-08 05:19:54 UTC 19,675 bytes, 1 lines download raw bytes

1"use strict";(globalThis.webpackChunkdocs=globalThis.webpackChunkdocs||[]).push([[1683],{3070(e,n,t){t.r(n),t.d(n,{assets:()=>a,contentTitle:()=>c,default:()=>u,frontMatter:()=>o,metadata:()=>i,toc:()=>l});const i=JSON.parse('{"id":"examples/aws-security-group-detection","title":"AWS Security Group Detection","description":"Detecting overly permissive AWS EC2 security group modifications using CloudTrail events","source":"@site/docs/examples/aws-security-group-detection.md","sourceDirName":"examples","slug":"/examples/aws-security-group-detection","permalink":"/docs/examples/aws-security-group-detection","draft":false,"unlisted":false,"tags":[],"version":"current","sidebarPosition":7,"frontMatter":{"sidebar_position":7,"title":"AWS Security Group Detection","description":"Detecting overly permissive AWS EC2 security group modifications using CloudTrail events"},"sidebar":"docsSidebar","previous":{"title":"Registry Persistence Detection","permalink":"/docs/examples/registry-persistence-detection"},"next":{"title":"FROM","permalink":"/docs/command-reference/from"}}');var s=t(4489),r=t(1468);const o={sidebar_position:7,title:"AWS Security Group Detection",description:"Detecting overly permissive AWS EC2 security group modifications using CloudTrail events"},c="AWS Security Group Detection",a={},l=[{value:"Attack overview",id:"attack-overview",level:2},{value:"Detection strategy",id:"detection-strategy",level:2},{value:"Complete detection query",id:"complete-detection-query",level:2},{value:"Query breakdown",id:"query-breakdown",level:2},{value:"JSON parsing and field extraction",id:"json-parsing-and-field-extraction",level:3},{value:"Event filtering and validation",id:"event-filtering-and-validation",level:3},{value:"Permissive rule detection",id:"permissive-rule-detection",level:3},{value:"Sensitive port identification",id:"sensitive-port-identification",level:3},{value:"Risk scoring logic",id:"risk-scoring-logic",level:3},{value:"Structured output generation",id:"structured-output-generation",level:3},{value:"Advanced techniques demonstrated",id:"advanced-techniques-demonstrated",level:2},{value:"Detection tuning",id:"detection-tuning",level:2}];function d(e){const n={code:"code",h1:"h1",h2:"h2",h3:"h3",header:"header",li:"li",ol:"ol",p:"p",pre:"pre",strong:"strong",...(0,r.R)(),...e.components};return(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(n.header,{children:(0,s.jsx)(n.h1,{id:"aws-security-group-detection",children:"AWS Security Group Detection"})}),"\n",(0,s.jsx)(n.p,{children:"This example shows how to build a detection for overly permissive AWS EC2\nsecurity group modifications. Security groups act as virtual firewalls for\nEC2 instances. Misconfigurations that allow unrestricted internet access\ncreate attack vectors that lead to breaches. You can identify dangerous\nmodifications by analyzing CloudTrail events for security group changes that\nexpose sensitive ports to the internet."}),"\n",(0,s.jsx)(n.h2,{id:"attack-overview",children:"Attack overview"}),"\n",(0,s.jsx)(n.p,{children:"Security group misconfigurations lead to cloud breaches through this pattern:"}),"\n",(0,s.jsxs)(n.ol,{children:["\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.strong,{children:"Initial misconfiguration"})," - Administrators accidentally allow unrestricted access"]}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.strong,{children:"Deliberate modification"})," - Attackers modify security groups after compromise"]}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.strong,{children:"Service exposure"})," - Sensitive services become accessible from the internet"]}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.strong,{children:"Lateral movement"})," - Exposed services provide entry points for further attacks"]}),"\n"]}),"\n",(0,s.jsx)(n.p,{children:"You can detect these misconfigurations by monitoring CloudTrail events for\nsecurity group modifications that create overly permissive rules."}),"\n",(0,s.jsx)(n.h2,{id:"detection-strategy",children:"Detection strategy"}),"\n",(0,s.jsxs)(n.p,{children:["The detection focuses on security group modifications that allow traffic from\nany source (",(0,s.jsx)(n.code,{children:"0.0.0.0/0"})," or ",(0,s.jsx)(n.code,{children:"::/0"}),") to sensitive ports. You write queries that\nparse CloudTrail JSON events to extract security group changes, identify\npermissive rules, and calculate risk scores based on the ports and protocols\nexposed."]}),"\n",(0,s.jsxs)(n.p,{children:["Hamelin targets CloudTrail events like ",(0,s.jsx)(n.code,{children:"AuthorizeSecurityGroupIngress"})," and\n",(0,s.jsx)(n.code,{children:"ModifySecurityGroupRules"})," that indicate firewall rule changes, then filters\nfor modifications that create internet-accessible endpoints."]}),"\n",(0,s.jsx)(n.h2,{id:"complete-detection-query",children:"Complete detection query"}),"\n",(0,s.jsx)(n.p,{children:"This detection query parses CloudTrail JSON events to identify security group\nmodifications that expose services to unrestricted internet access. Hamelin\nextracts relevant fields from nested JSON, identifies sensitive ports,\ncalculates risk scores based on exposure level, and creates structured output\nfor security team
1s:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-hamelin",children:"// Name: AWS EC2 Security Group Permissive Changes\n// Author: Detection Engineer\n//-\n//- Description: This detection identifies potentially dangerous modifications\n//- to EC2 security groups that could expose resources to unauthorized access.\n//- It specifically looks for rules that allow traffic from any source\n//- (0.0.0.0/0 or ::/0) on sensitive ports. Security groups act as virtual\n//- firewalls, and overly permissive rules are a common security misconfiguration\n//- that can lead to breaches.\n//-\n//- Tags: aws, ec2, security-group, network-security, misconfiguration\n//- Mitre-Tactic: TA0005 (Defense Evasion), TA0001 (Initial Access)\n//- Mitre-Technique: T1562.007 (Impair Defenses: Disable or Modify Cloud Firewall)\n//-\n\nFROM simba.cloudtrail_events\n\n// Parse the JSON and extract fields\n| SET evt = parse_json(event.original) AS variant\n| SET eventName = evt.eventName AS string\n| SET source_ip = evt.sourceIPAddress AS string\n| SET user_identity = evt.userIdentity.arn AS string\n| SET account_id = evt.userIdentity.accountId AS string\n| SET aws_region = evt.awsRegion AS string\n| SET error_code = evt.errorCode AS string\n| SET request_params = evt.requestParameters AS variant\n| SET group_id = evt.requestParameters.groupId AS string\n| SET group_name = evt.requestParameters.groupName AS string\n\n// Filter for security group modification events\n| WHERE coalesce(eventName, '') IN [\n    'AuthorizeSecurityGroupIngress',\n    'AuthorizeSecurityGroupEgress',\n    'RevokeSecurityGroupIngress',\n    'RevokeSecurityGroupEgress',\n    'CreateSecurityGroup',\n    'ModifySecurityGroupRules'\n  ]\n\n// Only process successful events\n| WHERE coalesce(error_code, '') == ''\n\n// Check for overly permissive rules in the original event\n| SET has_any_source = contains(event.original, '0.0.0.0/0') OR contains(event.original, '::/0')\n\n// Extract port information (simplified approach)\n| SET from_port = evt.requestParameters.ipPermissions[0].fromPort AS int\n| SET to_port = evt.requestParameters.ipPermissions[0].toPort AS int\n| SET ip_protocol = evt.requestParameters.ipPermissions[0].ipProtocol AS string\n\n// Define sensitive ports\n| SET sensitive_ports = [22, 23, 3389, 1433, 3306, 5432, 5984, 6379, 7000, 7001, 8020, 8086, 8888, 9042, 9160, 9200, 9300, 11211, 27017, 27018, 27019, 50070]\n| SET is_sensitive_port = coalesce(from_port, 0) IN sensitive_ports OR \n                          coalesce(to_port, 0) IN sensitive_ports OR\n                          (coalesce(from_port, 0) <= 22 AND coalesce(to_port, 0) >= 22) OR\n                          (coalesce(from_port, 0) <= 3389 AND coalesce(to_port, 0) >= 3389)\n\n// Check for allow all protocols\n| SET is_all_traffic = coalesce(ip_protocol, '') == '-1' OR \n                       (coalesce(from_port, 0) == 0 AND coalesce(to_port, 0) == 65535)\n\n// Only flag events with permissive rules\n| WHERE has_any_source\n\n// Calculate risk score\n| SET risk_score = if(\n    contains(coalesce(eventName, ''), 'Authorize') AND is_all_traffic,\n    100,  // Critical - allowing all traffic from anywhere\n    if(\n      contains(coalesce(eventName, ''), 'Authorize') AND is_sensitive_port,\n      90,   // Very high - sensitive ports exposed\n      if(\n        contains(coalesce(eventName, ''), 'Authorize'),\n        75,   // High - any port exposed to internet\n        40    // Low - revoking permissive rules (good action)\n      )\n    )\n  )\n\n// Create human-readable message\n| SET action_type = if(contains(coalesce(eventName, ''), 'Authorize'), 'opened', 'closed')\n| SET from_port_str = coalesce(from_port, 0) AS string\n| SET to_port_str = coalesce(to_port, 0) AS string\n| SET port_info = if(\n    is_all_traffic,\n    'all ports',\n    'port(s) ' + from_port_str + '-' + to_port_str\n  )\n| SET message = 'Security group ' + action_type + ' to internet: ' + \n                port_info + ' on group ' + coalesce(group_id, coalesce(group_name, 'unknown'))\n\n// Create labels map for context\n| SET labels = map(\n    'Severity': if(risk_score >= 90, 'CRITICAL', if(risk_score >= 75, 'HIGH', 'LOW')) AS string,\n    'Account': coalesce(account_id, '') AS string,\n    'Security Group': coalesce(group_id, coalesce(group_name, '')) AS string,\n    'User': coalesce(user_identity, '') AS string,\n    'Source IP': coalesce(source_ip, '') AS string,\n    'Event': coalesce(eventName, '') AS string,\n    'Region': coalesce(aws_region, '') AS string,\n    'Protocol': coalesce(ip_protocol, 'unknown') AS string,\n    'Port Range': port_info AS string,\n    'Action': action_type AS string,\n    'Tactic': 'Defense Evasion' AS string,\n    'Technique': 'T1562.007' AS string\n  )\n\n// Format the final output\n| SELECT\n    timestamp,\n    event.start = timestamp,\n    event.end = timestamp,\n    message,\n    labels,\n    host.name = coalesce(account_id, ''),\n    user.name = coalesce(user_identity, ''),\n    source.ip = coalesce(source_ip, ''),\n    cloud.region = coalesce(aws_region, ''),\n    cloud.service.name = 'ec2',\n    network.protocol = coalesce(ip_protocol, ''),\n    rule = {\n        name: 'AWS EC2 Security Group Permissive Changes' AS string,\n        description: 'Detects modifications to EC2 security groups that allow ' +\n                     'unrestricted access from the internet (0.0.0.0/0), especially ' +\n                     'on 
1sensitive ports commonly targeted by attackers.' AS string,\n        mitre_tactics: ['Defense Evasion', 'Initial Access'] AS array(string),\n        mitre_techniques: ['T1562.007'] AS array(string),\n        severity: if(risk_score >= 90, 'critical', if(risk_score >= 75, 'high', 'low')) AS string,\n        references: [\n            'https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/security-group-rules.html',\n            'https://docs.aws.amazon.com/vpc/latest/userguide/VPC_SecurityGroups.html'\n        ] AS array(string)\n    }\n"})}),"\n",(0,s.jsx)(n.h2,{id:"query-breakdown",children:"Query breakdown"}),"\n",(0,s.jsx)(n.p,{children:"This detection shows several techniques that make Hamelin effective for\ncloud security monitoring. Let's examine each component:"}),"\n",(0,s.jsx)(n.h3,{id:"json-parsing-and-field-extraction",children:"JSON parsing and field extraction"}),"\n",(0,s.jsx)(n.p,{children:"The detection starts by parsing CloudTrail JSON events and extracting relevant\nfields. CloudTrail events contain nested JSON structures, so you need to\nextract specific fields systematically:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-hamelin",children:"| SET evt = parse_json(event.original) AS variant\n| SET eventName = evt.eventName AS string\n| SET source_ip = evt.sourceIPAddress AS string\n| SET user_identity = evt.userIdentity.arn AS string\n| SET group_id = evt.requestParameters.groupId AS string\n"})}),"\n",(0,s.jsxs)(n.p,{children:["The ",(0,s.jsx)(n.code,{children:"parse_json()"})," function converts the JSON string into a variant type.\nYou can then extract specific fields using dot notation. The ",(0,s.jsx)(n.code,{children:"coalesce()"}),"\nfunction handles missing fields gracefully."]}),"\n",(0,s.jsx)(n.h3,{id:"event-filtering-and-validation",children:"Event filtering and validation"}),"\n",(0,s.jsx)(n.p,{children:"The detection filters for specific CloudTrail events that indicate security\ngroup modifications. You process only successful events to avoid false\npositives from failed API calls:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-hamelin",children:"| WHERE coalesce(eventName, '') IN [\n    'AuthorizeSecurityGroupIngress',\n    'AuthorizeSecurityGroupEgress',\n    'RevokeSecurityGroupIngress',\n    'RevokeSecurityGroupEgress',\n    'CreateSecurityGroup',\n    'ModifySecurityGroupRules'\n  ]\n| WHERE coalesce(error_code, '') == ''\n"})}),"\n",(0,s.jsx)(n.p,{children:"This filtering ensures you analyze actual security group changes, not\nfailed API calls or unrelated CloudTrail events."}),"\n",(0,s.jsx)(n.h3,{id:"permissive-rule-detection",children:"Permissive rule detection"}),"\n",(0,s.jsx)(n.p,{children:"The detection identifies overly permissive rules by searching for CIDR blocks\nthat allow traffic from anywhere on the internet:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-hamelin",children:"| SET has_any_source = contains(event.original, '0.0.0.0/0') OR contains(event.original, '::/0')\n| WHERE has_any_source\n"})}),"\n",(0,s.jsxs)(n.p,{children:["The ",(0,s.jsx)(n.code,{children:"contains()"})," function searches the raw JSON for these permissive CIDR\nblocks. This catches rules that expose services to the entire internet."]}),"\n",(0,s.jsx)(n.h3,{id:"sensitive-port-identification",children:"Sensitive port identification"}),"\n",(0,s.jsx)(n.p,{children:"The detection defines a list of commonly targeted ports and checks whether\nthe security group modification affects these sensitive services:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-hamelin",children:"| SET sensitive_ports = [22, 23, 3389, 1433, 3306, 5432, ...]\n| SET is_sensitive_port = coalesce(from_port, 0) IN sensitive_ports OR \n                          coalesce(to_port, 0) IN sensitive_ports\n"})}),"\n",(0,s.jsx)(n.p,{children:"This includes SSH (22), RDP (3389), database ports (1433, 3306, 5432), and\nother services commonly targeted by attackers."}),"\n",(0,s.jsx)(n.h3,{id:"risk-scoring-logic",children:"Risk scoring logic"}),"\n",(0,s.jsx)(n.p,{children:"The detection calculates risk scores based on the type of modification and\nthe sensitivity of exposed ports:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-hamelin",children:"| SET risk_score = if(\n    contains(coalesce(eventName, ''), 'Authorize') AND is_all_traffic,\n    100,  // Critical - allowing all traffic from anywhere\n    if(\n      contains(coalesce(eventName, ''), 'Authorize') AND is_sensitive_port,\n      90,   // Very high - sensitive ports exposed\n      if(\n        contains(coalesce(eventName, ''), 'Authorize'),\n        75,   // High - any port exposed to internet\n        40    // Low - revoking permissive rules (good action)\n      )\n    )\n  )\n"})}),"\n",(0,s.jsxs)(n.p,{children:["This nested ",(0,s.jsx)(n.code,{children:"if()"})," structure prioritizes the most dangerous configurations.\nIt also recognizes when administrators remove permissive rules."]}),"\n",(0,s.jsx)(n.h3,{id:"structured-output-generation",children:"Structured output generation"}),"\n",(0,s.jsx)(n.p,{children:"The detection creates both human-readable messages and structured labels for\ndifferent c
1onsumers:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-hamelin",children:"| SET message = 'Security group ' + action_type + ' to internet: ' + \n                port_info + ' on group ' + coalesce(group_id, group_name)\n| SET labels = map(\n    'Severity': if(risk_score >= 90, 'CRITICAL', 'HIGH'),\n    'Account': account_id,\n    'Security Group': group_id,\n    'User': user_identity\n  )\n"})}),"\n",(0,s.jsx)(n.p,{children:"This provides context for analysts and maintains machine-readable structure\nfor SIEM integration."}),"\n",(0,s.jsx)(n.h2,{id:"advanced-techniques-demonstrated",children:"Advanced techniques demonstrated"}),"\n",(0,s.jsx)(n.p,{children:"This detection uses several techniques that make Hamelin effective for\ncloud security monitoring:"}),"\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.strong,{children:"JSON parsing and navigation"})," handles complex nested CloudTrail event\nstructures using ",(0,s.jsx)(n.code,{children:"parse_json()"})," and dot notation for field extraction."]}),"\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.strong,{children:"Flexible field handling"})," uses ",(0,s.jsx)(n.code,{children:"coalesce()"})," to handle missing or null fields\nin CloudTrail events gracefully."]}),"\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.strong,{children:"String pattern matching"})," uses ",(0,s.jsx)(n.code,{children:"contains()"})," for efficient searching of\nCIDR blocks within raw JSON content."]}),"\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.strong,{children:"Conditional risk scoring"})," uses nested ",(0,s.jsx)(n.code,{children:"if()"})," statements to create\nnuanced risk assessments based on multiple factors."]}),"\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.strong,{children:"Cloud-native field mapping"})," creates ECS-compliant output with cloud-specific\nfields like ",(0,s.jsx)(n.code,{children:"cloud.region"})," and ",(0,s.jsx)(n.code,{children:"cloud.service.name"}),"."]}),"\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.strong,{children:"Reference documentation"})," includes links to official AWS documentation for\nanalyst context and verification."]}),"\n",(0,s.jsx)(n.h2,{id:"detection-tuning",children:"Detection tuning"}),"\n",(0,s.jsx)(n.p,{children:"You can adapt this detection for different cloud environments:"}),"\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.strong,{children:"Expand sensitive ports"})," by adding environment-specific services to the\nsensitive ports list based on applications in use."]}),"\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.strong,{children:"Adjust risk scores"})," by modifying the scoring logic based on risk tolerance\nand compliance requirements."]}),"\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.strong,{children:"Add region filtering"})," by including or excluding specific AWS regions based\non where resources should legitimately exist."]}),"\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.strong,{children:"Customize user filtering"})," by adding exclusions for automated tools or\nservice accounts that legitimately modify security groups."]}),"\n",(0,s.jsx)(n.p,{children:"The modular structure with clear variable definitions makes it easy to modify\nindividual components without affecting the overall detection logic."})]})}function u(e={}){const{wrapper:n}={...(0,r.R)(),...e.components};return n?(0,s.jsx)(n,{...e,children:(0,s.jsx)(d,{...e})}):d(e)}},1468(e,n,t){t.d(n,{R:()=>o,x:()=>c});var i=t(4349);const s={},r=i.createContext(s);function o(e){const n=i.useContext(r);return i.useMemo(function(){return"function"==typeof e?e(n):{...n,...e}},[n,e])}function c(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(s):e.components||s:o(e.components),i.createElement(r.Provider,{value:n},e.children)}}}]);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.