From e2d851ecd39b872e0d00c367e4c080bdda4a16b8 Mon Sep 17 00:00:00 2001 From: Caleb Stewart Date: Fri, 28 Aug 2020 21:38:56 -0400 Subject: [PATCH] Added search and info commands for modules --- data/pwncatrc | 14 +++---- pwncat/commands/info.py | 57 ++++++++++++++++++++++++++++ pwncat/commands/search.py | 39 +++++++++++++++++++ pwncat/modules/__init__.py | 1 + pwncat/modules/enumerate/__init__.py | 8 +++- pwncat/modules/enumerate/caps.py | 2 +- pwncat/modules/enumerate/quick.py | 8 +++- pwncat/modules/enumerate/report.py | 24 +++++++++--- 8 files changed, 137 insertions(+), 16 deletions(-) create mode 100644 pwncat/commands/info.py create mode 100644 pwncat/commands/search.py diff --git a/data/pwncatrc b/data/pwncatrc index 79eadf9..8cd584d 100644 --- a/data/pwncatrc +++ b/data/pwncatrc @@ -1,15 +1,15 @@ # Set your remote hosts file -set lhost "127.0.0.1" +set -g lhost "127.0.0.1" # Set your command prefix -set prefix c-k +set -g prefix c-k # Set the default private key to use for privilege escalation -set privkey "data/pwncat" +set -g privkey "data/pwncat" # Set the pwncat backdoor user and password -set backdoor_user "pwncat" -set backdoor_pass "pwncat" -set db "sqlite:///pwncat.sqlite" +set -g backdoor_user "pwncat" +set -g backdoor_pass "pwncat" +set -g db "sqlite:///pwncat.sqlite" -set on_load { +set -g on_load { # Run a command upon a stable connection # privesc -l } diff --git a/pwncat/commands/info.py b/pwncat/commands/info.py new file mode 100644 index 0000000..e08e946 --- /dev/null +++ b/pwncat/commands/info.py @@ -0,0 +1,57 @@ +#!/usr/bin/env python3 +import textwrap + +from rich.table import Table +from rich import box + +import pwncat +from pwncat.commands.base import CommandDefinition, Complete, Parameter +from pwncat.util import console + + +class Command(CommandDefinition): + """ View info about a module """ + + def get_module_choices(self): + yield from [module.name for module in pwncat.modules.match(".*")] + + PROG = "info" + ARGS = { + "module": Parameter( + Complete.CHOICES, + choices=get_module_choices, + metavar="MODULE", + help="The module to view information on", + nargs="?", + ) + } + + def run(self, args): + + if not args.module and pwncat.victim.config.module is None: + console.log("[red]error[/red]: no module specified") + return + + if args.module: + try: + module = pwncat.modules.find(args.module) + except KeyError: + console.log(f"[red]error[/red]: {args.module}: no such module") + return + else: + module = pwncat.victim.config.module + + console.print(f"[bold underline]Module {module.name}[/bold underline]") + console.print( + textwrap.indent(textwrap.dedent(module.__doc__.strip("\n")), " ") + "\n" + ) + + table = Table("Argument", "Default", "Help", box=box.MINIMAL_DOUBLE_HEAD) + for arg, info in module.ARGUMENTS.items(): + if info.default is pwncat.modules.NoValue: + default = "" + else: + default = info.default + table.add_row(arg, str(default), info.help) + + console.print(table) diff --git a/pwncat/commands/search.py b/pwncat/commands/search.py new file mode 100644 index 0000000..6f00409 --- /dev/null +++ b/pwncat/commands/search.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python3 +import textwrap + +from rich.table import Table, Column +from rich import box + +import pwncat +from pwncat.commands.base import CommandDefinition, Complete, Parameter +from pwncat.util import console + + +class Command(CommandDefinition): + """ View info about a module """ + + def get_module_choices(self): + yield from [module.name for module in pwncat.modules.match(".*")] + + PROG = "search" + ARGS = {"module": Parameter(Complete.NONE, help="Regular Expression Pattern",)} + + def run(self, args): + + table = Table( + Column(0, header="Name", ratio=0.2), + Column(1, header="Description", no_wrap=True, ratio=0.8), + title="Results", + box=box.MINIMAL_DOUBLE_HEAD, + expand=True, + ) + + for module in pwncat.modules.match(args.module): + table.add_row( + module.name, + textwrap.shorten( + module.__doc__.replace("\n", " "), width=200, placeholder="..." + ), + ) + + console.print(table) diff --git a/pwncat/modules/__init__.py b/pwncat/modules/__init__.py index 81ad335..275982d 100644 --- a/pwncat/modules/__init__.py +++ b/pwncat/modules/__init__.py @@ -35,6 +35,7 @@ class Argument: type: Callable[[str], Any] = str default: Any = NoValue + help: str = "" def List(_type=str): diff --git a/pwncat/modules/enumerate/__init__.py b/pwncat/modules/enumerate/__init__.py index e6916af..7f913c8 100644 --- a/pwncat/modules/enumerate/__init__.py +++ b/pwncat/modules/enumerate/__init__.py @@ -30,7 +30,13 @@ class EnumerateModule(BaseModule): # Arguments which all enumeration modules should take # This shouldn't be modified. Enumeration modules don't take any # parameters - ARGUMENTS = {"types": Argument(List(str), default=[])} + ARGUMENTS = { + "types": Argument( + List(str), + default=[], + help="A list of enumeration types to retrieve (default: all)", + ) + } def run(self, types): """ Locate all facts this module provides. diff --git a/pwncat/modules/enumerate/caps.py b/pwncat/modules/enumerate/caps.py index 81ad46b..31e6950 100644 --- a/pwncat/modules/enumerate/caps.py +++ b/pwncat/modules/enumerate/caps.py @@ -23,7 +23,7 @@ class FileCapabilityData: class Module(EnumerateModule): - """ Enumerate SUID binaries on the remote host """ + """ Enumerate capabilities of the binaries of the remote host """ PROVIDES = ["file.caps"] diff --git a/pwncat/modules/enumerate/quick.py b/pwncat/modules/enumerate/quick.py index e05d304..7e3ba7d 100644 --- a/pwncat/modules/enumerate/quick.py +++ b/pwncat/modules/enumerate/quick.py @@ -7,9 +7,13 @@ from pwncat.modules import BaseModule, Status, Argument class Module(BaseModule): """ Perform a quick enumeration of common useful data """ - ARGUMENTS = {"output": Argument(str, default=None)} + ARGUMENTS = { + "output": Argument( + str, default=None, help="Path a to file to write a markdown report" + ) + } def run(self, output): - return next(pwncat.modules.match("enumerate.report")).run( + return pwncat.modules.find("enumerate.report").run( types=["file.suid", "file.caps"], output=output ) diff --git a/pwncat/modules/enumerate/report.py b/pwncat/modules/enumerate/report.py index 2888f45..1c8f020 100644 --- a/pwncat/modules/enumerate/report.py +++ b/pwncat/modules/enumerate/report.py @@ -35,13 +35,27 @@ def FileType(mode: str = "r"): class Module(pwncat.modules.BaseModule): - """ Perform multiple enumeration modules and write a formatted - report to the filesystem. """ + """ + Perform multiple enumeration modules and write a formatted + report to the filesystem. + """ ARGUMENTS = { - "output": pwncat.modules.Argument(FileType("w"), default=None), - "modules": pwncat.modules.Argument(pwncat.modules.List(str), default=[".*"]), - "types": pwncat.modules.Argument(pwncat.modules.List(str), default=[]), + "output": pwncat.modules.Argument( + FileType("w"), + default=None, + help="The file to write a markdown report to (default: stdout)", + ), + "modules": pwncat.modules.Argument( + pwncat.modules.List(str), + default=[".*"], + help="List of modules to run (default: all)", + ), + "types": pwncat.modules.Argument( + pwncat.modules.List(str), + default=[], + help="List of enumeration types to collect (default: all)", + ), } def run(self, output, modules, types):