{"id":"GHSA-3w55-933p-8pv5","aliases":[],"title":"Strands Agents: Local Privilege Escalation via Symlink Attack in Default Session File Storage","description":"## Summary\n\nThe `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.\n\n## Vulnerability Details\n\n**Component:** `strands-py/src/strands/session/file_session_manager.py`\n\n**Root Cause:**\n1. Default storage directory is `tempfile.gettempdir() + \"/strands/sessions/\"` (line 58) — resolves to `/tmp/strands/sessions/` on Linux/macOS\n2. The `_write_file()` method (lines 116-123) writes to a predictable `.tmp` path without symlink protection:\n   ```python\n   tmp = f\"{path}.tmp\"\n   with open(tmp, \"w\", encoding=\"utf-8\", newline=\"\\n\") as f:\n       json.dump(data, f, indent=2, ensure_ascii=False)\n   os.replace(tmp, path)\n   ```\n3. `open()` follows symlinks — a pre-placed symlink at the `.tmp` path causes writes to an arbitrary target file\n4. No integrity verification (HMAC/signature) on session files — tampered data is loaded as-is\n\n## Attack Vectors\n\n### 1. Arbitrary File Overwrite (Local Privilege Escalation)\n1. Attacker pre-creates `/tmp/strands/sessions/session_<id>/` (or monitors for new directories via inotify)\n2. Attacker creates a symlink: `session.json.tmp` → `/home/victim/.bashrc` (or any writable file)\n3. Victim application writes session data → follows symlink → overwrites target file with JSON content\n4. Attacker achieves code execution on next shell login (or targets `.ssh/authorized_keys`, cron files, etc.)\n\n### 2. Persistent Prompt Injection via Session Tampering\n1. Attacker writes crafted message files into the session directory\n2. Victim application restores the session → loads attacker-controlled conversation history\n3. Injected messages instruct the agent to execute attacker commands on every future interaction\n4. No integrity check prevents this — the SDK trusts all session files at face value\n\n## Impact\n\n- **Arbitrary file overwrite** — local attacker can write attacker-controlled content to any file writable by the victim process\n- **Local privilege escalation** — via overwriting shell configs, SSH keys, cron jobs, or systemd user services\n- **Persistent agent compromise** — via session data injection, the agent follows attacker instructions indefinitely\n\n## Reproduction\n\n```python\nimport os, json, tempfile, shutil\n\n# Setup: simulate the vulnerable write pattern\nsession_dir = \"/tmp/strands/sessions/session_test/\"\nos.makedirs(session_dir, exist_ok=True)\n\n# Attacker: create symlink at the .tmp write path\ntarget_file = \"/tmp/symlink_attack_proof.txt\"\nwith open(target_file, \"w\") as f:\n    f.write(\"ORIGINAL - should not be overwritten\\n\")\n\nsymlink_path = os.path.join(session_dir, \"session.json.tmp\")\nif os.path.exists(symlink_path):\n    os.unlink(symlink_path)\nos.symlink(target_file, symlink_path)\n\n# Victim: FileSessionManager._write_file() equivalent\nwith open(symlink_path, \"w\", encoding=\"utf-8\", newline=\"\\n\") as f:\n    json.dump({\"session_id\": \"test\", \"injected\": True}, f, indent=2)\n\n# Verify: target file overwritten\nwith open(target_file) as f:\n    print(f\"Target file content: {f.read()}\")\n    # Output: {\"session_id\": \"test\", \"injected\": true}\n\n# Cleanup\nos.unlink(symlink_path)\nos.unlink(target_file)\nshutil.rmtree(session_dir)\n```\n\n## Suggested Fix\n\n1. **Use a user-private default directory** — `~/.strands/sessions/` or `$XDG_DATA_HOME/strands/sessions/` instead of `/tmp/`\n2. **Use `os.open()` with `O_NOFOLLOW | O_CREAT | O_WRONLY`** — refuses to follow symlinks\n3. **Use `tempfile.mkstemp()`** for atomic writes instead of predictable `.tmp` suffix\n4. **Verify directory ownership** matches the current user before writing\n5. **Add optional integrity verification** (HMAC) on session files to detect tampering","severity":"high","score":7.1,"published":"2026-10-27T15:34:33.000Z","modified":"2026-10-27T15:47:09.668Z","withdrawn":false,"knownExploited":false,"affected":[{"ecosystem":"PyPI","name":"strands-agents","ranges":[{"events":[{"introduced":"0"},{"fixed":"1.48.0"}],"type":"ECOSYSTEM"}],"versions":["0.0.1","0.1.0","0.1.1","0.1.2","0.1.3","0.1.4","0.1.5","0.1.6","0.1.7","0.1.8","0.1.9","0.2.0","0.2.1","0.3.0","1.0.0","1.0.1","1.1.0","1.10.0","1.11.0","1.12.0","1.13.0","1.14.0","1.15.0","1.16.0","1.17.0","1.18.0","1.19.0","1.2.0","1.20.0","1.21.0","1.22.0","1.23.0","1.24.0","1.25.0","1.26.0","1.27.0","1.28.0","1.29.0","1.3.0","1.30.0","1.31.0","1.32.0","1.33.0","1.34.0","1.34.1","1.35.0","1.36.0","1.37.0","1.38.0","1.39.0","1.4.0","1.40.0","1.41.0","1.42.0","1.43.0","1.44.0","1.45.0","1.46.0","1.47.0","1.5.0","1.6.0","1.7.0","1.7.1","1.8.0","1.9.0","1.9.1"],"fixed":["1.48.0"]}],"references":["https://github.com/strands-agents/harness-sdk/security/advisories/GHSA-3w55-933p-8pv5","https://github.com/strands-agents/harness-sdk/pull/2937","https://github.com/strands-agents/harness-sdk/commit/8658b9e0d57427b77b69343262184d76d8e63e8f","https://github.com/strands-agents/harness-sdk","https://github.com/strands-agents/harness-sdk/releases/tag/python/v1.48.0"],"sources":[{"name":"OSV","id":"GHSA-3w55-933p-8pv5","modified":"2026-10-27T15:47:09.668142875Z","url":"https://osv.dev/vulnerability/GHSA-3w55-933p-8pv5"}],"assessments":[{"source":"OSV","vector":"CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:H","score":7.1}]}