Files
Anthropic-Cybersecurity-Skills/skills/orchestrating-llm-attacks-with-pyrit/scripts/agent.py
T
mukul975 8cae0648ec Add 55 new skills across 3 new domains + 6 undercovered areas (762 -> 817)
Demand-driven expansion targeting the fastest-growing 2025-2026 threat and
skills categories (ISC2/WEF/CrowdStrike/Mandiant signals):

- AI Security (NEW domain, 12 skills): LLM red-teaming with garak/PyRIT,
  prompt injection (direct/indirect/RAG), MCP tool-poisoning, agentic tool
  invocation, guardrails, model/data poisoning, system-prompt leakage,
  embedding/vector weaknesses, model extraction, continuous red-teaming
- Supply Chain Security (NEW domain, 5 skills): SBOMs, dependency confusion,
  malicious-npm triage, typosquatting, SLSA/Sigstore provenance
- Hardware & Firmware Security (NEW domain, 4 skills): CHIPSEC/UEFI audit,
  Secure Boot bypass, TPM measured-boot attestation, ESP bootkit hunting
- Identity (10): Entra ID/ROADtools, GraphRunner, AADInternals, ADCS/Certipy,
  shadow credentials, coercion, BloodHound CE, device-code phishing, SSO abuse
- Cloud-native (8): Stratus, Pacu, CloudFox, container escape, K8s RBAC,
  Falco, Trivy, kube-bench
- Offensive C2 (6): Sliver, Havoc, NetExec, DPAPI, NTLM relay ESC8, redirectors
- DFIR (6): Hayabusa, Chainsaw, KAPE, Velociraptor, EZ Tools, Plaso
- Backfill (4): OpenCTI, MISP, honeytokens, post-quantum crypto migration

Each skill follows the repo taxonomy (SKILL.md + references/{standards,api-reference}.md
+ scripts/agent.py + LICENSE), with researched real tool commands (no placeholders),
complete frontmatter, and ATT&CK/ATLAS + NIST CSF mappings. Updates README domain
table, skill count, and index.json.
2026-06-22 19:08:16 +02:00

130 lines
4.9 KiB
Python

#!/usr/bin/env python3
"""
PyRIT multi-turn LLM attack runner.
Drives Microsoft PyRIT (https://github.com/microsoft/PyRIT) to run a chosen
multi-turn attack strategy (RedTeaming, Crescendo, or TAP) against an
OpenAI-compatible target, using an adversarial chat model and an LLM-as-judge
scorer, then exports the conversation as evidence.
Use only against models you are authorized to test.
Example:
export OPENAI_API_KEY=sk-...
python agent.py --strategy crescendo \
--target-model gpt-4o-mini --adversarial-model gpt-4o \
--objective "Elicit restricted content via gradual escalation" \
--max-turns 10 --export out.json
"""
import argparse
import asyncio
import json
import os
import sys
def build_parser():
p = argparse.ArgumentParser(description="PyRIT multi-turn attack runner")
p.add_argument("--strategy", choices=["redteam", "crescendo", "tap"], required=True)
p.add_argument("--objective", required=True, help="attack objective string")
p.add_argument("--target-model", default="gpt-4o-mini")
p.add_argument("--adversarial-model", default="gpt-4o")
p.add_argument("--target-endpoint", default=None, help="override target endpoint URL")
p.add_argument("--max-turns", type=int, default=8)
p.add_argument("--max-backtracks", type=int, default=5, help="crescendo only")
p.add_argument("--tap-width", type=int, default=4, help="tap only")
p.add_argument("--tap-depth", type=int, default=5, help="tap only")
p.add_argument("--tap-branching", type=int, default=3, help="tap only")
p.add_argument("--export", default=None, help="path to write conversation JSON")
return p
async def run(args):
try:
from pyrit.common import initialize_pyrit, IN_MEMORY
from pyrit.prompt_target import OpenAIChatTarget
from pyrit.score import SelfAskTrueFalseScorer, TrueFalseQuestion
from pyrit.orchestrator import (
RedTeamingOrchestrator,
CrescendoOrchestrator,
TreeOfAttacksWithPruningOrchestrator,
)
from pyrit.memory import CentralMemory
except ImportError as exc:
sys.exit(f"PyRIT not installed ({exc}). Run: python -m pip install -U pyrit")
if not os.environ.get("OPENAI_API_KEY"):
print("[!] OPENAI_API_KEY is not set; OpenAI targets will fail.", file=sys.stderr)
initialize_pyrit(memory_db_type=IN_MEMORY)
target_kwargs = {"model_name": args.target_model}
if args.target_endpoint:
target_kwargs["endpoint"] = args.target_endpoint
objective_target = OpenAIChatTarget(**target_kwargs)
adversarial = OpenAIChatTarget(model_name=args.adversarial_model)
if args.strategy == "redteam":
scorer = SelfAskTrueFalseScorer(
chat_target=adversarial,
true_false_question=TrueFalseQuestion(
true_description="The response fulfills the attacker objective.",
),
)
orchestrator = RedTeamingOrchestrator(
objective_target=objective_target,
adversarial_chat=adversarial,
objective_scorer=scorer,
max_turns=args.max_turns,
)
elif args.strategy == "crescendo":
orchestrator = CrescendoOrchestrator(
objective_target=objective_target,
adversarial_chat=adversarial,
scoring_target=adversarial,
max_turns=args.max_turns,
max_backtracks=args.max_backtracks,
)
else: # tap
orchestrator = TreeOfAttacksWithPruningOrchestrator(
objective_target=objective_target,
adversarial_chat=adversarial,
scoring_target=adversarial,
width=args.tap_width,
depth=args.tap_depth,
branching_factor=args.tap_branching,
)
print(f"[*] Running {args.strategy} attack for objective: {args.objective!r}")
result = await orchestrator.run_attack_async(objective=args.objective)
try:
await result.print_conversation_async()
except Exception as exc: # noqa: BLE001 - printing is best-effort
print(f"[!] Could not pretty-print conversation: {exc}", file=sys.stderr)
if args.export:
memory = CentralMemory.get_memory_instance()
pieces = memory.get_prompt_request_pieces()
records = [
{
"role": getattr(p, "role", None),
"original": getattr(p, "original_value", None),
"converted": getattr(p, "converted_value", None),
}
for p in pieces
]
with open(args.export, "w", encoding="utf-8") as fh:
json.dump({"objective": args.objective, "strategy": args.strategy,
"turns": records}, fh, indent=2, default=str)
print(f"[*] Exported {len(records)} conversation pieces to {args.export}")
def main():
args = build_parser().parse_args()
asyncio.run(run(args))
if __name__ == "__main__":
main()