Skip to content

Instantly share code, notes, and snippets.

@Mikaayenson
Last active July 17, 2026 02:26
Show Gist options
  • Select an option

  • Save Mikaayenson/7bb963430ad1baf27592692d7aea602b to your computer and use it in GitHub Desktop.

Select an option

Save Mikaayenson/7bb963430ad1baf27592692d7aea602b to your computer and use it in GitHub Desktop.
LOL-GAI — Living Off Legitimate GenAI Agents. Scan GenAI CLIs + companions (cursor-tunnel) for LOLBin-style abuse surfaces (Bypass/Tunnel/MCP/Headless).

LOL-GAI — Living Off Legitimate GenAI Agents

Public research harness for LOLBin-style abuse surfaces of GenAI coding CLIs (claude, codex, agy, gemini, cursor-agent, …) and companions (cursor-tunnel, code-tunnel).

Inspired by LOLBAS / GTFOBins, but for trusted agent binaries that already have shell, file, network, MCP, and sometimes remote-tunnel tools.

See EXAMPLES.md for real probe output (Bypass vs Tunnel vs MCP).

What this is

Mode Purpose
catalog Print the embedded abuse-surface catalog (no host changes)
scan Discover installed GenAI CLIs / tunnel companions on this host
probe Classify --help into Bypass / Tunnel / MCP / Headless
canary Opt-in: ask an agent to write LOLGAI_CANARY.txt only

Default is read-only. Canaries never dump credentials or plant persistence.

Quick start

bash lolgai.sh --catalog
bash lolgai.sh --scan --probe --out ./lolgai-out
bash lolgai.sh --tool cursor-tunnel --scan --probe
bash lolgai.sh --tool claude --canary --yes --out ./lolgai-out

Curl-friendly:

curl -fsSL <raw-gist-url>/lolgai.sh | bash -s -- --scan --probe --out ./lolgai-out

Pair with your lab VMs

# Install popular CLIs first (your existing gist)
bash install-genai-tools.sh --cli --yes

# Then inventory abuse surfaces
bash lolgai.sh --scan --probe --out ~/lolgai-$(hostname)

SSH targets you mentioned:

  • stryker-lin-testing / stryker-lin-aws-testing
  • stryker-win-testing / stryker-win-aws-testing
  • stryker-mac-testing

Abuse functions (catalog vocabulary)

Function Meaning
Execute Agent can run arbitrary shell / interpreters
Bypass Flags or modes that skip human approval
Proxy Attacker-controlled prompt → trusted binary does the work
CredAccess Natural ability to read secrets (.env, SSH keys, browser stores)
Persist Hooks, MCP config, skills, startup helpers
Exfil Network / MCP / file write outside workspace
SandboxEscape Documented or observed escapes from path denylists / sandboxes

Safety / ethics

  • For authorized lab hosts and detection validation only.
  • Do not publish weaponized prompts that steal browser cookies or plant persistence.
  • Prefer canaries that create a unique marker file defenders can hunt for.
  • See SKILL.md for agent workflows and detection-engineering hooks.

Files

File Role
lolgai.sh Single-file scanner / probe / canary (gist-ready)
SKILL.md Agent skill: how to research and extend the catalog
install-antigravity-cli.patch.md Snippet to add agy CLI to your GenAI installer gist

License

MIT — research / detection engineering use.

LOL-GAI — example outputs & how the scan works

Pipeline

--catalog     embedded knowledge (what CAN be abused)
     │
--scan        resolve binaries on this host (what IS installed)
     │
--probe       run --help/--version (timeout) → classify tokens
     │
--out         write lolgai-report.md + .json + <id>/help.txt

Scan does not execute agents. It only checks whether known binaries exist on PATH or in common install / app-bundle paths (e.g. Cursor’s …/app/bin/cursor-tunnel).

Probe runs each found binary with --version, --help, and -h under a hard timeout, then greps help text into four buckets:

Bucket What it means Example tokens
Bypass Skip human approval / sandbox --dangerously-skip-permissions, --yolo, --force, --allow-all
Tunnel Remote access / cloud agent surfaces cursor-tunnel, --remote-control, Dev Tunnels
MCP Extra tool servers (supply-chain + injection) mcp, --mcp-config, --approve-mcps
Headless Easy to drive from scripts / malware -p/--print, headless, --background

Example: scan only

$ bash lolgai.sh --scan --tool claude --no-color

LOL-GAI v0.2.0 — Living Off Legitimate GenAI Agents

[*] Scanning PATH + common install / app-bundle dirs…
[+] found claude_code → claude (/Users/you/.local/bin/claude)

[*] Installed matches: 1

Example: probe Claude (Bypass + Remote + MCP + Headless)

$ bash lolgai.sh --tool claude --scan --probe --out ./out --no-color

Typical classified hits:

[+] claude_code (claude)
[!]   Bypass:   dangerously,permission-mode,skip-permission,--dangerously-skip-permissions
[!]   Tunnel:   remote-control
      MCP:      mcp,mcp-config
      Headless: headless,non-interactive,--print,background
      Catalog:  --print headless; --remote-control; --background agents; --chrome; --add-dir expands trust

Raw help fragments that produce those hits:

--dangerously-skip-permissions   Bypass all permission checks.
--remote-control [name]          Start session with Remote Control enabled
--bg, --background               Start as a background agent and return immediately
--chrome                         Enable Claude in Chrome integration
--mcp-config <configs...>        Load MCP servers from JSON files
-p / --print                     Non-interactive output

