Wednesday, February 3, 2021

AWS IAM 101/201 and security notes.

Let's start from basic: What's is AWS IAM?

AWS Identity and Access Management (IAM) is a web service that helps you securely control access to AWS resources. You use IAM to control who is authenticated (signed in) and authorized (has permissions) to use resources.

What does exactly IAM provide:

  • Shared access to your AWS account
  • Granular permissions
  • Secure access to AWS resources for applications that run on Amazon EC2
  • Multi-factor authentication (MFA)
  • Identity federation
  • Identity information for assurance
  • PCI DSS Compliance (debatable, IMHO) 
  • Integrated with many AWS services
  • Eventually Consistent
  • Free to use
Do all AWS services work with IAM? 

Not exactly: Here the list:

IAM currently supports the following authorization models:

  • Role-based access control (RBAC). RBAC defines permissions based on a person's job function, known outside of AWS as a role. 
  • Attribute-based access control (ABAC) is an authorization strategy that defines permissions based on attributes. In AWS, these attributes are called tags. Tags can be attached to IAM principals (users or roles) and to AWS resources. 

AWS IAM principals:

- User

- Roles

- Groups 

IAM Policies: 

What is IAM policies?

Shared responsibilities in case of policies:

  • YOU -> Define policy
  • AWS -> Evaluates policy and enforce access

Where policy language is used:

Policy types

Policy types: 

Identity-based policies: 
  • Managed policies
    • AWS managed policy
    • Customer managed policy
  • Inline policies 
Resource-based policies:
  • S3, KMS, etc
  • IAM: 
    • The IAM service supports only one type of resource-based policy called a role trust policy, which is attached to an IAM role. An IAM role is both an identity and a resource that supports resource-based policies. For that reason, you must attach both a trust policy and an identity-based policy to an IAM role. Trust policies define which principal entities (accounts, users, roles, and federated users) can assume the role.
IAM permissions boundaries: A permissions boundary is an advanced feature for using a managed policy to set the maximum permissions that an identity-based policy can grant to an IAM entity. 
Note: usage of the permission boundary must be enforced via IAM policy that will allow users to act only if a particular permission boundary policy is attached.

Service control policies (SCPs): Service control policies (SCPs) are a type of organization policy that you can use to manage permissions in your organization. They attached to the OU or Accounts within AWS Organization and enforce permissions on the account level. These permissions restrictions can not be overridden by any user in the account, including root.

Access control lists (ACLs) - Note Different policy language format (XML)
Access control lists (ACLs) are service policies that allow you to control which principals in another account can access a resource. ACLs cannot be used to control access for a principal within the same account.

Session policies (STS): A session policy is an inline permissions policy that users pass in the session when they assume the role. You can pass the policy yourself, or you can configure your broker to insert the policy when your identities federate into AWS (if you have an identity broker configured in your environment).


How the policy structured? 

Policy structure

