JSON Structure for Defining .tool Files in LazyOwn: Complete Schema Guide

LazyOwn .tool files require a strict JSON object containing four mandatory fields—toolname, command, trigger, and active—where the command string supports dynamic placeholders like {ip}, {port}, and {outputdir} for runtime substitution.

LazyOwn treats every external utility—from Nmap scripts to web fuzzers—as a modular .tool file defined in JSON format. Understanding the exact JSON structure for defining .tool files in LazyOwn is essential for extending the framework with custom reconnaissance or exploitation utilities, as the pwntomate.py engine strictly enforces this schema during scan execution.

Core JSON Schema for LazyOwn .tool Files

Every .tool file must contain a single JSON object with exactly four required keys. The parser in main/pwntomate.py raises a KeyError if any field is missing, aborting execution immediately.

Required Fields

  • toolname (string): Human-readable identifier used to construct output filenames. The engine saves results to {outputdir}/{toolname}.txt.
  • command (string): The shell command to execute. Must include placeholders for dynamic values like target IPs and ports.
  • trigger (array of strings): Service names (as reported by Nmap) that activate this tool. Use ["all"] to run against any service.
  • active (boolean): Enable or disable the tool. Inactive tools (false) are ignored during scans.

Available Placeholders for Dynamic Command Construction

The command field supports string substitution using Python's str.replace method before execution. Valid placeholders include:

  • {ip} — Target IP address
  • {port} — Target service port
  • {domain} — Target domain name
  • {outputdir} — Base results directory (args.basedir)
  • {toolname} — Value of the toolname field
  • {s} — Inserts s for HTTPS or empty string for HTTP
  • {username} / {password} — Credentials from sessions/credentials.txt
  • {usrwordlist} / {dirworlist} — Wordlist paths from main/payload.json
  • {ext} / {nameserver} — Domain components for DNS tools

Real-World .tool File Examples from the Repository

Web Fuzzing with ffuf.tool

Located at main/tools/ffuf.tool:

{
    "toolname": "ffuf_tool",
    "command": "ffuf -u http{s}://{ip}:{port}/FUZZ -w /usr/share/wordlists/dirb/common.txt -mc 200,204,301,302,307,401 -o {outputdir}/{toolname}.txt",
    "trigger": ["http", "https", "http-mgmt", "http-alt"],
    "active": true
}

Kerberos Enumeration with kerbrute.tool

Located at main/tools/kerbrute.tool:

{
    "toolname": "kerbrute_tool_user",
    "command": "kerbrute userenum --dc {ip} -d {domain} -t 20 /usr/share/wordlists/SecLists-master/Usernames/xato-net-10-million-usernames.txt | tee {outputdir}/{toolname}.txt",
    "trigger": ["kerberos-sec"],
    "active": true
}

DNS Enumeration with dnsenum.tool

Located at main/tools/dnsenum.tool:

{
    "toolname": "dns_enum_tool",
    "command": "dnsenum --dnsserver {ip} --enum {domain} {dnswordlist} > {outputdir}/dnsenum.txt",
    "trigger": ["domain"],
    "active": true
}

Custom Exploit Wrapper (Inactive)

Located at main/tools/bigbang.tool:

{
    "toolname": "bigbang",
    "command": "cd /home/grisun0/src/scripts ; python3 exploit_bigbang.py | tee {outputdir}/{toolname}.txt",
    "trigger": ["http", "https", "http-mgmt", "http-alt"],
    "active": false
}

How LazyOwn Processes .tool Files

The execution logic resides in main/pwntomate.py. During a scan, the engine:

  1. Iterates through all .tool files in main/tools/
  2. Parses the JSON and validates the four required fields
  3. Checks if active is true and if the detected service matches any value in trigger
  4. Performs string substitution on command using the placeholder mapping
  5. Executes the final shell command and saves output to {outputdir}/{toolname}.txt

Optional runtime variables like {usrwordlist} and {dirworlist} are loaded from main/payload.json, while credentials ({username}, {password}) are sourced from sessions/credentials.txt when available.

Step-by-Step Guide to Creating Custom .tool Files

Follow this checklist when adding new tools to LazyOwn:

  1. Create the JSON file in main/tools/ with a .tool extension.
  2. Include all four mandatory fields: toolname, command, trigger, active.
  3. Use valid placeholders from the supported list only; unsupported variables will remain as literal text.
  4. Set active: true to enable automatic execution during scans.
  5. Define accurate triggers using Nmap service names (e.g., http, ssh, microsoft-ds, kerberos-sec).
  6. Test the command manually by replacing placeholders with real values before committing the file.

When the scanner runs, it will automatically pick up any new, active .tool file, inject the proper values, and execute the command only when the service matches a trigger.

Summary

  • LazyOwn .tool files use a strict four-field JSON schema: toolname, command, trigger, and active.
  • The command field supports dynamic placeholders like {ip}, {port}, and {outputdir} for runtime substitution.
  • Tools execute only when their trigger array matches the detected Nmap service name and active is set to true.
  • The parsing engine in main/pwntomate.py validates the schema and performs string replacement before shell execution.
  • Custom tools require testing with literal values before deployment to ensure placeholder alignment.

Frequently Asked Questions

What happens if a required field is missing from a .tool file?

The LazyOwn parser in main/pwntomate.py will raise a KeyError and abort execution. All four fields—toolname, command, trigger, and active—must be present for the tool to load correctly.

Can I use custom placeholders not listed in the documentation?

No. The engine only substitutes known placeholders like {ip}, {port}, and {domain}. Any custom variables will remain as literal text in the final command, likely causing execution errors or unexpected behavior.

How do I run a tool against every service regardless of the port?

Set the trigger array to ["all"]. This special value instructs the engine to execute the command for any detected service, bypassing the normal service-name matching logic in pwntomate.py.

Where does LazyOwn save the output from .tool executions?

Results are written to {outputdir}/{toolname}.txt, where {outputdir} maps to the base directory specified by args.basedir and {toolname} is the value defined in the tool's JSON configuration.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →