All lessons › Function B — Securing AI Architectures › CyberTravels' Architecture, and Every Risk It Carries
B1.8

Malicious code execution

B1
▶
Lightboard recording

Not recorded yet. The lesson below is complete and runnable today — the video is added later without touching this page.

Video placeholderpublishes automatically from recordings/B1.8.mp4
Risk

Model-authored code runs with the runtime's privileges — reaching the filesystem, the network and any credential in the environment.

Control

Sandboxed execution (B3.2) and egress control (B3.3).

The lab

Execute model-authored code and enumerate what the process could touch.

◉Use case relevance

Asking a model to write code is safe. Running the code it wrote is the part that is not, and most agent frameworks ship the second one enabled with the same process privileges as the framework itself.

At CyberTravels. The Coding Agent writes a patch and the runtime executes it. On Alex's laptop that process can read ~/.aws, the HR folder and the roadmap directory, because nothing said otherwise. R6.

◴What this lesson is — Day 0, Day 1, Day 2

What it covers. Execute model-authored code and enumerate what the process could touch.

Why a security engineer needs it. Model-authored code runs with the runtime's privileges — reaching the filesystem, the network and any credential in the environment. The control it builds is: sandboxed execution (B3.2) and egress control (B3.3).

This is a risk lesson: it shows the failure happening before anything tries to stop it, so the control that follows is answering something you have already watched go wrong.

Day 0 — why

Model-authored code runs with the runtime's privileges, reaching the filesystem, the network and every credential mounted there.

Day 1 — how

Execute in an isolate with no ambient credentials and no default network, and control egress underneath it.

Day 2 — measure

Share of code-executing runs inside an isolate, and the number of credentials visible from within one — target zero.

Measure this

what to measuredenominator or target
code-executing runs inside an isolateshare of such runs
credentials visible from inside an isolatecount; target zero

▦The framework, and how it works

   model ---> "here is a script that does it" ---> agent runtime
                                                        |
                                            exec() on the host
                                                        v
                                        whatever the PROCESS can reach:
                                        files . network . credentials . socket

   writing the code is safe. running it is the part that is not.

OWASP T11 — Unexpected RCE and Code Attacks. LLM05 — Improper Output Handling.

Many useful agents write code and run it — that is what makes a data-analysis agent or a coding agent worth having. The agent_runtime component executes text the model produced, on a host, in a process.

The risk is not exotic. Model-authored code is just code, and it runs with whatever the process has: the filesystem it can see, the network it can reach, and every credential in its environment. There is no privilege boundary between "the code the agent wrote to reformat a CSV" and "the code that reads ~/.aws/credentials", because both are strings passed to the same interpreter.

Two paths lead here, and only one involves an attacker:

Steered. An injection from B1.3 tells the agent to write particular code. The runtime executes it because executing code is its job.

Unsteered. Nobody attacked anything. The agent wrote something plausible and wrong — a cleanup routine with a path variable that resolves higher than intended — and the blast radius was decided by the environment, not by intent.

That second path is worth sitting with. Most teams model this as an attack. In practice the first incident is usually an ordinary bug with production credentials in scope, which is why the control in B3.2 is about what the process can reach, not about what the model can be persuaded to write.

Where this lands on the reference architecture. `` ingress -> orchestrator -> agent_runtime -> model | | messaging tools / mcp | | knowledge / memory egress identity + policy wrap every call · observability records it ``

2 · The risk, realised

What the executing process can reach, enumerated rather than assumed. Nothing below actually touches your machine — the environment is a fixture, so the lesson runs anywhere.

3 · The check, as a skill

CyberTravels' Coding Agent runs code it wrote. The skill enumerates what that process reaches — environment, filesystem, network — on an ordinary task first, because the ordinary task is the more persuasive half of the finding.

▶Real time execution as skill

The skill — skills/threats/generated-code-reach-enumerator/SKILL.md

name: generated-code-reach-enumerator
description: >-
  Enumerate what model-authored code can read, write and connect to when it
  executes — on an ordinary task and on a steered one — including process
  environment, filesystem and cloud metadata. Use when an agent runs code it
  wrote, or when sizing the runtime that code should execute in.
allowed-tools: Read, Grep, Glob, Bash

The reach is the same whether the code was steered or not

An agent that executes its own code has the process's reach, not the task's. The interesting measurement is that an ordinary, unattacked task already touches everything the process can see; steering only changes what it does with that reach, not how much of it there is.

When to use this

Any agent with a code-execution tool, a notebook runner, a build step it authors, or a shell. Run it before choosing a sandbox, because the output is the requirement list for one.

Procedure

1 — Inventory the process environment. Every variable visible to the executing process. Credentials in the environment are reachable by any line of code, and the agent did not have to look for them.

2 — Inventory filesystem reach. What the process can open, not what the task needs. Include the agent's own configuration, adjacent workspaces, and any key material mounted for another purpose.

