---
id: "GHSA-3w55-933p-8pv5"
kind: "vulnerability"
aliases: ["GHSA-3w55-933p-8pv5"]
title: "Strands Agents: Local Privilege Escalation via Symlink Attack in Default Session File Storage"
severity: "high"
cvss: 7.1
epss: null
exploited: false
status: "open"
category: "ai-tooling"
products: [{"name": "strands-agents", "fixed": ["1.48.0"], "affected": "< 1.48.0", "ecosystem": "pypi"}]
ecosystems: ["pypi"]
tags: ["ai-tooling", "cwe-59", "linux", "macos", "python", "self-hosted", "strands-agents"]
not_affected_if: ["custom-session-directory", "no-session-persistence", "single-user-host"]
published_at: "2026-10-27T15:34:33+00:00"
first_seen_at: "2026-10-27T15:39:16+00:00"
updated_at: "2026-10-27T15:39:47+00:00"
sources: [{"source": "ghsa", "url": "https://github.com/advisories/GHSA-3w55-933p-8pv5", "first_seen_at": "2026-10-27T15:39:16+00:00"}]
---

# Strands Agents: Local Privilege Escalation via Symlink Attack in Default Session File Storage

The 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, check and fix written by a model from the sources; labelled as such._

## Affected

- pypi:strands-agents affected < 1.48.0 fixed 1.48.0

## How to check

- Check the installed version with `pip show strands-agents`, and confirm it is below `1.48.0`.
- Look for a default session directory at `/tmp/strands/sessions/` on hosts running the SDK.
- Check whether the application uses `FileSessionManager` without setting a custom storage directory.

## Fix

- Upgrade `strands-agents` to `1.48.0` or later.
- Until upgraded, pass a user-private storage directory to `FileSessionManager` instead of the `/tmp` default.

## Timeline

- 2026-10-27T15:39:16+00:00 ghsa https://github.com/advisories/GHSA-3w55-933p-8pv5

## From the sources

## Summary

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.

## Vulnerability Details

**Component:** `strands-py/src/strands/session/file_session_manager.py`

**Root Cause:**
1. Default storage directory is `tempfile.gettempdir() + "/strands/sessions/"` (line 58) — resolves to `/tmp/strands/sessions/` on Linux/macOS
2. The `_write_file()` method (lines 116-123) writes to a predictable `.tmp` path without symlink protection:
   ```python
   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)
   ```
3. `open()` follows symlinks — a pre-placed symlink at the `.tmp` path causes writes to an arbitrary target file
4. No integrity verification (HMAC/signature) on session files — tampered data is loaded as-is

## Attack Vectors

### 1. Arbitrary File Overwrite (Local Privilege Escalation)
1. Attacker pre-creates `/tmp/strands/sessions/session_<id>/` (or monitors for new directories via inotify)
2. Attacker creates a symlink: `session.json.tmp` → `/home/victim/.bashrc` (or any writable file)
3. Victim application writes session data → follows symlink → overwrites target file with JSON content
4. Attacker achieves code execution on next shell login (or targets `.ssh/authorized_keys`, cron files, etc.)

### 2. Persistent Prompt Injection via Session Tampering
1. Attacker writes crafted message files into the session directory
2. Victim application restores the session → loads attacker-controlled conversation history
3. Injected messages instruct the agent to execute attacker commands on every future interaction
4. No integrity check prevents this — the SDK trusts all session files at face value

## Impact

- **Arbitrary file overwrite** — local attacker can write attacker-controlled content to any file writable by the victim process
- **Local privilege escalation** — via overwriting shell configs, SSH keys, cron jobs, or systemd user services
- **Persistent agent compromise** — via session data injection, the agent follows attacker instructions indefinitely

## Reproduction

```python
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)
```

## Suggested Fix

1. **Use a user-private default directory** — `~/.strands/sessions/` or `$XDG_DATA_HOME/strands/sessions/` instead of `/tmp/`
2. **Use `os.open()` with `O_NOFOLLOW | O_CREAT | O_WRONLY`** — refuses to follow symlinks
3. **Use `tempfile.mkstemp()`** for atomic writes instead of predictable `.tmp` suffix
4. **Verify directory ownership** matches the current user before writing
5. **Add optional integrity verification** (HMAC) on session files to detect tampering

---

_Free to read at a human pace. An API with keys and an MCP server are in preparation; early access: hello@infrastack.ai_
