Hossam Ahmed
4 min readAug 7, 2023

Account takeover via helping of refresh token

Why RefreshToken is a time bomb?
In this writeup, I’ll share how I takeover any account with the help of RefreshToken.

Clarifications:

In the case of user registration in the program through Google OAuth, this is done through several steps:

  1. User authenticates with the third party (Google) and after the authentication is successful, tokens (AccessToken and IdToken) are returned.
  2. In this case, the user account becomes inactive or unverified, so the targetApp creates a request to the endpoint [/v1/user/connect] which is responsible for linking the target account with the third-party account (Google).
  • This endpoint [/v1/user/connect] takes some parameters, such as:
    1. username: Stands for Static Unique Number and belongs to a Google account.
    2. Email.
    3. It also takes an Authorization header [Authorization header == IdToken]
Registration process

Notes for this endpoint:

  • The email must be identical to the email address in IdToken, and the Google_id also and, must be valid and not associated with another account.

Notes for the registration process:

  • If the targetApp finds that the account contains the user_id of the Google account as a username that connects directly to this endpoint: [/v1/user/connect]

Notes for change email function:

  • Any user can change his e-mail to another email that registered on the targetApp, but in this case the account status becomes unverified until the activation process is completed through the confirmation code.
  • I noticed that when I change my current email to another one already registered on the app, this change is reflected in the IdToken if I regenerate it using the RefreshToken.

Attack scenario:

  • After several tests, I found that I can inject the user_id(Google_id) instead of the username value during the registration process and after I get the AccessToken, IdToken, and RefreshToken, I use the AccessToken to change the current email to the targetUser’s email
  • So I can use the RefreshToken we got from the registration process at the beginning to retrieve new tokens so that we get an IdToken containing the targetUser’s email that is not yet verified and, Google_id.
  • Then, Send API call to [/v1/user/connect] to link my Google_id with targetUser’s email

Exploitation time:

  1. Sign-up via Google OAuth to obtain the Google user_id. And I completed the registration process, after that, deleted the account.
  2. Now, Sign-up via email and password to inject the Google user_id instead of the username value. After completing the registration process, I got AccessToken, IdToken, and RefreshToken.
  3. I used the AccessToken to change the current email to the targetUser’s email, in this case, my account is suspended until I confirm it.
  4. I used my RefreshToken to retrieve new Tokens.

After translating (decoding) the IdToken, I found that the targetUser’s email was already there, but not verified, and Google_id also.

  • 5. I used this endpoint [/v1/user/connect] to associate the user_id of my Google account that I got from the registration process (Step 1) with the targetUser’s email.
  • 6. Now, if I try to login or register a new account on the targetApp using Google OAuth with the same email associated with the user_id that I injected into the targetUser’s account previously { By linking my Google user_id with the targetUser’s email }, I will be transferred directly to the targetUser’s account. And, the account email has been replaced by the email I used in this login process via OAuth.

I hope you enjoyed reading, Thanks.

Follow me on Twitter: @iknowhatodo0x01