コード例 #1
0
ファイル: alias.py プロジェクト: trevorbryant/pwncat
class Command(CommandDefinition):
    """ Alias an existing command with a new name. Specifying no alias or command
    will list all aliases. Specifying an alias with no command will remove the 
    alias if it exists. """
    def get_command_names(self):
        return [c.PROG for c in pwncat.victim.command_parser.commands]

    PROG = "alias"
    ARGS = {
        "alias":
        parameter(Complete.NONE, help="name for the new alias", nargs="?"),
        "command":
        parameter(
            Complete.CHOICES,
            metavar="COMMAND",
            choices=get_command_names,
            help="the command the new alias will use",
            nargs="?",
        ),
    }
    LOCAL = True

    def run(self, args):
        if args.alias is None:
            for name, command in pwncat.victim.command_parser.aliases.items():
                print(f" {Fore.CYAN}{name}{Fore.RESET} \u2192 "
                      f"{Fore.YELLOW}{command.PROG}{Fore.RESET}")
        elif args.command is not None:
            # This is safe because of "choices" in the argparser
            pwncat.victim.command_parser.aliases[args.alias] = [
                c for c in pwncat.victim.command_parser.commands
                if c.PROG == args.command
            ][0]
        else:
            del pwncat.victim.command_parser.aliases[args.alias]
コード例 #2
0
ファイル: download.py プロジェクト: trevorbryant/pwncat
class Command(CommandDefinition):
    """ Download a file from the remote host to the local host"""

    PROG = "download"
    ARGS = {
        "source": parameter(Complete.REMOTE_FILE),
        "destination": parameter(Complete.LOCAL_FILE),
    }

    def run(self, args):

        try:
            length = pwncat.victim.get_file_size(args.source)
            started = time.time()
            with open(args.destination, "wb") as destination:
                with pwncat.victim.open(args.source, "rb", length=length) as source:
                    util.with_progress(
                        [
                            ("", "downloading "),
                            ("fg:ansigreen", args.source),
                            ("", " to "),
                            ("fg:ansired", args.destination),
                        ],
                        partial(util.copyfileobj, source, destination),
                        length=length,
                    )
            elapsed = time.time() - started
            util.success(
                f"downloaded {Fore.CYAN}{util.human_readable_size(length)}{Fore.RESET} "
                f"in {Fore.GREEN}{util.human_readable_delta(elapsed)}{Fore.RESET}"
            )
        except (FileNotFoundError, PermissionError, IsADirectoryError) as exc:
            self.parser.error(str(exc))
コード例 #3
0
ファイル: bind.py プロジェクト: trevorbryant/pwncat
class Command(CommandDefinition):

    PROG = "bind"
    ARGS = {
        "key":
        parameter(
            Complete.NONE,
            metavar="KEY",
            type=KeyType,
            help="The key to map after your prefix",
            nargs="?",
        ),
        "script":
        parameter(
            Complete.NONE,
            help="The script to run when the key is pressed",
            nargs="?",
        ),
    }
    LOCAL = True

    def run(self, args):
        if args.key is None:
            util.info("currently assigned key-bindings:")
            for key, binding in pwncat.victim.config.bindings.items():
                print(
                    f" {Fore.CYAN}{key}{Fore.RESET} = {Fore.YELLOW}{repr(binding)}{Fore.RESET}"
                )
        elif args.key is not None and args.script is None:
            if args.key in pwncat.victim.config.bindings:
                del pwncat.victim.config.bindings[args.key]
        else:
            pwncat.victim.config.bindings[args.key] = args.script
