Impersonate User with corresponding Deploy-Token ID
Opening a new issue from the new information provided in HackerOne report, extending the discussion https://gitlab.com/gitlab-org/gitlab/-/issues/235996#note_396638391 here
Quote from HackerOne report
Hi team, I think I have traced why the Deploy-Token could be used for unauthorized access to Package Registry for a particular project:
So in the following files, the workflow is
- Finding Deploy Tokens , dont have? -> finding Job Token, don't have? -> ....
If Finding Deploy Token is found, the return is the Token Object itself and it's not the user whose the Token belongs to lib/gitlab/auth/auth_finder.rb
def deploy_token_from_request
return unless route_authentication_setting[:deploy_token_allowed]
token = current_request.env[DEPLOY_TOKEN_HEADER].presence || parsed_oauth_token
if has_basic_credentials?(current_request)
_, token = user_name_and_password(current_request)
end
deploy_token = DeployToken.active.find_by_token(token)
@current_authenticated_deploy_token = deploy_token # rubocop:disable Gitlab/ModuleWithInstanceVariables
deploy_token
end
And then when looking for an User it is calling the .id
function to get the ID of the user. This is a serious flaw since the Token's id is not corresponding to an user id
lib/api/api_guard.rb
def find_user_from_sources
deploy_token_from_request ||
find_user_from_bearer_token ||
find_user_from_job_token ||
find_user_from_warden
end
def find_current_user!
user = find_user_from_sources
return unless user
# Sessions are enforced to be unavailable for API calls, so ignore them for admin mode
Gitlab::Auth::CurrentUserMode.bypass_session!(user.id) if Feature.enabled?(:user_mode_in_session)
unless api_access_allowed?(user)
forbidden!(api_access_denied_message(user))
end
user
So find_user_from_source
would call deploy_token_from_request
which inturn return the Token Object itself which is
<DeployToken id: 8, revoked: false, read_repository: true, read_registry: false, expires_at: "3000-01-01 00:00:00", created_at: "2020-08-14 03:16:29", name: "sdsd", token: nil, username: "test", token_encrypted: "QanagkeC4fW8efv0T8sdGAbqYDViqpLYLK3qK4CCo3u4V3m3", deploy_token_type: "project_type", write_registry: false, read_package_registry: false, write_package_registry: false>
In this example, this returning id
equal to 8
means my newly created Token could be used to impersonate user with id 8 and this applies to all the routes in Grape or Rails if they allow the use of Deploy-Token