Added prompt command to fix your prompt in the event of a simple shell like dash

This commit is contained in:
Caleb Stewart 2020-05-27 01:20:19 -04:00
parent d0e0179fda
commit 8dea0b61e8
6 changed files with 74 additions and 10 deletions

View File

@ -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.
features will continue to function properly no matter what your prompt looks like.

45
pwncat/commands/prompt.py Normal file
View File

@ -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)

View File

@ -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]:

View File

@ -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:

View File

@ -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")

View File

@ -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")