コード例 #4
0
class Command(CommandDefinition):
    """ View and revert any logged tampers which pwncat has performed on the remote system. """

    PROG = "tamper"
    ARGS = {
        "--tamper,-t":
        parameter(
            Complete.NONE,
            action=StoreForAction(["revert"]),
            type=int,
            help="Tamper ID to revert (IDs found in tamper list)",
        ),
        "--revert,-r":
        parameter(
            Complete.NONE,
            action=StoreConstOnce,
            nargs=0,
            dest="action",
            const="revert",
            help="Revert the selected tamper",
        ),
        "--list,-l":
        parameter(
            Complete.NONE,
            action=StoreConstOnce,
            nargs=0,
            dest="action",
            const="list",
            help="List all tampers currently logged by pwncat",
        ),
    }

    def run(self, args):

        if args.action == "revert":
            if args.tamper not in range(len(pwncat.victim.tamper.tampers)):
                self.parser.error("invalid tamper id")
            tamper = pwncat.victim.tamper.tampers[args.tamper]
            try:
                tamper.revert()
                pwncat.victim.tamper.tampers.pop(args.tamper)
            except RevertFailed as exc:
                util.error(f"revert failed: {exc}")
        else:
            for id, tamper in enumerate(pwncat.victim.tamper.tampers):
                print(f" {id} - {tamper}")
コード例 #5
0
class Command(CommandDefinition):

    PROG = "shortcut"
    ARGS = {
        "prefix":
        parameter(Complete.NONE,
                  help="the prefix character used for the shortcut"),
        "command":
        parameter(Complete.NONE, help="the command to execute"),
    }
    LOCAL = True

    def run(self, args):

        for command in pwncat.victim.command_parser.commands:
            if command.PROG == args.command:
                pwncat.victim.command_parser.shortcuts[args.prefix] = command
                return

        self.parser.error(f"{args.command}: no such command")
コード例 #6
0
ファイル: run.py プロジェクト: trevorbryant/pwncat
class Command(CommandDefinition):

    PROG = "run"
    ARGS = {
        "argv":
        parameter(Complete.NONE,
                  nargs="+",
                  help="The command to run on the remote host")
    }

    def run(self, args):
        sys.stdout.buffer.write(pwncat.victim.run(args.argv))
コード例 #7
0
ファイル: local.py プロジェクト: trevorbryant/pwncat
class Command(CommandDefinition):

    PROG = "local"
    ARGS = {
        "argv":
        parameter(Complete.NONE,
                  nargs="+",
                  help="the local shell command to run")
    }
    LOCAL = True

    def run(self, args):
        subprocess.run(args.argv, shell=True)
コード例 #8
0
class Command(CommandDefinition):
    """ List known commands and print their associated help documentation. """

    PROG = "help"
    ARGS = {"topic": parameter(Complete.NONE, nargs="?")}

    def run(self, args):
        if args.topic:
            for command in pwncat.victim.command_parser.commands:
                if command.PROG == args.topic:
                    command.parser.print_help()
                    break
        else:
            util.info("the following commands are available:")
            for command in pwncat.victim.command_parser.commands:
                print(f" * {command.PROG}")
コード例 #9
0
class Command(CommandDefinition):

    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:
                    print(
                        f"{Fore.GREEN}{service.name}{Fore.RESET} - {service.description}"
                    )
                else:
                    print(
                        f"{Fore.RED}{service.name}{Fore.RESET} - {service.description}"
                    )
        else:
            print(f"Host ID: {Fore.CYAN}{pwncat.victim.host.hash}{Fore.RESET}")
            print(
                f"Remote Address: {Fore.GREEN}{pwncat.victim.client.getpeername()}{Fore.RESET}"
            )
            print(
                f"Architecture: {Fore.RED}{pwncat.victim.host.arch}{Fore.RESET}"
            )
            print(
                f"Kernel Version: {Fore.RED}{pwncat.victim.host.kernel}{Fore.RESET}"
            )
            print(
                f"Distribution: {Fore.RED}{pwncat.victim.host.distro}{Fore.RESET}"
            )
            print(
                f"Init System: {Fore.BLUE}{pwncat.victim.host.init}{Fore.RESET}"
            )
