Skip to content

Partial Bypass for Device OAuth flow using Cross Window Forgery

⚠️ Please read the process on how to fix security issues before starting to work on the issue. Vulnerabilities must be fixed in a security mirror.

HackerOne report #2927555 by sim4n6 on 2025-01-08, assigned to @kmorrison1:

Report | Attachments | How To Reproduce

Report

Summary

This is a regression of #476670 (closed) where the ID was removed from the authorize button to prevent this attack. This bypass uses an id on the input field to do the same thing.

The concept behind the cross-window forgery attack is to pre-select the Authorize button via the URL fragment pointing to the button's ID attribute and induce the victim into clicking ENTER or SPACE, for instance.

In the GitLab Patch Release: 17.5.2, 17.4.4, 17.3.7, there was a fix deployed by Removing the id from authorize buttons. The fix was identified by CVE-2024-7404 for Device OAuth flow allows for cross window forgery.

REDACTED

Using Firefox's latest version (v133.0.3), the next form has the following code:

REDACTED

<form action="/oauth/device/confirm" method="post">  
<input type="hidden" name="authenticity_token" value="OMITTED" autocomplete="off">  
<div class="form-group row">  
<div class="col-lg-8 col-sm-10">  
<label>User code</label>  
<input type="text" name="user_code" id="user_code" value="REDACTED">  
</div>  
</div>  
<div class="div">  
<button data-testid="authorization-button" type="submit" >  
<span class="gl-button-text">  
Authorize  
</span>  
</button></div>  
</form>  

The ID attribute was deleted from the Authorize button in the fix but did you notice the id attribute remains in input inside the form tag.

<input type="text" name="user_code" id="user_code" value="REDACTED">  

Which means using the following URL:

https://209.38.77.108/oauth/device?user_code=REDACTED#user_code  

This would pre-select the input of the user_code inside the form and pressing Enter would induce the victim to submit the form immediately. Thus, the partial bypass of the Device OAuth flow.

Steps to reproduce
  1. Set up a gitlab instance on an ubuntu vps like digital ocean using the following commands:
curl -s https://packages.gitlab.com/install/repositories/gitlab/gitlab-ee/script.deb.sh | os=ubuntu dist=noble bash  
apt update  
apt install -y gitlab-ee  
gitlab-ctl reconfigure  
cat /etc/gitlab/initial_root_password  

1bis. login to the root or any user, you need a client_id for the poc to work.

  1. run the req_dev_authorization.py with a client_id:
 python3 req_dev_authorization.py  "1e51a269d87cb3d5dad717e66a51ac0255a25139f2715982f5298429ce9043a4"  

The code :

import requests  
import sys 

if len(sys.argv) != 2:  
    print("Usage: python req_dev_authorization.py <client_id>")  
    sys.exit(1)  
      
client_id = str(sys.argv[1]) # Replace with your actual client ID  
scope = "api"  # Specify the scope you need (e.g., 'read_api', 'api', etc.)

###  GitLab's OAuth device authorization endpoint  
url = "https://REDACTED/oauth/authorize_device"

###  Prepare the data to send in the POST request  
data = {  
    "client_id": client_id,  
    "scope": scope  
}

try:  
    # Send the POST request to initiate device authorization  
    response = requests.post(url, data=data, verify=False)

    # Check if the request was successful  
    if response.status_code == 200:  
        # Parse the JSON response  
        result = response.json()  
        print("Device Code:", result["device_code"])  
        print("User Code:", result["user_code"])  
        print("Verification URI:", result["verification_uri"])  
        print("Verification URI Complete:", result["verification_uri_complete"])  
        print("Expires In:", result["expires_in"], "seconds")  
        print("Polling Interval:", result["interval"], "seconds")  
    else:  
        print(f"Error: {response.status_code} - {response.text}")

except Exception as e:  
    print(f"An exception occurred: {e}")
  1. The corresponding poc index.html just induces the victim into visiting :
https://REDACTED/oauth/device?user_code=M9QOY9XU#user_code  

to pre-select the user_code input which with an Enter would trigger the form submission.

Impact

Partial Bypass of the Device OAuth Flow. The second part requires a TAB and an Enter press to fully bypass the Device OAuth flow.

Examples

(If the bug is project related, please create an example project and export it using the project export feature)

Nope

(If you are using an older version of GitLab, this will also help determine whether the bug has been fixed in a more recent version)

Nope , I'm using the Gitlab instance v17.7.0-ee hosted on my VPS on Digital Ocean.

(If the bug can be reproduced on GitLab.com without violating the Rules of Engagement as outlined in the program policy, please provide the full path to the project.)

did not try there.

What is the current bug behavior?

(What actually happens, include relevant screenshots, API results, or complete HTTP requests)

preselect the user_code input which leads to a similar behavior as preselecting the authorize button.

What is the expected correct behavior?

(What you should see instead, include relevant screenshots, API results, or complete HTTP requests)

No id attribute used, unless intended explicitly, but rather data-testid.

Relevant logs and/or screenshots

(Paste any relevant logs - please use code blocks (```) to format console output,
logs, and code as it's very hard to read otherwise.)

Output of checks

(If you are reporting a bug on GitLab.com, write: This bug happens on GitLab.com)

did not test there.

Results of GitLab environment info

(For installations with omnibus-gitlab package run and paste the output of:
sudo gitlab-rake gitlab:env:info)

System information  
System:         Ubuntu 24.10  
Proxy:          no  
Current User:   git  
Using RVM:      no  
Ruby Version:   3.2.5  
Gem Version:    3.5.23  
Bundler Version:2.5.11  
Rake Version:   13.0.6  
Redis Version:  7.0.15  
Sidekiq Version:7.2.4  
Go Version:     unknown

GitLab information  
Version:        17.7.0-ee  
Revision:       1fd574ee571  
Directory:      /opt/gitlab/embedded/service/gitlab-rails  
DB Adapter:     PostgreSQL  
DB Version:     14.11  
URL:            https://gitlab.sim4n6.com  
HTTP Clone URL: https://gitlab.sim4n6.com/some-group/some-project.git  
SSH Clone URL:  git@gitlab.sim4n6.com:some-group/some-project.git  
Elasticsearch:  no  
Geo:            no  
Using LDAP:     no  
Using Omniauth: yes  
Omniauth Providers:

GitLab Shell  
Version:        14.39.0  
Repository storages:  
- default:      unix:/var/opt/gitlab/gitaly/gitaly.socket  
GitLab Shell path:              /opt/gitlab/embedded/service/gitlab-shell

Gitaly  
- default Address:      unix:/var/opt/gitlab/gitaly/gitaly.socket  
- default Version:      17.7.0  
- default Git Version:  2.47.0  

(For installations from source run and paste the output of:
sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production)

Impact

As mentioned previously, the impact is a partial Bypass of the Device OAuth Flow. The second part requires a TAB and an Enter press to fully bypass the Device OAuth flow.

Attachments

Warning: Attachments received through HackerOne, please exercise caution!

How To Reproduce

Please add reproducibility information to this section:

Edited by Rohit Shambhuni