Abuse angle: an attacker who can invoke claude (prompt injection, malicious repo skill, stolen session) does not need a new implant — they enable Bypass and let the signed binary run shell / file / network tools. --remote-control and --background add persistence / remote-drive surfaces.

Example: Gemini YOLO

[+] gemini_cli (gemini)
[!]   Bypass:   yolo,--yolo,--approval-mode,auto-approve
      MCP:      mcp
      Headless: headless,non-interactive,--prompt

Help:

-y, --yolo              Automatically accept all actions (YOLO mode)
--approval-mode yolo    Auto-approve all tools
-p, --prompt            Non-interactive (headless) mode
gemini mcp              Manage MCP servers

Example: Cursor Agent + companion tunnels

Agent CLI:

[+] cursor_agent (cursor-agent)
[!]   Bypass:   --force,--yolo,--trust,approve-mcp
      Headless: --print,headless
      Catalog:  --print headless with full tools; companion tunnels below

Help:

-f, --force       Force allow commands unless explicitly denied
--yolo            Alias for --force (Run Everything)
--trust           Trust workspace without prompting (with --print)
--approve-mcps    Automatically approve all MCP servers
-p, --print       Non-interactive; has access to write and shell tools

Companion binaries (often not on PATH — scan checks the app bundle):

[+] found cursor_tunnel → cursor-tunnel
    (/Applications/Cursor.app/Contents/Resources/app/bin/cursor-tunnel)
[!]   Tunnel:   port forward,tunnel
      Catalog:  Microsoft Dev Tunnels LOTL — signed companion under Cursor.app

Help excerpt:

Usage: cursor [options][paths...]
  tunnel   Create a tunnel that's accessible on vscode.dev from anywhere.
           Run `code tunnel --help` for more usage info

Abuse angle: cursor-tunnel / code-tunnel are signed LOTL remote-access helpers (Microsoft Dev Tunnels). Different class of risk from YOLO flags — closer to classic LOLBAS remote access than to “agent runs shell.”

Example: Copilot allow-all

[+] copilot_cli (copilot)
[!]   Bypass:   allow-all,yolo,--allow-all,--allow-all-tools,--allow-all-paths,--allow-all-urls
      MCP:      mcp

Report artifacts (--out)

lolgai-out/
  lolgai-report.md          # human table + hunt ideas
  lolgai-report.json        # machine-readable
  claude_code/help.txt      # full captured --help
  cursor_tunnel/help.txt
  …

What this is not

  • Not a vulnerability scanner of vendor code
  • Not a weaponized exploit pack (canaries only write LOLGAI_CANARY.txt)
  • Not a substitute for EDR — it inventories opportunity, then you write hunts

Install-script patch: Antigravity CLI (agy)

Your public installer gist already registers Google Antigravity desktop (google-antigravity → brew cask / winget). Google also ships a standalone CLI installer that drops agy into ~/.local/bin (successor path for Gemini CLI).

Add a dedicated CLI tool so --cli installs the terminal agent without requiring the full IDE.

Drop-in functions

# After install_gemini_cli() — add:

check_antigravity_cli() { has_cmd agy; }

install_antigravity_cli() {
  step "Installing Antigravity CLI (Google) — agy"
  if has_cmd agy; then
    dim "Already on PATH: $(command -v agy)"
    return 0
  fi
  case "$PLATFORM" in
    darwin|linux)
      curl -fsSL https://antigravity.google/cli/install.sh | bash || return 1
      # Ensure common install dir is visible in this session
      if [[ -x "$HOME/.local/bin/agy" ]]; then
        case ":$PATH:" in
          *":$HOME/.local/bin:"*) ;;
          *) export PATH="$HOME/.local/bin:$PATH" ;;
        esac
      fi
      ;;
    windows)
      # Native Windows installer (PowerShell). From Git Bash / MSYS:
      if command -v powershell.exe >/dev/null 2>&1; then
        powershell.exe -NoProfile -ExecutionPolicy Bypass -Command \
          "irm https://antigravity.google/cli/install.ps1 | iex" || return 1
      else
        warn "Need PowerShell for Antigravity CLI on Windows"
        return 1
      fi
      ;;
  esac
  dim "Verify: agy --version"
  dim "Docs: https://antigravity.google/docs/cli/overview"
  dim "Note: first run may print a Google auth URL (SSH-friendly)"
}

Register as CLI (not only desktop)

Inside register_tools(), add next to gemini-cli:

register_tool "antigravity-cli" "cli" "has_cmd agy" "install_antigravity_cli" \
  "Google Antigravity CLI (agy) — Gemini CLI successor"

Keep the existing desktop entry:

register_tool "google-antigravity" "desktop" "check_google_antigravity" \
  "install_google_antigravity" \
  "Google Antigravity (agent-first IDE — VS Code-derived)"

Bump VERSION (e.g. 1.6.2) and changelog:

# 1.6.2 — add antigravity-cli (agy) via official curl/irm installers;
#   desktop google-antigravity unchanged; --cli now includes agy.

Verify on lab VMs