コード例 #10
0
ファイル: privesc.py プロジェクト: igoros777/pwncat
class Command(CommandDefinition):
    """ Attempt various privilege escalation methods. This command will attempt
    search for privilege escalation across all known modules. Privilege escalation
    routes can grant file read, file write or shell capabilities. The "escalate"
    mode will attempt to abuse any of these to gain a shell.

    Further, escalation and file read/write actions will attempt to escalate multiple
    times to reach the target user if possible, attempting all known escalation paths
    until one arrives at the target user. """
    def get_user_choices(self):
        """ Get a list of all users on the remote machine. This is used for
        parameter checking and tab completion of the "users" parameter below. """
        return list(pwncat.victim.users)

    PROG = "privesc"
    ARGS = {
        "--list,-l":
        parameter(
            Complete.NONE,
            action=StoreConstOnce,
            nargs=0,
            const="list",
            dest="action",
            help="Enumerate and list available privesc techniques",
        ),
        "--all,-a":
        parameter(
            Complete.NONE,
            action="store_const",
            dest="user",
            const=None,
            help="list escalations for all users",
        ),
        "--user,-u":
        parameter(
            Complete.CHOICES,
            default="root",
            choices=get_user_choices,
            metavar="USER",
            help="the user to gain privileges as",
        ),
        "--max-depth,-m":
        parameter(
            Complete.NONE,
            default=None,
            type=int,
            help="Maximum depth for the privesc search (default: no maximum)",
        ),
        "--read,-r":
        parameter(
            Complete.NONE,
            action=StoreConstOnce,
            nargs=0,
            const="read",
            dest="action",
            help="Attempt to read a remote file as the specified user",
        ),
        "--write,-w":
        parameter(
            Complete.NONE,
            action=StoreConstOnce,
            nargs=0,
            const="write",
            dest="action",
            help="Attempt to write a remote file as the specified user",
        ),
        "--path,-p":
        parameter(
            Complete.REMOTE_FILE,
            action=StoreForAction(["write", "read"]),
            help="Remote path for read or write actions",
        ),
        "--escalate,-e":
        parameter(
            Complete.NONE,
            action=StoreConstOnce,
            nargs=0,
            const="escalate",
            dest="action",
            help="Attempt to escalate to gain a full shell as the target user",
        ),
        "--data,-d":
        parameter(
            Complete.LOCAL_FILE,
            action=StoreForAction(["write"]),
            default=None,
            help="The local file to write to the remote file",
        ),
    }
    DEFAULTS = {"action": "list"}

    def run(self, args):

        if args.action == "list":
            techniques = pwncat.victim.privesc.search(args.user)
            if len(techniques) == 0:
                util.warn("no techniques found")
            else:
                for tech in techniques:
                    util.info(f" - {tech}")
        elif args.action == "read":
            if not args.path:
                self.parser.error("missing required argument: --path")
            try:
                read_pipe, chain = pwncat.victim.privesc.read_file(
                    args.path, args.user, args.max_depth)
                util.success("file successfully opened!")

                # Read the data from the pipe
                shutil.copyfileobj(read_pipe, sys.stdout.buffer)
                read_pipe.close()

                # Unwrap in case we had to privesc to get here
                pwncat.victim.privesc.unwrap(chain)

            except privesc.PrivescError as exc:
                util.error(f"read file failed: {exc}")
        elif args.action == "write":
            # Make sure the correct arguments are present
            if not args.path:
                self.parser.error("missing required argument: --path")
            if not args.data:
                self.parser.error("missing required argument: --data")

            # Read in the data file
            with open(args.data, "rb") as f:
                data = f.read()

            try:
                # Attempt to write the data to the remote file
                chain = pwncat.victim.privesc.write_file(
                    args.path,
                    data,
                    target_user=args.user,
                    depth=args.max_depth,
                )
                pwncat.victim.privesc.unwrap(chain)
                util.success("file written successfully!")
            except privesc.PrivescError as exc:
                util.error(f"file write failed: {exc}")
        elif args.action == "escalate":
            try:
                chain = pwncat.victim.privesc.escalate(args.user,
                                                       args.max_depth)

                ident = pwncat.victim.id
                backdoor = False
                if ident["euid"]["id"] == 0 and ident["uid"]["id"] != 0:
                    util.progress(
                        "EUID != UID. installing backdoor to complete privesc")
                    try:
                        pwncat.victim.privesc.add_backdoor()
                        backdoor = True
                    except privesc.PrivescError as exc:
                        util.warn(f"backdoor installation failed: {exc}")

                util.success("privilege escalation succeeded using:")
                for i, (technique, _) in enumerate(chain):
                    arrow = f"{Fore.YELLOW}\u2ba1{Fore.RESET} "
                    print(f"{(i+1)*' '}{arrow}{technique}")

                if backdoor:
                    print((f"{(len(chain)+1)*' '}{arrow}"
                           f"{Fore.YELLOW}pwncat{Fore.RESET} backdoor"))

                pwncat.victim.reset()
                pwncat.victim.state = State.RAW
            except privesc.PrivescError as exc:
                util.error(f"escalation failed: {exc}")
