Hey folks! In this writeup, I’m gonna show you how I managed to hijack user accounts by exploiting some sneaky gaps in how an app integrates with Google OAuth and Amazon Cognito. The short version is: if an app screws up how it handles your Google user_id
and your tokens, you can end up taking over someone else’s account just by juggling emails and token refreshes. Let’s dig in!
AccessToken
and an IdToken
from Amazon Cognito.IdToken
(it’s basically the sub
field or something similar). This user_id is unique to your Google account and doesn’t change across apps.user_id
./v1/user/connect
.
username
(which is basically your Google user_id) (The Google user_id must be valid, not already tied to another account and must match the username in the IdToken
).email
(**The email you pass in must match the email in the IdToken
).Authorization
header containing the IdToken
.Under the hood, the app uses Amazon Cognito to store user profiles. Here’s where it matters:
When you change your email in the app, it calls Cognito’s UpdateUserAttributes
operation with your AccessToken
.
The request looks something like this:
POST / HTTP/1.1
Host: cognito-idp.us-west-2.amazonaws.com
X-Amz-Date: 20230613T200059Z
Accept-Encoding: gzip, deflate, br
X-Amz-Target: AWSCognitoIdentityProviderService.UpdateUserAttributes
User-Agent: <UserAgentString>
Authorization: AWS4-HMAC-SHA256 Credential=<Credential>, SignedHeaders=<Headers>, Signature=<Signature>
Content-Length: <PayloadSizeBytes>
{
"AccessToken": "eyJra456defEXAMPLE",
"UserAttributes": [
{
"Name": "email",
"Value": "[email protected]"
}
]
}
Cognito updates your email to "[email protected]"
, but the app flags your account as unverified until you confirm it.
If you then refresh your tokens (using your RefreshToken
), your new IdToken
will contain this updated email—even though it’s unverified.