From 8dea0b61e8c38baa97ffc5aa9d07718a2d325aeb Mon Sep 17 00:00:00 2001 From: Caleb Stewart Date: Wed, 27 May 2020 01:20:19 -0400 Subject: [PATCH] Added prompt command to fix your prompt in the event of a simple shell like dash --- README.md | 14 ++++++--- pwncat/commands/prompt.py | 45 ++++++++++++++++++++++++++++ pwncat/enumerate/system/systemd.py | 2 -- pwncat/privesc/__init__.py | 11 +++++++ pwncat/privesc/facts/private_keys.py | 2 +- pwncat/remote/victim.py | 10 +++++-- 6 files changed, 74 insertions(+), 10 deletions(-) create mode 100644 pwncat/commands/prompt.py diff --git a/README.md b/README.md index 475de84..bf1b89e 100644 --- a/README.md +++ b/README.md @@ -132,9 +132,15 @@ terminal escape sequences which `pwncat` adds, so you may get a very long termin \[\033[01;31m\](remote)\[\033[00m\] \[\033[01;33m\]\u@\h\[\033[00m\]:\[\033[01;36m\]\w\[\033[00m\]$ ``` -We are currently trying to figure out an acceptible way of handling with. `dash` (and other -minimalist shells) are capable of handling terminal escape sequences for color, but inserting -things like user and host name automatically are unsupported. +Currently, the only workaround is to use the `prompt` command at the local `pwncat` prompt. +The command allows you to modify the prompt which `pwncat` will automatically set whenever +resetting the remote terminal. Two options are provided: "basic" and "fancy". The "fancy" +prompt is the default which causes the above output in Dash. To switch to the basic prompt +you can use the following command at the `pwncat` prompt: + +```shell script +prompt --basic +``` While this is inconvenient, it does not affect the behaviour of `pwncat`. All `pwncat` -features will continue to function properly. \ No newline at end of file +features will continue to function properly no matter what your prompt looks like. \ No newline at end of file diff --git a/pwncat/commands/prompt.py b/pwncat/commands/prompt.py new file mode 100644 index 0000000..210d955 --- /dev/null +++ b/pwncat/commands/prompt.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +import pwncat +from pwncat.commands.base import CommandDefinition, Parameter, Complete, Group + + +class Command(CommandDefinition): + """ + Reset the prompt used for shells in pwncat. This allows you to choose + between the fancier colored prompt and more basic prompt. You can + also specify a custom prompt if you'd like. + + This is mainly useful for basic shells such as /bin/sh or /bin/dash + which do not support the nicer prompts by default. + """ + + PROG = "prompt" + GROUPS = {"mutex": Group(mutex=True, required=True)} + ARGS = { + "--basic,-b": Parameter( + Complete.NONE, + group="mutex", + action="store_true", + help="Set a basic prompt with no color or automatic system information", + ), + "--fancy,-f": Parameter( + Complete.NONE, + group="mutex", + action="store_true", + help="Set a fancier prompt including auto-user, hostname, cwd information", + ), + } + + def run(self, args): + + if args.fancy: + pwncat.victim.remote_prefix = "\\[\\033[01;31m\\](remote)\\[\\033[00m\\]" + pwncat.victim.remote_prompt = ( + "\\[\\033[01;33m\\]\\u@\\h\\[\\033[00m\\]:\\[" + "\\033[01;36m\\]\\w\\[\\033[00m\\]\\$ " + ) + else: + pwncat.victim.remote_prefix = "(remote)" + pwncat.victim.remote_prompt = f"{pwncat.victim.host.ip}:$PWD\\$ " + + pwncat.victim.reset(hard=False) diff --git a/pwncat/enumerate/system/systemd.py b/pwncat/enumerate/system/systemd.py index 3dbaf97..6cb6eae 100644 --- a/pwncat/enumerate/system/systemd.py +++ b/pwncat/enumerate/system/systemd.py @@ -89,9 +89,7 @@ def enumerate() -> Generator[FactData, None, None]: for i in range(0, len(data), 5): if i >= (len(data) - 4): - print(data[i:]) break - print(data[i : i + 4]) name = data[i + 2].strip().rstrip(".service") pid = int(data[i].strip()) if "[not set]" in data[i + 1]: diff --git a/pwncat/privesc/__init__.py b/pwncat/privesc/__init__.py index 3dd1e97..cac9689 100644 --- a/pwncat/privesc/__init__.py +++ b/pwncat/privesc/__init__.py @@ -707,6 +707,9 @@ class Finder: # Attempt to escalate with the local persistence method if persist.escalate(target_user): + # Stabilize the terminal + pwncat.victim.reset(hard=False) + # The method thought it worked, but didn't appear to if pwncat.victim.update_user() != target_user: if pwncat.victim.getenv("SHLVL") != shlvl: @@ -738,6 +741,7 @@ class Finder: tech, exit_command = self.escalate_single( techniques[target_user], shlvl ) + pwncat.victim.reset(hard=False) pwncat.victim.update_user() chain.append((tech, exit_command)) return chain @@ -753,6 +757,11 @@ class Finder: f"checking local persistence implants: {persist.format(user)}" ) if persist.escalate(user): + + # Ensure history and prompt are correct + pwncat.victim.reset(hard=False) + + # Update the current user if pwncat.victim.update_user() != user: if pwncat.victim.getenv("SHLVL") != shlvl: pwncat.victim.run("exit", wait=False) @@ -780,6 +789,8 @@ class Finder: try: tech, exit_command = self.escalate_single(techs, shlvl) chain.append((tech, exit_command)) + pwncat.victim.reset(hard=False) + pwncat.victim.update_user() except PrivescError: continue try: diff --git a/pwncat/privesc/facts/private_keys.py b/pwncat/privesc/facts/private_keys.py index b3c2929..b739b95 100644 --- a/pwncat/privesc/facts/private_keys.py +++ b/pwncat/privesc/facts/private_keys.py @@ -22,7 +22,7 @@ class Method(BaseMethod): """ for fact in pwncat.victim.enumerate.iter("system.service"): - if "ssh" in fact.data.name and fact.data.running: + if "ssh" in fact.data.name and fact.data.state == "running": break else: raise PrivescError("no sshd service running") diff --git a/pwncat/remote/victim.py b/pwncat/remote/victim.py index 0a54d9b..eec32ca 100644 --- a/pwncat/remote/victim.py +++ b/pwncat/remote/victim.py @@ -1459,13 +1459,17 @@ class Victim: return output - def reset(self): + def reset(self, hard: bool = True): """ Reset the remote terminal using the ``reset`` command. This also restores your prompt, and sets up the environment correctly for ``pwncat``. - + + :param hard: whether to actually call the `reset` command. + This prevents a long pause when we simply need to reset other + things such as the prompt, aliases or history control. """ - self.run("reset", wait=False) + if hard: + self.run("reset", wait=False) self.has_cr = True self.has_echo = True self.run("unset HISTFILE; export HISTCONTROL=ignorespace")