https://blog.plerion.com/conditional-love-for-aws-metadata-enumeration/ Skip to content [plerion-logo-dark] * Platform + o # @ Cloud-Native Application Protection Platform (CNAPP) Cloud-Native Application Protection Platform (CNAPP) Gain visibility into the entire life cycle of your cloud-native applications @ Cloud Security Posture Management (CSPM) Cloud Security Posture Management (CSPM) Identify and remediate cloud misconfigurations @ Cloud Workload Protection Platform (CWPP) Cloud Workload Protection Platform (CWPP) Uncover and fix vulnerabilities in your most critical workloads before they are exploited @ Cloud Infrastructure Entitlement Management (CIEM) Cloud Infrastructure Entitlement Management (CIEM) Enforce least privilege and zero trust while maintaining balance between agility and security @ Cloud Detection and Response (CDR) Cloud Detection and Response (CDR) Detect, investigate, and respond to real-time threats in the cloud @ Data Security Posture Management (DSPM) Data Security Posture Management (DSPM) Discover, classify, and protect your sensitive data @ Cloud Asset Inventory Cloud Asset Inventory Continuously discover and track cloud assets @ Shift Left Security Shift Left Security Integrate security into the developer's toolchain, identify and fix gaps early in the development lifecycle @ Software Bill of Materials Software Bill of Materials Continuous software supply chain security * Resources + o # @ Cloud Knowledge Base Cloud Knowledge Base Explore and analyze a comprehensive cloud security knowledge base to strengthen your cloud security @ Plerion Product Updates Plerion Product Updates Stay up to date with the latest Plerion product updates * Company + o # @ About Plerion About Plerion We are revolutionizing cloud security @ Careers Careers Join our growing team located all over the world @ FAQs FAQs Have any questions? We have answers * Get a Demo Get a demo How would you feel if an attacker could read your AWS resource tags? Turns out they can! We've found a way to enumerate various metadata from public resources and created a tool to help you test your environment. * Daniel Grzelak Daniel Grzelak * February 7, 2024 * 2 min read Conditional Love for AWS Metadata Enumeration Did your parents ever tell you the one about the security researcher and the AWS policy condition engine? No? Weird. It's okay, I'll tell it to you now. You see, when a security researcher (me) and the AWS policy engine love each other very very much, they start spending a lot of time together. They learn about each other and tell nerdy jokes. Eventually, when they hug really tight, a metadata enumeration abuse technique pops out. That's not exactly how it works, but it's close enough. The truth is someone else loved the AWS policy engine before I did. Maybe he would describe the process a little differently but in 2021 Ben Bridts published a spicy approach for extracting AWS account IDs from public S3 buckets. It would be underselling it to say this post was inspired by his work. It certainly wouldn't be possible without it. [?] Ben. --------------------------------------------------------------------- Ben identified that S3 implemented a policy condition key called " s3:ResourceAccount" that restricts user, role, or application access to the Amazon S3 buckets owned by a specific AWS account ID. Expressed in nerd, the caller of the S3 API can make that call conditional on the account ID of the bucket they are trying to address. That means it's possible to write an IAM policy condition that says "Allow access to a bucket if the account it belong's to is 133713371337". { "Version": "2012-10-17", "Statement": [ { "Sid": "AllowResourceAccount", "Effect": "Allow", "Action": "s3:*", "Resource": "*", "Condition": { "StringEquals": {"s3:ResourceAccount": "133713371337"} } } ] } If a cheeky hacker attaches that policy to their own user: aws iam put-user-policy --user-name cheeky-user \ --policy-name yikes \ --policy-document file://./policy.json And they try to read from the "commoncrawl" public bucket, they will get an error: % aws s3api head-bucket --bucket commoncrawl An error occurred (403) when calling the HeadBucket operation: Forbidden But if they are really good at guessing 12 digit numbers and put "949746302274" in the policy instead of "133713371337", the operation will succeed (no error, no output): % aws s3api head-bucket --bucket commoncrawl % An observant reader might point out that the attacker in this example should have instead bought a lottery ticket if they are going to be that lucky. They would need to repeat this trick on average 10^12/2 times testing account ID 000000000000 through to 999999999999, to find the correct ID. That's cool and all, but who has the time and money to make 10^12/2 AWS API calls? Not this security researcher! Luckily, Ben also saved us the agony of finger calluses from infinite backspace and numpad keypresses. You see dear reader, the "s3:ResourceAccount" condition key can be used with any string operator including "StringLike", not just "StringEquals". Clever little hackers can use this to test conditions like does the account ID begins with "9": "Condition": { "StringLike": {"s3:ResourceAccount": "9*"} } Or "95" "Condition": { "StringLike": {"s3:ResourceAccount": "95*"} } That means they can go through digit by digit, instead of needing to guess all the digits at once. If one digit succeeds, they move onto the next. The most they will have to press backspace and a digit is just 120 times. Thank you Ben. --------------------------------------------------------------------- This is where Ben's story ends and ours begins. We wanted to know, were there any other resources in the global namespace that could have their account IDs enumerated in this manner? Could a public Route53 zone or Cloudfront distribution be enumerated for example? My favourite approach to finding security issues in AWS is reading the docs because AWS is very good at documenting everything, and I'm not smart enough to just figure it out on my own. It wasn't surprising to find AWS has documented all the service based condition keys and with the magic of copy and paste, we compiled them all. A few candidates popped up: kms:ResourceAliases ram:ResourceArn s3:ResourceAccount s3express:ResourceAccount Those didn't seem that exciting. Was this the end of the love fest? Yeh, nah. That's Australian for "ease into it with a yeh first so the no is not as harsh". At some point in history the accomodating folks at AWS realised the utility of this condition and made it global under the "aws" namespace. Except for a few specific exceptions "aws:ResourceAccount" can be applied to almost every AWS API. We are cooking with gas. [resouceaccount-exceptions] I'll save you from the boring painstaking part of trying to find resources and API calls that: * Allow cross-account or public access; and * Use a global namespace identifier (rather than an ARN that already has an account ID in it) Our search was not exhaustive, but I am exhausted. We did find the following resources allowed account ID enumeration given just their public identifier: * Data Exchange data sets - As AWS eloquently states, "AWS Data Exchange is a service that makes it easy for AWS customers to exchange data in the cloud". There are even public data sets that can be bought and sold on the AWS Marketplace. Data assets are grouped into data sets, which are addressed with a 32 alphanumeric character identifier, e.g. "935c01c3a7f5e3499df7dff4dedeebae". The "dataexchange:GetDataSet" operation is used to access a given dataset and is vulnerable to conditional love. * Lambda URL invocation - Lambda functions can be configured to be executable via a simple HTTPS endpoint. The idea is you send a web request without the extra mucking around with API Gateway. The URL includes a random 32 alphanumeric character identifier, e.g. "https:// zowoqxdmbmrm6iudgpuo57p2ma0lxolt.lambda-url.us-east-1.on.aws/". While it's typical to make these endpoints completely public, it's also possible to require authentication via the "AWS_IAM" mode. Public Lambda functions URLs do not process condition keys when invoked, however when in "AWS_IAM" mode policies are fully evaluated and therefore vulnerable to conditional love. * API Gateway invocation - Similarly to Lambda function URLs, API gateway invocation is vulnerable when in "AWS_IAM" mode. API gateway endpoints are shorter, e.g. "https:// vafkcspga1.execute-api.us-east-1.amazonaws.com/". They also differ in that execution happens under a separate service and action, "execute-api:Invoke". There's no material difference to how this works for these services. Instead of using "s3:ResourceAccount" in the policy, use "aws:ResourceAccount", and replace the action with the target service and API call. { "Version": "2012-10-17", "Statement": [ { "Sid": "AllowResourceAccount", "Effect": "Allow", "Action": ["SERVICE_NAME:*"], "Resource": "*", "Condition": { "StringLike": {"aws:ResourceAccount": ["PARTIALSTRING*"]} } } ] } The end. --------------------------------------------------------------------- Just kidding. Turns out, if you keep reading the docs, you learn more interesting stuff. Like that there are global other condition keys that operate on the destination rather than the source. aws:ResourceOrgPaths aws:ResourceOrgID aws:ResourceTag/{tag-key} lambda:FunctionArn The answer is "Yes". Yes you can apply the same technique to these conditions to enumerate your friend's OrgID and tags values. AWS explicitly states that confidential or otherwise sensitive data should never be put in tags. [no-sensitive-data-in-tags] However, AWS users often aren't aware of, or don't implement this well-reasoned guidance. As a cloud security platform we at Plerion do encounter credentials and other secrets in tags more than we'd hope. We hope for never but -\_(tsu)_/- attackers don't care about our hopes and dreams. There's another mitigating factor. You have to know / guess the tag key in order to enumerate a tag value. If an organisation has an esoteric tagging system, they are more protected through their excellent use of obscurity. Unfortunately, there are a bunch of tag keys that are pretty common. CloudFormation creates some tags automatically: aws:cloudformation:logical-id aws:cloudformation:stack-id aws:cloudformation:stack-name AWS publishes recommended tags: AppName AppId EnvironmentType OwnerTeamEmail ComplianceFramework CostCenter Customer DataClassification HoursOfOperation OwnerTeam PatchGroup ProjectId SupportPriority What would you put in your hacker wordlist? --------------------------------------------------------------------- As your reward for diligently reading this blog post we're also releasing Conditional Love, our AWS metadata enumeration tool. Never again will you have to grind your fingertips to the bone as you manually attach and detach policies. Simply setup a role for testing with all the necessary permissions, install python, and get going. Here are some examples of what it can do: Identify the value of the OwnerEmail tag of a role you can assume: % ./conditional-love.py --profile=<YOUR_CLI_PROFILE> \ --role=<YOUR_ROLE_ARN_TO_ASSUME> \ --action=sts:AssumeRole \ --condition=aws:ResourceTag \ --tag-key=OwnerEmail \ --target=<TARGET_ROLE_ARN> \ --alphabet=abcdefghijklmnopqrstuvwxyz.@ Starting to be wrong. Please be patient... =>d =>da =>dag =>dagr =>dagrz =>dagrz@ =>dagrz@p =>dagrz@pl =>dagrz@ple =>dagrz@pler =>dagrz@pleri =>dagrz@plerio =>dagrz@plerion =>dagrz@plerion. =>dagrz@plerion.c =>dagrz@plerion.co =>dagrz@plerion.com Identify the Organisation ID the 'Commoncrawl' S3 bucket belongs to: % ./conditional-love.py --profile=<YOUR_CLI_PROFILE> \ --role=<YOUR_ROLE_ARN_TO_ASSUME> \ --action=s3:HeadObject \ --condition=aws:ResourceOrgID \ --target=s3://commoncrawl/ \ --alphabet=abcdefghijklmnopqrstuvwxyz- Starting to be wrong. Please be patient... =>o =>o- =>o-f =>o-fz =>o-fz? =>o-fz?? =>o-fz??? =>o-fz???? =>o-fz????? =>o-fz?????o =>o-fz?????ot Identify the account ID that the Carvana 'Car Sales for United States' DataExchange dataset belongs to: % ./conditional-love.py --profile=<YOUR_CLI_PROFILE> \ --role=<YOUR_ROLE_ARN_TO_ASSUME> \ --action=dataexchange:GetDataSet \ --condition=aws:ResourceAccount \ --target=935c01c3a7f5e3499df7dff4dedeebae \ --region=us-west-2 Starting to be wrong. Please be patient... =>1 =>10 =>102 =>102? =>102?? =>102??? =>102???? =>102????? =>102?????? =>102??????7 =>102??????70 =>102??????709 At release Conditional Love supports the following actions: s3:HeadObject dataexchange:GetDataSet lambda:InvokeFunctionUrl execute-api:Invoke sts:AssumeRole sqs:ReceiveMessage And the following conditions: s3:ResourceAccount aws:ResourceAccount aws:ResourceOrgPaths aws:ResourceOrgID aws:ResourceTag lambda:FunctionArn Notice that not all target resource conditions start with "Resource". Adding more actions or conditions is trivial so check out the code if you are interested. --------------------------------------------------------------------- One question remains, what can AWS customers do to protect themselves from this kind of enumeration? * Do some basic threat modelling. Ask yourself, what could happen if all of our tags, account IDs and organisation IDs became public? * Assume everything in resource tags is public. Never put confidential information in tags and implement a cloud security a tool like Plerion to identify when that happens. * Where reasonable, publish resources like Images, lambda layers, applications, and integrations from separate purpose-built AWS accounts as source account IDs are also published as part of the process. Avoid doing so from production accounts. * Given that only principals that have access to a resource can enumerate metadata, AWS recommends customers ensure only authorized accounts are listed within their policies. This will prevent the ability of unauthorized accounts to perform actions on resources. --------------------------------------------------------------------- As this post goes to virtual print, I'm wondering to myself - is this technique more dangerous within a single account? If an attacker lands within a constrained context in an account, it's reasonable to assume they'll be able to map its tagging schema and list the resources they might have access to. Should there be any advantage to be gained from metadata enumeration, they'll be in a perfect position to maximise it. Something to ponder. A lot of time and love from the Plerion team went into making this happen. If you enjoyed this blog, please share it. Need to speak to Plerion? Get a demo More To Explore [circuit-faucet-300x171] AWS Tapping the Leaking AWS Account ID Faucet Think AWS account IDs are useful to attackers? Then these are all the places you might want to look to see if your account ID [keep-aws-account-ids-secret-300x171] AWS The final answer: AWS account IDs are secrets AWS has publicly stated that account IDs are not considered sensitive, but in practice they do more heavy lifting than we'd like to admit. [engineering-led-mission-300x171] Leadership How to give direction to an engineering-led startup without a chief product officer Building cool stuff with awesome engineers is fun but even great engineers need to know where they are going. Here's how you can give your [logo-large] About Platform Overview Cloud-Native Application Protection Platform (CNAPP) Cloud Security Posture Management (CSPM) Cloud Workload Protection Platform (CWPP) Cloud Infrastructure Entitlement Management (CIEM) Cloud Asset Inventory Cloud Detection and Response (CDR) Data Security Posture Management (DSPM) Shift Left Security Software Bill of Materials Resources Cloud Knowledge Base Plerion Product Updates Company About Plerion Careers FAQs Contact Us [iso] ISO/IEC 27001 Information Security Management [soc2-1] SOC 2 Type II Subscribe to exclusive content Twitter Linkedin (c) 2023 Plerion PTY LTD. All rights reserved Terms & Conditions Privacy Plerion acknowledges the first peoples of Australia, their elders past, present and emerging. Get a Demo Request a personalized 1:1 live demo to see how Plerion can help you secure your entire cloud By submitting this form you agree to our Privacy Policy.