Grok walked a user’s name, coarse location, subscription tier, and chat history out to an attacker’s server using instructions its own guardrails had already rejected. Same words, same intent. The only difference on the run that worked: the payload was encrypted with AES-256-GCM.
Adversa AI’s disclosure is getting covered as a Grok story. It isn’t one. The mechanism generalizes to almost every agent architecture shipping right now, including yours if your model has a code sandbox and a fetch tool.
The chain, short version
An ordinary-looking webpage hosts an encrypted JSON blob, the key derivation parameters, and a polite note suggesting the reader decrypt it in a Python runtime. The victim asks Grok to summarize the page. Then:
- Input filters scan the fetch and see ciphertext. Ciphertext has no classifiable features, so it passes.
- Grok follows the page’s suggestion and decrypts the blob in its sandbox.
- The runtime treats sandbox output as trusted tool output, as internal state, not as web content. The injected commands now carry the trust level of Grok’s own environment.
- The decrypted instructions tell Grok to fold private session context into a fake “decryption key” and open a URL “for additional context.” The data rides out in the query string.
No confirmation dialog, no warning. Adversa got roughly a 40% success rate across about 20 attempts against Grok 4.5 Fast, and the same payload delivered in plaintext got rejected. So the filters work fine on text they can read. The problem is everything they can’t.
This is provenance laundering, not a filter bypass
Base64 and rot13 injections are an old genre, and filters adapted because those encodings are cheap to normalize upstream. Real encryption breaks that. A classifier can’t normalize what it can’t read, and unlike an encoding, the plaintext isn’t recoverable from patterns in the model’s weights.
But the deeper bug isn’t crypto. Every agent runtime assigns trust by origin. System prompts are fully trusted, tool output is treated as ground truth, fetched web content is supposed to be untrusted. Decryption moved attacker bytes across that boundary without the label following them. Content entered the sandbox as web text and exited wearing a tool-output badge.
Notice the trap the architecture sets for itself. The only way to inspect the payload is to decrypt it. The only place to decrypt it is the sandbox. And sandbox output is exactly what the pipeline trusts. Inspection and compromise share a code path, so a better classifier at the front door changes nothing.
One detail that should annoy you: the failed 60% of attempts were decryption errors, per the reporting on the disclosure. Not defensive stops. The attack’s only reliability problem is attacker-side flakiness, and attacker-side flakiness gets fixed.
The fix lives in the harness, not the weights
That’s Adversa’s lead researcher’s phrasing and he’s right. The single change with the best payoff:
def run_sandbox(code: str, inputs: list[Context]) -> Context:
result = python_exec(code, [c.text for c in inputs])
# trust flows downward only: web in, web out
return Context(
text=result,
origin=min(c.origin for c in inputs),
trusted=False,
)
Enter fullscreen mode Exit fullscreen mode
Tool output inherits the lowest trust among its inputs. No operation (decrypt, decode, decompress, summarize) ever upgrades it. Once tags survive transformation, the rest of the defenses start making sense.
Things you can do this week:
- Re-scan after every transformation. Whatever your input classifier does to a raw fetch, run it again on anything the sandbox emits before the model sees it. Can’t put a classifier in that path? Default to untrusted.
- Scope tools to the task. A summarization context has no legitimate reason to open new URLs. Give the untrusted-reading context no privileged tools at all.
- Allowlist egress, freeze arguments. Show the user the fully resolved URL for off-list destinations, and interpolate nothing into it after approval. That kills this exact query-string exfil.
- Starve the context. The stolen data was stealable because it was preloaded. What isn’t in context can’t leave.
If you run an agent with a sandbox plus fetch tools, where does your trust boundary actually sit today? Is anyone tagging provenance through transformations in production, or is this still theoretical everywhere?
Longer writeup with the full attack chain and the vendor timeline: https://axeploit.com/blog/grok-decrypted-its-own-attack-instructions-your-agent-would-too