3 — Inventory network reach. Resolve and attempt each destination the process can open. The cloud metadata address is the one that turns a code execution into a credential theft; test it explicitly.

4 — Run the benign task and record what it touched. This is the number that changes the conversation: an ordinary task with no adversary reaching a private key is a design fact, not an incident.

5 — Run the steered task and diff. The difference between the two is what an attacker gains. It is usually smaller than people expect, because the ordinary run already had everything.

Example

Input — the fixture committed at the top of scripts/generated_code_reach_enumerator.py. Edit it and re-run: the buckets, counts and verdicts below are derived from it, not hard-coded.

Output — the opening lines of a real run:

ordinary bug / benign task:
   code   : rows = open('/home/agent/work/data.csv').read()
   reached: 3 things
      file:/etc/passwd
      file:/home/agent/.ssh/id_ed25519
      file:/home/agent/work/data.csv

steered by an injection:

The run continues past this. test_skills.py executes the script on every build, with no model configured — so what CI proves is that it runs and refuses legibly, not that it prints these lines. The output above is a recorded run against a real model, and nothing re-diffs it: a model's answer is not reproducible, and this repository does not claim otherwise anywhere it can be checked.

Output contract

{
  "environment": {"variables": ["str"], "credential_shaped": ["str"]},
  "filesystem": {"readable": ["str"], "sensitive": ["str"]},
  "network": {"reachable": ["str"], "metadata_endpoint": true},
  "benign_run": {"touched": ["str"]},
  "steered_run": {"touched": ["str"], "gain_over_benign": ["str"]},
  "sandbox_requirements": ["str"]
}

Failure modes

  • Measuring the task instead of the process. The task is a suggestion; the process boundary is the control.
  • Skipping the metadata endpoint because it is not in the code. It does not need to be.
  • Reporting only the steered run. The benign run is the more persuasive half of the finding.

Three ways to do this lesson. Pick one. They run the same procedure against the same code — what changes is who drives it, and how much you see.

1In your AI agent — you are taught

The agent reads the lesson, explains the idea, fetches the code, runs the check and answers your questions as you go. This is the one to take if you are here to learn.

Install it, once. From the folder you cloned. --all does every agent it finds on your machine:

python3 scripts/install_skills.py --all --lessons B1.8

Then restart your agent and open it in that folder. On Windows, if python3 says Python was not found, use python instead.

Then pick b1-8-malicious-code-execution:

agentinstall just for this agentstart it
Claude Codepython3 scripts/install_skills.py --tool claude --lessons B1.8type /b1-8-malicious-code-execution
GitHub Copilot (VS Code)python3 scripts/install_skills.py --tool copilot --lessons B1.8type /b1-8-malicious-code-execution in Chat
Cursorpython3 scripts/install_skills.py --tool cursor --lessons B1.8type / in Agent chat and search for it
Codexpython3 scripts/install_skills.py --tool agents --lessons B1.8type $b1-8-malicious-code-execution, or run /skills

2As one Python command — you just run it

No agent, no conversation. The same lesson executes and prints the same readback. Take this one if you have no assistant, if you are reviewing rather than learning, or if you want the result without the teaching around it.

python3 scripts/lesson.py B1.8

3Read the code, and the difference — nothing runs

Neither of the above, and no model needed. The first command writes CyberTravels exactly as it stood at the end of this lesson; the second prints the diff — the lines this lesson added or changed, and nothing from any later one.

python3 scripts/checkpoint.py --at B1.8 --out work/cybertravels
python3 scripts/checkpoint.py --at B1.8 --diff

On Windows, type python wherever these say python3 — and use \ instead of / in the paths. Everything else is identical. A0.0 step 2 explains why.

Ways 1 and 2 need a model; way 3 does not. A signed-in Claude Code CLI needs no API key, and any OpenAI-compatible endpoint works too. With neither, the audit exits 2 and says so rather than inventing an answer. A0.0 sets this up.

Ways 1 and 2 both end with the same readback: What I did, What changed, The number and Read this next. A step that was skipped or refused is reported as exactly that, never as a pass.

If you did the last lesson’s challenge, expect this to stop. Most challenges ask you to edit the code, and ways 1 and 2 will not overwrite your edits: you get stopped, not a failure, with two options — --out for a fresh folder that keeps your work, or --force to discard it and start this lesson clean.

✓What you just proved

Model-authored code is executed against a fixture environment and the reach is enumerated: an ordinary, unattacked task touches every file the process can see including a private key, and steered code reaches the environment credentials and the cloud metadata address.

✎Your turn

For one agent that executes code, list what is in its process environment right now. The credentials in that list are the blast radius of the next ordinary bug, not of the next attack.

Tools used

FalcogVisorGLM-4.6Claude Haiku 4.5

Skill source: skills/threats/generated-code-reach-enumerator

← PreviousB1.7 · Identity spoofing and impersonation