Plugin Development Guide
This guide explains how to extend pwninit using custom plugins. Plugins allow you to register custom actions under two lifecycle roles: providing challenge assets (e.g., fetching files over network targets) and setting up target environments (e.g., adding customization templates or Docker dependencies).
Architecture Lifecycle
The pwninit plugin manager automatically loads python scripts from your configuration folder at ~/.config/pwninit/plugins/.
When designing your plugin, you can choose to implement either one or both functional methods depending on where you want to hook into the runtime lifecycle:
provide(self, args, path): Executes first. It handles asset ingestion. The framework passes the current working directoryPathas its environment argument.setup(self, args, bins): Executes last (after binaries are sorted, patched, and Docker containers build). The framework passes the parsedsorted_binsdictionary as its environment argument.
Core Plugin API Template
Every custom extension must expose a class exactly named Plugin inheriting from the base architecture. Command-line flags are configured via list arrays using the helper utility arg().
Here is the exact developer blueprint for a clean, minimal plugin:
from pwninit.plugins import Plugin, arg
from pwn import log
class Plugin(Plugin):
name = "my_plugin"
description = "A clean minimal blueprint for custom pwninit plugins."
# Define CLI parameters parsing rules for 'pwninit -p my_plugin ...'
provide_args = [
arg("target_id", help="An implicit positional parameter"),
arg("--force", help="Toggle operational behavior modifiers", action="store_true")
]
# Define CLI parameters parsing rules for 'pwninit -s my_plugin ...'
setup_args = [
arg("--port", help="Network tracking port setup override", default="1337")
]
def provide(self, args, path):
"""Executed during the asset provisioning phase.
Args:
args: An argparse Namespace containing validated variables.
path (Path): A pathlib.Path object pointing to the output directory workspace.
Returns:
Path: The directory containing your new challenge files. If a custom Path
is returned, pwninit pivots its binary analysis lookup to that folder!
"""
log.info(f"[{self.name}] Fetching assets for target: {args.target_id}")
# Implement custom file generation, network requests, or downloads here.
return path
def setup(self, args, bins):
"""Executed during the final environment/utility initialization step.
Args:
args: An argparse Namespace containing validated variables.
bins (dict): A dictionary populated with sorted file mappings:
{"libc": [...], "ld": [...], "challs": [...], "libs": [...]}
Returns:
dict: Key/Value pairs of file names and string contents.
pwninit will automatically write these out as output files!
"""
log.info(f"[{self.name}] Initializing post-patch system adjustments on port: {args.port}")
extra_output_files = {}
if bins.get("challs"):
target_binary = bins["challs"][0]
log.info(f"[{self.name}] Hooking configurations into target: {target_binary}")
# Example: Generating a localized runner or configuration block
extra_output_files["gdb_script.ini"] = f"file {target_binary}\nb target remote :{args.port}\n"
return extra_output_files
Argument Format Configurations
The arg(name, kwargs) decorator mirrors parameters directly into the initialization layer of Python’s standard argparse.ArgumentParser.add_argument engine.
Common Mappings Cheat-Sheet:
- Positionals:
arg("variable_name", help="...") - Optional Flags:
arg("--force", action="store_true") - Strict Options Selector:
arg("--arch", choices=["x86", "amd64"], default="amd64") - Typed Input Parsing:
arg("--timeout", type=int, default=30)
Deploying and Running Extensions
To activate your script, place your file directly inside your user configuration workspace directory:
mkdir -p ~/.config/pwninit/plugins/
cp my_plugin.py ~/.config/pwninit/plugins/
Discovery and Execution Syntax
To print out a structural overview of all locally resolved plugin commands:
pwninit --list-plugins
To run your plugin as an automated asset downloader component:
pwninit -p my_plugin "CHALLENGE_01" --force
To run your plugin as a post-processing scaffolding environment engine:
pwninit -s my_plugin --port 4444