# RCA Report: CVE-2026-52830 (fast-mcp-telegram path-traversal session bypass)

## Summary

fast-mcp-telegram <= 0.19.0 uses the raw HTTP Bearer token as a file-name fragment when building a session-file path. It checks the token against a set of reserved names (e.g. `telegram`) but never rejects path separators or normalizes the result. A token such as `../fast-mcp-telegram/telegram` therefore resolves to the same reserved `telegram.session` file that the exact name check is meant to protect, while bypassing the name check. A remote HTTP client who knows the target session directory layout can authenticate as the default account and list or invoke the exposed Telegram MCP tools.

## Impact

- **Package/component:** fast-mcp-telegram (PyPI), specifically `src.server_components.session_token_verifier.SessionFileTokenVerifier` and `src.server_components.auth_middleware.UrlTokenMiddleware`.
- **Affected versions:** `<= 0.19.0`.
- **Patched version:** `0.19.1`.
- **Risk level:** High. A remote, unauthenticated attacker can bypass bearer-token authentication and gain access to the victim's Telegram MCP tools as the default session without any Telegram credentials.
- **Consequences:** Unauthorized access to tools such as `get_messages`, `send_message`, `send_message_to_phone`, `invoke_mtproto`, etc., under the identity of the default `telegram` session.

## Impact Parity

- **Disclosed/claimed maximum impact:** Authorization bypass via HTTP Bearer token path traversal (`api_remote` / `authz_bypass`).
- **Reproduced impact from this run:** Real HTTP auth bypass on a running `fast-mcp-telegram` 0.19.0 instance. The reserved token `telegram` is rejected (HTTP 401), while the traversal alias `../fast-mcp-telegram/telegram` is accepted (HTTP 200) and returns the protected `tools/list` response. The patched 0.19.1 build rejects the same traversal token (HTTP 401).
- **Parity:** `full` for the claimed auth-bypass surface; the reproduction exercises the actual remote HTTP API and reaches the authenticated MCP path.
- **Not demonstrated:** We did not demonstrate actual Telegram message exfiltration or message sending, because no real Telegram session credentials are configured. However, the auth layer is clearly bypassed and the tool list is exposed, proving the claimed authorization bypass.

## Root Cause

In `src/server_components/session_token_verifier.py` (0.19.0), `verify_token()` does:

```python
if token.lower() in RESERVED_SESSION_NAMES:
    return None
session_path = self._session_directory / f"{token}.session"
if not session_path.is_file():
    return None
return AccessToken(token=token, ...)
```

The reserved-name check is exact and case-insensitive, but the token string is then inserted directly into the path with only a `.session` suffix. Because `pathlib.Path` does not normalize `..` in the operand, the token `../fast-mcp-telegram/telegram` yields:

```
<session_dir>/../fast-mcp-telegram/telegram.session
```

When `session_dir` is the default `~/.config/fast-mcp-telegram`, this resolves to `~/.config/fast-mcp-telegram/telegram.session`, the same default session file the exact-name check is trying to protect. The same vulnerable path construction is also present in `UrlTokenMiddleware`, which rewrites the URL token into the `Authorization` header.

The 0.19.1 fix introduces `src/server_components/session_token_validation.py`. It validates the token against a strict `^[A-Za-z0-9_-]{43}$` pattern and uses `session_file_path()`, which resolves the constructed path and verifies it is still inside `session_dir` with `is_relative_to()`. Any traversal sequence or reserved name is rejected before the file existence check.

## Reproduction Steps

Run the self-contained script:

```bash
bash bundle/repro/reproduction_steps.sh
```

The script:

1. Reads `bundle/project_cache_context.json` and uses the provided `project_cache_dir` for persistent Python venvs.
2. Creates two virtual environments, installing `fast-mcp-telegram==0.19.0` (vulnerable) and `==0.19.1` (fixed).
3. Creates a controlled `HOME` and default session directory `~/.config/fast-mcp-telegram`, then touches `telegram.session` so the default session exists.
4. Starts each version in `http-auth` mode on a different localhost port and waits for `/health` to return 200.
5. Sends a JSON-RPC `tools/list` request to `POST /v1/mcp` with three different bearer tokens on the vulnerable server:
   - `telegram` (reserved, expected 401)
   - `../fast-mcp-telegram/telegram` (traversal alias, expected 200)
   - `invalid-token` (no matching session file, expected 401)
6. Sends the same traversal token to the fixed server (expected 401).
7. Writes `bundle/repro/runtime_manifest.json` and exits 0 only when the expected statuses are observed.

### Expected evidence

- `repro/artifacts/http_vuln_reserved.txt` and `http_vuln_noauth.txt`: HTTP 401 response body.
- `repro/artifacts/http_vuln_traversal.txt`: HTTP 200 SSE event containing the full `tools/list` result.
- `repro/artifacts/http_fixed_traversal.txt`: HTTP 401 response body from the patched version.
- `logs/server_vuln.log` and `logs/server_fixed.log`: server startup logs showing mode `http-auth` and the session directory.

## Evidence

Captured artifacts:

- `bundle/repro/artifacts/http_vuln_traversal.txt` (HTTP 200, tools list returned)
- `bundle/repro/artifacts/http_vuln_reserved.txt` (HTTP 401, reserved token rejected)
- `bundle/repro/artifacts/http_vuln_noauth.txt` (HTTP 401, invalid token rejected)
- `bundle/repro/artifacts/http_fixed_traversal.txt` (HTTP 401, traversal token rejected on 0.19.1)
- `bundle/logs/server_vuln.log`
- `bundle/logs/server_fixed.log`
- `bundle/repro/runtime_manifest.json`
- `bundle/logs/reproduction_steps.log`
- `bundle/logs/reproduction_steps_run2.log`

Key excerpt from the vulnerable traversal response (first line only for brevity):

```
event: message\r\n
data: {"jsonrpc":"2.0","id":1,"result":{"tools":[{"name":"search_messages_globally", ...
```

This demonstrates that the protected `tools/list` endpoint returned successfully using only the traversal token.

The fixed server log confirms the same traversal token now returns HTTP 401:

```
{"error": "invalid_token", "error_description": "Authentication failed..."}
```

Environment details captured in the logs: Python 3.14, uvicorn, fastmcp-slim 3.4.2, fast-mcp-telegram 0.19.0 / 0.19.1, session directory `repro/fakehome/.config/fast-mcp-telegram`.

## Recommendations / Next Steps

- **Upgrade** to `fast-mcp-telegram >= 0.19.1`, which enforces a strict token format and resolves/session-directory containment checks.
- **Network-level mitigation** until patched: restrict access to the MCP HTTP port so only trusted clients can reach it.
- **Detection:** monitor authentication logs for tokens containing path separators or unusual patterns.
- **Testing recommendation:** add regression tests that attempt tokens such as `../session`, `..\\session`, `telegram`, and `/etc/passwd` and assert they are rejected before any file existence check.

## Additional Notes

- The script was run twice consecutively from a clean project-cache state; both runs exited 0 and produced the same status sequence (401, 200, 401 on vulnerable; 401 on fixed), confirming idempotency.
- The GitHub source repository for fast-mcp-telegram was not directly reachable in this environment, so the reproduction relies on the official PyPI wheels. The relevant source code is visible in the installed site-packages and confirms the vulnerable `Path` concatenation in 0.19.0 and the new `session_token_validation.py` containment check in 0.19.1.
- No real Telegram credentials or network connectivity to Telegram are required for the reproduction; the bypass is demonstrated purely against the local authentication layer.
