# Shieldly — Full Content Reference > Shieldly (shieldly.io) is AI-powered security analysis for AWS. It analyzes AWS IAM policies, resource-based policies (S3, Lambda, SQS, KMS, SNS, cross-account roles), CloudFormation templates, and AWS costs — surfacing over-privileged access, privilege-escalation paths, and misconfigurations with plain-English explanations and remediation steps. Free plan: 20 analysis units/day. A no-signup demo analyzer is available at shieldly.io/app/iam. User input is never logged or stored (one-way SHA-256 hash caching only). Shieldly ships as a web app, CLI (`@shieldly/cli` on npm), VS Code extension, GitHub Action, and CDK construct (`@shieldly/cdk-guard`). Shieldly is independent and not affiliated with Amazon Web Services. This file inlines the full AWS IAM privilege-escalation reference (26 methods) and IAM glossary (28 terms) published on shieldly.io. The link-index version is at https://www.shieldly.io/llms.txt. --- # AWS IAM Privilege Escalation Methods ## iam:CreatePolicyVersion Privilege Escalation Source: https://www.shieldly.io/iam/iam-createpolicyversion Severity: Critical Primary permission: iam:CreatePolicyVersion A principal allowed iam:CreatePolicyVersion on a managed policy can publish a brand-new policy version with full administrator permissions and set it as the default in a single call. Because the new version becomes the active one immediately, any identity attached to that policy — including the attacker — instantly gains those permissions. Required permissions: iam:CreatePolicyVersion How the escalation works: 1. The attacker identifies a customer-managed policy they (or a role they hold) are attached to. 2. They call CreatePolicyVersion with --set-as-default and a document granting Action "*" on Resource "*". 3. AWS activates the new version immediately. The attacker now has administrator access through the policy they were already attached to. How to fix and detect it: - Never grant iam:CreatePolicyVersion on Resource "*". Scope it to specific, non-privileged policy ARNs only when genuinely required. - Apply a permissions boundary so newly created policy versions cannot exceed the boundary, even if the document says Action "*". - Alert on CreatePolicyVersion calls in CloudTrail, especially where SetAsDefault is true. ## iam:SetDefaultPolicyVersion Privilege Escalation Source: https://www.shieldly.io/iam/iam-setdefaultpolicyversion Severity: High Primary permission: iam:SetDefaultPolicyVersion Managed policies keep up to five versions. iam:SetDefaultPolicyVersion lets a principal switch the active version to any existing one. If an older, more permissive version is still stored on the policy, the attacker can roll the policy back to it and inherit those broader permissions. Required permissions: iam:SetDefaultPolicyVersion, iam:ListPolicyVersions (to enumerate) How the escalation works: 1. The attacker lists the stored versions of a policy they are attached to. 2. They find a non-default version that grants more than the current default (a common result of someone tightening a policy without deleting the old version). 3. They call SetDefaultPolicyVersion to make that broader version active again. How to fix and detect it: - Delete old policy versions after editing a policy so a permissive version cannot be reactivated. - Scope iam:SetDefaultPolicyVersion to specific policy ARNs, never Resource "*". - Use a permissions boundary to cap the effective permissions regardless of which version is active. ## iam:AttachUserPolicy Privilege Escalation Source: https://www.shieldly.io/iam/iam-attachuserpolicy Severity: Critical Primary permission: iam:AttachUserPolicy iam:AttachUserPolicy lets a principal attach any managed policy to an IAM user. If the attacker can target their own user, they simply attach AdministratorAccess to themselves and gain full control of the account. Required permissions: iam:AttachUserPolicy How the escalation works: 1. The attacker confirms which IAM user their credentials map to. 2. They attach the AWS-managed AdministratorAccess policy to that user. 3. On the next API call the user has administrator permissions. How to fix and detect it: - Restrict iam:AttachUserPolicy to specific users and add a condition limiting which policy ARNs can be attached (iam:PolicyARN). - Apply a permissions boundary so an attached admin policy cannot exceed the boundary. - Treat any self-service permission-granting capability as administrator-equivalent and gate it accordingly. ## iam:PutUserPolicy Privilege Escalation Source: https://www.shieldly.io/iam/iam-putuserpolicy Severity: Critical Primary permission: iam:PutUserPolicy iam:PutUserPolicy writes an inline policy directly onto an IAM user. Unlike attaching a managed policy, the attacker authors the document themselves, so they can grant any permission to their own user without depending on an existing policy. Required permissions: iam:PutUserPolicy How the escalation works: 1. The attacker targets their own IAM user. 2. They call PutUserPolicy with an inline document granting Action "*" on Resource "*". 3. The inline policy takes effect immediately, giving the user administrator access. How to fix and detect it: - Avoid granting iam:PutUserPolicy except to tightly controlled automation, and scope it to specific users. - Enforce a permissions boundary so inline policies cannot exceed it. - Monitor PutUserPolicy, PutRolePolicy, and PutGroupPolicy in CloudTrail — inline policy writes are a strong escalation signal. ## iam:AddUserToGroup Privilege Escalation Source: https://www.shieldly.io/iam/iam-addusertogroup Severity: High Primary permission: iam:AddUserToGroup iam:AddUserToGroup lets a principal add an IAM user to any group. If a privileged group exists (for example one with AdministratorAccess attached), the attacker adds their own user to it and inherits the group’s permissions. Required permissions: iam:AddUserToGroup, iam:ListGroups / iam:ListAttachedGroupPolicies (to enumerate) How the escalation works: 1. The attacker enumerates groups and their attached policies to find a privileged group. 2. They add their own user to that group. 3. The user inherits the group’s permissions on the next call. How to fix and detect it: - Scope iam:AddUserToGroup to specific, non-privileged groups via resource ARNs. - Apply a permissions boundary so group-inherited permissions cannot exceed it. - Review which groups carry AdministratorAccess and who can modify membership. ## iam:CreateAccessKey Privilege Escalation Source: https://www.shieldly.io/iam/iam-createaccesskey Severity: High Primary permission: iam:CreateAccessKey iam:CreateAccessKey on Resource "*" lets a principal mint long-lived access keys for any IAM user, including more privileged ones. The attacker creates a key for a powerful user and then operates with that user’s permissions. Required permissions: iam:CreateAccessKey How the escalation works: 1. The attacker enumerates users to find one with broad permissions. 2. They create a new access key for that user (each user can hold up to two). 3. They authenticate with the new key and inherit the target user’s permissions. How to fix and detect it: - Scope iam:CreateAccessKey to the calling user only, using a condition like aws:username matching the principal, or remove it entirely in favor of short-lived role credentials. - Prefer IAM roles and STS over long-lived user access keys wherever possible. - Alert on CreateAccessKey where the target user differs from the caller. ## iam:CreateLoginProfile and UpdateLoginProfile Privilege Escalation Source: https://www.shieldly.io/iam/iam-createloginprofile Severity: High Primary permission: iam:CreateLoginProfile / iam:UpdateLoginProfile A login profile is the console password for an IAM user. iam:CreateLoginProfile sets one for a user that has none; iam:UpdateLoginProfile resets an existing one. Either lets an attacker set a known password on a more privileged user and log into the console as them. Required permissions: iam:CreateLoginProfile or iam:UpdateLoginProfile How the escalation works: 1. The attacker finds a privileged user (ideally one without a console password, to avoid disrupting them). 2. They set a password they control with CreateLoginProfile, or reset an existing password with UpdateLoginProfile. 3. They sign in to the AWS console as that user with the chosen password. How to fix and detect it: - Scope these actions to the caller’s own user ARN so a user cannot set passwords for others. - Require MFA for console sign-in so a password alone is insufficient. - Alert on CreateLoginProfile / UpdateLoginProfile where the target differs from the caller. ## iam:PassRole + Lambda Privilege Escalation Source: https://www.shieldly.io/iam/passrole-lambda Severity: Critical Primary permission: iam:PassRole + lambda:CreateFunction iam:PassRole lets a principal hand an existing role to an AWS service. Combined with lambda:CreateFunction and a way to invoke the function, an attacker can create a Lambda that runs as a far more privileged role, then execute code with that role’s permissions. Required permissions: iam:PassRole, lambda:CreateFunction, lambda:InvokeFunction or lambda:CreateEventSourceMapping How the escalation works: 1. The attacker enumerates roles they are allowed to pass and picks a privileged one (for example a role with AdministratorAccess). 2. They create a Lambda function, passing that privileged role as the execution role. 3. They invoke the function (directly, or via an event source such as a DynamoDB stream). The function code runs with the privileged role’s permissions. How to fix and detect it: - Scope iam:PassRole to specific role ARNs the principal legitimately needs, never Resource "*". - Add the iam:PassedToService condition (for example lambda.amazonaws.com) so a role can only be passed to the intended service. - Keep highly privileged execution roles out of reach of PassRole grants; least-privilege the execution roles themselves. ## iam:PassRole + EC2 RunInstances Privilege Escalation Source: https://www.shieldly.io/iam/passrole-ec2 Severity: Critical Primary permission: iam:PassRole + ec2:RunInstances With iam:PassRole and ec2:RunInstances, an attacker can launch an EC2 instance attached to a privileged instance profile, then read that role’s temporary credentials from the instance metadata service and use them anywhere. Required permissions: iam:PassRole, ec2:RunInstances, iam:PassRole on the instance profile role How the escalation works: 1. The attacker finds an instance profile / role they can pass that has broad permissions. 2. They run an EC2 instance with that instance profile and a user-data script (or SSH access) under their control. 3. They retrieve the role’s temporary credentials from the instance metadata service (IMDS) and use them from anywhere. How to fix and detect it: - Scope iam:PassRole to specific instance-profile roles and add iam:PassedToService = ec2.amazonaws.com. - Enforce IMDSv2 (HttpTokens=required) to make metadata credential theft harder, especially via SSRF. - Least-privilege the instance roles themselves so a launched instance cannot reach sensitive resources. ## iam:UpdateAssumeRolePolicy Privilege Escalation Source: https://www.shieldly.io/iam/iam-updateassumerolepolicy Severity: Critical Primary permission: iam:UpdateAssumeRolePolicy + sts:AssumeRole iam:UpdateAssumeRolePolicy rewrites a role’s trust policy — the document that decides who may assume it. An attacker can point a privileged role’s trust policy at their own principal, then assume the role and take on its permissions. Required permissions: iam:UpdateAssumeRolePolicy, sts:AssumeRole How the escalation works: 1. The attacker identifies a role with more permissions than they have. 2. They overwrite that role’s trust policy to allow their own user or role as a trusted principal. 3. They call sts:AssumeRole and operate with the privileged role’s permissions. How to fix and detect it: - Scope iam:UpdateAssumeRolePolicy to specific, non-privileged role ARNs only. - Apply a permissions boundary so an assumed role’s effective permissions cannot exceed it. - Alert on UpdateAssumeRolePolicy in CloudTrail; trust-policy changes on privileged roles are high-signal. ## lambda:UpdateFunctionCode Privilege Escalation Source: https://www.shieldly.io/iam/lambda-updatefunctioncode Severity: High Primary permission: lambda:UpdateFunctionCode lambda:UpdateFunctionCode lets a principal overwrite the code of an existing Lambda function. If that function already runs as a more privileged execution role, the attacker replaces the code with their own and inherits the role’s permissions the next time it runs — no PassRole required. Required permissions: lambda:UpdateFunctionCode, a trigger or lambda:InvokeFunction to run it How the escalation works: 1. The attacker lists functions and finds one whose execution role is more privileged than they are. 2. They call UpdateFunctionCode to replace that function’s deployment package with their own code. 3. When the function next runs (an existing trigger, or a direct invoke), their code executes with the privileged execution role’s permissions. How to fix and detect it: - Scope lambda:UpdateFunctionCode to specific function ARNs the principal owns, never Resource "*". - Least-privilege every Lambda execution role so a hijacked function cannot reach sensitive resources. - Alert on UpdateFunctionCode in CloudTrail, especially on functions with broad execution roles. ## iam:PassRole + Glue Dev Endpoint Privilege Escalation Source: https://www.shieldly.io/iam/passrole-glue Severity: Critical Primary permission: iam:PassRole + glue:CreateDevEndpoint A Glue development endpoint runs as an IAM role and gives interactive (SSH/notebook) access. With iam:PassRole and glue:CreateDevEndpoint, an attacker creates a dev endpoint backed by a privileged role and then reads that role’s credentials from inside the endpoint. Required permissions: iam:PassRole, glue:CreateDevEndpoint, glue:GetDevEndpoint How the escalation works: 1. The attacker picks a privileged role they are allowed to pass. 2. They create a Glue dev endpoint, passing that role and their own SSH public key. 3. They connect to the endpoint and retrieve the role’s temporary credentials, then use them anywhere. How to fix and detect it: - Scope iam:PassRole to specific roles and add iam:PassedToService = glue.amazonaws.com. - Restrict glue:CreateDevEndpoint to the roles/teams that genuinely need interactive Glue access. - Least-privilege Glue roles so an endpoint cannot reach beyond its data sources. ## iam:PassRole + CloudFormation Privilege Escalation Source: https://www.shieldly.io/iam/passrole-cloudformation Severity: Critical Primary permission: iam:PassRole + cloudformation:CreateStack CloudFormation can act with a service role you pass to it, performing every action in the template as that role. With iam:PassRole and cloudformation:CreateStack, an attacker passes a privileged role and deploys a template that does whatever that role allows — including granting themselves admin. Required permissions: iam:PassRole, cloudformation:CreateStack How the escalation works: 1. The attacker finds a privileged role they can pass (for example one with IAM or admin permissions). 2. They create a stack with --role-arn set to that role and a template that performs privileged actions (e.g. attach AdministratorAccess to their user). 3. CloudFormation executes the template as the passed role, carrying out the actions on the attacker’s behalf. How to fix and detect it: - Scope iam:PassRole to specific roles and add iam:PassedToService = cloudformation.amazonaws.com. - Use least-privileged CloudFormation service roles scoped to the resources a stack legitimately manages. - Review who can pass roles to CloudFormation; treat it as equivalent to those roles’ permissions. ## iam:PassRole + SageMaker Notebook Privilege Escalation Source: https://www.shieldly.io/iam/passrole-sagemaker Severity: Critical Primary permission: iam:PassRole + sagemaker:CreateNotebookInstance A SageMaker notebook instance runs as an IAM role and gives the user an interactive Jupyter shell. With iam:PassRole and sagemaker:CreateNotebookInstance, an attacker launches a notebook backed by a privileged role and reads that role’s credentials from inside the instance metadata. Required permissions: iam:PassRole, sagemaker:CreateNotebookInstance, sagemaker:CreatePresignedNotebookInstanceUrl How the escalation works: 1. The attacker picks a privileged role they are allowed to pass. 2. They create a SageMaker notebook instance, passing that role. 3. They open a presigned notebook URL, then read the role’s temporary credentials from the instance metadata and use them anywhere. How to fix and detect it: - Scope iam:PassRole to specific roles and add iam:PassedToService = sagemaker.amazonaws.com. - Restrict sagemaker:CreateNotebookInstance to the data-science teams that need it. - Use least-privilege SageMaker execution roles so a notebook cannot reach beyond its datasets. ## iam:PassRole + CodeBuild Privilege Escalation Source: https://www.shieldly.io/iam/passrole-codebuild Severity: Critical Primary permission: iam:PassRole + codebuild:CreateProject A CodeBuild project runs build commands as an IAM service role. With iam:PassRole, codebuild:CreateProject, and codebuild:StartBuild, an attacker creates a project backed by a privileged role and runs arbitrary commands as that role — including exfiltrating its credentials. Required permissions: iam:PassRole, codebuild:CreateProject, codebuild:StartBuild How the escalation works: 1. The attacker picks a privileged role they are allowed to pass. 2. They create a CodeBuild project with that service role and a buildspec that prints the role’s credentials (from the metadata endpoint) or performs privileged actions directly. 3. They start the build; CodeBuild runs the commands as the passed role. How to fix and detect it: - Scope iam:PassRole to specific roles and add iam:PassedToService = codebuild.amazonaws.com. - Restrict codebuild:CreateProject to the teams that own the build pipeline. - Use least-privilege CodeBuild service roles scoped to the resources a build legitimately needs. ## iam:PassRole + Data Pipeline Privilege Escalation Source: https://www.shieldly.io/iam/passrole-datapipeline Severity: High Primary permission: iam:PassRole + datapipeline:CreatePipeline AWS Data Pipeline runs activities on EC2 resources that assume a passed IAM role. With iam:PassRole plus datapipeline:CreatePipeline and PutPipelineDefinition, an attacker defines a pipeline that runs shell commands on a resource backed by a privileged role, then reads that role’s credentials. Required permissions: iam:PassRole, datapipeline:CreatePipeline, datapipeline:PutPipelineDefinition, datapipeline:ActivatePipeline How the escalation works: 1. The attacker picks a privileged role they are allowed to pass as the pipeline’s resource role. 2. They create and define a pipeline whose activity runs shell commands on an EC2 resource using that role. 3. They activate the pipeline; the commands run as the passed role, and its credentials can be exfiltrated. How to fix and detect it: - Scope iam:PassRole to specific roles and add an iam:PassedToService condition for the pipeline resource role. - Restrict the datapipeline:* create/define/activate actions to the few teams that use Data Pipeline. - Prefer modern, least-privileged alternatives (Step Functions, Glue) with tightly scoped roles. ## iam:UpdateLoginProfile Privilege Escalation Source: https://www.shieldly.io/iam/iam-updateloginprofile Severity: Critical Primary permission: iam:UpdateLoginProfile iam:UpdateLoginProfile resets the console password of an IAM user that already has a login profile. An attacker resets the password of a more-privileged user, then signs in to the console as that user — unless the target is protected by MFA. Required permissions: iam:UpdateLoginProfile How the escalation works: 1. The attacker enumerates IAM users and finds one with broad permissions that has a console login profile. 2. They call UpdateLoginProfile to set a new password they control. 3. They sign in to the AWS console as that user and inherit its privileges. How to fix and detect it: - Scope iam:UpdateLoginProfile to specific users and never grant it on Resource "*". - Require MFA on every human user — a reset password alone cannot complete sign-in when MFA is enforced. - Alert on UpdateLoginProfile and CreateLoginProfile in CloudTrail; a password set on another principal is a strong escalation signal. ## iam:AttachRolePolicy Privilege Escalation Source: https://www.shieldly.io/iam/iam-attachrolepolicy Severity: Critical Primary permission: iam:AttachRolePolicy iam:AttachRolePolicy attaches any managed policy to an IAM role. If the attacker can assume the target role (directly via sts:AssumeRole or through a service they control), they attach AdministratorAccess to it and then operate with full permissions. Required permissions: iam:AttachRolePolicy, sts:AssumeRole How the escalation works: 1. The attacker identifies a role they can assume — or one used by a service they can trigger. 2. They attach the AWS-managed AdministratorAccess policy to that role. 3. They assume the role (or invoke the service) and act as an administrator. How to fix and detect it: - Restrict iam:AttachRolePolicy to specific roles and add an iam:PolicyARN condition limiting which policies can be attached. - Apply a permissions boundary to every role so an attached admin policy cannot exceed the boundary. - Tighten role trust policies — escalation requires the ability to assume the role, so a narrow trust policy reduces the blast radius. ## iam:AttachGroupPolicy Privilege Escalation Source: https://www.shieldly.io/iam/iam-attachgrouppolicy Severity: Critical Primary permission: iam:AttachGroupPolicy iam:AttachGroupPolicy attaches any managed policy to an IAM group. An attacker attaches AdministratorAccess to a group they belong to, and every member — including their own user — inherits administrator permissions. Required permissions: iam:AttachGroupPolicy How the escalation works: 1. The attacker confirms which group their IAM user is a member of. 2. They attach the AWS-managed AdministratorAccess policy to that group. 3. Their user inherits administrator permissions on the next API call. How to fix and detect it: - Scope iam:AttachGroupPolicy to specific groups and add an iam:PolicyARN condition allowing only approved policies. - Apply permissions boundaries so policies attached to a group cannot grant more than the boundary permits. - Treat group-policy management as an administrator-equivalent capability and restrict it accordingly. ## iam:PutRolePolicy Privilege Escalation Source: https://www.shieldly.io/iam/iam-putrolepolicy Severity: Critical Primary permission: iam:PutRolePolicy iam:PutRolePolicy writes an inline policy directly onto an IAM role. If the attacker can assume that role, they author a document granting Action "*" on Resource "*" and gain full control — without depending on any existing managed policy. Required permissions: iam:PutRolePolicy, sts:AssumeRole How the escalation works: 1. The attacker identifies a role they can assume or trigger via a service. 2. They call PutRolePolicy with an inline document granting Action "*" on Resource "*". 3. They assume the role and operate as an administrator. How to fix and detect it: - Restrict iam:PutRolePolicy to specific roles and tightly controlled automation only. - Enforce a permissions boundary so an inline policy on the role cannot exceed it. - Monitor PutRolePolicy, PutUserPolicy, and PutGroupPolicy in CloudTrail — inline writes do not appear in managed-policy inventories and are easy to miss. ## iam:PutGroupPolicy Privilege Escalation Source: https://www.shieldly.io/iam/iam-putgrouppolicy Severity: Critical Primary permission: iam:PutGroupPolicy iam:PutGroupPolicy writes an inline policy onto an IAM group. An attacker who belongs to the group authors an inline document granting Action "*" on Resource "*", and every member — including their own user — inherits those permissions. Required permissions: iam:PutGroupPolicy How the escalation works: 1. The attacker confirms the group their IAM user belongs to. 2. They call PutGroupPolicy with an inline document granting Action "*" on Resource "*". 3. Their user inherits the escalated permissions immediately. How to fix and detect it: - Scope iam:PutGroupPolicy to specific groups and tightly controlled automation only. - Apply permissions boundaries so inline group policies cannot exceed the boundary. - Audit inline group policies explicitly — they are invisible to managed-policy inventories. Shieldly and CloudTrail both surface them. ## iam:PassRole + ECS Privilege Escalation Source: https://www.shieldly.io/iam/passrole-ecs Severity: Critical Primary permission: iam:PassRole + ecs:RunTask With iam:PassRole and the ECS task APIs, an attacker registers a task definition whose task role is a privileged role, runs it, and the container receives temporary credentials for that role through the container metadata endpoint. Required permissions: iam:PassRole, ecs:RegisterTaskDefinition, ecs:RunTask How the escalation works: 1. The attacker registers a task definition that sets taskRoleArn to a privileged role they are allowed to pass. 2. They run the task on a cluster they can reach. 3. The container reads the role credentials from the ECS task metadata endpoint and acts with the role permissions. How to fix and detect it: - Scope iam:PassRole to specific roles and add an iam:PassedToService condition for ecs-tasks.amazonaws.com. - Restrict ecs:RegisterTaskDefinition and ecs:RunTask so task roles cannot be chosen freely. - Apply a permissions boundary to roles that ECS tasks can assume. ## EC2 Instance Profile Swap Privilege Escalation Source: https://www.shieldly.io/iam/ec2-replaceinstanceprofile Severity: Critical Primary permission: iam:PassRole + ec2:ReplaceIamInstanceProfileAssociation With iam:PassRole and ec2:ReplaceIamInstanceProfileAssociation, an attacker swaps a privileged instance profile onto an EC2 instance they can run code on, then reads the role credentials from the instance metadata service. Required permissions: iam:PassRole, ec2:ReplaceIamInstanceProfileAssociation How the escalation works: 1. The attacker controls or can run commands on an EC2 instance. 2. They replace the instance profile association with a profile wrapping a privileged role they may pass. 3. They read the new role credentials from the instance metadata service and act with its permissions. How to fix and detect it: - Scope iam:PassRole to specific roles with an iam:PassedToService condition for ec2.amazonaws.com. - Restrict ec2:ReplaceIamInstanceProfileAssociation and ec2:AssociateIamInstanceProfile to administrators. - Require IMDSv2 and limit which instance profiles can be associated with which instances. ## ssm:SendCommand Privilege Escalation Source: https://www.shieldly.io/iam/ssm-sendcommand Severity: High Primary permission: ssm:SendCommand ssm:SendCommand runs arbitrary commands on a managed EC2 instance. If the target instance has a more-privileged instance-profile role, the attacker runs commands on it and uses the role credentials from the metadata service. Required permissions: ssm:SendCommand How the escalation works: 1. The attacker lists managed instances and finds one whose instance profile carries broad permissions. 2. They send a command to that instance via SSM Run Command, executing as root or the SSM agent user. 3. Their command reads the instance role credentials from the metadata service and uses them off-box. How to fix and detect it: - Scope ssm:SendCommand to specific instances using resource ARNs or the ssm:resourceTag condition. - Keep instance-profile roles least-privileged so a compromised instance yields little. - Require IMDSv2 and monitor SendCommand in CloudTrail for unexpected targets. ## Lambda Event Source Mapping Privilege Escalation Source: https://www.shieldly.io/iam/lambda-eventsourcemapping Severity: Critical Primary permission: iam:PassRole + lambda:CreateFunction + lambda:CreateEventSourceMapping An attacker creates a Lambda function with a privileged execution role and triggers it through an event source mapping (for example a DynamoDB or Kinesis stream), escalating without needing lambda:InvokeFunction. Required permissions: iam:PassRole, lambda:CreateFunction, lambda:CreateEventSourceMapping How the escalation works: 1. The attacker creates a function whose execution role is a privileged role they may pass, with code that uses the role. 2. They attach an event source mapping from a stream they can write to. 3. Writing a record to the stream invokes the function, which runs with the privileged role. How to fix and detect it: - Scope iam:PassRole with an iam:PassedToService condition for lambda.amazonaws.com and a specific role allowlist. - Restrict lambda:CreateFunction and lambda:CreateEventSourceMapping to trusted automation. - Apply a permissions boundary to Lambda execution roles. ## ec2:ModifyInstanceAttribute User Data Privilege Escalation Source: https://www.shieldly.io/iam/ec2-modifyinstanceattribute Severity: Critical Primary permission: ec2:ModifyInstanceAttribute ec2:ModifyInstanceAttribute lets a principal rewrite the user-data script of an existing EC2 instance. If that instance already carries a privileged instance profile, the attacker injects a script that runs as root on next boot and exfiltrates the role's credentials — no iam:PassRole needed at all. Required permissions: ec2:ModifyInstanceAttribute, ec2:StopInstances, ec2:StartInstances How the escalation works: 1. The attacker finds a running instance already attached to a privileged instance profile — they never need permission to launch instances or pass roles, only to modify one that already exists. 2. They stop the instance, then call ModifyInstanceAttribute to overwrite its user-data with a payload using the #cloud-boothook directive (or a cloud-config "scripts-user: always" module), which forces execution on every boot instead of only the first. 3. They start the instance. The payload runs as root, reads the instance role's temporary credentials from the instance metadata service, and exfiltrates them for use anywhere. How to fix and detect it: - Restrict ec2:ModifyInstanceAttribute (and the stop/start pair needed to trigger it) to administrators, separate from general EC2 operating permissions. - Least-privilege the instance role itself, since anyone who can reach it via this path gets its full permission set. - Enforce IMDSv2 — it does not block this technique (the payload runs on the instance and can complete the token handshake locally) but it does close the separate remote-SSRF credential-theft path. - Alert on ModifyInstanceAttribute (userData) calls in CloudTrail, especially immediately after StopInstances and before StartInstances on the same instance. --- # AWS IAM Glossary ## IAM Trust Policy Source: https://www.shieldly.io/iam/glossary/trust-policy A trust policy is the resource-based policy attached to an IAM role that defines which principals are allowed to assume it. Every IAM role has two policies that do different jobs. The permissions policy says what the role can do once assumed. The trust policy says who is allowed to assume it in the first place. The trust policy lives in the role’s AssumeRolePolicyDocument and is evaluated when someone calls sts:AssumeRole against the role. The Principal element names the trusted identity: an AWS account, an IAM user or role, or an AWS service (for example ec2.amazonaws.com). Conditions can narrow the trust further, such as requiring an ExternalId for cross-account access or an aws:SourceArn for a service principal. Common mistake: A trust policy with Principal set to "*" or a whole account without conditions lets far more identities assume the role than intended — the root cause of many confused-deputy and cross-account escalation issues. ## IAM Permissions Boundary Source: https://www.shieldly.io/iam/glossary/permission-boundary A permissions boundary is an advanced policy that sets the maximum permissions an IAM user or role can ever have, regardless of its attached policies. A permissions boundary does not grant anything. It caps. The effective permissions of an identity are the intersection of its permissions policies and its boundary: an action is allowed only if both permit it. This makes boundaries the key control for safe delegation — you can let a team create their own roles while guaranteeing those roles can never exceed the boundary. Boundaries are why several privilege-escalation paths are survivable. Even if an attacker attaches AdministratorAccess to themselves, their effective permissions are still clamped to whatever the boundary allows. Common mistake: Granting permission-management actions (iam:CreatePolicyVersion, iam:AttachUserPolicy, iam:PutUserPolicy) without also enforcing a permissions boundary leaves the identity able to grant itself more than intended. ## Service Control Policy (SCP) Source: https://www.shieldly.io/iam/glossary/service-control-policy A Service Control Policy is an AWS Organizations policy that sets the maximum available permissions for the accounts it is applied to. SCPs operate at the organization level, above individual IAM policies. Like a permissions boundary, an SCP does not grant access — it defines a ceiling. An action is only allowed in a member account if the SCP permits it and the IAM policies in that account also permit it. SCPs are the right tool for org-wide guardrails: denying use of certain regions, blocking the disabling of CloudTrail, or preventing changes to specific roles. They apply to everything in the account, including the root user, which makes them stronger than account-level controls. Common mistake: Relying on SCPs alone for least privilege. An SCP only caps the maximum; the actual IAM policies inside the account still need to be scoped, because an over-broad policy under a permissive SCP is still over-broad. ## Resource-Based Policy Source: https://www.shieldly.io/iam/glossary/resource-based-policy A resource-based policy is a policy attached directly to a resource (such as an S3 bucket or KMS key) that specifies who can access it and how. Identity-based policies are attached to users, groups, and roles and travel with the principal. Resource-based policies are attached to the resource itself and include a Principal element naming who is allowed. S3 bucket policies, KMS key policies, SQS queue policies, and Lambda resource policies are all examples. Resource-based policies enable cross-account access without assuming a role: account A can grant account B access to a bucket directly in the bucket policy. When both an identity-based and a resource-based policy apply, access is the union within an account, but cross-account access requires both sides to allow it. Common mistake: A resource-based policy with Principal "*" and no condition makes the resource public. This is the most common cause of exposed S3 buckets. ## Resource Control Policy (RCP) Source: https://www.shieldly.io/iam/glossary/resource-control-policy A Resource Control Policy is an AWS Organizations policy that sets the maximum available permissions for resource-based policies across accounts in the organization — the resource-side counterpart to a Service Control Policy. SCPs cap what an identity (user or role) can be granted. RCPs cap what a resource-based policy can grant to anyone accessing that resource, including principals outside the account or organization entirely. That last part matters: an SCP only constrains principals inside the org, but a resource-based policy on an S3 bucket or KMS key can name a principal from any AWS account, so SCPs alone can't cap that exposure. RCPs close that gap by applying at the resource side regardless of who's asking. RCPs apply org-wide the same way SCPs do, and they cover services incrementally as AWS adds RCP support to them — a new RCP-supported service is covered by the guardrail automatically without editing individual resource policies. Common mistake: Assuming an SCP already covers cross-account resource exposure. It does not — SCPs only constrain principals inside your organization. A bucket policy granting access to an external AWS account is outside SCP reach but inside RCP reach. ## IAM Condition Keys Source: https://www.shieldly.io/iam/glossary/condition-keys Condition keys let you add requirements to a policy statement so it only applies when specific context values match — source IP, MFA, account, encryption, and more. The Condition element is how a broad-looking statement becomes safe. It tests context keys from the request against values you specify, using operators like StringEquals, ArnLike, Bool, and IpAddress. Global keys (prefixed aws:) such as aws:SourceArn, aws:PrincipalOrgID, aws:MultiFactorAuthPresent, and aws:SecureTransport apply across services; each service also has its own keys. Conditions are central to least privilege and to closing cross-account gaps. The ExternalId on a trust policy, the aws:SourceArn that prevents the confused-deputy problem, and the requirement that S3 requests use TLS are all conditions. Common mistake: Assuming a condition tightens access when the operator is wrong — for example using StringLike with a wildcard, or applying a condition to the wrong statement, can silently leave the broad grant in place. ## sts:AssumeRole Source: https://www.shieldly.io/iam/glossary/assume-role sts:AssumeRole is the STS action that returns temporary security credentials for an IAM role, letting a principal operate with that role’s permissions. AssumeRole is the mechanism behind cross-account access, federation, and service-to-service permissions. The caller must be allowed sts:AssumeRole on their identity policy, and the target role’s trust policy must allow the caller as a principal. Both sides have to agree. The credentials returned are temporary and scoped to a session, which is why roles are preferred over long-lived access keys. An optional session policy can further restrict the session below the role’s permissions for the duration of that session only. Common mistake: Granting sts:AssumeRole on Resource "*" lets a principal assume any role whose trust policy is loose. Scope it to the specific role ARNs the workload actually needs. ## Least Privilege Source: https://www.shieldly.io/iam/glossary/least-privilege Least privilege is the principle of granting an identity only the permissions it needs to do its job — nothing more. In IAM terms, least privilege means scoping the Action, Resource, and Condition of every statement as tightly as the workload allows. Instead of Action "*" on Resource "*", you enumerate the specific actions on the specific ARNs, and add conditions where context matters. It is a process, not a one-time setting. Permissions drift as workloads change, so least privilege is maintained by reviewing policies at authoring time, removing unused permissions, and preferring temporary role credentials over long-lived keys. Common mistake: Starting from a broad policy and intending to tighten it later. The tightening rarely happens, so the over-broad grant becomes permanent. Start narrow and widen only when something genuinely breaks. ## IAM Policy Evaluation Logic Source: https://www.shieldly.io/iam/glossary/policy-evaluation IAM policy evaluation is the order in which AWS decides whether a request is allowed: an explicit deny always wins, then an explicit allow, otherwise the default is deny. When a request is made, AWS gathers every applicable policy — identity-based, resource-based, permissions boundaries, SCPs, and session policies — and evaluates them together. The rules are simple but strict: by default everything is denied; a single explicit Deny anywhere overrides any Allow; and an Allow only takes effect if nothing denies it and all applicable boundaries permit it. Understanding this order explains many surprises. A permissions boundary or SCP that does not explicitly allow an action effectively denies it, even if an attached policy says Allow, because the effective permission is the intersection. Common mistake: Expecting an Allow to override a Deny. It never does — an explicit Deny always wins, which is why broad Deny guardrails are a reliable safety net. ## IAM Managed Policy Source: https://www.shieldly.io/iam/glossary/managed-policy A managed policy is a standalone IAM policy that can be attached to multiple users, groups, or roles, and exists independently of any single identity. Managed policies come in two kinds. AWS-managed policies are created and maintained by AWS (for example AdministratorAccess or ReadOnlyAccess). Customer-managed policies are authored in your account and versioned, so you can roll back to a previous version. Because a managed policy is a reusable object, attaching it to many principals keeps permissions consistent and auditable. Each entity can have up to a documented number of attached managed policies, and a customer-managed policy keeps up to five versions. Managed policies show up cleanly in inventories, which makes them easier to audit than inline policies attached directly to one identity. Common mistake: Attaching a broad AWS-managed policy such as AdministratorAccess for convenience grants far more than most workloads need. Prefer a scoped customer-managed policy and review attachments regularly. ## IAM Inline Policy Source: https://www.shieldly.io/iam/glossary/inline-policy An inline policy is a policy embedded directly in a single user, group, or role, with a strict one-to-one relationship to that identity. Unlike a managed policy, an inline policy has no independent existence: it is part of the identity it is written on, and it is deleted when that identity is deleted. Inline policies are useful when you want to guarantee a policy is never accidentally attached elsewhere. The trade-off is auditability. Inline policies do not appear in managed-policy inventories, so reviewing them requires enumerating the inline policies of every principal one by one. Attackers exploit this gap, because an inline grant on a role or group can persist unnoticed. Common mistake: Relying on inline policies for broad permissions hides them from standard audits. If you use inline policies, enumerate them per principal during every review. ## IAM Session Policy Source: https://www.shieldly.io/iam/glossary/session-policy A session policy is an inline permissions policy passed at the moment a role is assumed, further limiting the permissions of that temporary session. When a principal calls sts:AssumeRole (or related APIs), it can pass one or more session policies. The effective permissions of the resulting session are the intersection of the role permissions and the session policy: the session can never gain more than the role already allows, only less. Session policies are the standard way for a broker or application to hand out temporary credentials that are narrower than the underlying role. They are evaluated alongside identity-based and resource-based policies during authorization. Common mistake: Assuming a session policy can grant extra permissions. It can only narrow what the role already permits, so a missing role permission cannot be added back through a session policy. ## EC2 Instance Profile Source: https://www.shieldly.io/iam/glossary/instance-profile An instance profile is a container for an IAM role that lets an EC2 instance receive temporary credentials for that role automatically. An EC2 instance cannot hold an IAM role directly. Instead, the role is wrapped in an instance profile, and the instance profile is attached to the instance. The instance metadata service then provides rotating temporary credentials for the role to anything running on the instance. Instance profiles are a common privilege path: any code or process on the instance can read those credentials from the metadata endpoint. Pinning the instance to the second version of the metadata service (IMDSv2) and scoping the role tightly are the core defenses. Common mistake: Attaching an over-permissioned role through an instance profile means any process on the box — including a compromised dependency — inherits those permissions via the metadata service. ## IAM Service-Linked Role Source: https://www.shieldly.io/iam/glossary/service-linked-role A service-linked role is a special IAM role that is predefined and owned by an AWS service so the service can perform actions on your behalf. Service-linked roles have a trust policy fixed to a single AWS service and a set of permissions the service requires. You cannot edit the permissions arbitrarily, and in many cases the role is created automatically the first time you use the related feature. This keeps the permissions a service needs predictable and scoped. Because the service controls the role definition, the main review question is which services have linked roles in the account and whether those services are still in use. Unused service-linked roles can be removed to shrink the surface area. Common mistake: Treating every role with a service principal as suspicious. Service-linked roles are expected, but you should still confirm the linked service is actually used in the account. ## IAM ExternalId Source: https://www.shieldly.io/iam/glossary/external-id ExternalId is a shared secret placed in a cross-account role trust policy condition to prevent the confused-deputy problem when a third party assumes your role. When you grant a third-party vendor access by letting their AWS account assume a role in your account, naming their account as the principal is not enough. Any of that vendor customers could trick the vendor into assuming your role on their behalf. Requiring a unique ExternalId in the trust policy condition closes that gap. The vendor stores the ExternalId you assign and includes it on every sts:AssumeRole call. Because the value is unique per customer, it prevents one customer of the vendor from impersonating another. AWS specifically recommends ExternalId for third-party cross-account access. Common mistake: Omitting the ExternalId condition on a third-party cross-account role, or reusing the same value across customers, reopens the confused-deputy problem the condition is meant to solve. ## IAM Principal Source: https://www.shieldly.io/iam/glossary/principal A principal is an entity that can make a request to AWS — an IAM user, an IAM role session, a federated user, or an AWS service acting on your behalf. In a policy, the Principal element appears in resource-based policies and trust policies to name who is allowed (or denied) access. Identity-based policies do not use a Principal element, because the principal is implied by the identity the policy is attached to. Principals are referenced by ARN, by account, by service name, or with the special value of an account root. A principal set to a whole account trusts every identity in that account, which is far broader than naming a specific role or user. Common mistake: Using a Principal of "*" or a bare account root without conditions trusts far more identities than intended and is a common root cause of cross-account exposure. ## Attribute-Based Access Control (ABAC) Source: https://www.shieldly.io/iam/glossary/abac ABAC is an authorization model where access is granted based on tags (attributes) on principals and resources rather than on static lists of resource ARNs. In AWS, ABAC uses tag condition keys such as aws:PrincipalTag, aws:ResourceTag, and aws:RequestTag. A single policy can say a principal may act on any resource whose tag matches the principal tag, which scales as new resources are created without rewriting policies. ABAC reduces policy sprawl, but it depends entirely on tag hygiene. If users can set their own tags or tags are inconsistent, the model can grant unintended access, so tag governance and the aws:TagKeys condition matter. Common mistake: Adopting ABAC without controlling who can set tags. If a principal can change its own or a resource tag, it can widen its own access. ## IAM Access Advisor (Last Accessed) Source: https://www.shieldly.io/iam/glossary/access-advisor Access Advisor reports the services a principal is allowed to use and when each was last accessed, making it the primary signal for removing unused permissions. For every IAM user, group, role, or policy, AWS records service last-accessed data. Reviewing it shows which granted services a principal has not touched in months, which are safe candidates for removal on the path to least privilege. The data is available in the console and through the GenerateServiceLastAccessedDetails API, and newer versions extend it to action-level and resource-level last-accessed information for some services. Common mistake: Granting permissions and never revisiting them. Without periodic Access Advisor review, unused permissions accumulate and become the surface area for privilege escalation. ## NotAction Element Source: https://www.shieldly.io/iam/glossary/notaction NotAction matches every action except the ones listed, so it is an inverted action set that is easy to misread as a denial. A statement with Effect Allow and NotAction set to a single action allows every other action — often far more than intended. NotAction does not deny the listed action; it simply excludes it from the set the statement applies to. NotAction is occasionally useful with an explicit Deny to carve out exceptions, but in an Allow statement it is a frequent source of accidental over-permissioning. The safer default is to list the exact actions you intend to allow. Common mistake: Reading "NotAction": "s3:DeleteObject" as "deny delete". It actually allows every action except delete — close to full access. ## Wildcard Action Source: https://www.shieldly.io/iam/glossary/wildcard-action A wildcard action uses an asterisk to match many or all actions, with Action set to "*" granting every action in AWS. Wildcards can be partial, such as s3:Get*, or total, such as a bare "*". A total wildcard on Resource "*" is administrator-equivalent and is the single most common over-permissioning pattern in real policies. Partial wildcards are sometimes reasonable for read families, but they still grant future actions that match the pattern. Enumerating the exact actions a workload needs is the reliable way to avoid silently widening access as AWS adds new APIs. Common mistake: Leaving a wildcard action in place to unblock a deploy. Action "*" on Resource "*" grants full administrator access and rarely gets tightened later. ## IAM Access Key Source: https://www.shieldly.io/iam/glossary/access-key An access key is a long-lived credential pair (access key ID and secret access key) used to authenticate programmatic requests to AWS as an IAM user. Access keys do not expire on their own, which is exactly why they are a frequent source of compromise: a key leaked in a commit, a log, or a container image keeps working until someone disables it. Best practice is to prefer short-lived role credentials over long-lived user keys wherever possible. When keys are unavoidable, rotate them on a schedule, scope the user tightly, and monitor last-used timestamps to deactivate keys that are no longer needed. The credential report lists every key and its age and last use. Common mistake: Embedding a long-lived access key in code or a CI config. Use an IAM role or OIDC federation so the credential is temporary and cannot be exfiltrated as a static secret. ## IAM Multi-Factor Authentication (MFA) Source: https://www.shieldly.io/iam/glossary/mfa MFA adds a second authentication factor on top of a password or access pattern, so a stolen credential alone is not enough to act. For human users, MFA on console sign-in blocks attackers who reset or steal a password, which is why several privilege-escalation paths depend on the target not having MFA. AWS supports virtual authenticator apps, FIDO security keys, and hardware tokens. MFA can also gate sensitive API actions through the aws:MultiFactorAuthPresent condition key, so a policy can require a fresh MFA session before allowing high-impact operations such as deleting resources or changing IAM. Common mistake: Enforcing MFA on the root user but not on privileged IAM users. Any human user with broad permissions should require MFA, both to sign in and for sensitive actions. ## AWS Account Root User Source: https://www.shieldly.io/iam/glossary/root-user The root user is the identity created with the AWS account that has unrestricted access to every resource and cannot be limited by IAM policies. Because the root user bypasses IAM permissions and even some guardrails, it should almost never be used for day-to-day work. AWS recommends locking it down: enable MFA, remove any root access keys, and use it only for the few tasks that strictly require it, such as changing the account email or closing the account. Service control policies in AWS Organizations can restrict what the root user of a member account can do, which is the main way to put guardrails around an otherwise unconstrained identity. Common mistake: Using the root user for routine administration or leaving root access keys active. Create an IAM admin role for daily work and keep root locked behind MFA. ## IAM Credential Report Source: https://www.shieldly.io/iam/glossary/credential-report The credential report is an account-level CSV listing every IAM user and the status of their passwords, access keys, and MFA devices. The report is the fastest way to answer audit questions across all users at once: who has console access, whose access keys are old or unused, who is missing MFA, and which credentials have never been used. It is generated on demand and covers the whole account. Reviewing it on a schedule surfaces stale and risky credentials before they become an incident. It pairs well with Access Advisor, which adds which services each principal actually uses. Common mistake: Auditing users one at a time. The credential report gives an account-wide view in a single CSV, which is far harder to overlook gaps in. ## IAM Policy Simulator Source: https://www.shieldly.io/iam/glossary/policy-simulator The IAM policy simulator evaluates whether a given principal would be allowed or denied a specific action, without making a real request. The simulator applies the same evaluation logic AWS uses at runtime — identity-based policies, resource-based policies, permissions boundaries, and SCPs — and reports the decision plus which statement was decisive. It is useful for testing a policy change before it ships. Because it models effective permissions rather than just listing grants, it helps answer the question that matters for least privilege: not what a policy says, but what a principal can actually do once every policy type is combined. Common mistake: Reading a single policy document and assuming it reflects effective access. Permissions boundaries, SCPs, and resource policies all combine, so simulate the real decision instead of eyeballing one document. ## iam:PassRole Source: https://www.shieldly.io/iam/glossary/passrole iam:PassRole is the IAM permission that lets a principal hand an existing IAM role to an AWS service, so the service can act as that role. IAM roles cannot be "used" directly the way access keys can — a principal has to explicitly pass a role to a service (EC2, Lambda, Glue, CloudFormation, ECS, and others) for that service to assume it on their behalf. iam:PassRole is the permission that authorizes handing a role over this way. Without it, an identity can see a role exists but cannot attach it to anything. It is one of the most consequential permissions in IAM because it does not grant the passed role's permissions directly — it grants the ability to make a service run *as* that role. If the role passed is more privileged than the principal itself, the principal has effectively escalated its own reach, which is why nearly every documented IAM privilege-escalation path involving compute services (EC2, Lambda, Glue, CloudFormation, ECS, SageMaker, CodeBuild, DataPipeline) requires PassRole as one of the two ingredients. Common mistake: Granting iam:PassRole on Resource "*" for convenience. That lets the principal pass *any* role in the account — including highly privileged ones — to any service they control. Scope Resource to the specific role ARNs required, and add an iam:PassedToService condition so the role can only be handed to the intended service. ## Confused Deputy Problem Source: https://www.shieldly.io/iam/glossary/confused-deputy The confused deputy problem is when a more-privileged service can be tricked into using its own permissions to act on a resource the caller should not have access to. The name comes from a classic security pattern: a "deputy" (a service with broad permissions of its own) is fooled into misusing that authority on behalf of a less-privileged caller. In AWS, this typically happens with cross-service, cross-account access — Service A in your account calls Service B, and Service B has no way to tell whether the request is legitimately about your resource or was engineered by an attacker referencing your resource from a different account. AWS's standard fix is the aws:SourceArn and aws:SourceAccount global condition keys on resource-based / trust policies. They let the resource policy require that the calling service is acting specifically on behalf of a named source resource or account, closing the gap that lets an unrelated caller impersonate a legitimate one. Cross-account role trust policies use a similar pattern with a shared ExternalId condition. Common mistake: Writing a trust or resource policy that allows a service principal (e.g. dms.amazonaws.com, s3.amazonaws.com) without any aws:SourceArn / aws:SourceAccount condition. It works in testing but leaves the policy open to any caller who can get that service to reference your resource — including from someone else's account. ## IMDSv2 (Instance Metadata Service v2) Source: https://www.shieldly.io/iam/glossary/imdsv2 IMDSv2 is the session-oriented, token-required version of the EC2 instance metadata service, designed to make remote theft of an instance role's credentials much harder. Every EC2 instance with an attached instance profile can fetch that role's temporary credentials from a well-known local address, 169.254.169.254. The original version (IMDSv1) answered any plain HTTP GET to that address with no authentication, which made it a common target for SSRF vulnerabilities in web applications running on the instance — a bug in unrelated app code could be turned into full credential theft for the instance role. IMDSv2 requires a session token, fetched first via a PUT request with a custom header, before any metadata GET is honored. Because SSRF vulnerabilities are almost always limited to controlling a GET/URL and cannot set arbitrary headers or issue a PUT, this closes the most common remote exploitation path. It does not stop someone who already has code execution on the instance itself — they can complete the PUT+GET flow locally just as easily as the legitimate application does. Common mistake: Treating IMDSv2 as a fix for over-privileged instance roles rather than a mitigation for one specific attack path (remote SSRF). The instance role itself still needs least-privilege scoping — anyone with shell access on the instance can retrieve its credentials under IMDSv2 exactly as under IMDSv1. --- # Try Shieldly - Free IAM policy analyzer, no signup: https://www.shieldly.io/app/iam - Nine free browser tools (linter, trust-policy explainer, ARN parser, policy diff, and more): https://www.shieldly.io/tools - Pricing (Free 20 analysis units/day; Builder $19/mo; Pro $49/mo; Team $99/mo): https://www.shieldly.io/pricing