CVE-2026-6615: Path Traversal to RCE in SuperAGI Multipart Upload
SuperAGI's multipart upload handler fails to sanitize the filename argument, enabling path traversal that allows remote attackers to write arbitrary files and achieve code execution.
Imagine you're uploading a document to a secure filing system, but a flaw lets you sneak into the back room and plant files wherever you want. That's what's happening with TransformerOptimus SuperAGI, a tool used by companies to automate work tasks.
The problem is in how the software handles file uploads. When someone uploads a file, they're supposed to drop it in a designated folder. But attackers have discovered they can manipulate the file path — adding commands like "go up two directories" — to stash files in secret parts of the computer system where they shouldn't be.
Why should you care? An attacker could use this to plant malicious code that takes over the entire server. This is especially dangerous for companies using SuperAGI to handle sensitive work like customer data, financial records, or confidential projects.
Who's at risk? Any organization running SuperAGI version 0.0.14 or earlier. If your company uses this software for automation, you need to act fast. The good news is there's no confirmed active attacks yet — this is a heads-up, not an active emergency.
What to do right now:
First, check if you're using SuperAGI and what version. If you find it's 0.0.14 or earlier, update immediately to the latest version.
Second, if updating isn't possible immediately, limit who can upload files to your SuperAGI system. Treat it like you would a sensitive server.
Third, monitor your system logs for unusual file creation, especially in unexpected places. Think of it like checking for muddy footprints in parts of your house that should be clean.
Want the full technical analysis? Click "Technical" above.
CVE-2026-6615 is a path traversal vulnerability in TransformerOptimus SuperAGI versions up to and including 0.0.14. The flaw lives in the Upload function inside superagi/controllers/resources.py, which handles multipart file uploads. Because the server never sanitizes or validates the name field extracted from the multipart form data, an attacker can supply a filename like ../../app/superagi/models/agent.py and overwrite arbitrary files accessible to the process user. Given that SuperAGI is a Python-based autonomous agent framework that executes code on behalf of AI tasks, overwriting a loaded module or a startup script trivially escalates path traversal into full remote code execution. The exploit is publicly available and requires no authentication in default deployments.
Root cause: The Upload handler in resources.py passes the attacker-controlled name parameter from the multipart form directly into an os.path.join call without stripping leading slashes, .. sequences, or validating that the resolved path falls within the intended upload directory.
Affected Component
File: superagi/controllers/resources.py
Function: Upload (registered as a FastAPI/Flask route handler)
Transport: HTTP multipart/form-data, reachable over the network on the default port (8001)
Authentication: None enforced on the upload endpoint in versions ≤ 0.0.14
Dependency chain: Upload → os.path.join → open(..., 'wb') → disk write
Root Cause Analysis
The following pseudocode reconstructs the vulnerable handler from the described behavior and the SuperAGI 0.0.14 codebase structure. The bug is a single missing validation call before the path is constructed.
// Pseudocode: reconstructed from superagi/controllers/resources.py
// Original is Python; represented here for structural clarity.
// Multipart form fields:
// "file" -> binary content
// "name" -> attacker-supplied filename string
int Upload(multipart_form_t *form, config_t *cfg) {
char *name = form_get_field(form, "name"); // attacker-controlled
char *content = form_get_field(form, "file");
size_t content_len = form_get_field_len(form, "file");
// BUG: no sanitization of 'name' before path construction.
// os.path.join(RESOURCES_DIR, name) in Python will discard the base
// directory entirely if 'name' is an absolute path, or will traverse
// upward if 'name' contains '../' sequences.
char dest_path[PATH_MAX];
path_join(dest_path, cfg->resources_dir, name); // BUG: traversal not prevented
// BUG: resolved path never checked to confirm it is a child of resources_dir
FILE *fp = fopen(dest_path, "wb");
if (!fp) return HTTP_500;
fwrite(content, 1, content_len, fp); // arbitrary write to traversed location
fclose(fp);
return HTTP_200;
}
In Python, os.path.join("/app/resources", "../../etc/cron.d/evil") evaluates to /app/etc/cron.d/evil — still traversing two directories up from resources. An absolute path like /app/superagi/models/agent.py completely overrides the base. Neither case is rejected.
# Actual Python equivalent of the vulnerable logic (reconstructed, ~0.0.14):
@router.post("/resources/upload")
async def upload_file(name: str = Form(...), file: UploadFile = File(...)):
resource_dir = get_config("RESOURCES_INPUT_ROOT_DIR") # e.g. /app/resources/input
# BUG: os.path.join does not confine path to resource_dir
file_path = os.path.join(resource_dir, name) # traversal here
# BUG: no call to os.path.realpath() + prefix check before open
with open(file_path, "wb") as f: # arbitrary write
contents = await file.read()
f.write(contents)
# file metadata stored in DB referencing the attacker-controlled path
Resource.add_resource(name=name, path=file_path, ...)
return {"file": file_path}
Exploitation Mechanics
EXPLOIT CHAIN:
1. Identify a running SuperAGI instance (default port 8001, no auth on /resources/upload).
2. Enumerate a writable Python module that is imported at request time or on agent
startup. Good candidates:
- /app/superagi/helper/resource_helper.py (imported by every agent run)
- /app/superagi/models/agent.py (imported on task dispatch)
3. Craft a multipart POST where:
name = "../../superagi/helper/resource_helper.py"
file =
4. Server calls:
os.path.join("/app/resources/input",
"../../superagi/helper/resource_helper.py")
resolving to:
/app/superagi/helper/resource_helper.py
— the module file is overwritten in place.
5. Trigger module reload:
a. (Passive) Wait for any agent task to be dispatched; Python's import
caching is bypassed because gunicorn/uvicorn workers restart on SIGHUP,
OR the module is re-imported dynamically via importlib.
b. (Active) POST /agents/run with a new agent task, which imports
resource_helper fresh in a subprocess worker — payload executes.
6. Payload achieves OS command execution under the superagi process user
(typically root in default Docker deployments).
This is a logic/file-write vulnerability rather than a heap corruption bug, so the relevant "layout" is the on-disk and in-process module state:
FILESYSTEM STATE — BEFORE UPLOAD:
/app/resources/input/ <- intended write boundary
/app/superagi/helper/
resource_helper.py <- legitimate module, imported by workers
[sha256: 3a9f...e21c]
FILESYSTEM STATE — AFTER TRAVERSAL WRITE:
/app/resources/input/ <- boundary completely bypassed
/app/superagi/helper/
resource_helper.py <- OVERWRITTEN with attacker payload
[sha256: deadbeef...0000] <- mtime updated, size differs
PROCESS IMPORT STATE (uvicorn worker):
sys.modules["superagi.helper.resource_helper"]
-> module object @ <- stale cached copy in long-lived worker
__file__ = "/app/superagi/helper/resource_helper.py"
On next agent subprocess spawn (fresh Python interpreter):
import superagi.helper.resource_helper
-> reads overwritten file from disk
-> executes attacker shellcode at module import time
Patch Analysis
The correct fix requires two independent controls: normalize the path with os.path.realpath and assert the result is prefixed by the intended base directory. Either check alone is insufficient.
# BEFORE (vulnerable, ~0.0.14):
@router.post("/resources/upload")
async def upload_file(name: str = Form(...), file: UploadFile = File(...)):
resource_dir = get_config("RESOURCES_INPUT_ROOT_DIR")
file_path = os.path.join(resource_dir, name) # no validation
with open(file_path, "wb") as f:
f.write(await file.read())
return {"file": file_path}
# AFTER (patched):
@router.post("/resources/upload")
async def upload_file(name: str = Form(...), file: UploadFile = File(...)):
resource_dir = os.path.realpath(get_config("RESOURCES_INPUT_ROOT_DIR"))
# Strip any leading slashes/dots to prevent os.path.join override
safe_name = os.path.basename(name) # drop all directory components
if not safe_name or safe_name != name: # reject if name had path separators
raise HTTPException(status_code=400, detail="Invalid filename")
candidate = os.path.realpath(os.path.join(resource_dir, safe_name))
# PATCH: verify resolved path is strictly under resource_dir
if not candidate.startswith(resource_dir + os.sep):
raise HTTPException(status_code=400, detail="Path traversal detected")
with open(candidate, "wb") as f:
f.write(await file.read())
return {"file": candidate}
Note: using os.path.basename alone is the minimal safe fix for flat-file uploads; if subdirectory organization is needed, each component must be validated individually against a whitelist of allowed characters before rejoining.
Detection and Indicators
HTTP access logs — look for POST requests to /resources/upload where the name multipart field contains ../, URL-encoded variants (%2e%2e%2f, %2e%2e/, ..%2f), or an absolute path beginning with /:
# Nginx/uvicorn log pattern (traversal attempt):
POST /resources/upload HTTP/1.1 <- multipart body contains name=../../...
# Grep for encoded variants in raw request capture:
name=%2e%2e%2fsuperagi%2fhelper%2fresource_helper.py
name=..%2fsuperagi%2fhelper%2fresource_helper.py
name=/app/superagi/models/agent.py
Filesystem indicators — unexpected mtime changes on .py files outside /app/resources/; inotify alerts on /app/superagi/ subtree triggered by the web worker UID.
Runtime indicators — outbound connections from the superagi process immediately following an agent task dispatch; /proc/<pid>/fd showing open sockets to non-configured endpoints.
Remediation
Immediate: Update to a version of SuperAGI that includes the path validation fix. If no patched release is available, apply the diff above as a local patch.
Defense-in-depth: Run SuperAGI workers with a read-only filesystem mount for /app/superagi/; only /app/resources/ should be writable by the web process.
Network controls: Place the SuperAGI API behind an authenticated reverse proxy. The upload endpoint should never be directly Internet-accessible.
Integrity monitoring: Deploy a file integrity monitor (e.g., auditd, Falco rule) on /app/superagi/**/*.py to alert on writes from the web server process UID.
Container hardening: Drop DAC_OVERRIDE and run the container as a non-root UID; this limits the blast radius of arbitrary file writes to files owned by that UID.