SmartHire
Status: COMPLETE. Raw flags and reusable secrets are stored only in <local workspace><TARGET>-SmartHire/loot/. 1. Recon found only SSH and HTTP. HTTP redirected to smarthire.htb; vhost fuzzing discovered models.smarthire.htb. 2. models.smarthire.htb exposed...
Scenario
SmartHire attack path
Status: COMPLETE. Raw flags and reusable secrets are stored only in -SmartHire/loot/. 1. Recon found only SSH and HTTP. HTTP redirected to smarthire.htb; vhost fuzzing discovered models.smarthire.htb. 2. models.smarthire.htb exposed...
Objective
Machine walkthrough focused on Machines evidence, validation, and reusable operator lessons.
Walkthrough flow
Verify target IP and /etc/hosts if machine respawned.
Recreate a normal app account and MLflow default...
Use notes/walkthrough evidence to reproduce; do not...
Source coverage
High source coverage
Status: complete. This article is generated from 7 sanitized Markdown sources and keeps raw flags, credentials, keys, cookies, and reusable secrets out of the rendered blog.
High confidence: the page is reconstructed from a primary walkthrough plus multiple supporting notes or evidence sources. Treat the chain as source-backed, while still checking the listed source files for sensitive values.
- <TARGET>-SmartHire/walkthrough.md
- HTB/<TARGET>-SmartHire/notes.md
- HTB/<TARGET>-SmartHire/memory-summary.md
- HTB/<TARGET>-SmartHire/session-resume.md
- HTB/_knowledge/exports/ctf-lightrag-latest-203412/documents/machine__<TARGET>-SmartHire__notes.md.c4c01223f4.md
- HTB/_knowledge/exports/ctf-lightrag-latest-203412/documents/machine__<TARGET>-SmartHire__session-resume.md.14811b658d.md
- HTB/_knowledge/exports/ctf-lightrag-latest-203412/documents/machine__<TARGET>-SmartHire__notes.md.14aab64937.md
Technical Walkthrough
SmartHire — Walkthrough
Status: COMPLETE. Raw flags and reusable secrets are stored only in <local workspace><TARGET>-SmartHire/loot/.
Chain
- Recon found only SSH and HTTP. HTTP redirected to
smarthire.htb; vhost fuzzing discoveredmodels.smarthire.htb. models.smarthire.htbexposed MLflow 2.14.1 behind Basic Auth. A default credential succeeded; value stored inloot/mlflow-basic-auth.json.- The SmartHire app allowed normal registration/login and exposed
/dashboard,/model_info,/upload_hiring_data, and/predict. /model_inforevealed an app-derived MLflow registered model name. Authenticated MLflow API access allowed registering model versions under that name.- A benign model registration proved SmartHire consumed the MLflow registry. A callback model proved command execution as
svcwebwhen/predictloaded the registered model. - A reverse-shell model gave a shell as
svcweb;user.txtwas captured toloot/user.txt. sudo -lshowedsvcwebcould run/usr/bin/python3.10 /opt/tools/mlflow_ctl/mlflowctl.py *as root.- The script imports plugin directories with
site.addsitedir()./opt/tools/mlflow_ctl/plugins/devwas group-writable bydevs, andsvcwebbelonged todevs. - A temporary
.pthhook in the dev plugin directory executed as root via the allowed sudo command, copied root proof/flag to/tmp, and was removed immediately. root.txtwas captured toloot/root.txt; MLflow model versions and the temporary.pthhook were cleaned up.
Key Evidence
- Nmap:
<local workspace><TARGET>-SmartHire/nmap/initial,<local workspace><TARGET>-SmartHire/nmap/allports - Vhost discovery:
<local workspace><TARGET>-SmartHire/enum/ffuf-vhosts-filtered.json - MLflow version/API:
<local workspace><TARGET>-SmartHire/enum/mlflow-api-summary.json - App endpoints:
<local workspace><TARGET>-SmartHire/enum/auth-js-endpoints.json - Callback proof:
<local workspace><TARGET>-SmartHire/enum/proof-callback-review.txt - Foothold shell log:
<local workspace><TARGET>-SmartHire/enum/tmux-shell.log - Sudo/privesc script inspection:
<local workspace><TARGET>-SmartHire/enum/mlflowctl-inspect.txt - Root proof and cleanup evidence:
<local workspace><TARGET>-SmartHire/enum/root-privesc-exec.txt,<local workspace><TARGET>-SmartHire/enum/mlflow-cleanup-summary.json
Source-Backed Dossier
The sections below are merged from companion Markdown notes for the same case. They are rendered after sanitization so the article stays precise without publishing raw flags, credentials, or target-specific secrets.
Notes
Target: SmartHire
Active IP: <TARGET>
Prior IP(s): <TARGET> (stale; prior target state showed filtered ports only)
Difficulty: Medium
OS: Linux
Created by: retrib3
Pwnbox: <TARGET> (profex0r), hostname htb-pbtmg2qrop
Attacker/VPN IP: <TARGET>
Workspace Local: <local workspace><TARGET>-SmartHire
Workspace Pwnbox: /home/profex0r/<TARGET>-SmartHire
Started: 2026-05-27
Completion State: COMPLETE
Evidence Ledger
| Timestamp (UTC) | Command / Action | Output File | Finding | Confidence | Next Action |
|---|---|---|---|---|---|
| 2026-05-27T00:00:00Z | Workspace initialization | target-state.json | New active workspace created for <TARGET>; old prior IP treated as stale | HIGH | Verify Pwnbox/VPN/target reachability |
| 2026-05-27T22:43:36Z | Pwnbox connectivity and target route/ping | enum/connectivity-baseline.txt | Pwnbox SSH works; attacker/VPN IP <TARGET>; route via tun0; target ping responsive | HIGH | Baseline recon |
| 2026-05-27T22:44:30Z | nmap -sC -sV | nmap/initial | 22/tcp OpenSSH 8.9p1 Ubuntu; 80/tcp nginx 1.18.0 redirects to smarthire.htb | HIGH | Add hostname and enumerate web |
| 2026-05-27T22:44:35Z | Full TCP scan | nmap/allports | Only 22 and 80 open | HIGH | Target HTTP thoroughly |
| 2026-05-27T22:47:00Z | Directory fuzz | enum/ffuf-summary.txt | Main routes: /register, /login, /logout, /dashboard, /predict | HIGH | Authenticated app enumeration |
| 2026-05-27T22:48:20Z | Vhost fuzz | enum/ffuf-vhosts-filtered.json | Discovered models.smarthire.htb, MLflow Basic Auth realm; /health public | HIGH | Research/test MLflow |
| 2026-05-27T22:47:30Z | Targeted UDP scan | nmap/udp-targeted | No actionable UDP service | MEDIUM | Continue TCP/web path |
| 2026-05-27T22:55:00Z | Register/login using live form fields | enum/web-auth-register-login.txt; secret ref loot/web-account.json | Normal app account works; /dashboard and /predict authenticated | HIGH | Inspect endpoints |
| 2026-05-27T22:57:28Z | MLflow Basic Auth checks | enum/mlflow-default-cred-check.txt; secret ref loot/mlflow-basic-auth.json | A default MLflow admin credential succeeded; values stored only in loot | HIGH | Enumerate MLflow API |
| 2026-05-27T22:59:00Z | Authenticated MLflow API enumeration | enum/mlflow-api-summary.json, enum/mlflow-api-_version.txt | MLflow 2.14.1; default experiment exists; registry write reachable | HIGH | Validate app MLflow consumption |
| 2026-05-27T23:00:00Z | Benign registered model | enum/benign-mlflow-register-summary.json, enum/post-benign-model_info.txt | App-derived registered model name consumed by /model_info; /predict reaches model load/prediction path | HIGH | Controlled callback proof |
| 2026-05-27T23:04:35Z | MLflow model-load callback proof | enum/proof-callback-review.txt; token ref loot/model-load-proof-token.txt | Target callback received; identity was svcweb with mlflowweb and devs groups | HIGH | Reverse shell model payload |
| 2026-05-27T23:07:30Z | Reverse-shell MLflow model → /predict | enum/mlflow-revshell-register-summary.json, enum/tmux-shell.log | Reverse shell connected as svcweb | HIGH | Foothold baseline and user flag |
| 2026-05-27T23:08:30Z | find /home -name user.txt; nc transfer | loot/user.txt, enum/user-transfer.log | Live user flag captured and verified 32-hex format; raw flag kept only in loot | HIGH | Privilege escalation |
| 2026-05-27T23:10:00Z | Linux baseline and sudo check | enum/tmux-shell.log, enum/mlflowctl-inspect.txt | svcweb may run /usr/bin/python3.10 /opt/tools/mlflow_ctl/mlflowctl.py * as root without password | HIGH | Inspect sudo script/import path |
| 2026-05-27T23:12:00Z | Sudo script inspection | enum/mlflowctl-inspect.txt | mlflowctl.py uses site.addsitedir() for plugin dirs; /opt/tools/mlflow_ctl/plugins/dev is group-writable by devs; svcweb is in devs | HIGH | Use temporary .pth hook with cleanup |
| 2026-05-27T23:16:43Z | Temporary .pth hook + allowed sudo command | enum/root-privesc-exec.txt, loot/root.txt | Root execution proven; live root flag captured and verified 32-hex format; temporary hook removed | HIGH | Cleanup MLflow artifacts |
| 2026-05-27T23:18:00Z | MLflow cleanup | enum/mlflow-cleanup-summary.json | Created model versions and registered models removed | HIGH | Finalize docs |
LightRAG Checkpoints
| Timestamp (UTC) | Query | Retrieved Refs | Tag | Live Validation | Accepted/Rejected |
|---|---|---|---|---|---|
| 2026-05-27T22:44:00Z | HTB Linux Medium SmartHire retrib3 prior sanitized patterns startup advisory only | enum/lightrag-startup.txt | MISSING | No live claims accepted | Rejected for execution guidance; continued live recon |
| 2026-05-27T22:56:00Z | HTB Linux Medium nginx Flask app authenticated predict route MLflow Basic auth models vhost health public SmartHire attack path | enum/lightrag-after-baseline.txt | MISSING | No live claims accepted | Rejected for execution guidance; used live evidence and external docs |
Findings
- TCP: only 22/tcp OpenSSH and 80/tcp nginx exposed.
- HTTP redirects bare IP to
smarthire.htb. - Vhost fuzzing found
models.smarthire.htb, an MLflow service behind Basic Auth. - SmartHire app supports normal user registration and uses app-derived MLflow registered model names.
- MLflow default credential worked and allowed model registry writes.
- SmartHire
/predictloads the latest MLflow model for the account/company model name, enabling model-load code execution. - Foothold user:
svcweb; groups includemlflowwebanddevs. - Privilege escalation: sudo-allowed Python script adds group-writable plugin directory to
site, allowing temporary.pthexecution as root.
Phase 2 Research
- Research file:
enum/research-mlflow-websearch.txt(advisory; deleted/recreated only if needed, not required for proof). - MLflow Basic Auth/default credential risk was validated live by successful authenticated access.
- MLflow model artifact/deserialization and model registry abuse classes were treated as advisory until live validation proved the app consumed a registered model and loaded it during prediction.
- The
.pthprivesc vector was verified directly from the sudo script and filesystem permissions, not external research.
Phase 3 Synthesis / Ranked Hypotheses
| Rank | Path | Evidence | Missing Proof | Cheapest Validation | Confidence | Status |
|---|---|---|---|---|---|---|
| 1 | MLflow default credential → register model under app-derived name → SmartHire /predict loads model → command execution as app user | MLflow auth succeeded; registry write worked; /model_info and callback proof confirmed app load path | None after callback/reverse shell | Completed | High | Confirmed |
| 2 | SmartHire /upload_hiring_data CSV parser/training abuse | Authenticated upload endpoint exists | Server-side parser weakness not needed | Not pursued after H1 success | Low | Closed |
| 3 | MLflow path traversal / artifact read | MLflow reachable; research showed path traversal class | Exact vulnerable endpoint/file proof not needed | Not pursued after H1 success | Low | Closed |
Completion State
COMPLETE
Memory Summary
Platform: HackTheBox machine
Type: Linux Medium
Status: solved from live target
Reusable Lessons
- Vhost fuzzing on a simple nginx/Flask app found an MLflow service behind Basic Auth on a subdomain.
- MLflow default/auth weakness plus SmartHire's app-specific registered model lookup formed the foothold chain.
- Before exploit, validate model-registry consumption with
/model_infoand a benign model/callback; do not trust public MLflow CVE claims until the app actually loads the model. - MLflow pyfunc/model-load deserialization can execute before prediction fully succeeds, so an application error after model load can still mean command execution occurred.
- For privesc, a sudo-allowed Python script that uses
site.addsitedir()on group-writable plugin directories is exploitable via temporary.pthcode execution.
Evidence Workspace
<local workspace><TARGET>-SmartHire
No raw flags or reusable credentials are included here. Ingest only after user approval.
Session Resume
Completion state: COMPLETE
Access
- Foothold obtained as
svcwebthrough SmartHire MLflow model-load execution. - Root execution proven through temporary
.pthplugin hook via allowed sudo command. - User flag:
loot/user.txt - Root flag:
loot/root.txt
Cleanup
- Temporary MLflow registered model versions removed. Evidence:
enum/mlflow-cleanup-summary.json. - Temporary sudo
.pthhook removed. Evidence:enum/root-privesc-exec.txt. - Active tmux/listeners stopped at end of session.
Active Sessions/Tunnels/Tickets
None expected after cleanup.
If resuming
- Verify target IP and
/etc/hostsif machine respawned. - Recreate a normal app account and MLflow default credential proof if needed.
- Use notes/walkthrough evidence to reproduce; do not rely on stale model registry objects because cleanup removed them.
Notes
Target: SmartHire
Active IP: <TARGET>
Prior IP(s): <TARGET> (stale; prior target state showed filtered ports only)
Difficulty: Medium
OS: Linux
Created by: retrib3
Pwnbox: <TARGET> (<<secret redacted>>), hostname htb-pbtmg2qrop
Attacker/VPN IP: <TARGET>
Workspace Local: <local workspace><TARGET>-SmartHire
Workspace Pwnbox: /home/<<secret redacted>>/<TARGET>-SmartHire
Started: 2026-05-27
Completion State: COMPLETE
Operating Notes
- Source of truth is live target <TARGET>.
- Older workspace <local workspace><TARGET>-SmartHire is advisory stale state only.
- Raw flags, credentials, tokens, and reusable secrets stay in loot/ only and are referenced by path.
- Medium flow completed: baseline recon, mandatory research, synthesis, foothold, privesc, cleanup.
Evidence Ledger
| Timestamp (UTC) | Command / Action | Output File | Finding | Confidence | Next Action |
|---|---|---|---|---|---|
| 2026-05-27T00:00:00Z | Workspace initialization | target-state.json | New active workspace created for <TARGET>; old prior IP treated as stale | HIGH | Verify Pwnbox/VPN/target reachability |
| 2026-05-27T22:43:36Z | Pwnbox connectivity and target route/ping | enum/connectivity-baseline.txt | Pwnbox SSH works; attacker/VPN IP <TARGET>; route via tun0; target ping responsive | HIGH | Baseline recon |
| 2026-05-27T22:44:30Z | nmap -sC -sV | nmap/initial | 22/tcp OpenSSH 8.9p1 Ubuntu; 80/tcp nginx 1.18.0 redirects to smarthire.htb | HIGH | Add hostname and enumerate web |
| 2026-05-27T22:44:35Z | Full TCP scan | nmap/allports | Only 22 and 80 open | HIGH | Target HTTP thoroughly |
| 2026-05-27T22:47:00Z | Directory fuzz | enum/ffuf-summary.txt | Main routes: /register, /login, /logout, /dashboard, /predict | HIGH | Authenticated app enumeration |
| 2026-05-27T22:48:20Z | Vhost fuzz | enum/ffuf-vhosts-filtered.json | Discovered models.smarthire.htb, MLflow Basic Auth realm; /health public | HIGH | Research/test MLflow |
| 2026-05-27T22:47:30Z | Targeted UDP scan | nmap/udp-targeted | No actionable UDP service | MEDIUM | Continue TCP/web path |
| 2026-05-27T22: <REDACTED> | |||||
| 2026-05-27T22: <REDACTED> | |||||
| 2026-05-27T22:59:00Z | Authenticated MLflow API enumeration | enum/mlflow-api-summary.json, enum/mlflow-api-_version.txt | MLflow 2.14.1; default experiment exists; registry write reachable | HIGH | Validate app MLflow consumption |
| 2026-05-27T23:00:00Z | Benign registered model | enum/benign-mlflow-register-summary.json, enum/post-benign-model_info.txt | App-derived registered model name consumed by /model_info; /predict reaches model load/prediction path | HIGH | Controlled callback proof |
| 2026-05-27T23: <REDACTED> | |||||
2026-05-27T23: <REDACTED>, enum/tmux-shell.log | Reverse shell connected as svcweb | HIGH | Foothold baseline and user flag | ||
2026-05-27T23: <REDACTED>, enum/user-transfer.log | Live user flag captured and verified 32-hex format; raw flag kept only in loot | HIGH | Privilege escalation | ||
2026-05-27T23: <REDACTED>, enum/mlflowctl-inspect.txt | svcweb may run /usr/bin/python3.10 /opt/tools/mlflow_ctl/mlflowctl.py * as root without password | HIGH | Inspect sudo script/import path | ||
| 2026-05-27T23:12:00Z | Sudo script inspection | enum/mlflowctl-inspect.txt | mlflowctl.py uses site.addsitedir() for plugin dirs; /opt/tools/mlflow_ctl/plugins/dev is group-writable by devs; svcweb is in devs | HIGH | Use temporary .pth hook with cleanup |
2026-05-27T23: <REDACTED>, loot/root.txt | Root execution proven; live root flag captured and verified 32-hex format; temporary hook removed | HIGH | Cleanup MLflow artifacts | ||
| 2026-05-27T23:18:00Z | MLflow cleanup | enum/mlflow-cleanup-summary.json | Created model versions and registered models removed | HIGH | Finalize docs |
LightRAG Checkpoints
| Timestamp (UTC) | Query | Retrieved Refs | Tag | Live Validation | Accepted/Rejected |
|---|---|---|---|---|---|
| 2026-05-27T22:44:00Z | HTB Linux Medium SmartHire retrib3 prior sanitized patterns startup advisory only | enum/lightrag-startup.txt | MISSING | No live claims accepted | Rejected for execution guidance; continued live recon |
| 2026-05-27T22:56:00Z | HTB Linux Medium nginx Flask app authenticated predict route MLflow Basic auth models vhost health public SmartHire attack path | enum/lightrag-after-baseline.txt | MISSING | No live claims accepted | Rejected for execution guidance; used live evidence and external docs |
Findings
- TCP: only 22/tcp OpenSSH and 80/tcp nginx exposed.
- HTTP redirects bare IP to
smarthire.htb. - Vhost fuzzing found
models.smarthire.htb, an MLflow service behind Basic Auth. - SmartHire app supports normal user registration and uses app-derived MLflow registered model names.
- MLflow default credential worked and allowed model registry writes.
- SmartHire
/predictloads the latest MLflow model for the account/company model name, enabling model-load code execution. - Foothold user:
svcweb; groups includemlflowwebanddevs. - Privilege escalation: sudo-allowed Python script adds group-writable plugin directory to
site, allowing temporary.pthexecution as root.
Phase 2 Research
- Research file:
enum/research-mlflow-websearch.txt(advisory; deleted/recreated only if needed, not required for proof). - MLflow Basic Auth/default credential risk was validated live by successful authenticated access.
- MLflow model artifact/deserialization and model registry abuse classes were treated as advisory until live validation proved the app consumed a registered model and loaded it during prediction.
- The
.pthprivesc vector was verified directly from the sudo script and filesystem permissions, not external research.
Phase 3 Synthesis / Ranked Hypotheses
| Rank | Path | Evidence | Missing Proof | Cheapest Validation | Confidence | Status |
|---|---|---|---|---|---|---|
| 1 | MLflow default credential → register model under app-derived name → SmartHire /predict loads model → command execution as app user | MLflow auth succeeded; registry write worked; /model_info and callback proof confirmed app load path | None after callback/reverse shell | Completed | High | Confirmed |
| 2 | SmartHire /upload_hiring_data CSV parser/training abuse | Authenticated upload endpoint exists | Server-side parser weakness not needed | Not pursued after H1 success | Low | Closed |
| 3 | MLflow path traversal / artifact read | MLflow reachable; research showed path traversal class | Exact vulnerable endpoint/file proof not needed | Not pursued after H1 success | Low | Closed |
Completion State
COMPLETE
Session Resume
Completion state: COMPLETE
Access
- Foothold obtained as
svcwebthrough SmartHire MLflow model-load execution. - Root execution proven through temporary
.pthplugin hook via allowed sudo command. - User flag: <REDACTED>
- Root flag: <REDACTED>
Cleanup
- Temporary MLflow registered model versions removed. Evidence:
enum/mlflow-cleanup-summary.json. - Temporary sudo
.pthhook removed. Evidence:enum/root-privesc-exec.txt. - Active tmux/listeners stopped at end of session.
Active Sessions/Tunnels/Tickets
None expected after cleanup.
If resuming
- Verify target IP and
/etc/hostsif machine respawned. - Recreate a normal app account and MLflow default credential proof if needed.
- Use notes/walkthrough evidence to reproduce; do not rely on stale model registry objects because cleanup removed them.
Notes
Target: SmartHire
IP: <TARGET>
Difficulty: Medium
OS: Linux
Pwnbox: <TARGET> (<<secret redacted>>)
VPN/Attacker IP: <TARGET>
Started: 2026-05-25
Completion State: <secret redacted>
Evidence Ledger
| Timestamp | Command | Output File | Finding | Confidence | Next Action |
|---|---|---|---|---|---|
| 08:33 | nmap -Pn -p 22,80,443 | — | All filtered, host not live | HIGH | Wait for spawn |
| 08:35 | nmap -Pn -sC -sV -p 22,80,443,8080,8443,3000,5000,9090 | — | All 8 ports filtered | HIGH | Confirm machine spawned on HTB |
Findings
(Pending target availability)