Skip to content

fix(storage/artifacts): block path traversal via artifact_path#3407

Open
Sp1kyss wants to merge 1 commit into
aimhubio:mainfrom
Sp1kyss:fix/artifact-storage-path-traversal
Open

fix(storage/artifacts): block path traversal via artifact_path#3407
Sp1kyss wants to merge 1 commit into
aimhubio:mainfrom
Sp1kyss:fix/artifact-storage-path-traversal

Conversation

@Sp1kyss
Copy link
Copy Markdown

@Sp1kyss Sp1kyss commented May 8, 2026

Summary

Both FilesystemArtifactStorage and S3ArtifactStorage joined attacker-controlled artifact_path values to a fixed prefix using pathlib.Path / artifact_path. Two pathlib quirks made that join unsafe:

  • pathlib.Path('/a') / '/etc/passwd' returns /etc/passwd (the / operator silently discards the prefix when the right operand is absolute).
  • pathlib.Path('/a') / '../etc/passwd' returns /a/../etc/passwd, which the OS resolves to /etc/passwd once the value is passed to shutil.copy / shutil.rmtree / boto3.

The SDK exposes Run.log_artifact(path, name=...), where name is propagated unchanged to upload_artifact(..., artifact_path=name), so a malicious caller can pick the destination filename and escape the configured artifact root.

Impact

  • Arbitrary file overwrite via upload_artifact -> shutil.copy (e.g. dropping a file into /etc/cron.d/, /var/www/html/, or any path the Aim process can write to).
  • Arbitrary directory deletion via delete_artifact -> shutil.rmtree.
  • Arbitrary S3 key write / delete outside the configured prefix.

This is a distinct code path from previously-reported issues:

  • CVE-2024-6396 -- _backup_run path traversal.
  • CVE-2024-8769 -- LockManager.release_locks traversal.
  • The previously-reported restore_run_backup tar extraction issue.

Fix

  • Add safe_join (filesystem) and safe_join_posix (S3 keys) helpers in aim/storage/artifacts/artifact_storage.py. Both reject absolute paths, drive letters, and any .. segments. safe_join additionally re-validates that the resolved filesystem path stays inside the prefix.
  • Switch FilesystemArtifactStorage and S3ArtifactStorage to use the helpers for upload, download, and delete.
  • Add regression tests covering absolute paths, embedded .., delete safety, and a legitimate relative-path case.

Reproducer (before this PR)

`python
from aim.storage.artifacts.filesystem_storage import FilesystemArtifactStorage

storage = FilesystemArtifactStorage('file:///tmp/artifacts')

Writes attacker-controlled content to /etc/cron.d/aim_pwn:

storage.upload_artifact('/path/to/payload', '/etc/cron.d/aim_pwn')

Or via the SDK:

run.log_artifact('/path/to/payload', name='/etc/cron.d/aim_pwn')

`

After this PR the same call raises ValueError before any I/O happens.

FilesystemArtifactStorage and S3ArtifactStorage joined attacker-
controlled artifact_path values to a fixed prefix using `pathlib.Path /
artifact_path`. Two pathlib quirks made that join unsafe:

  * `pathlib.Path('/a') / '/etc/passwd'` returns `/etc/passwd` because
    the `/` operator silently discards the prefix when the right operand
    is absolute.
  * `pathlib.Path('/a') / '../etc/passwd'` returns `/a/../etc/passwd`,
    which the OS resolves to `/etc/passwd` once the value is passed to
    shutil.copy / shutil.rmtree / boto3.

Combined with the SDK's `Run.log_artifact(path, name=...)` API, where
`name` is fully attacker-controlled and propagated to
`upload_artifact(..., artifact_path=name)`, this allowed:

  * arbitrary file overwrite (upload destination outside the artifact
    root, e.g. /etc/cron.d/, /var/www/...)
  * arbitrary directory deletion via delete_artifact -> shutil.rmtree
  * arbitrary S3 key write/delete outside the configured prefix

This is a distinct code path from CVE-2024-6396 (`_backup_run`),
CVE-2024-8769 (`LockManager.release_locks`), and the previously-reported
restore_run_backup tar extraction issue.

Fix:
  * Add `safe_join` (filesystem) and `safe_join_posix` (S3 keys) helpers
    in artifact_storage.py that reject absolute paths, drive letters,
    and any `..` segments, and re-validate the resolved filesystem path
    stays inside the prefix.
  * Switch FilesystemArtifactStorage and S3ArtifactStorage to use them
    for upload, download, and delete.
  * Add regression tests covering absolute paths, embedded `..`,
    delete safety, and the legitimate relative-path case.
@CLAassistant
Copy link
Copy Markdown

CLAassistant commented May 8, 2026

CLA assistant check
All committers have signed the CLA.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants