Using Service Control Policies to protect security baselines

Service Control Policies (SCPs) can be a great way to prevent actions from happening in AWS accounts. In this post, we will illustrate a specific use case of SCPs that protects the security baseline, or landing zone, configuration you’ve created for accounts

9 minutes read

What is a security baseline? 

A security baseline is an initial configuration of an AWS account that should always remain configured in a certain way. For example, when you first create a new AWS account in your organization, you likely want to ensure that CloudTrail and GuardDuty are enabled, and certain IAM roles exist in the account for your vendors or your own access. You have an expectation that those things will always exist in all your accounts.  

Some companies will create audit scripts that periodically scan all their accounts to check for these things, and alert if any of these assertions become untrue. They will then manually, or possibly through auto-remediation, attempt to correct the baseline. While we recommend this auditing always be done, some problems can be avoided by using SCPs to prevent modifications to this baseline. Without an SCP, you may find this baseline is modified either accidentally (such as by a curious engineer, or one that runs aws-nuke), or maliciously by an attacker to avoid detection, investigation, and remediation.

General guidance for SCPs 

Although we’ll be showing a specific use case for SCPs, they have many other uses. The Control Tower Mandatory controls has examples of many ways they can be used. You are limited in the number of SCPs you can use, so you should combine multiple of the examples here into a single policy by making each SCP its own statement. 
 
SCPs are very powerful, but can create usability problems, so one should be cautious in how they are used. As general guidance, if you find yourself needing to maintain exception cases for an SCP, it may be a use case where the usability problems outweigh the benefits. 

One problem SCPs can create is confusion and difficulty in debugging when actions are denied. In an earlier blog post we showed how to use Delegated Administrator for AWS Organizations to give engineers visibility into the SCPs being applied to them if you want to do such a thing. 

Protecting your IAM roles 

A security baseline commonly contains IAM roles from vendors, your own tooling, or for different teams to gain access to the accounts. We want to prevent modification or deletion of these roles, and privilege escalation where even someone with admin privileges in an account could assume this role in order to take actions that you may be allowing a special role to perform. This can be accomplished as follows:

{ 
  "Version": "2012-10-17", 
  "Statement": [ 
     { 
        "Sid": "SecurityBaselineRoleProtection", 
        "Effect": "Deny", 
        "Action": [ 
          "iam:AttachRolePolicy", 
          "iam:CreateRole", 
          "iam:DeleteRole", 
          "iam:DeleteRolePermissionsBoundary", 
          "iam:DeleteRolePolicy", 
          "iam:DetachRolePolicy", 
          "iam:PutRolePermissionsBoundary", 
          "iam:PutRolePolicy", 
          "iam:UpdateAssumeRolePolicy", 
          "iam:UpdateRole", 
          "iam:UpdateRoleDescription" 
        ], 
        "Resource": [ 
          "arn:aws:iam::*:role/WizAccess-Role" 
          "arn:aws:iam::*:role/stacksets-exec-*" 
        ], 
        "Condition": { 
          "ArnNotLike": { 
            "aws:PrincipalArn": [ 
                "arn:aws:iam::*:role/stacksets-exec-*"  
         ] 
       } 
      } 
    } 
  ] 
} 

This example protects the IAM role name commonly used by Wiz and is based on a Control Tower Mandatory Control. If you’ve modified the Wiz access role name from the default, you should change this policy to meet your environment. You can also add additional role names here that you wish to protect.  

This policy assumes that the Wiz role is going to be created by a Cloud Formation StackSet, and therefore it both grants an exception to that role to create and modify the Wiz role, and also protects that role. The CloudFormation StackSet role that is created by the AWS Organizations feature is deployed via an AWS service-linked role and is thus not going to be restricted by this SCP, because SCPs cannot prevent the actions of AWS service-linked roles. 

Protecting AWS services 

Services such as CloudTrail and GuardDuty can be managed using organization level features, or individually within accounts. When the organization-level features are used (such as an organization trail, or Delegated Admin for GuardDuty) you benefit from inherent protections against member account modifications. For example, a member account that is part of an organization that has an organization trail enabled for CloudTrail cannot disable that CloudTrail monitoring.   

This next SCP protects CloudTrail in cases where you are not using an organization trail, and additionally protects GuardDuty. Note that the trail name CHANGEME must be changed for your environment, along with the account ID 111111111111 which you should set to the account that manages GuardDuty for your organization.

