1
0
mirror of https://github.com/calebstewart/pwncat.git synced 2024-11-30 12:24:14 +01:00

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\]$ \[\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 Currently, the only workaround is to use the `prompt` command at the local `pwncat` prompt.
minimalist shells) are capable of handling terminal escape sequences for color, but inserting The command allows you to modify the prompt which `pwncat` will automatically set whenever
things like user and host name automatically are unsupported. 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` 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): for i in range(0, len(data), 5):
if i >= (len(data) - 4): if i >= (len(data) - 4):
print(data[i:])
break break
print(data[i : i + 4])
name = data[i + 2].strip().rstrip(".service") name = data[i + 2].strip().rstrip(".service")
pid = int(data[i].strip()) pid = int(data[i].strip())
if "[not set]" in data[i + 1]: if "[not set]" in data[i + 1]:

View File

@ -707,6 +707,9 @@ class Finder:
# Attempt to escalate with the local persistence method # Attempt to escalate with the local persistence method
if persist.escalate(target_user): if persist.escalate(target_user):
# Stabilize the terminal
pwncat.victim.reset(hard=False)
# The method thought it worked, but didn't appear to # The method thought it worked, but didn't appear to
if pwncat.victim.update_user() != target_user: if pwncat.victim.update_user() != target_user:
if pwncat.victim.getenv("SHLVL") != shlvl: if pwncat.victim.getenv("SHLVL") != shlvl:
@ -738,6 +741,7 @@ class Finder:
tech, exit_command = self.escalate_single( tech, exit_command = self.escalate_single(
techniques[target_user], shlvl techniques[target_user], shlvl
) )
pwncat.victim.reset(hard=False)
pwncat.victim.update_user() pwncat.victim.update_user()
chain.append((tech, exit_command)) chain.append((tech, exit_command))
return chain return chain
@ -753,6 +757,11 @@ class Finder:
f"checking local persistence implants: {persist.format(user)}" f"checking local persistence implants: {persist.format(user)}"
) )
if persist.escalate(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.update_user() != user:
if pwncat.victim.getenv("SHLVL") != shlvl: if pwncat.victim.getenv("SHLVL") != shlvl:
pwncat.victim.run("exit", wait=False) pwncat.victim.run("exit", wait=False)
@ -780,6 +789,8 @@ class Finder:
try: try:
tech, exit_command = self.escalate_single(techs, shlvl) tech, exit_command = self.escalate_single(techs, shlvl)
chain.append((tech, exit_command)) chain.append((tech, exit_command))
pwncat.victim.reset(hard=False)
pwncat.victim.update_user()
except PrivescError: except PrivescError:
continue continue
try: try:

View File

@ -22,7 +22,7 @@ class Method(BaseMethod):
""" """
for fact in pwncat.victim.enumerate.iter("system.service"): 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 break
else: else:
raise PrivescError("no sshd service running") raise PrivescError("no sshd service running")

View File

@ -1459,13 +1459,17 @@ class Victim:
return output return output
def reset(self): def reset(self, hard: bool = True):
""" """
Reset the remote terminal using the ``reset`` command. This also restores Reset the remote terminal using the ``reset`` command. This also restores
your prompt, and sets up the environment correctly for ``pwncat``. 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_cr = True
self.has_echo = True self.has_echo = True
self.run("unset HISTFILE; export HISTCONTROL=ignorespace") self.run("unset HISTFILE; export HISTCONTROL=ignorespace")