コード例 #11
0
ファイル: persist.py プロジェクト: trevorbryant/pwncat
class Command(CommandDefinition):
    """ Manage various persistence methods on the remote host """
    def get_method_choices(self):
        return [method.name for method in pwncat.victim.persist]

    def get_user_choices(self):
        """ Get the user options """
        current = pwncat.victim.current_user
        if current.id == 0:
            return [name for name in pwncat.victim.users]
        else:
            return [current.name]

    PROG = "persist"
    ARGS = {
        "--method,-m":
        parameter(
            Complete.CHOICES,
            metavar="METHOD",
            help="Select a persistence method to deploy",
            choices=get_method_choices,
        ),
        "--user,-u":
        parameter(
            Complete.CHOICES,
            metavar="USER",
            help=
            "For non-system persistence modules, the user to install as (only valid if currently UID 0)",
            choices=get_user_choices,
        ),
        "--status,-s":
        parameter(
            Complete.NONE,
            action=StoreConstOnce,
            nargs=0,
            dest="action",
            const="status",
            help="Check the status of the given persistence method",
        ),
        "--install,-i":
        parameter(
            Complete.NONE,
            action=StoreConstOnce,
            nargs=0,
            dest="action",
            const="install",
            help="Install the selected persistence method",
        ),
        "--list,-l":
        parameter(
            Complete.NONE,
            nargs=0,
            action=StoreConstOnce,
            dest="action",
            const="list",
            help="List all available persistence methods",
        ),
        "--remove,-r":
        parameter(
            Complete.NONE,
            nargs=0,
            action=StoreConstOnce,
            dest="action",
            const="remove",
            help="Remove the selected persistence method",
        ),
        "--clean,-c":
        parameter(
            Complete.NONE,
            nargs=0,
            action=StoreConstOnce,
            dest="action",
            const="clean",
            help="Remove all installed persistence methods",
        ),
    }
    DEFAULTS = {"action": "status"}

    # List of available persistence methods
    METHODS: Dict[str, Type["PersistenceMethod"]] = {}

    @property
    def installed_methods(
            self) -> Iterator[Tuple[str, str, PersistenceMethod]]:
        me = pwncat.victim.current_user
        for method in pwncat.victim.persist:
            if method.system and method.installed():
                yield (method.name, None, method)
            elif not method.system:
                if me.id == 0:
                    for user in pwncat.victim.users:
                        util.progress(f"checking {method.name} for: {user}")
                        if method.installed(user):
                            util.erase_progress()
                            yield (method.name, user, method)
                        util.erase_progress()
                else:
                    if method.installed(me.name):
                        yield (method.name, me.name, method)

    def run(self, args):

        if args.action == "status":
            ninstalled = 0
            for user, method in pwncat.victim.persist.installed:
                print(f" - {method.format(user)} installed")
                ninstalled += 1
            if not ninstalled:
                util.warn("no persistence methods observed as "
                          f"{Fore.GREEN}{pwncat.victim.whoami()}{Fore.RED}")
            return
        elif args.action == "list":
            if args.method:
                try:
                    method = next(pwncat.victim.persist.find(args.method))
                    print(f"\033[4m{method.format()}{Style.RESET_ALL}")
                    print(
                        textwrap.indent(textwrap.dedent(method.__doc__), "  "))
                except StopIteration:
                    util.error(f"{args.method}: no such persistence method")
            else:
                for method in pwncat.victim.persist:
                    print(f" - {method.format()}")
            return
        elif args.action == "clean":
            util.progress("cleaning persistence methods: ")
            for user, method in pwncat.victim.persist.installed:
                try:
                    util.progress(
                        f"cleaning persistance methods: {method.format(user)}")
                    pwncat.victim.persist.remove(method.name, user)
                    util.success(f"removed {method.format(user)}")
                except PersistenceError as exc:
                    util.erase_progress()
                    util.warn(
                        f"{method.format(user)}: removal failed: {exc}\n",
                        overlay=True)
            util.erase_progress()
            return
        elif args.method is None:
            self.parser.error("no method specified")
            return

        # Grab the user we want to install the persistence as
        if args.user:
            user = args.user
        else:
            # Default is to install as current user
            user = pwncat.victim.whoami()

        try:
            if args.action == "install":
                pwncat.victim.persist.install(args.method, user)
            elif args.action == "remove":
                pwncat.victim.persist.remove(args.method, user)
        except PersistenceError as exc:
            util.error(f"{exc}")
