AWS introduced S3 Block Public Access in late 2018 to prevent granting public access to S3 buckets and in April 2023, S3 Block Public Access was automatically enabled for all new S3 buckets.
What if there's a way to still make a S3 Bucket public despite having AWS's S3 Block Public Access enabled?
We found the following:
The following were used:
This was reported to AWS Security in February 2024.
S3 Block Public Access is a useful tool for AWS users to ensure there's no accidental exposure of data assets in S3. With this finding, there's a false sense of security for organizations utilizing S3 Block Public Access as a safeguard. For examples of S3 Bucket misconfigurations and data exposure, an internet search will yield examples including recent ones in late 2023 and 2024.
S3 Block Public Access is useful in many ways. The additional of S3 Block Public Access adds another "secure by default" layer to configuring S3 Buckets. Since it's enabled by default on new S3 Buckets, if one wants to make a bucket public, there's an additional step to disable Block Public Access prior to making an S3 Bucket public such as via an ACL (now outdated) or Bucket Policy.
S3 Block Public Access has 4 settings:
We will focus on the 2 non-ACL settings: Block Public Policy and Restrict Public Buckets:
By using the s3:x-amz-server-side-encryption-aws-kms-key-id condition key in a bucket policy, we can configure a bucket policy to evaluate as not public and to be deployed and bypass the AWS Block Public Access settings.
The bucket policy deployed will permit the following public actions:
Thus, we can create a publicly writeable bucket despite S3 Block Public Access settings being enabled to block public bucket creation.
This can be done with the following:
Potential impact Includes:
What does "public" mean? Our understanding of the word "public" is similar to Merriam Webster of exposed to general view.
AWS's S3 documentation on blocking public access includes a section on the meaning of "public". Specifically, this section covers how a bucket policy is evaluated and how "to be considered non-public, a bucket policy must grant access to fixed values". That is a technical description of "public".
What if fixed values could be manipulated into being "public" and open for general use?
We'll explore how to do so with a fixed value for the s3:x-amz-server-side-encryption-aws-kms-key-id condition key. This key can be used to require that a specific AWS KMS key is used to encrypt objects in a bucket. We noticed that when using this condition key, AWS Identity and Access Management does not validate if the string for s3:x-amz-server-side-encryption-aws-kms-key-id exists.
We'll start by confirming the Block Public Access settings of the AWS Account we're in.
Note that there can be Bucket-level Block Public Access settings, but we'll focus on account level settings since account level settings override settings on individual objects. As noted by AWS, "Account level settings override settings on individual objects. Configuring your account to block public access will override any public access settings made to individual objects within your account."
We've confirmed that the Block Public Access settings are all turned to on. Thus, we expect the creation of any public bucket to be denied and for no bucket to be public.
Next, we'll create a customer managed KMS key. For this key, we will create a symmetric key used to encrypt and decrypt data. For simpicity, we will use KMS as the key material origin and use a single-region key. We will use the following KMS key policy that permits public access to encrypt and decrypt with the KMS key.
Note: We could have used a more permissive key policy. For this walkthrough, we chose to only allow encryption and data key generation on this reference key and did not include explicit management of the key itself.
A standard aws kms create-key command may be used here. There are 2 options for the key policy: either to create the key with a key policy or to create the key first, then to change the key policy via aws kms put-key-policy. When a key is created such as via aws kms create-key without a key policy specified, AWS applies a default key policy.
Note: A KMS key alias is optional.
Now, we'll create a S3 Bucket.
We'll use the following settings:
Note: We disabled ACLs to ensure no access is granted via means outside of the S3 bucket policy.
We will use the following bucket policy:
The bucket policy used allows for any principal (*) to put an object as long as the KMS key used is the KMS key from above.
For reference, here's the error when creating a "public" bucket. We did not receive this error from the bucket policy above:
Now let's test out our newly created bucket. We will use the CLI for the next part so we can test from a different account. The IAM principal we're using in Account B will have appropriate KMS and S3 permissions. Since the "public" test will be cross account, the IAM principal will need KMS and S3 permissions. For more information about cross-account evaluation, see AWS's documentation here.
Here's our setup:
In this test, we'll use Account B as the "public" test as that's a unknown account to Account A, where we created the publicly writeable S3 Bucket and KMS Key.
We will use the AWS CLI for the following.
Note: we can also use the high-level s3 commands. We will use the low-level s3api commands.
We can take this one step further and not specify a key since our public KMS key was used to set default encryption for the S3 Bucket.
In both of these commands, the write from an external account succeeded in writing to the S3 bucket in Account A.
To confirm, we can look at the bucket and verify that both the objects were written from an external account to the S3 bucket in Account A.
We can use the S3 GetBucketPolicyStatus API operation to retrieve the policy status for an Amazon S3 bucket, and thus indicating whether the bucket is public or not.
We will run the S3 GetBucketPolicyStatus operation from within Account A itself (where the S3 bucket lives). S3 will tell us that this bucket is not public, yet we were able to publicly write to the bucket.
With the following 2 components, we were able to create a publicly-writeable S3 bucket that Amazon S3 does not consider "public".
Since Amazon S3 did not consider the bucket "public", we were able to create this bucket despite S3 Block Public Access being enabled. Our definition of "public" is different from AWS's definition of "public" which does not evaluate past the bucket policy itself.
We'd expect the following condition keys to potentially yield similar results yet with more complex architecture:
If you're interested in help with KMS keys, encryption management, and cloud data protection, reach out to us via email or subscribe at the bottom for cloud data security updates!