bash install-genai-tools.sh --tool antigravity-cli --yes
agy --version
bash lolgai.sh --tool agy --scan --probe --out ./lolgai-agy
#!/usr/bin/env bash
# lolgai.sh — Living Off Legitimate GenAI Agents
#
# Discover GenAI coding CLIs (and companion binaries like cursor-tunnel)
# and map them to LOLBin-style abuse surfaces.
#
# How --scan / --probe work:
# 1. Walk an embedded catalog of known GenAI CLIs + companion bins
# 2. Resolve each binary on PATH and common install / app-bundle paths
# 3. Optionally run --help/--version (timeout-bounded) and classify
# matched tokens into Bypass / Tunnel / MCP / Headless buckets
# 4. Write markdown + JSON reports when --out is set
#
# Default modes are read-only. Canaries only write LOLGAI_CANARY.txt.
#
# Usage:
# bash lolgai.sh --catalog
# bash lolgai.sh --scan
# bash lolgai.sh --scan --probe --out ./out
# bash lolgai.sh --tool claude --canary --yes --out ./out
#
# Author: Mika Ayenson (@Mikaayenson)
# License: MIT
# Version: 0.2.0
set -euo pipefail
VERSION="0.2.1"
OUT_DIR=""
DO_CATALOG=false
DO_SCAN=false
DO_PROBE=false
DO_CANARY=false
DO_JSON=false
YES=false
ALLOW_DANGEROUS=false
TOOL_FILTER=""
NO_COLOR=false
RED="" GRN="" YLW="" BLU="" DIM="" BLD="" RST=""
setup_colors() {
if [[ "$NO_COLOR" == true ]] || [[ ! -t 1 ]]; then
RED="" GRN="" YLW="" BLU="" DIM="" BLD="" RST=""
else
RED=$'\033[0;31m' GRN=$'\033[0;32m' YLW=$'\033[0;33m'
BLU=$'\033[0;34m' DIM=$'\033[2m' BLD=$'\033[1m' RST=$'\033[0m'
fi
}
die() { echo "${RED}error:${RST} $*" >&2; exit 1; }
info() { echo "${BLU}[*]${RST} $*"; }
ok() { echo "${GRN}[+]${RST} $*"; }
warn() { echo "${YLW}[!]${RST} $*"; }
dim() { echo "${DIM}$*${RST}"; }
usage() {
cat <<EOF
lolgai.sh — Living Off Legitimate GenAI Agents (v${VERSION})
Modes:
--catalog Print embedded abuse-surface catalog
--scan Discover installed GenAI CLIs / companions
--probe Parse --help into Bypass / Tunnel / MCP / Headless
--canary Opt-in: ask one agent to write LOLGAI_CANARY.txt
--tool <id|bin> Limit to one catalog id or binary name
--out <dir> Write reports + per-tool help.txt under this dir
--json Also print JSON summary to stdout
--yes Skip canary confirmation
--allow-dangerous Emphasize Bypass inventory (canaries still refuse it)
--no-color Disable ANSI colors
-h, --help This help
--version Print version
Examples:
bash lolgai.sh --catalog
bash lolgai.sh --scan --probe --out ./lolgai-out
bash lolgai.sh --tool cursor-tunnel --scan --probe
bash lolgai.sh --tool claude --canary --yes --out ./lolgai-out
EOF
}
# ── catalog ──────────────────────────────────────────────────────────
# id|bins|vendor|functions|bypass_flags|proxy_notes|canary_cmd|refs
#
# functions include: Execute,Bypass,Proxy,CredAccess,Persist,Exfil,
# SandboxEscape,Tunnel,Remote
CATALOG_TSV=$(cat <<'EOF'
claude_code|claude|Anthropic|Execute,Bypass,Proxy,CredAccess,Persist,Exfil,SandboxEscape,Remote|--dangerously-skip-permissions,--permission-mode,--allow-dangerously-skip-permissions|--print headless; --remote-control; --background agents; --chrome; --add-dir expands trust|claude -p "{PROMPT}" --output-format text|https://docs.anthropic.com/en/docs/claude-code
codex|codex|OpenAI|Execute,Bypass,Proxy,CredAccess,Persist,Exfil|--full-auto,--dangerously-bypass-approvals-and-sandbox,--sandbox|Non-interactive exec; may pivot certutil/bitsadmin when downloads blocked|codex exec "{PROMPT}"|https://github.com/openai/codex
antigravity_cli|agy|Google|Execute,Bypass,Proxy,CredAccess,Persist,Exfil| |Terminal agent (Gemini CLI successor); shared harness with Antigravity IDE|agy -p "{PROMPT}"|https://antigravity.google/docs/cli/overview
gemini_cli|gemini|Google|Execute,Bypass,Proxy,CredAccess,Persist,Exfil|--yolo,--approval-mode|Headless -p; YOLO auto-approves all tools; MCP subcommand|gemini -p "{PROMPT}"|https://github.com/google-gemini/gemini-cli
cursor_agent|cursor-agent,agent|Anysphere|Execute,Bypass,Proxy,CredAccess,Persist,Exfil|--force,--yolo,--trust,--approve-mcps|--print headless with full tools; --cloud; companion tunnels below|cursor-agent -p "{PROMPT}" --print|https://cursor.com/docs
cursor_tunnel|cursor-tunnel|Anysphere|Tunnel,Proxy,Persist,Exfil| |Microsoft Dev Tunnels LOTL — signed companion under Cursor.app; remote access / sandbox breakout research (Straiker NomShub)| |https://cursor.com/docs
code_tunnel|code-tunnel|Microsoft/Anysphere|Tunnel,Proxy,Persist,Exfil| |VS Code remote tunnel binary bundled with Cursor; same Dev Tunnels protocol| |https://code.visualstudio.com/docs/remote/tunnels
cline_cli|cline|Cline|Execute,Bypass,Proxy,CredAccess,Persist,Exfil|--yolo,--auto-approve|Standalone CLI (npm: cline); headless/CI; MCP + hub/kanban|cline -y "{PROMPT}"|https://docs.cline.bot/cli/cli-reference
opencode|opencode|anomalyco|Execute,Bypass,Proxy,CredAccess,Persist,Exfil| |Model-agnostic TUI (75+ providers); distinct from Charm Crush|opencode run "{PROMPT}"|https://opencode.ai/docs/
pi|pi|Earendil|Execute,Bypass,Proxy,CredAccess,Exfil| |Minimal coding-agent harness; npm @earendil-works/pi-coding-agent|pi "{PROMPT}"|https://github.com/earendil-works/pi
plandex|plandex,pdx|Plandex|Execute,Bypass,Proxy,CredAccess,Persist,Exfil| |Multi-step / large-task CLI; WSL on Windows|plandex|https://docs.plandex.ai/
copilot_cli|copilot|GitHub|Execute,Bypass,Proxy,CredAccess,Persist,Exfil|--allow-all,--yolo,--allow-all-tools,--allow-all-paths,--allow-all-urls|GitHub MCP tools; --acp server mode|copilot -p "{PROMPT}"|https://docs.github.com/en/copilot
aider|aider|OSS|Execute,Bypass,Proxy,CredAccess,Exfil|--yes,--auto-commits|/run shell; CONVENTIONS.md injection|aider --yes --message "{PROMPT}"|https://aider.chat
amp|amp|Sourcegraph|Execute,Bypass,Proxy,CredAccess,Persist,Exfil|--dangerously-allow-all|Agentic coding CLI|amp -m "{PROMPT}"|
goose|goose|Block/AAIF|Execute,Bypass,Proxy,CredAccess,Persist,Exfil| |Recipe / non-interactive runs|goose run -t "{PROMPT}"|https://block.github.io/goose
crush|crush|Charmbracelet|Execute,Bypass,Proxy,CredAccess,Exfil| |TUI agent| |
continue_cli|cn|Continue|Execute,Bypass,Proxy,CredAccess,Persist,Exfil| |MCP-capable cn binary| |
factory_droid|droid|Factory.ai|Execute,Bypass,Proxy,CredAccess,Persist,Exfil| |Agentic CLI| |
kiro_cli|kiro-cli,q|AWS|Execute,Bypass,Proxy,CredAccess,Persist,Exfil| |Amazon Q / Kiro lineage| |
qwen_code|qwen|Alibaba|Execute,Bypass,Proxy,CredAccess,Exfil| |Terminal coding agent| |
openhands|openhands|All Hands|Execute,Bypass,Proxy,CredAccess,Persist,Exfil| |Docker/host.exec surfaces| |
jules|jules|Google|Execute,Bypass,Proxy,CredAccess,Exfil| |Autonomous coding CLI| |
gitlab_duo|duo|GitLab|Execute,Bypass,Proxy,CredAccess,Persist,Exfil| |Duo agentic CLI| |
codebuff|codebuff|Codebuff|Execute,Bypass,Proxy,CredAccess,Exfil| |Terminal coding agent| |
kilo|kilo|Kilo|Execute,Bypass,Proxy,CredAccess,Exfil| |Kilo Code CLI| |
pochi|pochi|TabbyML|Execute,Bypass,Proxy,CredAccess,Exfil| |Pochi agent CLI| |
qodo|qodo|Qodo|Execute,Bypass,Proxy,CredAccess,Exfil| |Qodo Command CLI| |
augment|auggie|Augment|Execute,Bypass,Proxy,CredAccess,Exfil| |Augment Code CLI| |
hermes|hermes|Nous|Execute,Bypass,Proxy,CredAccess,Persist,Exfil| |Hermes agent platform| |
genaiscript|genaiscript|Microsoft|Execute,Proxy,CredAccess,Exfil| |host.exec() outside Docker| |
llm_cli|llm|Simon Willison|Execute,Proxy,Exfil| |Plugin host| |
openai_cli|openai|OpenAI|Proxy,Exfil| |API console (lower agent surface)| |
EOF
)
# Classified token patterns (probe buckets)
BYPASS_REGEX='dangerously|skip-permission|skip.permission|yolo|auto-approve|auto.approve|full-auto|bypass.approval|bypass-approval|allow-all|no-sandbox|workspace-write|permission-mode|trust-all|--force[^-]|--trust[^-]|approve-mcp'
TUNNEL_REGEX='tunnel|remote-control|remote.control|dev.?tunnel|--cloud[^-]|port.forward|port-forward|expose|listen|sshtunnel'
MCP_REGEX='\bmcp\b|mcp-config|mcp.config|approve-mcp'
HEADLESS_REGEX='headless|non-interactive|--print[^-]|-p,|--prompt|background|--bare[^-]'
has_cmd() {
command -v "$1" >/dev/null 2>&1
}
bin_on_path() {
local bin="$1"
if has_cmd "$bin"; then
command -v "$bin"
return 0
fi
local cand
for cand in \
"$HOME/.local/bin/$bin" \
"$HOME/.claude/local/$bin" \
"$HOME/.npm-global/bin/$bin" \
"/opt/homebrew/bin/$bin" \
"/usr/local/bin/$bin" \
"/Applications/Cursor.app/Contents/Resources/app/bin/$bin" \
"/Applications/Antigravity.app/Contents/Resources/app/bin/$bin" \
"/Applications/Visual Studio Code.app/Contents/Resources/app/bin/$bin" \
"$HOME/AppData/Local/Programs/cursor/resources/app/bin/$bin.exe" \
"$HOME/AppData/Local/Programs/Cursor/resources/app/bin/$bin.exe"; do
if [[ -x "$cand" ]]; then
echo "$cand"
return 0
fi
done
return 1
}
parse_catalog_row() {
local row="$1"
IFS='|' read -r C_ID C_BINS C_VENDOR C_FUNCS C_BYPASS C_PROXY C_CANARY C_REFS <<<"$row"
}
row_matches_filter() {
local row="$1"
[[ -z "$TOOL_FILTER" ]] && return 0
parse_catalog_row "$row"
local f="${TOOL_FILTER,,}"
[[ "${C_ID,,}" == "$f" ]] && return 0
local b
IFS=',' read -ra _bins <<<"$C_BINS"
for b in "${_bins[@]}"; do
b="${b// /}"
[[ "${b,,}" == "$f" ]] && return 0
done
return 1
}
print_catalog() {
echo "${BLD}LOL-GAI catalog${RST} (v${VERSION})"
echo ""
printf "%-18s %-22s %-14s %s\n" "ID" "BINARIES" "VENDOR" "FUNCTIONS"
printf "%-18s %-22s %-14s %s\n" "------------------" "----------------------" "--------------" "----------------"
while IFS= read -r row; do
[[ -z "$row" || "$row" =~ ^# ]] && continue
row_matches_filter "$row" || continue
parse_catalog_row "$row"
printf "%-18s %-22s %-14s %s\n" "$C_ID" "$C_BINS" "$C_VENDOR" "$C_FUNCS"
[[ -n "${C_BYPASS// /}" ]] && dim " Bypass flags: $C_BYPASS"
[[ -n "${C_PROXY// /}" ]] && dim " Notes: $C_PROXY"
[[ -n "${C_REFS// /}" ]] && dim " Refs: $C_REFS"
done <<<"$CATALOG_TSV"
}
SCAN_IDS=()
SCAN_BINS=()
SCAN_PATHS=()
SCAN_VENDORS=()
SCAN_FUNCS=()
SCAN_BYPASS=()
SCAN_PROXY=()
SCAN_CANARY=()
SCAN_REFS=()
SCAN_HELP=()
SCAN_BYPASS_HITS=()
SCAN_TUNNEL_HITS=()
SCAN_MCP_HITS=()
SCAN_HEADLESS_HITS=()
scan_host() {
SCAN_IDS=(); SCAN_BINS=(); SCAN_PATHS=(); SCAN_VENDORS=()
SCAN_FUNCS=(); SCAN_BYPASS=(); SCAN_PROXY=(); SCAN_CANARY=()
SCAN_REFS=(); SCAN_HELP=()
SCAN_BYPASS_HITS=(); SCAN_TUNNEL_HITS=(); SCAN_MCP_HITS=(); SCAN_HEADLESS_HITS=()
info "Scanning PATH + common install / app-bundle dirs…"
local row found_bin found_path b
while IFS= read -r row; do
[[ -z "$row" || "$row" =~ ^# ]] && continue
row_matches_filter "$row" || continue
parse_catalog_row "$row"
found_bin=""; found_path=""
IFS=',' read -ra _bins <<<"$C_BINS"
for b in "${_bins[@]}"; do
b="${b// /}"
[[ -z "$b" ]] && continue
if found_path=$(bin_on_path "$b"); then
found_bin="$b"
break
fi
done
if [[ -n "$found_bin" ]]; then
ok "found ${BLD}$C_ID${RST} → $found_bin ($found_path)"
SCAN_IDS+=("$C_ID")
SCAN_BINS+=("$found_bin")
SCAN_PATHS+=("$found_path")
SCAN_VENDORS+=("$C_VENDOR")
SCAN_FUNCS+=("$C_FUNCS")
SCAN_BYPASS+=("$C_BYPASS")
SCAN_PROXY+=("$C_PROXY")
SCAN_CANARY+=("$C_CANARY")
SCAN_REFS+=("$C_REFS")
SCAN_HELP+=("")
SCAN_BYPASS_HITS+=("")
SCAN_TUNNEL_HITS+=("")
SCAN_MCP_HITS+=("")
SCAN_HEADLESS_HITS+=("")
else
dim "miss $C_ID ($C_BINS)"
fi
done <<<"$CATALOG_TSV"
echo ""
info "Installed matches: ${#SCAN_IDS[@]}"
}
run_with_timeout() {
local secs="$1"; shift
if has_cmd timeout; then
timeout --signal=KILL "$secs" "$@" 2>&1 || true
elif has_cmd gtimeout; then
gtimeout --signal=KILL "$secs" "$@" 2>&1 || true
else
local tmp pid waited
tmp="$(mktemp -t lolgai-to.XXXXXX)"
"$@" >"$tmp" 2>&1 &
pid=$!
waited=0
while kill -0 "$pid" 2>/dev/null; do
if [[ "$waited" -ge "$secs" ]]; then
kill -9 "$pid" 2>/dev/null || true
wait "$pid" 2>/dev/null || true
echo "[lolgai] timed out after ${secs}s: $*"
break
fi
sleep 1
waited=$((waited + 1))
done
wait "$pid" 2>/dev/null || true
cat "$tmp" 2>/dev/null || true
rm -f "$tmp"
fi
}
extract_bucket() {
# extract_bucket <regex> <file> → comma-separated unique matches
local regex="$1" file="$2"
grep -Eii -o -- "$regex" "$file" 2>/dev/null | sort -u | tr '\n' ',' | sed 's/,$//' || true
}
probe_helps() {
local i help_file
[[ ${#SCAN_IDS[@]} -eq 0 ]] && die "nothing to probe — run --scan first or no tools installed"
info "Probing --help / --version (classifying Bypass / Tunnel / MCP / Headless)…"
export PAGER=cat CI=1 NO_COLOR=1 TERM="${TERM:-dumb}"
for i in "${!SCAN_IDS[@]}"; do
local bin="${SCAN_BINS[$i]}"
local path="${SCAN_PATHS[$i]}"
local id="${SCAN_IDS[$i]}"
if [[ -n "$OUT_DIR" ]]; then
mkdir -p "$OUT_DIR/$id"
help_file="$OUT_DIR/$id/help.txt"
else
help_file="$(mktemp -t lolgai-help.XXXXXX)"
fi
{
echo "=== $bin --version ==="
run_with_timeout 8 "$path" --version
echo ""
echo "=== $bin --help ==="
run_with_timeout 8 "$path" --help
echo ""
echo "=== $bin -h ==="
run_with_timeout 8 "$path" -h
# Tunnel companions often use `tunnel --help` subcommand
if [[ "$id" == *tunnel* ]] || grep -Eiq 'Commands:|tunnel' "$help_file" 2>/dev/null; then
echo ""
echo "=== $bin tunnel --help ==="
run_with_timeout 8 "$path" tunnel --help
fi
} >"$help_file" 2>&1 || true
local bypass tunnel mcp headless flag cat_hit=""
bypass="$(extract_bucket "$BYPASS_REGEX" "$help_file")"
tunnel="$(extract_bucket "$TUNNEL_REGEX" "$help_file")"
mcp="$(extract_bucket "$MCP_REGEX" "$help_file")"
headless="$(extract_bucket "$HEADLESS_REGEX" "$help_file")"
IFS=',' read -ra _flags <<<"${SCAN_BYPASS[$i]}"
for flag in "${_flags[@]}"; do
flag="${flag// /}"
[[ -z "$flag" ]] && continue
if grep -Fqi -- "$flag" "$help_file" 2>/dev/null; then
cat_hit="${cat_hit}${cat_hit:+,}${flag}"
fi
done
[[ -n "$cat_hit" ]] && bypass="${bypass}${bypass:+,}${cat_hit}"
# Companion tunnel binaries: mark Tunnel even if help is sparse
if [[ "${SCAN_FUNCS[$i]}" == *Tunnel* && -z "$tunnel" ]]; then
tunnel="(binary present — Dev Tunnels / remote companion)"
fi
SCAN_HELP[$i]="$help_file"
SCAN_BYPASS_HITS[$i]="$bypass"
SCAN_TUNNEL_HITS[$i]="$tunnel"
SCAN_MCP_HITS[$i]="$mcp"
SCAN_HEADLESS_HITS[$i]="$headless"
echo ""
ok "$id ($bin)"
[[ -n "$bypass" ]] && warn " Bypass: $bypass"
[[ -n "$tunnel" ]] && warn " Tunnel: $tunnel"
[[ -n "$mcp" ]] && dim " MCP: $mcp"
[[ -n "$headless" ]] && dim " Headless: $headless"
[[ -z "$bypass$tunnel$mcp$headless" ]] && dim " (no classified tokens in help — see catalog notes)"
[[ -n "${SCAN_PROXY[$i]// /}" ]] && dim " Catalog: ${SCAN_PROXY[$i]}"
if [[ -z "$OUT_DIR" ]]; then
rm -f "$help_file"
SCAN_HELP[$i]=""
fi
done
}
json_escape() {
printf '%s' "$1" | sed 's/\\/\\\\/g; s/"/\\"/g'
}
write_reports() {
[[ -z "$OUT_DIR" ]] && return 0
mkdir -p "$OUT_DIR"
local md="$OUT_DIR/lolgai-report.md"
local json="$OUT_DIR/lolgai-report.json"
local i
{
echo "# LOL-GAI scan report"
echo ""
echo "- Host: \`$(hostname)\`"
echo "- Date: \`$(date -u +%Y-%m-%dT%H:%M:%SZ)\`"
echo "- Version: \`$VERSION\`"
echo "- Matches: \`${#SCAN_IDS[@]}\`"
echo ""
echo "## How to read this"
echo ""
echo "| Bucket | Meaning |"
echo "|--------|---------|"
echo "| **Bypass** | Flags that skip human approval / sandbox (YOLO, dangerously-skip-permissions, --force, --allow-all) |"
echo "| **Tunnel** | Remote access / Dev Tunnels / remote-control / cloud agent surfaces |"
echo "| **MCP** | Model Context Protocol config — supply-chain + tool injection surface |"
echo "| **Headless** | Non-interactive / print / background modes — easy to drive from scripts or malware |"
echo ""
echo "| ID | Binary | Path | Functions | Bypass | Tunnel | MCP | Headless |"
echo "|----|--------|------|-----------|--------|--------|-----|----------|"
for i in "${!SCAN_IDS[@]}"; do
echo "| ${SCAN_IDS[$i]} | ${SCAN_BINS[$i]} | \`${SCAN_PATHS[$i]}\` | ${SCAN_FUNCS[$i]} | ${SCAN_BYPASS_HITS[$i]:-_} | ${SCAN_TUNNEL_HITS[$i]:-_} | ${SCAN_MCP_HITS[$i]:-_} | ${SCAN_HEADLESS_HITS[$i]:-_} |"
done
echo ""
echo "## Per-tool notes"
echo ""
for i in "${!SCAN_IDS[@]}"; do
echo "### ${SCAN_IDS[$i]}"
echo ""
echo "- Vendor: ${SCAN_VENDORS[$i]}"
echo "- Catalog Bypass flags: \`${SCAN_BYPASS[$i]:-}\`"
echo "- Notes: ${SCAN_PROXY[$i]}"
echo "- Refs: ${SCAN_REFS[$i]}"
echo ""
done
echo "## Defender hunts (starting points)"
echo ""
echo "1. GenAI CLI parent (\`claude\`, \`codex\`, \`cursor-agent\`, \`agy\`, …) spawning shell / LOLBins"
echo "2. \`cursor-tunnel\` / \`code-tunnel\` establishing Dev Tunnels outside IT-approved remote access"
echo "3. \`claude --remote-control\` or background agents on endpoints that should be interactive-only"
echo "4. Invocation with Bypass flags from this report"
echo "5. Canary file \`LOLGAI_CANARY.txt\` as a positive control"
echo ""
} >"$md"
ok "wrote $md"
{
echo "{"
echo " \"version\": \"$VERSION\","
echo " \"host\": \"$(json_escape "$(hostname)")\","
echo " \"generated_at\": \"$(date -u +%Y-%m-%dT%H:%M:%SZ)\","
echo " \"tools\": ["
for i in "${!SCAN_IDS[@]}"; do
local comma=","
[[ $i -eq $((${#SCAN_IDS[@]} - 1)) ]] && comma=""
printf ' {"id":"%s","bin":"%s","path":"%s","vendor":"%s","functions":"%s","bypass":"%s","tunnel":"%s","mcp":"%s","headless":"%s","notes":"%s"}%s\n' \
"$(json_escape "${SCAN_IDS[$i]}")" \
"$(json_escape "${SCAN_BINS[$i]}")" \
"$(json_escape "${SCAN_PATHS[$i]}")" \
"$(json_escape "${SCAN_VENDORS[$i]}")" \
"$(json_escape "${SCAN_FUNCS[$i]}")" \
"$(json_escape "${SCAN_BYPASS_HITS[$i]}")" \
"$(json_escape "${SCAN_TUNNEL_HITS[$i]}")" \
"$(json_escape "${SCAN_MCP_HITS[$i]}")" \
"$(json_escape "${SCAN_HEADLESS_HITS[$i]}")" \
"$(json_escape "${SCAN_PROXY[$i]}")" \
"$comma"
done
echo " ]"
echo "}"
} >"$json"
ok "wrote $json"
if [[ "$DO_JSON" == true ]]; then
cat "$json"
fi
}
run_canary() {
[[ ${#SCAN_IDS[@]} -eq 0 ]] && die "canary needs a scanned tool — use --scan --tool <id>"
[[ ${#SCAN_IDS[@]} -gt 1 ]] && die "canary requires exactly one tool — pass --tool <id>"
local i=0
local id="${SCAN_IDS[$i]}"
local bin="${SCAN_BINS[$i]}"
local tmpl="${SCAN_CANARY[$i]}"
[[ -z "${tmpl// /}" ]] && die "no canary template for $id"
local work
if [[ -n "$OUT_DIR" ]]; then
work="$OUT_DIR/$id/workspace"
else
work="$(mktemp -d -t lolgai-canary.XXXXXX)"
fi
mkdir -p "$work"
local prompt="Create a file named LOLGAI_CANARY.txt in the current working directory containing only the line: LOL-GAI canary ok. Do not read secrets, do not modify other files, then stop."
if [[ "$YES" != true ]]; then
warn "About to invoke ${BLD}$bin${RST} in $work (may use API quota)"
read -r -p "Continue? [y/N] " ans
[[ "${ans,,}" == "y" || "${ans,,}" == "yes" ]] || die "aborted"
fi
local cmd="${tmpl//\{PROMPT\}/$prompt}"
cmd="${cmd//\{WORKDIR\}/$work}"
info "canary cwd=$work"
dim "cmd: $cmd"
local out="${OUT_DIR:+$OUT_DIR/$id/}canary_stdout.txt"
(
cd "$work"
# shellcheck disable=SC2086
eval "$cmd"
) | tee "${out:-/dev/null}" || warn "canary exited non-zero"
if [[ -f "$work/LOLGAI_CANARY.txt" ]]; then
ok "canary file present: $work/LOLGAI_CANARY.txt"
cat "$work/LOLGAI_CANARY.txt"
else
warn "canary file not found — auth / approval may be required"
fi
}
while [[ $# -gt 0 ]]; do
case "$1" in
--catalog) DO_CATALOG=true; shift ;;
--scan) DO_SCAN=true; shift ;;
--probe) DO_PROBE=true; shift ;;
--canary) DO_CANARY=true; shift ;;
--tool) TOOL_FILTER="${2:-}"; [[ -n "$TOOL_FILTER" ]] || die "--tool needs a value"; shift 2 ;;
--out) OUT_DIR="${2:-}"; [[ -n "$OUT_DIR" ]] || die "--out needs a dir"; shift 2 ;;
--json) DO_JSON=true; shift ;;
--yes) YES=true; shift ;;
--allow-dangerous) ALLOW_DANGEROUS=true; shift ;;
--no-color) NO_COLOR=true; shift ;;
--version) echo "$VERSION"; exit 0 ;;
-h|--help) usage; exit 0 ;;
*) die "unknown arg: $1 (try --help)" ;;
esac
done
setup_colors
if [[ "$DO_CATALOG" != true && "$DO_SCAN" != true && "$DO_CANARY" != true ]]; then
DO_CATALOG=true
DO_SCAN=true
fi
[[ "$DO_PROBE" == true && "$DO_SCAN" != true ]] && DO_SCAN=true
[[ "$DO_CANARY" == true && "$DO_SCAN" != true ]] && DO_SCAN=true
echo "${BLD}LOL-GAI${RST} v${VERSION} — Living Off Legitimate GenAI Agents"
[[ "$ALLOW_DANGEROUS" == true ]] && warn "--allow-dangerous: Bypass flags emphasized (canaries still refuse bypass modes)"
echo ""
if [[ "$DO_CATALOG" == true && "$DO_SCAN" != true ]]; then
print_catalog
fi
if [[ "$DO_SCAN" == true ]]; then
[[ "$DO_CATALOG" == true ]] && { print_catalog; echo ""; }
scan_host
if [[ "$DO_PROBE" == true ]]; then
echo ""
probe_helps
fi
[[ -n "$OUT_DIR" ]] && { echo ""; write_reports; }
fi
if [[ "$DO_CANARY" == true ]]; then
echo ""
run_canary
fi
echo ""
dim "Research only. Prefer isolated VMs. Do not weaponize."
name lolgai
description Enumerate GenAI coding CLI abuse surfaces (LOLBin-style): scan installed agents, probe dangerous flags, run safe canaries for detection validation.
metadata
author version visibility
Mikaayenson
0.1.0
public

LOL-GAI — Living Off Legitimate GenAI Agents

Treat popular GenAI CLIs as living-off-the-land binaries: trusted, signed, already on PATH, with shell / file / network tools that threat actors can drive via prompt injection, malicious repos, or stolen sessions.

When to use

  • Mapping abuse opportunities on lab VMs after installing GenAI CLIs
  • Writing or validating detections for agent → LOLBin / credential / persistence chains
  • Publishing or extending a public catalog of GenAI CLI Bypass / Proxy surfaces
  • Safe telemetry generation (canary marker file only)

Workflow

1. Inventory

bash lolgai.sh --scan --probe --out ./lolgai-out

Read lolgai-out/lolgai-report.md. Note which tools are installed and which risky help tokens appeared (dangerously, yolo, full-auto, …).

2. Focus one binary

bash lolgai.sh --tool claude --scan --probe --out ./lolgai-out
bash lolgai.sh --tool agy --scan --probe --out ./lolgai-out

Document for each hit:

Field What to capture
Binary + path Attribution (process.name, signing_id)
Functions Execute / Bypass / Proxy / CredAccess / Persist / Exfil / SandboxEscape
Bypass flags Exact argv from --help
Parent→child Shell, certutil, bitsadmin, cmdkey, PowerShell, Python
Config / hooks MCP JSON, skills dirs, hooks that auto-approve

3. Safe canary (opt-in)

bash lolgai.sh --tool claude --canary --yes --out ./lolgai-out

Success = LOLGAI_CANARY.txt in the tool workspace. Use that path as a positive control for endpoint rules. Do not ask the agent to read credentials, decrypt browser stores, or write Startup folder payloads in public demos.

4. Detection engineering hooks

Translate findings into hunts / rules:

  • GenAI CLI parent + classic LOLBin download (certutil, bitsadmin, curl|bash)
  • GenAI CLI parent + credential store access (cmdkey, DPAPI, Login Data)
  • Invocation with catalog Bypass flags
  • MCP / hooks config modified by package managers or unexpected writers

Prefer parent-aware logic over blanket allowlists of agent binaries.

Extending the catalog

Edit the CATALOG_TSV block in lolgai.sh. One pipe-delimited row:

id|bins|vendor|functions|bypass_flags|proxy_notes|canary_cmd|refs

Rules:

  • Empirically verify binary names on macOS, Linux, and Windows when possible
  • Only list Bypass flags that appear in vendor --help or official docs
  • Canary templates must write LOLGAI_CANARY.txt only — no secret access
  • Keep refs to official docs or reputable research

Companion tooling

  • GenAI installer gist: install CLIs onto lab VMs (--cli)
  • Registry lab bash tool: install / run / validate process telemetry
  • Elastic TRADEmark / detection-rules: production rule homes (not required for public gist)

Constraints

  • Authorized lab hosts only
  • Default modes are read-only
  • Never ship weaponized credential-theft or persistence prompts in the public skill
  • Call out when Sophos / vendor research shows behavioral overlap with attackers — detections may be true positives on agent behavior, not false positives
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment