Commiting directories containing LF character results in 500 errors in Web UI when viewing commit
:warning: **Please read [the process](https://gitlab.com/gitlab-org/release/docs/-/blob/master/general/security/developer.md) on how to fix security issues before starting to work on the issue. Vulnerabilities must be fixed in a security mirror.** **[HackerOne report #1937213](https://hackerone.com/reports/1937213)** by `cryptopone` on 2023-04-06, assigned to @ottilia_westerlund: [Report](#report) | [Attachments](#attachments) | [How To Reproduce](#how-to-reproduce) ## Report ##### Summary An attacker is able to add new directories containing the line feed character (`%0a` or `\n`). Once committed to a repository, attempting to view the details of the commit via the GitLab website will result in various 500 Internal Server Errors. Some of these activities include: * Viewing changes inside of a merge request containing the malicious directory * Viewing the malicious directory in the project/repository file browser * Viewing the commit details containing the malicious directory * Comparing branch revisions when one of the branches contain the malicious directory ##### Steps to reproduce Note: Two accounts will be needed (Attacker and Victim). ###### Prerequisites (Victim Account): 1. Have the victim user log in and navigate to (http://gitlab.example.com/projects/new). 1. Create a new project (VictimProject) with `Internal` visibility and `Initialize repository with a README` checked. 1. Navigate to the project members page (http://gitlab.example.com/victim/victimproject/-/project_members) and use `Invite members` to invite the attacker as a developer for the project. ###### Reproduction Steps (Attacker with BurpSuite): 1. Have the attacker login, navigate to the VictimProject and create a new branch called `AttackerBranch` (via http://gitlab.example.com/victim/victimproject/-/branches/new). 1. The attacker will be redirected to http://gitlab.example.com/victim/victimproject/-/tree/AttackerBranch 1. Click the `+` dropdown button and select `New file` (redirects to http://gitlab.example.com/victim/victimproject/-/new/AttackerBranch/). 1. In BurpSuite use the Proxy -> Intercept tab and ensure `Intercept is on` is set. 1. Set the file name to `beforeafter/beforeafter/hiddenfile.txt`. For the file contents put in `Hidden attacker contents.`. Target branch should be `AttackerBranch`. 1. Press the `Commit changes` button and have BurpSuite intercept the POST request. 1. The POST request will use a url encoded body. Change the `file_name` from: ``` file_name=beforeafter%2Fbeforeafter%2Fhiddenfile.txt ``` by adding `%0a` between `before` and `after` for the two directories: ``` file_name=before%0aafter%2Fbefore%0aafter%2Fhiddenfile.txt ``` 8. Forward the request to the server. Intercept can also be turned off at this point. 9. The page will reload and if done successfully you'll see a new message `"before after/before after/hiddenfile.txt" did not exist on "AttackerBranch"` and the commit message area will display a loading indicator. 10. If you press F12 to open Developer Tools and access the console you will also see several 500 errors. 11. Have the attacker create a new merge request by clicking the `Create merge request` in the green message `You pushed to AttackerBranch just now` (or by navigating to http://gitlab.example.com/victim/victimproject/-/merge_requests/new?merge_request%5Bsource_branch%5D=AttackerBranch). Then click `Create merge request` at the bottom of the page. ###### Reproduction Steps (Victim): 1. Navigate to the merge request created by the attacker (http://gitlab.example.com/victim/victimproject/-/merge_requests/1). 1. Click on the `Changes` tab to view the changes but note the `Error: Couldn't load some or all of the changes.` error message. 1. Try clicking on the commits tab, then on `Add new file` to view the commit changes and again on `Add new file` and note the resulting 500 error. At this point the web UI will throw 500 Internal Server Errors on a variety of tasks, as seen when attempting to view the merge request changes: * Compare Revisions (between AttackerBranch and main) - http://gitlab.example.com/victim/victimproject/-/compare?from=main&to=AttackerBranch when clicking the `Compare` button. * Attempting to view the AttackerBranch and clicking on the `before after` directory to view its contents (http://gitlab.example.com/victim/victimproject/-/tree/AttackerBranch) ##### Impact An attacker could disrupt regular processes such as reviewing merge requests or viewing portions of a project via the web UI. My initial report uses the following reasoning for the CVSS score: `AC:L`: Attacker can use the web UI to add a directory and modify the POST request before it reaches the server. `PR:L`: Attacker requires an authenticated account but does not require elevated roles within a project. `UI:N`: Attack can be accomplished without any user interaction. `S:U`: Impact is localized to the exploitable component. `C:N`: No confidential information is disclosed. `I:N`: No integrity loss. `A:L`: The commit details cannot be viewed in the Web UI but could still be reviewed using a local git repo. ##### Examples Attached is a copy of the project I created on my self-hosted instance running 15.10.2 following these repro steps. It's possible to import this project file as a "GitLab Export" to see the merge request and resulting 500 errors. [directories_with_lf_victim_victimproject_export.tar.gz](https://h1.sec.gitlab.net/a/d1861ae9-6b75-438c-a573-e67d2ec38e54/directories_with_lf_victim_victimproject_export.tar.gz) Additionally, I created a project on GitLab.com and setup a similar project (though I only used a single account) to demonstrate the 500 errors as well. The project is currently Private but please let me know if you would like access. https://gitlab.com/Cryptopone/crlf-directory-repro/-/tree/AttackerBranch?ref_type=heads ##### What is the current *bug* behavior? If a directory containing a line feed character is committed to a project's repository, the web UI will throw a 500 Internal Server Error when any attempts are made to view the contents of the commit. ##### What is the expected *correct* behavior? The web page should be displayed normally, without throwing a 500 error. ##### Relevant logs and/or screenshots Attacker adds linefeed characters to the directory paths to the hidden file: ![AttackerAddsLFtoDirectoriesFilePath.png](https://h1.sec.gitlab.net/a/0acc8e3f-ac9a-4e78-8067-395ac216f346/AttackerAddsLFtoDirectoriesFilePath.png) Response when attacker pushes the malicious directories and hidden file to the project repo: ![AfterAttackerPushesFileToRepo.png](https://h1.sec.gitlab.net/a/784854ff-038c-4a96-8efa-8463d82ec03f/AfterAttackerPushesFileToRepo.png) Victim attempting to view changes inside of a merge request: ![VictimViewingMergeRequestChanges.png](https://h1.sec.gitlab.net/a/807aa023-1ac0-4ee5-a274-57d7a6e647c5/VictimViewingMergeRequestChanges.png) ``` ==> /var/log/gitlab/gitlab-rails/production.log <== Gitlab::Git::CommandError (13:error streaming commits: cat-file get commit "6280e1a0a6ffab99ab5ea8b9ff21ea442fa953a1": object not found.): lib/gitlab/git/wraps_gitaly_errors.rb:15:in `rescue in wrapped_gitaly_errors' lib/gitlab/git/wraps_gitaly_errors.rb:6:in `wrapped_gitaly_errors' lib/gitlab/git/repository.rb:377:in `log' lib/gitlab/git/commit.rb:47:in `where' app/models/repository.rb:168:in `commits' lib/gitlab/code_navigation_path.rb:28:in `block in build' lib/gitlab/utils/strong_memoize.rb:34:in `strong_memoize' lib/gitlab/code_navigation_path.rb:26:in `build' lib/gitlab/code_navigation_path.rb:16:in `full_json_path_for' app/serializers/diff_file_entity.rb:66:in `block in <class:DiffFileEntity>' <trimmed> ``` Victim attempting to compare revisions: ![VictimAttemptingToCompareAttackerBranchWithMain.png](https://h1.sec.gitlab.net/a/f38301de-966f-4d8a-908c-9ea20ac9d24c/VictimAttemptingToCompareAttackerBranchWithMain.png) ``` ==> /var/log/gitlab/gitlab-rails/production.log <== ActionView::Template::Error (undefined method `lines' for nil:NilClass): 1: - diff_file = viewer.diff_file 2: - blob = diff_file.blob 3: - total_lines = blob.lines.size 4: - total_lines -= 1 if total_lines > 0 && blob.lines.last.blank? 5: - if diff_view == :parallel 6: = render "projects/diffs/parallel_view", diff_file: diff_file, total_lines: total_lines app/views/projects/diffs/viewers/_text.html.haml:3 app/views/projects/diffs/_viewer.html.haml:14 app/views/projects/diffs/_content.html.haml:11 app/views/projects/diffs/_file.html.haml:42 app/views/projects/diffs/_diffs.html.haml:39 app/views/projects/compare/show.html.haml:12 app/controllers/application_controller.rb:142:in `render' app/controllers/projects/compare_controller.rb:37:in `show' <trimmed> ``` Attempting to view the project repository or navigating into one of the malicious folders: ![VictimAttemptingToBrowseLFDirectory.png](https://h1.sec.gitlab.net/a/c64c424b-1655-4c3a-aafd-49971f286f13/VictimAttemptingToBrowseLFDirectory.png) ``` ==> /var/log/gitlab/gitlab-rails/production.log <== Gitlab::Git::CommandError (13:error streaming commits: cat-file get commit "035a86f727a8236a721ae4bccdd22ce1a5b54be5": object not found.): lib/gitlab/git/wraps_gitaly_errors.rb:15:in `rescue in wrapped_gitaly_errors' lib/gitlab/git/wraps_gitaly_errors.rb:6:in `wrapped_gitaly_errors' lib/gitlab/git/repository.rb:377:in `log' lib/gitlab/git/commit.rb:47:in `where' lib/gitlab/git/commit.rb:97:in `last_for_path' app/graphql/resolvers/last_commit_resolver.rb:14:in `resolve' lib/gitlab/graphql/present/field_extension.rb:18:in `resolve' lib/gitlab/graphql/tracers/timer_tracer.rb:20:in `trace' <trimmed> ``` ##### Output of checks This bug happens on GitLab.com ###### Results of GitLab environment info ``` System information System: Proxy: no Current User: git Using RVM: no Ruby Version: 3.0.5p211 Gem Version: 3.2.33 Bundler Version:2.3.15 Rake Version: 13.0.6 Redis Version: 6.2.11 Sidekiq Version:6.5.7 Go Version: unknown GitLab information Version: 15.10.2-ee Revision: a54d6973eae Directory: /opt/gitlab/embedded/service/gitlab-rails DB Adapter: PostgreSQL DB Version: 13.8 URL: http://gitlab.example.com HTTP Clone URL: http://gitlab.example.com/some-group/some-project.git SSH Clone URL: git@gitlab.example.com:some-group/some-project.git Elasticsearch: no Geo: no Using LDAP: no Using Omniauth: yes Omniauth Providers: GitLab Shell Version: 14.18.0 Repository storages: - default: unix:/var/opt/gitlab/gitaly/gitaly.socket GitLab Shell path: /opt/gitlab/embedded/service/gitlab-shell ``` #### Impact ##### Impact An attacker could disrupt regular processes such as reviewing merge requests or viewing portions of a project via the web UI. My initial report uses the following reasoning for the CVSS score: `AC:L`: Attacker can use the web UI to add a directory and modify the POST request before it reaches the server. `PR:L`: Attacker requires an authenticated account but does not require elevated roles within a project. `UI:N`: Attack can be accomplished without any user interaction. `S:U`: Impact is localized to the exploitable component. `C:N`: No confidential information is disclosed. `I:N`: No integrity loss. `A:L`: The commit details cannot be viewed in the Web UI but could still be reviewed using a local git repo. ## Attachments **Warning:** Attachments received through HackerOne, please exercise caution! * [directories_with_lf_victim_victimproject_export.tar.gz](https://h1.sec.gitlab.net/a/d1861ae9-6b75-438c-a573-e67d2ec38e54/directories_with_lf_victim_victimproject_export.tar.gz) * [AttackerAddsLFtoDirectoriesFilePath.png](https://h1.sec.gitlab.net/a/0acc8e3f-ac9a-4e78-8067-395ac216f346/AttackerAddsLFtoDirectoriesFilePath.png) * [AfterAttackerPushesFileToRepo.png](https://h1.sec.gitlab.net/a/784854ff-038c-4a96-8efa-8463d82ec03f/AfterAttackerPushesFileToRepo.png) * [VictimViewingMergeRequestChanges.png](https://h1.sec.gitlab.net/a/807aa023-1ac0-4ee5-a274-57d7a6e647c5/VictimViewingMergeRequestChanges.png) * [VictimAttemptingToCompareAttackerBranchWithMain.png](https://h1.sec.gitlab.net/a/f38301de-966f-4d8a-908c-9ea20ac9d24c/VictimAttemptingToCompareAttackerBranchWithMain.png) * [VictimAttemptingToBrowseLFDirectory.png](https://h1.sec.gitlab.net/a/c64c424b-1655-4c3a-aafd-49971f286f13/VictimAttemptingToBrowseLFDirectory.png) ## 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