Overview

SmartHire is a medium-difficulty Linux machine that simulates an AI-first hiring platform powered by an MLflow model management backend. The attack chain flows through three distinct stages:

  1. Enumeration - discovering a hidden MLflow virtual host via subdomain fuzzing

  2. Foothold - registering a malicious pickle model via the MLflow REST API to achieve remote code execution (RCE) as svcweb, then using an artifact-based exfil technique to drop an SSH key (egress firewall bypassed entirely using localhost callbacks)

  3. Privilege Escalation - abusing a group-writable Python plugin directory and the site.addsitedir / .pth execution primitive to hijack a NOPASSWD sudo Python script and gain a root SUID shell

The machine teaches valuable lessons about ML pipeline security, Python import hijacking, and living-off-the-land in restricted-egress environments.

1. Enumeration

1.1 Full TCP Port Scan

nmap -sC -sV -T4 -p- --min-rate 5000 10.129.51.202

Output:

PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 8.9p1 Ubuntu 3ubuntu0.15 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
|   256 41:3c:e3:bb:88:70:99:7f:b8:96:59:48:9b:85:98:69 (ECDSA)
|_  256 d5:9d:fd:6b:be:d8:39:6f:3f:43:ab:0e:f6:3e:22:db (ED25519)
80/tcp open  http    nginx/1.18.0 (Ubuntu)
|_http-title: Did not follow redirect to http://smarthire.htb/
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Analysis:

Only two ports open - SSH and HTTP. The Nmap title shows that nginx performs a redirect to http://smarthire.htb/, indicating virtual host-based routing. All endpoints are served under this hostname, so it must be added to /etc/hosts before any further enumeration or browsing.

Flag: -p- ensures no ports are missed. --min-rate 5000 keeps the scan fast on HTB's network without sacrificing accuracy. -sC -sV grabs default NSE scripts and version banners, which is particularly useful for SSH hostkey fingerprinting and HTTP header analysis.

echo "10.129.51.202 smarthire.htb" | sudo tee -a /etc/hosts

1.2 Virtual Host (VHost) Fuzzing - Discovering MLflow

The main site being titled "SmartHIRE" and visually referencing ML pipelines suggests internal tooling may be exposed under a subdomain. We fuzz for virtual hosts using ffuf:

ffuf -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-20000.txt \
  -u http://smarthire.htb \
  -H "Host: FUZZ.smarthire.htb" \
  -fc 301,302

Output:

models                  [Status: 401, Size: 137, Words: 11, Lines: 1, Duration: 224ms]
:: Progress: [19966/19966] :: Job [1/1] :: 195 req/sec :: Duration: [0:01:40] :: Errors: 0 ::

Analysis:

The models subdomain returns HTTP 401 with a WWW-Authenticate: Basic realm="mlflow" header. This is a production MLflow tracking server hidden behind HTTP Basic Auth. MLflow is a popular open-source ML lifecycle platform that manages experiments, runs, and model artifacts.

Why this is critical: The main app has endpoints like /upload_hiring_data, /predict, and /model_info that interact with this MLflow instance. If we control what's in the model registry, we can inject a malicious model that executes code when the app calls load_model().

Add to hosts:

echo "10.129.51.202 models.smarthire.htb" | sudo tee -a /etc/hosts

🔐 PREMIUM WRITEUP - MEMBERSHIP REQUIRED

This machine is still active in HTB, so the full walkthrough, exploitation path, and flags cannot be publicly released.

But you can access the entire premium writeup right now.

🌟 Get Instant Access

Unlock the complete step-by-step solution, techniques used, notes, and exclusive insights by becoming a member.

Why Go Premium?

  • Early access to full detailed writeups

  • Passwords for active CTF solutions

  • Advanced exploitation techniques

Upgrade once - unlock everything instantly.

Keep hacking, keep learning, keep winning. 🎯

Keep Reading