コード例 #12
0
ファイル: busybox.py プロジェクト: igoros777/pwncat
class Command(CommandDefinition):
    """ Manage installation of a known-good busybox binary on the remote system.
    After installing busybox, pwncat will be able to utilize it's functionality
    to augment or stabilize local binaries. This command can download a remote
    busybox binary appropriate for the remote architecture and then upload it
    to the remote system. """

    PROG = "busybox"
    ARGS = {
        "--list,-l":
        parameter(
            Complete.NONE,
            action=StoreConstOnce,
            nargs=0,
            const="list",
            dest="action",
            help="List applets which the remote busybox provides",
        ),
        "--install,-i":
        parameter(
            Complete.NONE,
            action=StoreConstOnce,
            nargs=0,
            const="install",
            dest="action",
            help="Install busybox on the remote host for use with pwncat",
        ),
        "--status,-s":
        parameter(
            Complete.NONE,
            action=StoreConstOnce,
            nargs=0,
            const="status",
            dest="action",
            help="List the current busybox installation status",
        ),
        "--url,-u":
        parameter(
            Complete.NONE,
            action=StoreForAction(["install"]),
            nargs=1,
            help=
            "The base URL to download busybox binaries from (default: 1.31.0-defconfig-multiarch-musl)",
            default=
            ("https://busybox.net/downloads/binaries/1.31.0-defconfig-multiarch-musl/"
             ),
        ),
    }
    DEFAULTS = {"action": "status"}

    def run(self, args):

        if args.action == "list":
            if not pwncat.victim.has_busybox:
                util.error(
                    "busybox hasn't been installed yet (hint: run 'busybox'")
                return
            util.info("binaries which the remote busybox provides:")
            for name in pwncat.victim.busybox_provides:
                print(f" * {name}")
        elif args.action == "status":
            if not pwncat.victim.has_busybox:
                util.error("busybox hasn't been installed yet")
                return
            util.info(
                f"busybox is installed to: {Fore.BLUE}{pwncat.victim.busybox_path}{Fore.RESET}"
            )
            util.info(
                f"busybox provides {Fore.GREEN}{len(pwncat.victim.busybox_provides)}{Fore.RESET} applets"
            )
        elif args.action == "install":
            pwncat.victim.bootstrap_busybox(args.url)
