Command Parser¶
The local pwncat
prompt and scripting configuration language are powered by the CommandParser
class which is responsible for parsing lines of text, extracting arguments, and dispatching them
to the appropriate command.
Commands are loaded automatically through the pkgutils
module in Python from the pwncat/commands
directory. Every Python file from this directory is loaded as a module and checked for a Command
attribute. This attribute must be a class which inherits from the pwncat.commands.base.CommandDefinition
class. This class defines the structure of a command and allows the CommandParser
to intelligently
create argparse
objects, syntax highlighting lexers, and prompt_toolkit
completers for your
commands.
To create a new command, simply create a python file under the pwncat/commands
directory. The name
can be anything that conforms to python module naming standards. As an example, the sysinfo
command
is fairly straightforward:
#!/usr/bin/env python3
from pwncat.commands.base import CommandDefinition, Complete, Parameter
from pwncat.util import console
import pwncat
class Command(CommandDefinition):
"""
Display remote system information including host ID, IP address,
architecture, kernel version, distribution and init system. This
command also provides the capability to view installed services
if the init system is supported by ``pwncat``.
"""
PROG = "sysinfo"
ARGS = {
"--services,-s": Parameter(
Complete.NONE, action="store_true", help="List all services and their state"
)
}
def run(self, args):
if args.services:
for service in pwncat.victim.services:
if service.running:
console.print(
f"[green]{service.name}[/green] - {service.description}"
)
else:
console.print(f"[red]{service.name}[/red] - {service.description}")
else:
console.print(f"Host ID: [cyan]{pwncat.victim.host.hash}[/cyan]")
console.print(
f"Remote Address: [green]{pwncat.victim.client.getpeername()}[/green]"
)
console.print(f"Architecture: [red]{pwncat.victim.host.arch}[/red]")
console.print(f"Kernel Version: [red]{pwncat.victim.host.kernel}[/red]")
console.print(f"Distribution: [red]{pwncat.victim.host.distro}[/red]")
console.print(f"Init System: [blue]{pwncat.victim.host.init}[/blue]")
This is a simple command that will print system information from the host database and provide
the capability to view installed services, provided the init system is supported. This command also shows a
basic example of interacting with the remote victim. The pwncat.victim
object allows you to
interact abstractly with the currently connected victim. The LOCAL
property tells the CommandParser
whether this command operates only on local resources. If set to true, the command will be allowed
to run prior to a connected victim. In this case, we interact directly with the victim, and therefore
set the LOCAL
property to false.
Command Arguments¶
Argument parsing is achieved using the python built-in module argparse
. The parser is automatically
created based on the ARGS
, and DEFAULTS
dictionaries defined in your Command
class.
DEFAULTS
is a dictionary mapping argument names to default values. This is passed directly to
the argparse.ArgumentParser.set_defaults
method. This allows you to set defaults for values which
can’t be set in the argument definition (such as values referenced in multiple arguments with
dest parameter).
The ARGS
property is a dictionary which matches argument names to the parameter
objects.
The key for this dictionary is a string representing the a comma-separated list of parameter
names (e.g. “–param,-p”). The values in this dictionary are built from the parameter
method
imported above:
def parameter(complete, token=Name.Label, *args, **kwargs):
The first parameter is one of the pwncat.commands.base.Complete
enumeration items. Which includes
things like Complete.REMOTE_FILE
, Complete.LOCAL_FILE
, Complete.CHOICES
and Complete.NONE
.
For parameters with no argument (“switches”), this should be Complete.NONE
. This controls how
the CommandParser tab-completes your command at the local prompt.
The second parameter is the Pygments token which this option should be highlighted with. Normally,
you can leave this as default, but you may change it if you like. The remaining arguments are passed
directly to argparse.ArgumentParser.add_argument
.
Command Helpers¶
-
class
pwncat.commands.base.
Complete
¶ Command argument completion options
-
CHOICES
= 1¶ Complete argument from the list of choices specified in
parameter
-
LOCAL_FILE
= 2¶ Complete argument as a local file path
-
NONE
= 4¶ Do not provide argument completions
-
REMOTE_FILE
= 3¶ Complete argument as a remote file path
-
-
pwncat.commands.base.
parameter
(complete, token=Token.Name.Label, *args, **kwargs)¶ Generate a parameter definition from completer options, token definition, and argparse add_argument options.
Parameters: - complete (Complete) – the completion type
- token (Pygments Token) – the Pygments token to highlight this argument with
- args – positional arguments for
add_argument
- kwargs – keyword arguments for
add_argument
Returns: Parameter definition
-
class
pwncat.commands.base.
StoreConstOnce
(option_strings, dest, nargs=None, const=None, default=None, type=None, choices=None, required=False, help=None, metavar=None)¶ Only allow the user to store a value in the destination once. This prevents users from selection multiple actions in the privesc parser.
-
pwncat.commands.base.
StoreForAction
(action: List[str]) → Callable¶ Generates a custom argparse Action subclass which verifies that the current selected “action” option is one of the provided actions in this function. If not, an error is raised.
-
pwncat.commands.base.
RemoteFileType
(file_exist=True, directory_exist=False)¶
CommandDefinition Object¶
-
class
pwncat.commands.base.
CommandDefinition
¶ Generic structure for a local command. The docstring for your command class becomes the long-form help for your command.
-
ARGS
= {}¶ A dictionary of parameter definitions created with the
Parameter
class. If this is None, your command will receive the raw argument string and no processing will be done except removing the leading command name.
-
DEFAULTS
= {}¶ A dictionary of default values (passed directly to
ArgumentParser.set_defaults
)
-
GROUPS
= {}¶ A dictionary mapping group definitions to group names. The parameters to Group are passed directly to either add_argument_group or add_mutually_exclusive_group with the exception of the mutex arg, which determines the group type.
-
LOCAL
= False¶ Whether this command is purely local or requires an connected remote host
-
PROG
= 'unimplemented'¶ The name of your new command
-
build_parser
(parser: argparse.ArgumentParser, args: Dict[str, pwncat.commands.base.Parameter], group_defs: Dict[str, pwncat.commands.base.Group])¶ Parse the ARGS and DEFAULTS dictionaries to build an argparse ArgumentParser for this command. You should not need to overload this.
Parameters: - parser – the parser object to add arguments to
- args – the ARGS dictionary
-
run
(args)¶ This is the “main” for your new command. This should perform the action represented by your command.
Parameters: args – the argparse Namespace containing your parsed arguments
-