An AWS metadata enumeration tool by Daniel Grzelak of Plerion. Use it to enumerate resource tags, account IDs, org IDs etc.
Inspired by S3 Account Search by Cloudar.
During the dark times of 2021 Ben Bridts published a smashing blog post titled "Finding the Account ID of any public S3 bucket name".
In his blog post Ben pointed out that the condition key "S3:ResourceAccount" could be used to enumerate account IDs one character at a time by using the "StringLike" operator and including a wild card. For example, if you want to know if the first 2 digits of an account ID are "43", you would use this in a caller policy:
"Condition": {
"StringLike": {"s3:ResourceAccount": ["43*"]}
}
There are now a number of global resource condition keys for use in policy evaluation. There are also many other services that allow cross-account resource sharing with identifiers that don't include account IDs. We extended Ben's work with the following findings:
- Finding 1: The account ID enumeration technique with global condition "aws:ResourceAccount" can be applied to almost all other services and resources.
- Finding 2: There are other global resource condition keys that can be similarly abused for enumeration of other metadata such as organisation IDs and resource tags.
Conditional Love is a Python tool that allows the user to execute these techniques against an AWS target.
A more complete discussion of the tool and technique has been published on the Plerion Blog
usage: conditional-love.py [-h] [--profile PROFILE] --role ROLE --target TARGET
--condition {s3:ResourceAccount,aws:ResourceAccount,aws:ResourceOrgPaths,aws:ResourceOrgID,aws:ResourceTag,lambda:FunctionArn}
--action {s3:HeadObject,dataexchange:GetDataSet,lambda:InvokeFunctionUrl,execute-api:Invoke,sts:AssumeRole,sqs:ReceiveMessage}
[--alphabet ALPHABET] [--region REGION] [--tag-key TAG_KEY]
options:
-h, --help show this help message and exit
--profile PROFILE AWS CLI profile to execute with
--role ROLE ARN of the role to assume
--target TARGET ARN or identifier of the target resource
--condition {s3:ResourceAccount,aws:ResourceAccount,aws:ResourceOrgPaths,aws:ResourceOrgID,aws:ResourceTag,lambda:FunctionArn}
AWS API to call
--action {s3:HeadObject,dataexchange:GetDataSet,lambda:InvokeFunctionUrl,execute-api:Invoke,sts:AssumeRole,sqs:ReceiveMessage}
Condition context key to test with
--alphabet ALPHABET String of all characters to test
--region REGION AWS region to perform action in
--tag-key TAG_KEY Tag key when using aws:ResourceTag condition
pip install -r requirements.txt
- Python3 with requirements installed
- AWS CLI installed with credentials configured or a profile setup
- A IAM role you can assume with permissions to perform the API actions you want to test
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
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
To add a new policy condition, add an item to the SUPPORTED_CONDITIONS
list at the top of conditional-love.py
. That's it.
A full list of global conditions is documented here and service specific conditions here.
Adding actions is a little bit more involved than conditions:
- Add an item to the
SUPPORTED_ACTIONS
list at the top ofconditional-love.py
. - Locate the cascading action if statement inside the
can_access_target_with_policy
function and use the botoclient
to make the API call you want.
More work may be required for API calls that are less standard. For example, invoking Lambda functionl URLs and API Gateways requires authentication headers to be built manually.
- Watch out for missed permissions on the role you pass in to assume. If you are testing or adding a new action, make sure that role has permissions to execute that action.
- Some target resources are region sensitive, so make sure you are passing in the right region if in doubt.
- Target API Gateways and Lambda URLs must have their authentication type set to
AWS_IAM
in order to be processed by the policy engine. - The default alphabet is just digits 0-9 so if you are enumerating something with other chatacters, make sure to pass your alphabet in.
Distributed under the MIT License. See LICENSE.txt for more information.
Want to discuss Conditional Love? Get in touch on Twitter or LinkedIn.