# Python eval() Sink Exploitation - RunGptLLM Command Injection

- **Severity:** High
- **Signature ID:** `b3f7a91c-4d2e-4a06-9f18-6c5d0e83a1b7`

## Summary

Some AI agent frameworks pass text returned by a language model straight into Python's eval() function. If an attacker controls that model output, they can smuggle in code disguised as a normal expression and get it to run with the same privileges as the agent.

## How the attack works

The LlamaIndex RunGptLLM class reads streaming (SSE) responses from a RunGpt model host and passes them to eval() without checking their content. Because eval() only accepts a single expression, not full statements, an attacker cannot use ordinary code like 'import os; os.system(...)'. Instead they use expression-only tricks such as __import__('os').system(...), getattr-based lookups, class-hierarchy walking to reach subclasses, or a base64-decode-then-exec chain. When that payload also launches a shell or second interpreter, its distinctive gadget syntax shows up in the resulting process command line. This rule looks for that specific syntax pattern together with evidence that an OS-level process was actually spawned.

## Why it matters

An attacker who can influence the model's response text can achieve arbitrary command execution on the system running the agent, using whatever privileges that process has.

## What you can do

- Never pass model-generated text directly to eval() or exec(); use a restricted parser or a strict output schema instead.
- Update or patch LlamaIndex components that use eval() on RunGpt (or similar) SSE responses, per GHSA-pw38-xv9x-h8ch.
- Run agent processes with the minimum privileges needed, so a successful injection has limited reach.
- Treat any hit from this detection as a starting point for investigation, not a blocked attack — confirm whether it is a real exploit, a test fixture, or code search activity before acting.

## Known benign look-alikes

- Developers or CI jobs grepping a repository for exploit gadget strings (e.g. `grep -rn "__import__('os').system" .`, `semgrep --pattern ...`) - suppressed by filter_code_search unless the search flag itself contains an '=' sign.
- Security regression suites for LlamaIndex/RunGpt or for eval-sink hardening that execute the exploit string as a test fixture - partially suppressed by filter_test_runner; residual hits are expected and should be baselined per repository.
- Hand-golfed build or bootstrap one-liners such as `python -c "__import__('subprocess').run(['ls'])"`. This is a genuine residual false positive; the rule reports rather than blocks so these can be enumerated before any enforcement change.
- Vendor installers and packagers that base64-decode and exec a bootstrap payload (matched by sel_decode_then_eval). Legitimate but worth a one-time review - the same shape is used by loader malware.
- Shell scripts that echo an exploit string into a corpus/fixture file - suppressed by filter_echo_literal only when no pipe follows, so `echo <payload> | python` is still reported.

## References

- https://github.com/advisories/GHSA-pw38-xv9x-h8ch
- https://owasp.org/Top10/A03_2021-Injection/
- https://attack.mitre.org/techniques/T1059/006/

---
Source: https://www.netzilo.com/threats/rungpt-eval-command-injection