コード例 #13
0
class Command(CommandDefinition):
    """ Set variable runtime variable parameters for pwncat """

    def get_config_variables(self):
        return ["state"] + list(pwncat.victim.config.values) + list(pwncat.victim.users)

    PROG = "set"
    ARGS = {
        "--password,-p": parameter(
            Complete.NONE, action="store_true", help="set a user password",
        ),
        "variable": parameter(
            Complete.CHOICES,
            nargs="?",
            choices=get_config_variables,
            metavar="VARIABLE",
            help="the variable name to modify",
        ),
        "value": parameter(
            Complete.LOCAL_FILE, nargs="?", help="the value for the given variable"
        ),
    }
    LOCAL = True

    def run(self, args):
        if args.password:
            if args.variable is None:
                found = False
                for user, props in pwncat.victim.users.items():
                    if "password" in props and props["password"] is not None:
                        print(
                            f" - {Fore.GREEN}{user}{Fore.RESET} -> {Fore.RED}{repr(props['password'])}{Fore.RESET}"
                        )
                        found = True
                if not found:
                    util.warn("no known user passwords")
            else:
                if args.variable not in pwncat.victim.users:
                    self.parser.error(f"{args.variable}: no such user")
                print(
                    f" - {Fore.GREEN}{args.variable}{Fore.RESET} -> {Fore.RED}{repr(args.value)}{Fore.RESET}"
                )
                pwncat.victim.users[args.variable]["password"] = args.value
        else:
            if (
                args.variable is not None
                and args.variable == "state"
                and args.value is not None
            ):
                try:
                    pwncat.victim.state = util.State._member_map_[args.value.upper()]
                except KeyError:
                    util.error(f"{args.value}: invalid state")
            elif args.variable is not None and args.value is not None:
                try:
                    pwncat.victim.config[args.variable] = args.value
                except ValueError as exc:
                    util.error(str(exc))
            elif args.variable is not None:
                value = pwncat.victim.config[args.variable]
                print(
                    f" {Fore.CYAN}{args.variable}{Fore.RESET} = "
                    f"{Fore.YELLOW}{repr(value)}{Fore.RESET}"
                )
            else:
                for name in pwncat.victim.config:
                    value = pwncat.victim.config[name]
                    print(
                        f" {Fore.CYAN}{name}{Fore.RESET} = "
                        f"{Fore.YELLOW}{repr(value)}{Fore.RESET}"
                    )
コード例 #14
0
class Command(CommandDefinition):
    """ View and revert any logged tampers which pwncat has performed on the remote system. """

    PROG = "tamper"
    ARGS = {
        "--tamper,-t":
        parameter(
            Complete.NONE,
            action=StoreForAction(["revert"]),
            type=int,
            help="Tamper ID to revert (IDs found in tamper list)",
        ),
        "--all,-a":
        parameter(
            Complete.NONE,
            action="store_true",
            help="Attempt to revert all tampered files",
        ),
        "--revert,-r":
        parameter(
            Complete.NONE,
            action=StoreConstOnce,
            nargs=0,
            dest="action",
            const="revert",
            help="Revert the selected tamper",
        ),
        "--list,-l":
        parameter(
            Complete.NONE,
            action=StoreConstOnce,
            nargs=0,
            dest="action",
            const="list",
            help="List all tampers currently logged by pwncat",
        ),
    }

    def run(self, args):

        if args.action == "revert":
            if args.all:
                removed_tampers = []
                util.progress(f"reverting tamper")
                for tamper in pwncat.victim.tamper:
                    try:
                        util.progress(f"reverting tamper: {tamper}")
                        tamper.revert()
                        removed_tampers.append(tamper)
                    except RevertFailed as exc:
                        util.warn(f"{tamper}: revert failed: {exc}")
                for tamper in removed_tampers:
                    pwncat.victim.tamper.remove(tamper)
                util.success("tampers reverted!")
                pwncat.victim.session.commit()
            else:
                if args.tamper not in range(len(pwncat.victim.tamper)):
                    self.parser.error("invalid tamper id")
                tamper = pwncat.victim.tamper[args.tamper]
                try:
                    tamper.revert()
                    pwncat.victim.tamper.remove(tamper)
                except RevertFailed as exc:
                    util.error(f"revert failed: {exc}")
                pwncat.victim.session.commit()
        else:
            for id, tamper in enumerate(pwncat.victim.tamper):
                print(f" {id} - {tamper}")