mirror of
https://github.com/calebstewart/pwncat.git
synced 2024-11-27 10:54:14 +01:00
Removed custom prompt_toolkit and fixed init enumerator bug
This commit is contained in:
parent
3c381f5f1f
commit
80225ca7e0
@ -18,7 +18,6 @@ It is recommended to use a virtual environment, however. This can be done easily
|
||||
|
||||
python -m venv env
|
||||
source env/bin/activate
|
||||
pip install -r requirements.txt
|
||||
python setup.py install
|
||||
|
||||
When updating ``pwncat`` is it recommended to setup and update the virtual environment again.
|
||||
|
@ -163,20 +163,6 @@ class CommandParser:
|
||||
complete_while_typing=False,
|
||||
history=history,
|
||||
)
|
||||
self.toolbar = PromptSession(
|
||||
[
|
||||
("fg:ansiyellow bold", "(local) "),
|
||||
("fg:ansimagenta bold", "pwncat"),
|
||||
("", "$ "),
|
||||
],
|
||||
completer=completer,
|
||||
lexer=lexer,
|
||||
style=style,
|
||||
auto_suggest=auto_suggest,
|
||||
complete_while_typing=False,
|
||||
prompt_in_toolbar=True,
|
||||
history=history,
|
||||
)
|
||||
|
||||
@property
|
||||
def loaded(self):
|
||||
@ -209,7 +195,7 @@ class CommandParser:
|
||||
def run_single(self):
|
||||
|
||||
try:
|
||||
line = self.toolbar.prompt().strip()
|
||||
line = self.prompt.prompt().strip()
|
||||
except (EOFError, OSError, KeyboardInterrupt):
|
||||
pass
|
||||
else:
|
||||
|
@ -61,6 +61,28 @@ def StoreForAction(action: List[str]) -> Callable:
|
||||
return StoreFor
|
||||
|
||||
|
||||
def StoreConstForAction(action: List[str]) -> Callable:
|
||||
""" Generates a custom argparse Action subclass which verifies that the current
|
||||
selected "action" option is one of the provided actions in this function. If
|
||||
not, an error is raised. This stores the constant `const` to the `dest` argument.
|
||||
This is comparable to `store_const`, but checks that you have selected one of
|
||||
the specified actions. """
|
||||
|
||||
class StoreFor(argparse.Action):
|
||||
""" Store the value if the currently selected action matches the list of
|
||||
actions passed to this function. """
|
||||
|
||||
def __call__(self, parser, namespace, values, option_string=None):
|
||||
if getattr(namespace, "action", None) not in action:
|
||||
raise argparse.ArgumentError(
|
||||
self, f"{option_string}: only valid for {action}",
|
||||
)
|
||||
|
||||
setattr(namespace, self.dest, self.const)
|
||||
|
||||
return StoreFor
|
||||
|
||||
|
||||
def RemoteFileType(file_exist=True, directory_exist=False):
|
||||
def _type(command: "CommandDefinition", name: str):
|
||||
""" Ensures that the remote file named exists. This should only be used for
|
||||
|
@ -15,6 +15,8 @@ from pwncat.commands.base import (
|
||||
Parameter,
|
||||
StoreConstOnce,
|
||||
Group,
|
||||
StoreForAction,
|
||||
StoreConstForAction,
|
||||
)
|
||||
|
||||
|
||||
@ -34,16 +36,14 @@ class Command(CommandDefinition):
|
||||
"""
|
||||
Interface with the underlying enumeration module. This provides methods
|
||||
for enumerating, viewing and clearing cached facts about the victim.
|
||||
Types of enumeration data include the following options:
|
||||
|
||||
* all - all known enumeration techniques
|
||||
* common - common useful information
|
||||
* suid - Set UID binaries on the remote host
|
||||
* passwords - Known passwords for remote users
|
||||
* keys - Known private keys found on the remote host
|
||||
|
||||
Other enumeration data may be available which was dynamically registered by
|
||||
other ``pwncat`` modules.
|
||||
There are various types of enumeration data which can be collected by
|
||||
pwncat. Some enumeration data is provided by "enumerator" modules which
|
||||
will be automatically run if you request a type which they provide. On
|
||||
the other hand, some enumeration is performed as a side-effect of other
|
||||
operations (normally a privilege escalation). This data is only stored
|
||||
when it is found organically. To find out what types are available, you
|
||||
should use the tab-completion at the local prompt. Some shortcuts are
|
||||
provided with the "enumeration groups" options below.
|
||||
|
||||
"""
|
||||
|
||||
@ -68,7 +68,14 @@ class Command(CommandDefinition):
|
||||
"action": Group(
|
||||
title="enumeration actions",
|
||||
description="Exactly one action must be chosen from the below list.",
|
||||
)
|
||||
),
|
||||
"groups": Group(
|
||||
title="enumeration groups",
|
||||
description=(
|
||||
"common enumeration groups; these put together various "
|
||||
"groups of enumeration types which may be useful"
|
||||
),
|
||||
),
|
||||
}
|
||||
ARGS = {
|
||||
"--show,-s": Parameter(
|
||||
@ -78,12 +85,12 @@ class Command(CommandDefinition):
|
||||
dest="action",
|
||||
const="show",
|
||||
group="action",
|
||||
help="Find and display all facts of the given type",
|
||||
help="Find and display all facts of the given type and provider",
|
||||
),
|
||||
"--long,-l": Parameter(
|
||||
Complete.NONE,
|
||||
action="store_true",
|
||||
help="Show long description of enumeration results",
|
||||
help="Show long description of enumeration results (only valid for --show)",
|
||||
),
|
||||
"--no-enumerate,-n": Parameter(
|
||||
Complete.NONE,
|
||||
@ -92,9 +99,11 @@ class Command(CommandDefinition):
|
||||
),
|
||||
"--type,-t": Parameter(
|
||||
Complete.CHOICES,
|
||||
action=StoreForAction(["show", "flush"]),
|
||||
nargs=1,
|
||||
choices=get_fact_types,
|
||||
metavar="TYPE",
|
||||
help="The type of enumeration data to query",
|
||||
help="The type of enumeration data to query (only valid for --show/--flush)",
|
||||
),
|
||||
"--flush,-f": Parameter(
|
||||
Complete.NONE,
|
||||
@ -103,10 +112,17 @@ class Command(CommandDefinition):
|
||||
nargs=0,
|
||||
dest="action",
|
||||
const="flush",
|
||||
help="Flush the queried enumeration data from the database",
|
||||
help=(
|
||||
"Flush the queried enumeration data from the database. "
|
||||
"This only flushed the data specified by the --type and "
|
||||
"--provider options. If no type or provider or specified, "
|
||||
"all data is flushed"
|
||||
),
|
||||
),
|
||||
"--provider,-p": Parameter(
|
||||
Complete.CHOICES,
|
||||
action=StoreForAction(["show", "flush"]),
|
||||
nargs=1,
|
||||
choices=get_provider_names,
|
||||
metavar="PROVIDER",
|
||||
help="The enumeration provider to filter by",
|
||||
@ -116,7 +132,36 @@ class Command(CommandDefinition):
|
||||
group="action",
|
||||
action=ReportAction,
|
||||
nargs=1,
|
||||
help="Generate an enumeration report containing the specified enumeration data",
|
||||
help=(
|
||||
"Generate an enumeration report containing all enumeration "
|
||||
"data pwncat is capable of generating in a Markdown format."
|
||||
),
|
||||
),
|
||||
"--quick,-q": Parameter(
|
||||
Complete.NONE,
|
||||
action=StoreConstForAction(["show"]),
|
||||
dest="type",
|
||||
const=[
|
||||
"system.hostname",
|
||||
"system.arch",
|
||||
"system.distro",
|
||||
"system.kernel.version",
|
||||
"system.kernel.exploit",
|
||||
"system.network.hosts",
|
||||
"system.network",
|
||||
],
|
||||
nargs=0,
|
||||
help="Activate the set of 'quick' enumeration types",
|
||||
group="groups",
|
||||
),
|
||||
"--all,-a": Parameter(
|
||||
Complete.NONE,
|
||||
action=StoreConstForAction(["show"]),
|
||||
dest="type",
|
||||
const=None,
|
||||
nargs=0,
|
||||
help="Activate all enumeration types (this is the default)",
|
||||
group="groups",
|
||||
),
|
||||
}
|
||||
DEFAULTS = {"action": "help"}
|
||||
@ -128,22 +173,22 @@ class Command(CommandDefinition):
|
||||
self.parser.print_help()
|
||||
return
|
||||
|
||||
# if not args.type:
|
||||
# args.type = "all"
|
||||
|
||||
if args.action == "show":
|
||||
self.show_facts(args.type, args.provider, args.long)
|
||||
elif args.action == "flush":
|
||||
self.flush_facts(args.type, args.provider)
|
||||
elif args.action == "report":
|
||||
self.generate_report(args.report, args.type, args.provider)
|
||||
self.generate_report(args.report)
|
||||
|
||||
def generate_report(self, report_path: str, typ: str, provider: str):
|
||||
""" Generate a markdown report of enumeration data for the remote host """
|
||||
def generate_report(self, report_path: str):
|
||||
""" Generate a markdown report of enumeration data for the remote host. This
|
||||
report is generated from all facts which pwncat is capable of enumerating.
|
||||
It does not need nor honor the type or provider options. """
|
||||
|
||||
# Dictionary mapping type names to facts. Each type name is mapped
|
||||
# to a dictionary which maps sources to a list of facts. This makes
|
||||
# organizing the output report easier.
|
||||
report_data: Dict[str, Dict[str, List[pwncat.db.Fact]]] = {}
|
||||
hostname = ""
|
||||
|
||||
system_details = []
|
||||
|
||||
try:
|
||||
@ -202,7 +247,8 @@ class Command(CommandDefinition):
|
||||
table_writer.value_matrix = system_details
|
||||
table_writer.margin = 1
|
||||
|
||||
# Note enumeration data we don't need anymore
|
||||
# Note enumeration data we don't need anymore. These are handled above
|
||||
# in the system_details table which is output with the table_writer.
|
||||
ignore_types = [
|
||||
"system.hostname",
|
||||
"system.kernel.version",
|
||||
@ -234,9 +280,7 @@ class Command(CommandDefinition):
|
||||
]
|
||||
|
||||
util.progress("enumerating report_data")
|
||||
for fact in pwncat.victim.enumerate.iter(
|
||||
typ, filter=lambda f: provider is None or f.source == provider
|
||||
):
|
||||
for fact in pwncat.victim.enumerate.iter():
|
||||
util.progress(f"enumerating report_data: {fact.data}")
|
||||
if fact.type in ignore_types:
|
||||
continue
|
||||
@ -308,16 +352,22 @@ class Command(CommandDefinition):
|
||||
|
||||
facts: Dict[str, Dict[str, List[pwncat.db.Fact]]] = {}
|
||||
|
||||
if isinstance(typ, list):
|
||||
types = typ
|
||||
else:
|
||||
types = [typ]
|
||||
|
||||
util.progress("enumerating facts")
|
||||
for fact in pwncat.victim.enumerate.iter(
|
||||
typ, filter=lambda f: provider is None or f.source == provider
|
||||
):
|
||||
util.progress(f"enumerating facts: {fact.data}")
|
||||
if fact.type not in facts:
|
||||
facts[fact.type] = {}
|
||||
if fact.source not in facts[fact.type]:
|
||||
facts[fact.type][fact.source] = []
|
||||
facts[fact.type][fact.source].append(fact)
|
||||
for typ in types:
|
||||
for fact in pwncat.victim.enumerate.iter(
|
||||
typ, filter=lambda f: provider is None or f.source == provider
|
||||
):
|
||||
util.progress(f"enumerating facts: {fact.data}")
|
||||
if fact.type not in facts:
|
||||
facts[fact.type] = {}
|
||||
if fact.source not in facts[fact.type]:
|
||||
facts[fact.type][fact.source] = []
|
||||
facts[fact.type][fact.source].append(fact)
|
||||
|
||||
util.erase_progress()
|
||||
|
||||
|
@ -33,6 +33,9 @@ def enumerate() -> Generator[FactData, None, None]:
|
||||
:return:
|
||||
"""
|
||||
|
||||
init = util.Init.UNKNOWN
|
||||
version = None
|
||||
|
||||
# Try to get the command name of the running init process
|
||||
try:
|
||||
with pwncat.victim.open("/proc/1/comm", "r") as filp:
|
||||
@ -62,16 +65,13 @@ def enumerate() -> Generator[FactData, None, None]:
|
||||
elif "upstart" in comm.lower():
|
||||
init = util.Init.UPSTART
|
||||
|
||||
try:
|
||||
with pwncat.victim.subprocess(f"{comm} --version", "r") as filp:
|
||||
version = filp.read().decode("utf-8").strip()
|
||||
if "systemd" in version.lower():
|
||||
init = util.Init.SYSTEMD
|
||||
elif "sysv" in version.lower():
|
||||
init = util.Init.SYSV
|
||||
elif "upstart" in version.lower():
|
||||
init = util.Init.UPSTART
|
||||
except:
|
||||
version = ""
|
||||
with pwncat.victim.subprocess(f"{comm} --version", "r") as filp:
|
||||
version = filp.read().decode("utf-8").strip()
|
||||
if "systemd" in version.lower():
|
||||
init = util.Init.SYSTEMD
|
||||
elif "sysv" in version.lower():
|
||||
init = util.Init.SYSV
|
||||
elif "upstart" in version.lower():
|
||||
init = util.Init.UPSTART
|
||||
|
||||
yield InitSystemData(init, version)
|
||||
|
@ -56,6 +56,7 @@ class Access(Flag):
|
||||
|
||||
class Init(Enum):
|
||||
|
||||
UNKNOWN = auto()
|
||||
SYSTEMD = auto()
|
||||
UPSTART = auto()
|
||||
SYSV = auto()
|
||||
|
@ -1,5 +1,5 @@
|
||||
colorama==0.4.3
|
||||
git+https://github.com/calebstewart/python-prompt-toolkit
|
||||
prompt-toolkit
|
||||
git+https://github.com/calebstewart/paramiko
|
||||
wcwidth==0.1.9
|
||||
netifaces==0.10.9
|
||||
|
1
setup.py
1
setup.py
@ -21,7 +21,6 @@ dependencies = [
|
||||
]
|
||||
|
||||
dependency_links = [
|
||||
"https://github.com/calebstewart/python-prompt-toolkit/tarball/master#egg=prompt-toolkit",
|
||||
"https://github.com/calebstewart/paramiko/tarball/master#egg=paramiko",
|
||||
"https://github.com/JohnHammond/base64io-python/tarball/master#egg=base64io",
|
||||
]
|
||||
|
Loading…
Reference in New Issue
Block a user