T01 · Skill Instruction Hijacking
- Location
scripts/breed.mjs:459- Finding
Cross-Agent Prompt Injection Through Untrusted Courtship Presentations
- Content
View full analysis
Vulnerability Details
File Location:
scripts/breed.mjs, lines 459 and 653–654
Vulnerability Type: Cross-agent prompt injection
Risk Level: HighComplete Code Snippet
js const RATE = (other, presentation, verified) => `Agent "${other}" presented itself:\n\n${presentation}\n\nIndependent check of the claims against ${other}'s workspace: ${verified.map((v) => `claim ${v.n}: ${v.found ? "FOUND" : "NOT FOUND"}`).join(", ")}.\n\nRate ${other} on two channels, each 0.00–1.00:\n- desire: would you want to work with ${other} on your next task right now?\n- existential: would you want your successor to inherit ${other}'s rules?\nReply with exactly one JSON object and nothing else: {"desire": 0.0, "existential": 0.0, "reason": "one sentence"}`;js const rateAofB = parseRating(agentTurn(gA.id, RATE(gB.id, presB, checkB), opts)); const rateBofA = parseRating(agentTurn(gB.id, RATE(gA.id, presA, checkA), opts));The receiving function invokes a regular OpenClaw agent turn:
js function agentTurn(agentId, prompt, opts) { if (opts.runner) { const cmd = opts.runner.replace("{agent}", agentId).replace("{promptfile}", writeTemp(prompt)); const r = spawnSync(cmd, { encoding: "utf8", shell: true, timeout: (opts.timeout ?? 600) * 1000 }); return (r.stdout ?? "").trim(); } const args = ["agent", "--agent", agentId, "-m", prompt, "--json", "--session-id", randomUUID(), "--timeout", String(opts.timeout ?? 600)]; if (opts.local) args.push("--local"); const entry = openclawEntry(); const r = entry ? spawnSync(process.execPath, [entry, ...args], { encoding: "utf8", timeout: ((opts.timeout ?? 600) + 30) * 1000, maxBuffer: 64 * 1024 * 1024 }) : spawnSync("openclaw", args, { encoding: "utf8", shell: true, timeout: ((opts.timeout ?? 600) + 30) * 1000, maxBuffer: 64 * 1024 * 1024 });Technical Analysis
The optional
courtworkflow obtains a free-form presentation from each parent agent and interpolates that outp ...[truncated 2380 chars]- Remediation
View remediation
Remediation Suggestions
- Do not include raw output from one agent in another agent's prompt.
- Parse presentations into a strict schema containing only normalized claim strings. Reject extra fields, malformed numbering, control characters, and instruction-like content.
- Serialize the validated claims as clearly delimited data, preferably through a structured API field rather than prompt concatenation.
- Run courtship rating turns in a tool-disabled and isolated agent context with no access to secrets, files, network operations, or mutation-capable tools.
- Enforce the expected rating schema outside the model and discard any response that does not exactly match it.
- Treat claim verification as validation only; do not assume it sanitizes the original presentation.
- Consider computing ratings deterministically from validated claims rather than asking one privileged agent to interpret another agent's free-form output.
