Welcome to another Hack the Box walkthrough. In this blog post, I have demonstrated how I owned the SmartHire machine on Hack the Box. Hack The Box is a cybersecurity platform that helps you bridge knowledge gaps and prepares you for cyber security jobs.
You can also test and grow your penetration testing skills, from gathering information to reporting. If you are new to this blog, please do not forget to like, comment and subscribe to my YouTube channel and follow me on LinkedIn for more updates.
About the Machine
SmartHire is a medium-difficulty Linux machine on Hack The Box that focuses heavily on modern machine learning infrastructure abuse, insecure ML model handling, authenticated web exploitation, MLflow deserialization vulnerabilities, and Python module hijacking for privilege escalation. The machine combines web application enumeration with real-world AI/ML operational weaknesses, requiring players to chain multiple vulnerabilities together to achieve full system compromise.
The attack chain began with Nmap enumeration, which revealed SSH and an Nginx web server hosting the smarthire.htb application. After performing host configuration by updating the local /etc/hosts file, web enumeration exposed an AI-powered hiring platform containing authentication functionality and ML-related workflows. Further subdomain enumeration using FFUF uncovered models.smarthire.htb, which was discovered to host an MLflow instance protected only by weak default credentials.
After successfully authenticating to MLflow using admin:password, I returned to the main application and completed application registration through an exposed /register endpoint that lacked email verification or access restrictions. Once authenticated, dashboard enumeration revealed functionality for uploading training datasets in CSV format and dynamically training machine learning models.
To interact with the backend AI pipeline, I prepared a controlled CSV dataset and successfully completed model training, which automatically registered a new MLflow model version. Using authenticated MLflow API enumeration, I extracted sensitive backend details including the model artifact path and associated run_id, confirming that model artifacts were directly accessible through the MLflow artifact storage system.
While researching vulnerabilities affecting MLflow, I identified a malicious pickle deserialization attack path capable of triggering remote code execution when poisoned models were loaded. I created a custom Python exploit that abused unsafe deserialization by uploading a malicious pickle payload into the MLflow artifact store. During exploit execution, I registered a weaponized model version and triggered it through the application’s prediction workflow by uploading a secondary CSV file.
Although the application returned prediction errors, ICMP callbacks confirmed successful remote code execution on the target system. I then weaponized the MLflow payload further to perform command execution and output exfiltration by abusing the internal MLflow artifact API. This allowed me to retrieve command output remotely and confirm execution as the svcweb user.
To gain stable access, I prepared SSH persistence by generating a new SSH key pair locally and modifying the payload to inject my public key into the target user’s authorized_keys file. After deploying the SSH persistence payload and retriggering the vulnerable prediction workflow, I successfully established SSH access as the svcweb user and captured the user flag.
Privilege escalation enumeration revealed that the svcweb account could execute a root-owned MLflow management utility through sudo without requiring a password. While analyzing the root-owned MLflow utility, I discovered that it dynamically imported Python modules from plugin directories, including a writable plugin path controlled by the devs group - a group to which svcweb belonged.
I exploited this misconfiguration through Python module hijacking by creating a malicious .pth file and a fake mlflow_actions.py module inside the writable plugin directory. When the privileged utility was executed with sudo, my malicious Python module ran as root and modified /bin/bash to become SUID-enabled. Finally, I leveraged the SUID bash binary to spawn a privileged shell, achieving full root compromise and capturing the root flag.
Protected Page
The first step in owning the SmartHire machine like I have always done in my previous writeups is to connect my Kali Linux terminal with Hack the Box server. To establish this connection, I ran the following command in the terminal:
Once the connection between my Kali Linux terminal and Hack the Box server has been established, I started the SmartHire machine and I was assigned an IP address (10.129.245.215).
Nmap Enumeration
I started the enumeration phase by running an aggressive Nmap scan against the target to identify open ports, running services, and possible attack vectors. The scan quickly revealed that the machine was exposing both SSH and HTTP services, indicating a Linux-based environment hosting a web application.
The web server on port 80 redirected requests to smarthire.htb, which strongly suggested that the application relied on virtual host routing. This gave me the first clue that I needed to update my /etc/hosts file before continuing with further web enumeration and directory discovery.
The scan also confirmed the target was running Ubuntu with OpenSSH 8.9p1 and Nginx 1.18.0. Since no additional services were exposed externally, my focus shifted entirely toward attacking the web application hosted on smarthire.htb.
Host Configuration
After identifying that the web server redirected traffic to smarthire.htb, I updated my local /etc/hosts file to properly resolve the virtual host. This step was necessary because the application depended on hostname-based routing instead of direct IP access.
I authenticated with sudo privileges and mapped the target IP address to the domain name discovered during enumeration. Once the entry was added successfully, I was able to access the website correctly through the browser and continue with further web application testing.
Web Enumeration
After configuring the virtual host, I navigated to the target IP address in the browser and confirmed that the application automatically redirected requests to http://smarthire.htb. This validated that the web application relied on hostname-based routing rather than direct IP access.
Once the redirect completed successfully, the SmartHire landing page became accessible. The application appeared to be an AI-powered hiring platform exposing features such as model registry, resume scoring, and developer-focused workflows, which hinted that machine learning infrastructure might play an important role later in the attack chain.
The presence of links such as “Sign in” and references to AI evaluation workflows suggested that the target likely used backend services related to ML operations, making the web application the primary focus for further enumeration.
Subdomain Enumeration
After confirming the main application was accessible, I began enumerating for additional virtual hosts using ffuf. Since the target relied on hostname-based routing, I fuzzed the Host header to identify hidden subdomains that might expose internal services or administrative panels.
The scan revealed a new subdomain called models.smarthire.htb returning an HTTP 401 Unauthorized response. Although access was restricted, the response confirmed that the subdomain existed and was likely hosting a protected service related to machine learning models or backend infrastructure.
The discovery of the models subdomain was particularly interesting because it aligned with the AI-focused functionality observed on the main website, suggesting that the target might expose MLflow or another machine learning management platform internally.
Host Configuration
After discovering the models.smarthire.htb subdomain during virtual host enumeration, I added it to my local /etc/hosts file so the domain could properly resolve inside the browser. This allowed me to interact directly with the newly discovered service instead of relying on manual Host header manipulation.
I authenticated with sudo privileges and mapped the target IP address to the subdomain. Once the entry was added successfully, I was able to access the models.smarthire.htb application for further enumeration.
The naming convention of the subdomain strongly suggested that the service was related to machine learning infrastructure, making it a promising target for deeper investigation.
MLflow Enumeration
After accessing the models.smarthire.htb subdomain, I attempted to authenticate using default credentials to determine whether the service was protected by weak authentication. I sent a request with the credentials admin:password and inspected the returned response.
The request successfully returned the HTML source code for an MLflow application, confirming that the subdomain was hosting an MLflow instance used for managing machine learning models and experiments. This was a significant finding because MLflow has previously been associated with insecure configurations and vulnerable model handling functionality.
The presence of MLflow immediately shifted my focus toward model registry abuse, artifact handling, and potential remote code execution vectors through malicious machine learning models.
MLflow Authentication
After confirming that the models.smarthire.htb subdomain was running MLflow, I opened the application in the browser and attempted to authenticate using the default credentials discovered earlier. The login was successful, granting me full access to the MLflow dashboard.
Once authenticated, I was presented with the MLflow interface containing sections such as Experiments and Models. Although no runs were currently logged, access to the platform confirmed that weak default credentials were in use, exposing sensitive machine learning infrastructure to unauthenticated users.
This was a critical finding because authenticated access to MLflow often allows interaction with model artifacts, experiment tracking, and backend APIs, which can potentially lead to remote code execution through malicious model uploads or artifact abuse.
Application Registration
After gaining access to the MLflow platform, I returned to the main SmartHire application and continued enumerating the authentication functionality. I noticed a “Sign in” button on the homepage, which redirected me to /login.
While inspecting the authentication flow, I discovered that the application also exposed a /register endpoint. The registration form only required a username, company name, and password, with no email verification or access restrictions in place.
Since account creation was fully open, I proceeded to register a new user account and successfully authenticated into the SmartHire dashboard.
The lack of registration controls suggested that the platform trusted any newly created user account, which opened the door for authenticated feature abuse and deeper application testing.
Dashboard Enumeration
After successfully authenticating to the SmartHire application, I was redirected to the user dashboard where additional functionality became available. The dashboard exposed a “Model Training” feature that allowed authenticated users to upload training datasets in .csv format for machine learning processing.
I enumerated the available options and observed that the application expected user-supplied CSV files which would later be processed by the backend AI pipeline. Since file upload functionality is frequently associated with insecure parsing or backend execution flaws, this immediately became a high-value attack surface for further testing.
The combination of user-controlled file uploads and the previously discovered MLflow infrastructure strongly suggested that the application might be vulnerable to model poisoning, insecure deserialization, or arbitrary code execution through malicious training data.
CSV File Preparation
Before interacting with the model training functionality, I first needed to create a valid .csv dataset that could be uploaded to the SmartHire platform. I generated a small sample dataset containing hiring-related fields such as years of experience, education level, and hiring decisions.
After creating the file, I verified that test.csv was successfully written to disk and available inside my working directory. This dataset would later be used to test how the backend processed user-supplied training data.
Creating a controlled CSV file allowed me to safely interact with the upload functionality while observing how the application handled machine learning training inputs behind the scenes.
Model Training
After preparing the training dataset, I uploaded test.csv through the SmartHire dashboard to observe how the backend machine learning pipeline handled user-supplied data. The upload completed successfully, and the application automatically trained and registered a new model.
Once the training process finished, the dashboard displayed the generated model name, version, and creation timestamp. This confirmed that user-controlled data was being processed directly by the backend ML infrastructure and linked to the previously discovered MLflow instance.
Training Result:
The successful model creation was particularly important because it demonstrated that authenticated users could generate and register ML models dynamically, potentially exposing attack paths involving malicious model artifacts or unsafe deserialization within MLflow.
MLflow API Enumeration
After successfully training the model, I began interacting directly with the MLflow API to gather additional information about the registered model. Using the authenticated MLflow endpoint, I queried the latest model version associated with the generated model name.
The API response revealed important backend details including the model version, associated run_id, and most importantly the artifact storage path. The source field exposed the exact MLflow artifact location where the trained model files were stored on the server.
This was a critical discovery because the exposed artifact path and run_id provided direct insight into how MLflow stored model artifacts internally, opening the possibility of interacting with or abusing the backend model files directly.
MLflow Pickle Deserialization Exploit
While researching potential attack vectors against MLflow, I discovered that vulnerable MLflow deployments could be abused through malicious pickle deserialization during model loading. Since the application allowed authenticated interaction with model artifacts, I decided to weaponize this behavior to test for remote code execution.
I created an exploit.py script that generated a malicious pickle payload designed to execute a system command when the model was loaded. The script automatically created a new MLflow run, uploaded the malicious artifacts, and registered a new model version pointing to the attacker-controlled payload.
The exploit leveraged unsafe deserialization inside MLflow’s model handling mechanism, allowing arbitrary system commands to execute once the malicious model artifact was processed by the backend service.
Exploit Execution
Before triggering the malicious MLflow payload, I started an ICMP listener using tcpdump on my VPN interface to monitor whether the target executed the injected command successfully. This allowed me to verify remote code execution through outbound ping requests from the server.
After setting up the listener, I executed the Python exploit script. The script successfully created a new MLflow run, uploaded the malicious artifacts, and registered a second version of the model containing the weaponized pickle payload.
The successful registration of the malicious model version confirmed that attacker-controlled artifacts could be uploaded directly into MLflow, setting the stage for triggering deserialization-based remote code execution when the backend attempted to load the poisoned model.
Triggering the Malicious Model
To trigger the malicious pickle payload, I first created a small prediction dataset that matched the expected CSV format used by the “Make Predictions” functionality. The goal was to force the backend application to load the poisoned MLflow model during resume analysis.
Next, I navigated to the "Make Predictions" tab and upload the predict.csv file.
After uploading the CSV file through the prediction interface, the application returned an error stating 'int' object has no attribute 'predict'. Although the prediction process failed internally, the important observation was that the backend still deserialized and executed the malicious pickle object before the crash occurred.
The ICMP requests received on my listener confirmed successful remote code execution, proving that the malicious MLflow pickle payload was executed on the target server when the prediction workflow attempted to load the compromised model.
Important Information: When I attempted it the next time (after owning the machine) while writing this walkthrough, the error 'int' object has no attribute 'predict' error didn't show and I got a response "Analysis complete!". There is nothing to be worried about if you got the error. You are still on the right track.
Weaponizing the MLflow Payload
After confirming remote code execution through ICMP callbacks, I modified the original exploit to perform local command execution and exfiltrate the results through the internal MLflow artifact API. Instead of using a simple ping command, the new payload executed system enumeration commands and stored the output in /tmp/pwned.txt.
I then abused the locally accessible MLflow service running on 127.0.0.1:5000 to upload the generated file back into the MLflow artifact store. This technique allowed me to retrieve command output remotely without relying on a traditional reverse shell connection.
This updated payload transformed the vulnerability from simple proof-of-concept RCE into a practical post-exploitation primitive, allowing me to execute commands on the target and retrieve the results directly through MLflow’s artifact management functionality.
Uploading the Enhanced Malicious Model
After modifying the payload to execute system enumeration commands and exfiltrate the results through the internal MLflow API, I executed the updated shell_exploit.py script to register a new malicious model version containing the enhanced pickle payload.
The script successfully created a fresh MLflow run, uploaded the malicious artifacts, and registered version 3 of the target model. This confirmed that I still had full control over the MLflow model registration workflow and could repeatedly weaponize new model versions.
At this stage, the poisoned model was fully prepared for execution, allowing the backend prediction workflow to trigger command execution and automatically upload the captured output back into the MLflow artifact storage.
Command Output Exfiltration
After re-uploading the predict.csv file to trigger the malicious model again, I attempted to retrieve the exfiltrated command output from the MLflow artifact storage. Using the authenticated artifact API endpoint, I downloaded the contents of pwned.txt that had been uploaded internally by the payload.
The returned output confirmed successful command execution on the target host. The payload revealed that the application was running as the svcweb user and also exposed local system information including user accounts and group memberships from /etc/passwd.
This was a major breakthrough because it confirmed arbitrary command execution under the svcweb account and provided valuable insight into the system’s local users and privilege groups, which would later assist in privilege escalation and lateral movement.
Preparing SSH Persistence
After confirming code execution as the svcweb user, I prepared an SSH key pair to establish persistent access to the target system. I generated a new ED25519 key pair locally and saved it inside /tmp/htb_key without a passphrase for easier authentication during exploitation.
Once the key pair was created successfully, I displayed the generated public key so it could later be injected into the target user’s authorized_keys file through the MLflow RCE vulnerability.
The generated public key would later be used to gain stable SSH access as the compromised svcweb user instead of relying solely on command execution through the MLflow application.
Establishing SSH Access
After generating my SSH key pair, I modified the malicious MLflow payload again to establish persistent SSH access on the target system. Instead of exfiltrating command output, the new payload created the .ssh directory for the svcweb user and appended my public key into the authorized_keys file.
I created a new exploit script called modified_shell_exploit.py containing the updated Shell class. The payload also adjusted file permissions to ensure that the SSH configuration would be accepted by the OpenSSH service during authentication.
By weaponizing the MLflow deserialization vulnerability in this way, I was able to transform temporary command execution into stable SSH persistence as the svcweb user, eliminating the need to repeatedly trigger the vulnerable prediction workflow.
Deploying the SSH Persistence Payload
After preparing the modified payload that injected my public SSH key into the target system, I executed the modified_shell_exploit.py script to register another malicious MLflow model version. This version was specifically crafted to establish persistent SSH access for the compromised svcweb user.
The exploit completed successfully by creating a new MLflow run, uploading the malicious artifacts, and registering version 4 of the poisoned model. This confirmed that the payload containing the SSH key injection commands had been accepted by the backend MLflow service.
At this point, the malicious model was ready to be triggered through the prediction workflow, which would execute the payload and silently install my SSH public key into the svcweb account for stable remote access.
Triggering the SSH Key Injection
After registering the malicious model containing the SSH key injection payload, I needed to trigger the vulnerable prediction workflow once again so the backend would deserialize and execute the poisoned pickle object. To do this, I re-uploaded the previously created predict.csv file through the SmartHire interface.
When the prediction process started, the application loaded the latest active model version (v4), which executed the embedded commands responsible for creating the .ssh directory and appending my public key into the svcweb user’s authorized_keys file.
The dashboard confirmed that model version v4 was active during processing, indicating that the malicious payload had been loaded successfully. This step effectively converted the MLflow RCE vulnerability into stable SSH persistence on the target host.
Gaining Initial Access
After triggering the malicious model payload, I attempted to authenticate to the target system over SSH using the private key I had generated earlier. Since the payload successfully injected my public key into the svcweb account’s authorized_keys file, SSH authentication completed successfully.
During the connection, I accepted the host fingerprint and established an interactive shell as the svcweb user on the target machine. This provided stable command execution outside of the web application and marked the transition from web exploitation to direct system access.
The successful SSH login confirmed full compromise of the svcweb account and provided a reliable foothold on the SmartHire server for post-exploitation and privilege escalation activities.
Capturing the User Flag
With stable SSH access established as the svcweb user, I began post-exploitation enumeration inside the user’s home directory. Listing the contents immediately revealed the presence of user.txt, which contained the user flag for the machine.
I successfully read the flag, confirming full compromise of the initial user account on the target system.
Privilege Escalation Enumeration
After obtaining access as the svcweb user, I began enumerating the system for potential privilege escalation vectors. I checked the user’s sudo privileges to identify commands that could be executed as root without requiring a password.
The sudo -l output revealed that the svcweb account was allowed to execute a Python-based MLflow management script as root through sudo without authentication. Since the command accepted arbitrary arguments, this immediately became a promising path for escalating privileges.
The wildcard (*) at the end of the sudo rule was particularly interesting because it suggested that user-controlled arguments could potentially influence how the Python script executed, opening the door for command injection or unsafe module loading as the root user.
Analyzing the Root-Owned MLflow Utility
After discovering the sudo rule, I started inspecting the mlflow_ctl application to understand how it operated internally. I enumerated the directory structure and quickly noticed a plugins directory containing both core and dev plugin folders.
While reviewing the Python source code, I observed that the script dynamically imported Python modules from the plugin directories using site.addsitedir(). More importantly, the dev plugin directory was group-writable by the devs group, and the svcweb user was already a member of that group.
This combination of a root-executed Python script, dynamic module imports, and a writable plugin directory strongly suggested a Python module hijacking opportunity, making the dev directory the ideal target for privilege escalation to root.
Python Module Hijacking
After confirming that the dev plugin directory was writable by the devs group, I abused Python’s module loading behavior to hijack the imports performed by the root-owned mlflowctl.py script. I first created a malicious .pth file to force Python to prioritize the writable dev directory during module resolution.
Next, I created a fake mlflow_actions.py module containing a malicious check_status() function that executed chmod +s /bin/bash. Since the legitimate script imported mlflow_actions as root, my malicious module would be executed automatically when the sudo command was triggered.
This effectively weaponized the writable plugin directory into a privilege escalation vector, allowing arbitrary code execution as root through Python module hijacking when the privileged MLflow utility was executed with sudo.
Root Privilege Escalation
After planting the malicious Python module, I executed the privileged mlflowctl.py script with the status action to trigger the import hijacking vulnerability. Since the script was executed through sudo as root, my malicious mlflow_actions.py module ran with elevated privileges and successfully modified /bin/bash to become SUID-enabled.
I verified that the SUID bit had been applied to /bin/bash, then launched a privileged shell using bash -p. From there, I confirmed root access and captured the final root flag from /root/root.txt.
This completed the full attack chain on the SmartHire machine, starting from exposed MLflow infrastructure and ending with root compromise through Python module hijacking in a misconfigured sudo-enabled management utility.
If you enjoy reading my write-ups, please consider subscribing to my YouTube channel and following me on LinkedIn | Medium | Twitter | Boltech Twitter | Buy Me a Coffee. Found this walkthrough helpful? Buying me a coffee helps power the late nights spent writing technical walkthroughs and keeping them free for everyone ☕
Keywords:
SmartHire HTB Write Up Premium
SmartHire HackTheBox
SmartHire Hack the Box Walkthrough
SmartHire Hack the Box Writeup
smarthire.htb Season 10 Machine Solution
models.smarthire.htb
SmartHire HTB Walkthrough
Owned SmartHire on Hack the Box
Helix HTB Machine Complete Walkthrough
Helix HTB Write Up
helix.htb machine
Rooted SmartHire from Hack the Box
Pwned SmartHire from Hack the Box Season 10 Machine
Helix HTB Walkthrough
Owned Helix from Hack the Box
Rooted Helix from Hack the Box
Helix HackTheBox
Pwned Helix from Hack the Box
logging.htb machine season 10 hack the box
logging htb writeup
logging htb walkthrough
pingpong.htb machine
I just solved Logging from Hack the Box
Rooted Logging from Hack the Box
Pwned Logging from Hack the Box
Logging Hack the Box Walkthrough
Logging Hack the Box Writeup
Logging HackTheBox
pingpong hack the box write up
pingpong hack the box walkthrough
pingpong htb walkthrough
pingpong htb writeup
PingPong HackTheBox
Rooted PingPong from Hack the Box
Owned PingPong from Hack the Box
Pwned PingPong from Hack the Box
I just solved PingPong on Hack the Box






































1 Comments
To current members, the password to access this encrypted page and other pages has been sent to your email address. If you haven't received it yet, reach out to me at isiaqibrahim.tr@gmail.com
ReplyDeleteNote: This write up includes the complete code blocks and commands. The password for each write up is different. I have sent the password to your inbox on Buy Me A Coffee.
Happy Hacking!!!😈😈