Should policy always have these components? No, it depends on usage and the service:
  • Identity-based policies: Principal is not required ( and can't be used at all), as we always attach the policy to the particular principal.
  • Resource-based policies: Resource is not required (but could be provided), as the resource to which the action applies, is the resource to which the policy is attached.
  • Service control policies (SCPs): Principal is not required ( and can't be used at all), but could be specified using condition (and in several different ways: StringLike: aws:PrincipalArn,  aws:PrincipalAccount,  and even like ArnNotLike: aws:PrincipalARN) 

Policy evaluation process:


Generic IAM Policy evaluations

But based on what IAM makes a decision? 
Based on request context.

What is request context and what it includes: 

AWS processes each request to gather the following information into a request context:
  • Actions (or operations) – The actions or operations that the principal wants to perform.
  • Resources – The AWS resource object upon which the actions or operations are performed.
  • Principal – The user, role, federated user, or application that sent the request. Information about the principal includes the policies that are associated with that principal.
  • Environment data – Information about the IP address, user agent, SSL enabled status, or the time of day.
  • Resource data – Data related to the resource that is being requested. This can include information such as a DynamoDB table name or a tag on an Amazon EC2 instance.

Does this cover all cases of the policy evaluations?  Not really.

2 Main cases of the policy evaluation process:

Single account:

Rule of thumb:

Some examples:

Overall evaluation process:

IAM evaluation_in_account


How you can test the IAM policy?  IAM Policy Simulator  

Note: IAM policy simulator only supports Identity-based policy, but can be used for the SCP evaluation as well.


SCP evaluation  is a bit more complicated  as it includes policy inheritance (remember MS AD? ) 
At the end, each AWS API call will be evaluated against effective policy (similar to the  Resultant Set of Policy in classical Microsfot AD):
Hint: Always remember that for action to be allowed in the particular account, this action must be allowed in all policies that attached to all parent to the account OU (continues,  uninterrupted chain of Allow)

Session policy (STS)

A resource-based policy can specify the ARN of the user or role as a principal: 

A resource-based policy can specify the ARN of the session as a principal.

A permissions boundary can set the maximum permissions for a user or role that is used to create a session

Various policies and Root user: 

The AWS account root user is affected by some policy types but not others:

  • You cannot attach identity-based policies to the root user
  • You cannot set the permissions boundary for the root user. 
  • You can specify the root user as the principal in a resource-based policy or an ACL. 
  • Resource-based policies do not affect Root user. (If you mistakenly locked your bucket(or KMS) with Deny *, you can use root user to regain control over this bucket)
  • As a member of an account, the root user is affected by any SCPs for the account.


Known security issues and best practices:

Identity-based policies: 

Managed policies:

AWS managed policy: 

AWS manage all life cycle of the policy. This seems to be nice, but:
  • AWS  regularly makes mistakes in the managed policy that could even lead to the privilege escalations: 

  • AWS adds and removes permissions on it will, causing either potential outage (unexpected lack of permission) or violation of the least permission principle (extra permissions that were not expected)
Customer managed policy:

Configuration mistakes leading to the simple privilege escalation: 
  • A user/role allowed to add its own account to an Admin group
  • A user/role allowed to create a new API key for a more privileged user account
  • A user/role allowed to update the account password for a more privileged user account
  • A user/role allowed to assume a privileged role directly 

Quite often we use  NotActions. Using NotActions in policy Allow statements is like creating a blacklist of actions. But, everything that not included in such a list will be implicitly allowed. 
Unfortunately for the security, some privileged or dangerous permissions have not obvious names and easy to forget/miss when building this list.

Granting user permissions on the IAM policies:
 Following policy permissions provide an easy path to privilege escalation:
  • iam:CreatePolicyVersion
  • iam:SetDefaultPolicyVersion
  • iam:AttachUserPolicy
  • iam:AttachGroupPolicy
  • iam:AttachRolePolicy
  • iam:PutUserPolicy
  • iam:PutGroupPolicy
  • iam:PutRolePolicy
Simply as that: CreatePolicyVersion really means the user can create a new policy of his choice.

iam:UpdateAssumeRolePolicy :

Allowing users to update assume role policy (IAM resource-based policy a.k.a trust policy) can lead to the modification of this trust and adding malicious actors to the list of trusted entities.  


allows a user to pass a role to an AWS entity, ANY role. A malicious actor can use this option to pass a privileged role to the entity in his control (Example: pass a privileged role to the EC2 instance I created or pwned). 
Compensating control, in this case, is AssumRole policy that should allow resources to assume this privileged role being passed to it. Note: this control won't help if the attacker has control over the resource trust configuration.

Privilege Escalation Using AWS Services:
Using AWS service such as Lambda or Glue to perform actions on attacker's request if the malicious actor can update Lambda code.

Inline policies:

As we all know,  AWS  managed policies are recommended over inline policy.


- Inline policies are not reusable
- lifecycle of the policy match lifecycle of the principal it's attached to.
- Inline policies do not have versions
- inline policy can't be used as permissions boundaries 
- hard to automate using config management as it's part of the user/role definition

Resource-based policies:

S3 :

IAM policy without resource restrictions allows user/role to read All S3 buckets:
    "Version": "2012-10-17",
    "Statement": [
            "Effect": "Allow",
            "Action": [
            "Resource": "*"

Not a big deal? what about buckets with sensitive information in this case. 

It's becoming even more challenging when we are using ALLOW based bucket policies:

{ "Version": "2012-10-17", "Id": ProtectSensitiveBucket", "Statement": [{ "Sid": "AllowCICDOnly", "Effect": "Allow", "Principal": { "AWS": [ "arn:aws:iam::12345678:role/jenkins-prod" ] }, "Action": [ "s3:GetObject", "s3:PutObject", "s3:PutObjectACL ], "Resource": [ "arn:aws:s3:::generated-gift-cards", "arn:aws:s3:::generated-gift-cards/*", ] }]

Looks like we are protecting sensitive bucket by allowing only Jenkins role to have access there, right?
- Not really, if we have anywhere in IAM role that grants access to S3 WITHOUT resource restrictions like we mentioned above.  IAM policy and Resource-based (S3) policies evaluated as  OR. 
So in this case our bucket (or KMS key, or smth else) will stay unprotected as IAM will still allow access regardless of the ALLOW statements in the S3 policy.  

How to fix it?
Use DENY statements in the Resource-based policies to restrict access to sensitive resources:

{ "Version": "2012-10-17", "Id": ProtectSensitiveBucket", "Statement": [{ "Sid": "AllowCICDOnly", "Effect": "Allow", "Principal": { "AWS": [ "arn:aws:iam::12345678:role/jenkins-prod" ] }, "Action": [ "s3:GetObject", "s3:PutObject", "s3:PutObjectACL ], "Resource": [ "arn:aws:s3:::generated-gift-cards", "arn:aws:s3:::generated-gift-cards/*" ]
        "Sid": "DenyOthers",
        "Effect": "Deny",
        "Principal": "*",
        "Action": "s3:*",
        "Resource": [
        "Condition": {
          "ArnNotEquals": {
            "aws:PrincipalArn": [

Now it should work as expected explicitly blocking access to the sensitive bucket regardless of the IAM policy.

IAM (trust policy)

Cross-account trust: 

1. Trusting the root of the external account: 
in fact, “root” means that the whole account is trusted, and that trust can be delegated on the other side to any principal in the external account.

{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::111122223333:root" }, "Action": "sts:AssumeRole", "Condition": {} } ] }

In this example, we are trusting ANY user and role from the AWS account 111122223333.

How to do this better? 

{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::111122223333:user/LiJuan" }, "Action": "sts:AssumeRole", "Condition": {} } ] }

Here we are trusting only a particular user (LiJuan) from the external account to perform an action (assume role) in our account.

2. Not using or easy guessable ExternalId:
If this trust is used for the s3 party SaaS service integration, a malicious actor can use this 3d party website to get access (within 3 party SaaS offer functionality) to your account by misrepresenting/claiming it as own. 

{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::111122223333:root" }, "Action": "sts:AssumeRole", "Condition": { "StringEquals": { "sts:ExternalId": "123jjjuu-dhdydy-2344-djju766-ddhh" } } } ] }

Use strong random-based unique ExternalId

Additional important security measures that could and should be used within Trust policy:

1. MFA enforcement
2. Time frame enforcement 
3. Location (IP) enforcement 
4. ABAC or granting access based on Tags - Tags enforcement
5. AWS ORG enforcement 
6. Enforce proper role chaining  

IAM permissions boundaries:

Why we need this? To delegate developer ability to create and manage required roles and policy, but prevent privilege escalation or account takeover. 

How to apply these principles?

1. Allow user/group to create policies a.k.a delegate

2. Allow the developer to create required roles and attach policies (managed policies), but only with enforced boundaries.

The overall workflow looks like this:

ABAC or Tag-based controls:

Further extends IAM capabilities allowing resource Tags to become part of the access authorization process. 

Examples of the steps:

Potential Security issues? 
  • Tags becoming the Key to the whole AWS kingdom. 
  • The ability to create/manage tags must be extremely well enforced and controlled.
  • Not all AWS services support Tags (limited scope of usage)

Service control policies (SCPs)

  • Conditions could be used only in the DENY statements. Details here.
  • Always remember about spelling mistakes  in the IAM action names. 
  • You can't attach more than 5 SCP policies to the OU/Account (Hard limit)
  • Policy size must be 5120 bytes max, including spaces. (Hard limit)

Access control lists (ACLs) 

Mistake prone groups: 
  • Authenticated Users group: represents all AWS accounts. Access permission to this group allows any AWS account to access the resource
  • All Users group: Access permission to this group allows anyone in the world access to the resource. The requests can be signed (authenticated) or unsigned (anonymous)


Reading list:

AWS IAM Assume Role Vulnerabilities Found in Many Top Vendors:

Breaking Attacker Kill Chains in AWS: IAM Roles:

AWS videos:


No comments:

Post a Comment