{ 
    "Version": "2012-10-17", 
    "Statement": [ 
        { 
            "Sid": "ProtectCloudTrail", 
            "Effect": "Deny", 
            "Action": [ 
                "cloudtrail:DeleteTrail", 
                "cloudtrail:PutEventSelectors", 
                "cloudtrail:StopLogging", 
                "cloudtrail:UpdateTrail" 
            ], 
            "Resource": ["arn:aws:cloudtrail:*:*:trail/CHANGEME"], 
            "Condition": { 
                "ArnNotLike": { 
                    "aws:PrincipalARN":"arn:aws:iam::*:role/stacksets-exec-*" 
                } 
            } 
        }, 
        {  
            "Sid": "ProtectGuardduty",  
            "Effect": "Deny",  
            "Action": [  
                "guardduty:ArchiveFindings",  
                "guardduty:CreateFilter",   
                "guardduty:CreateIPSet", 
                "guardduty:DeleteDetector",  
                "guardduty:DeleteFilter", 
                "guardduty:DeleteIPSet", 
                "guardduty:DeleteThreatIntelSet", 
                "guardduty:DisassociateFromMasterAccount",  
                "guardduty:DisassociateFromAdministratorAccount",  
                "guardduty:UpdateDetector",  
                "guardduty:UpdateFilter",  
                "guardduty:UpdateIPSet",  
                "guardduty:UpdateThreatIntelSet"  
            ],  
            "Resource": ["*"],  
            "Condition": {  
                "ArnNotLike": {  
                    "aws:PrincipalARN": [ 
                        "arn:aws:iam::*:role/stacksets-exec-*", 
                        "arn:aws:iam::111111111111:role/*" 
                    ] 
                } 
            }  
        } 
    ] 
} 

Preventing accounts from leaving 

One way to circumvent SCPs is for an account to leave the organization that is enforcing them, which is why we should also prevent an account from leaving said organization.

{   
    "Version": "2012-10-17",   
    "Statement": {   
        "Effect": "Deny",   
        "Action": "organizations:LeaveOrganization",   
        "Resource": "*"   
    }   
} 

Preventing root access 

The root user presents a number of problems, such as account takeover risks and understanding what person was involved in an action if they authenticate with the root user. You can prevent use of the root user with:

{  
  "Version": "2012-10-17",  
  "Statement": {  
    "Sid": "DenyRootUser",  
    "Effect": "Deny",  
    "Action": "*",  
    "Resource": "*",  
    "Condition": {  
      "StringLike": { "aws:PrincipalArn": "arn:aws:iam::*:root" },  
      "StringNotEquals": { "aws:PrincipalAccount": "111111111111"}  
    },  
  }  
} 

Note in this SCP, we’ve also made an exemption for the account 111111111111. There are a few use cases that may require the root user to be used, so I wanted to show how to do this. These use cases are listed here, along with the use case of fixing resource policies that are denying all access. 

This next policy also prevents some account takeover risks. You can see an example of how these privileges are used legitimately to takeover misconfigured accounts in this post by Twilio. You should have the contact information for your accounts set to approved phone numbers and other values. Note that on January 11, AWS announced the retirement of the privilege aws-portal:ModifyAccount in favor of the account privileges, but until that takes effect on July 6, 2023, both the account and aws-portal privileges should be denied.

{  
  "Version": "2012-10-17",  
  "Statement": {  
    "Sid": "DenyChangingContactInfo",  
    "Effect": "Deny",  
    "Action": [  
      "account:PutAlternateContact",  
      "account:PutContactInformation",  
      "aws-portal:ModifyAccount"  
    ],  
    "Resource": "*",  
  }  
} 

Conclusion 

Ensuring the initial configuration of an AWS account is not modified from the expectations of the security team, even when someone with admin privileges in the account attempts to modify that configuration, is not a simple task. Ensuring an AWS account’s configuration does not depart from the security team’s initial design is no easy feat, especially when an account user with admin privileges attempts to modify the configuration. 

Continue reading

Get a personalized demo

Ready to see Wiz in action?

“Best User Experience I have ever seen, provides full visibility to cloud workloads.”
David EstlickCISO
“Wiz provides a single pane of glass to see what is going on in our cloud environments.”
Adam FletcherChief Security Officer
“We know that if Wiz identifies something as critical, it actually is.”
Greg PoniatowskiHead of Threat and Vulnerability Management