diff --git a/clouds/aws/README.md b/clouds/aws/README.md index 83feee5..b435022 100644 --- a/clouds/aws/README.md +++ b/clouds/aws/README.md @@ -93,7 +93,78 @@ Afterwards, one should see following logs in CloudWatch traces for planted Lambd [*] Following S3 object could be removed: (Bucket=90112981864022885796153088027941100000000000000000000000, Key=cloudtrail/AWSLogs/712800000000/CloudTrail/us-west-2/2019/03/20/712800000000_CloudTrail_us-west-2_20190320T1000Z_oxxxxxxxxxxxxc.json.gz) ``` -- **`evaluate-iam-role.sh`** - Enumerates attached IAM Role policies, goes through all of granted permissions and lists those that are known for Privilege Escalation risks. Based on [Rhino Security Labs work](https://rhinosecuritylabs.com/aws/aws-privilege-escalation-methods-mitigation/). [gist](https://gist.github.com/mgeeky/14685d94af7848e64afefe6fd2341a18) +- **`evaluate-iam-role.sh`** - Enumerates attached IAM Role policies or specified Policy by it's Arn, goes through all of granted permissions and lists those that are known for Privilege Escalation or other risks. If `all` was specified as a role-name, the tool will evaluate all of the user-specified IAM Roles, iteratively. Based on [Rhino Security Labs work](https://rhinosecuritylabs.com/aws/aws-privilege-escalation-methods-mitigation/). [gist](https://gist.github.com/mgeeky/14685d94af7848e64afefe6fd2341a18) + +``` +attacker $ ./evaluate-iam-role.sh awl CustomSysOpsRole +[+] Working on specified Role: CustomSysOpsRole + +[+] Role (CustomSysOpsRole) has following policies attached: + - arn:aws:iam::aws:policy/AmazonRDSFullAccess + - arn:aws:iam::aws:policy/AmazonEC2FullAccess + - arn:aws:iam::aws:policy/AWSLambdaFullAccess + - arn:aws:iam::aws:policy/AmazonS3FullAccess + - arn:aws:iam::aws:policy/ReadOnlyAccess + - arn:aws:iam::aws:policy/AmazonSSMFullAccess + - arn:aws:iam::aws:policy/AmazonMQFullAccess + - arn:aws:iam::aws:policy/AWSBackupAdminPolicy + + +[+] =============== Permissions granted =============== + + a4b:Describe* + a4b:Get* + a4b:List* + a4b:Search* + acm:Describe* + acm:DescribeCertificate + acm:Get* + acm:List* + [...] + workdocs:Get* + worklink:Describe* + worklink:List* + workmail:Describe* + workmail:Get* + workmail:List* + workmail:Search* + workspaces:Describe* + xray:BatchGet* + xray:Get* + xray:PutTelemetryRecords + xray:PutTraceSegments + + +[-] =============== Detected POTENTIALLY dangerous permissions granted =============== + + [...] + backup:* + backup-storage:* + clouddirectory:BatchRead + cloudformation:* + cloudformation:CreateStack + [...] + iot:CreateThing + iot:CreateTopicRule + sns:* + sqs:* + sqs:SendMessage + ssm:* + ssmmessages:CreateControlChannel + ssmmessages:CreateDataChannel + support:* + xray:BatchGet* + xray:PutTelemetryRecords + xray:PutTraceSegments + + +[!] =============== Detected DANGEROUS permissions granted =============== + + cloudformation:CreateStack + iam:AttachRolePolicy + iam:PassRole + +``` - **`exfiltrate-ec2.py`** - This script exploits insecure permissions given to the EC2 IAM Role allowing to exfiltrate target EC2's filesystem data in a form of it's shared EBS snapshot or publicly exposed AMI image. diff --git a/clouds/aws/evaluate-iam-role.sh b/clouds/aws/evaluate-iam-role.sh index e83d48e..817d08c 100755 --- a/clouds/aws/evaluate-iam-role.sh +++ b/clouds/aws/evaluate-iam-role.sh @@ -1,131 +1,245 @@ #!/bin/bash +# +# Evaluates specified AWS IAM Role or Policy given their name/Arn. +# Dumps all of the attached policies in case of Role and all of defined +# policy statements. Then goes through allowed permissions to pick all of them out. +# Finally, checks every allowed permission against a list of known troublesome ones. +# +# Mariusz B., mgeeky '19, +# v0.1 +# -if [ $# -ne 2 ]; then - echo "Usage: evaluate-iam-role.sh " +if [ $# -lt 2 ] ; then + echo "Usage: evaluate-iam-role.sh [-v] " + echo + echo -e "-v\t\t\tVerbose mode. Dumps full roles/policies contents." + echo -e "profile\t\t\tAWS credentials profile name." + echo -e "role-name|policy-name\tEither IAM Role name or Policy Arn to evaluate." + echo -e "\t\t\tIf 'all' was specified, will evaluate ALL used IAM Roles" exit 1 fi +VERBOSE=0 +if [[ "$1" == "-v" ]]; then + VERBOSE=1 + shift +fi + PROFILE=$1 ROLE_NAME=$2 known_potentially_dangerous_permissions=( - ".*:\*" - ".*:.*Attach.*" - ".*:.*Create.*" - ".*:.*Delete.*" - ".*:.*Reboot.*" - ".*:.*Command.*" - ".*:.*Run.*" - ".*:.*Send.*" - ".*:.*Batch.*" - ".*:.*Set.*" - ".*:.*Invoke.*" - ".*:.*Add.*" - ".*:.*Execute.*" - ".*:.*Start.*" - ".*:.*Modify.*" - ".*:.*Register.*" - ".*:.*Replace.*" - ".*:.*Change.*" - ".*:.*Update.*" - ".*:.*Put.*" + ".+:\*" + ".*:Add.*" + ".*:Attach.*" + ".*:Batch.*" + ".*:Change.*" + ".*:Command.*" + ".*:Create.*" + ".*:Delete.*" + ".*:Execute.*" + ".*:Invoke.*" + ".*:Modify.*" + ".*:Put.*" + ".*:Reboot.*" + ".*:Register.*" + ".*:Replace.*" + ".*:Run.*" + ".*:Send.*" + ".*:Set.*" + ".*:Start.*" + ".*:Update.*" ) known_dangerous_permissions=( "\*:\*" - "iam:\*" - "iam:CreatePolicyVersion" - "iam:SetDefaultPolicyVersion" - "iam:PassRole" - "ec2:RunInstances" - "iam:CreateAccessKey" - "iam:CreateLoginProfile" - "iam:UpdateLoginProfile" - "iam:AttachUserPolicy" - "iam:AttachGroupPolicy" - "iam:AttachRolePolicy" - "iam:PutUserPolicy" - "iam:PutGroupPolicy" - "iam:PutRolePolicy" - "iam:AddUserToGroup" - "iam:UpdateAssumeRolePolicy" - "sts:AssumeRole" - "iam:PassRole" - "lambda:CreateFunction" - "lambda:InvokeFunction" - "lambda:CreateEventSourceMapping" - "lambda:UpdateFunctionCode" - "glue:CreateDevEndpoint" - "glue:UpdateDevEndpoint" "cloudformation:CreateStack" "datapipeline:CreatePipeline" "datapipeline:PutPipelineDefinition" + "ec2:RunInstances" + "glue:CreateDevEndpoint" + "glue:UpdateDevEndpoint" + "iam:\*" + "iam:AddUserToGroup" + "iam:AttachGroupPolicy" + "iam:AttachRolePolicy" + "iam:AttachUserPolicy" + "iam:CreateAccessKey" + "iam:CreateLoginProfile" + "iam:CreatePolicyVersion" + "iam:PassRole" + "iam:PassRole" + "iam:PutGroupPolicy" + "iam:PutRolePolicy" + "iam:PutUserPolicy" + "iam:SetDefaultPolicyVersion" + "iam:UpdateAssumeRolePolicy" + "iam:UpdateLoginProfile" + "lambda:CreateEventSourceMapping" + "lambda:CreateFunction" + "lambda:InvokeFunction" + "lambda:UpdateFunctionCode" + "sts:AssumeRole" ) -role_policy=$(aws --profile $PROFILE iam get-role --role-name $ROLE_NAME) - -echo -e "=============== Role: $ROLE_NAME ===============" -echo "$role_policy" - -IFS=$'\n' -attached_role_policies=($(aws --profile $PROFILE iam list-attached-role-policies --role-name $ROLE_NAME | jq -r '.AttachedPolicies[].PolicyArn')) +known_dangerous_aws_managed_policies=( + "arn:aws:iam::aws:policy/service-role/AmazonEC2RoleforSSM" + "arn:aws:iam::aws:policy/service-role/AmazonMachineLearningRoleforRedshiftDataSource" +) dangerous_permissions=() potentially_dangerous_permissions=() all_perms=() +used_bad_policies=() -for policy in "${attached_role_policies[@]}" ; do - echo -e "\n=============== Attached Policy Arn: $policy ===============" +function examine_policy() { + policy=$1 + role_name=$2 - version_id=$(aws --profile $PROFILE iam get-policy --policy-arn $policy | jq -r '.Policy.DefaultVersionId') + out=$(aws --profile $PROFILE iam get-policy --policy-arn $policy) + version_id=$(echo "$out" | jq -r '.Policy.DefaultVersionId') + policy_name=$(echo "$out" | jq -r '.Policy.PolicyName') policy_version=$(aws --profile $PROFILE iam get-policy-version --policy-arn $policy --version-id $version_id) - echo "$policy_version" + + if [[ $VERBOSE == 1 ]]; then + echo -e "\n------------[ Policy Arn: $policy ]------------" + echo "$policy_version" + fi permissions=($(echo "$policy_version" | jq -r '.PolicyVersion.Document.Statement[] | select(.Effect=="Allow") | if .Action|type=="string" then [.Action] else .Action end | .[]')) + path="" + if [[ "$role_name" != "" ]]; then + path="$role_name.$policy_name." + else + path="$policy_name." + fi + + if [[ "$ROLE_NAME" != "all" ]] ; then + path="" + fi + + for bad_policy in "${known_dangerous_aws_managed_policies[@]}"; do + if echo "$policy" | grep -iq "$bad_policy" ; then + used_bad_policies+=("$path$bad_policy") + fi + done + for perm in "${permissions[@]}" ; do - all_perms+=("$perm") - for dangperm in "${known_dangerous_permissions[@]}"; do - if echo "$dangperm" | grep -iq $perm ; then - dangerous_permissions+=("$perm") + permadd="$path$perm" + all_perms+=("$permadd") + + for potdangperm in "${known_potentially_dangerous_permissions[@]}"; do + if [[ "$perm" == "iam:*" ]]; then continue ; fi + if echo "$perm" | grep -Piq "$potdangperm" ; then + potentially_dangerous_permissions+=("$permadd") fi done - for dangperm in "${known_potentially_dangerous_permissions[@]}"; do - if echo "$perm" | grep -Piq "$dangperm" ; then - potentially_dangerous_permissions+=("$perm") + + for dangperm in "${known_dangerous_permissions[@]}"; do + if echo "$perm" | grep -iq "$dangperm" ; then + dangerous_permissions+=("$permadd") fi done done -done +} + +function examine_role() { + role_name=$1 + + role_policy=$(aws --profile $PROFILE iam get-role --role-name $role_name) + + if [[ $VERBOSE == 1 ]]; then + echo -e "------------[ Role: $role_name ]------------" + echo "$role_policy" + fi + + attached_role_policies=($(aws --profile $PROFILE iam list-attached-role-policies --role-name $role_name | jq -r '.AttachedPolicies[].PolicyArn')) + + if [[ $VERBOSE == 1 ]]; then + echo + fi + echo "[+] Role ($role_name) has following policies attached:" + for policy in "${attached_role_policies[@]}" ; do + echo -e "\t- $policy" + done + + if [[ $VERBOSE == 1 ]]; then + echo + fi + + for policy in "${attached_role_policies[@]}" ; do + examine_policy $policy $role_name + done +} + +# +#------------------------------------------------------------------ +# + +IFS=$'\n' + +if [[ "$ROLE_NAME" == "all" ]]; then + echo "[+] Evaluating ALL used IAM Roles" + echo + + out=($(aws --profile awl iam list-roles --query 'Roles[*].RoleName' --output text | tr '\t' '\n')) + + for role in "${out[@]}"; do + examine_role $role + done + +elif echo "$ROLE_NAME" | grep -q "arn:aws:iam:" && echo "$ROLE_NAME" | grep -q ":policy/" ; then + echo "[+] Working on specified Policy Arn: $ROLE_NAME" + echo + + examine_policy $ROLE_NAME +else + echo "[+] Working on specified Role: $ROLE_NAME" + echo + + examine_role $ROLE_NAME +fi + +#------------------------------------------------------------------ + +if [[ ${#used_bad_policies[@]} -gt 0 ]]; then + echo -e "\n\n[-] =============== Found AWS Managed Insecure Policies in Use ===============" + echo + sorted=($(echo "${used_bad_policies[@]}" | tr ' ' '\n' | sort -u )) + for pol in "${sorted[@]}"; do + echo -e "\t$pol" + done +fi if [[ ${#all_perms[@]} -gt 0 ]]; then - echo -e "\n\n=============== All permissions granted to this role ===============" + echo -e "\n\n[+] =============== Permissions granted ===============" + echo sorted=($(echo "${all_perms[@]}" | tr ' ' '\n' | sort -u )) for perm in "${sorted[@]}"; do - echo -e "\t$perm" + echo -e "\t$perm" | sed -r 's/\./ -> /g' done if [[ ${#potentially_dangerous_permissions[@]} -gt 0 ]]; then - echo -e "\n\n=============== Detected POTENTIALLY dangerous permissions granted ===============" + echo -e "\n\n[-] =============== Detected POTENTIALLY dangerous permissions granted ===============" + echo sorted=($(echo "${potentially_dangerous_permissions[@]}" | tr ' ' '\n' | sort -u )) for dangperm in "${sorted[@]}"; do - echo -e "\t$dangperm" + echo -e "\t$dangperm" | sed -r 's/\./ -> /g' done - else - echo -e "\nNo potentially dangerous permissions were found to be granted." fi if [[ ${#dangerous_permissions[@]} -gt 0 ]]; then - echo -e "\n\n=============== Detected dangerous permissions granted ===============" + echo -e "\n\n[!] =============== Detected DANGEROUS permissions granted ===============" + echo sorted=($(echo "${dangerous_permissions[@]}" | tr ' ' '\n' | sort -u )) for dangperm in "${sorted[@]}"; do - echo -e "\t$dangperm" + echo -e "\t$dangperm" | sed -r 's/\./ -> /g' done - else - echo -e "\nNo dangerous permissions were found to be granted." fi else echo -e "\nNo permissions were found to be granted." fi +echo