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

More changed logging

This commit is contained in:
Caleb Stewart 2020-06-29 20:43:44 -04:00
parent f1affd82c1
commit c6c194d1d3
7 changed files with 126 additions and 127 deletions

View File

@ -9,7 +9,7 @@ from sqlalchemy import exc as sa_exc
from sqlalchemy.exc import InvalidRequestError from sqlalchemy.exc import InvalidRequestError
import pwncat import pwncat
from pwncat import util from pwncat.util import console
from pwncat.remote import Victim from pwncat.remote import Victim
@ -60,9 +60,9 @@ def main():
sys.stdout.flush() sys.stdout.flush()
except ConnectionResetError: except ConnectionResetError:
pwncat.victim.restore_local_term() pwncat.victim.restore_local_term()
util.warn("connection reset by remote host") console.log("[yellow]warning[/yellow]: connection reset by remote host")
except SystemExit: except SystemExit:
util.success("closing down connection.") console.log("closing connection")
finally: finally:
# Restore the shell # Restore the shell
pwncat.victim.restore_local_term() pwncat.victim.restore_local_term()
@ -71,7 +71,7 @@ def main():
pwncat.victim.session.commit() pwncat.victim.session.commit()
except InvalidRequestError: except InvalidRequestError:
pass pass
util.success("local terminal restored") console.log("local terminal restored")
if __name__ == "__main__": if __name__ == "__main__":

View File

@ -5,7 +5,7 @@ from typing import Dict, Type, Tuple, Iterator
from colorama import Fore, Style from colorama import Fore, Style
import pwncat import pwncat
from pwncat import util from pwncat.util import console
from pwncat.commands.base import CommandDefinition, Complete, Parameter, StoreConstOnce from pwncat.commands.base import CommandDefinition, Complete, Parameter, StoreConstOnce
from pwncat.persist import PersistenceMethod, PersistenceError from pwncat.persist import PersistenceMethod, PersistenceError
@ -84,80 +84,65 @@ class Command(CommandDefinition):
# List of available persistence methods # List of available persistence methods
METHODS: Dict[str, Type["PersistenceMethod"]] = {} METHODS: Dict[str, Type["PersistenceMethod"]] = {}
@property def show_status(self):
def installed_methods(self) -> Iterator[Tuple[str, str, PersistenceMethod]]: """ Show the list of installed methods """
me = pwncat.victim.current_user
for method in pwncat.victim.persist: ninstalled = 0
if method.system and method.installed(): for user, method in pwncat.victim.persist.installed:
yield (method.name, None, method) console.print(f" - {method.format(user)} installed")
elif not method.system: ninstalled += 1
if me.id == 0: if not ninstalled:
for user in pwncat.victim.users: console.log("[yellow]warning[/yellow]: no persistence methods installed")
util.progress(f"checking {method.name} for: {user}")
if method.installed(user): def list_methods(self, method):
util.erase_progress() """ List available methods or help for a specific method """
yield (method.name, user, method)
util.erase_progress() if method:
else: try:
if method.installed(me.name): method = next(pwncat.victim.persist.find(method))
yield (method.name, me.name, method) console.print(f"[underline bold]{method.format()}")
console.print(textwrap.indent(textwrap.dedent(method.__doc__), " "))
except StopIteration:
console.log(f"[red]error[/red]: {method}: no such persistence method")
else:
for method in pwncat.victim.persist:
console.print(f" - {method.format()}")
def clean_methods(self):
""" Remove all persistence methods from the victim """
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()
def run(self, args): 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: try:
if args.action == "install": if args.action == "status":
pwncat.victim.persist.install(args.method, user) self.show_status()
elif args.action == "list":
self.list_methods(args.method)
elif args.action == "clean":
self.clean_methods()
elif args.action == "install":
pwncat.victim.persist.install(
args.method, args.user if args.user else pwncat.victim.whoami()
)
elif args.action == "remove": elif args.action == "remove":
pwncat.victim.persist.remove(args.method, user) pwncat.victim.persist.remove(
args.method, args.user if args.user else pwncat.victim.whoami()
)
elif args.method is None:
self.parser.error("no method specified")
return
except PersistenceError as exc: except PersistenceError as exc:
util.error(f"{exc}") console.log(f"[red]error[/red]: {exc}")

View File

@ -5,7 +5,7 @@ from sqlalchemy.orm import sessionmaker
import pwncat import pwncat
from pwncat.commands.base import CommandDefinition, Complete, Parameter from pwncat.commands.base import CommandDefinition, Complete, Parameter
from pwncat import util from pwncat.util import console, State
class Command(CommandDefinition): class Command(CommandDefinition):
@ -38,17 +38,17 @@ class Command(CommandDefinition):
found = False found = False
for name, user in pwncat.victim.users.items(): for name, user in pwncat.victim.users.items():
if user.password is not None: if user.password is not None:
print( console.print(
f" - {Fore.GREEN}{user}{Fore.RESET} -> {Fore.RED}{repr(user.password)}{Fore.RESET}" f" - [green]{user}[/green] -> [red]{repr(user.password)}[/red]"
) )
found = True found = True
if not found: if not found:
util.warn("no known user passwords") console.log("[yellow]warning[/yellow]: no known user passwords")
else: else:
if args.variable not in pwncat.victim.users: if args.variable not in pwncat.victim.users:
self.parser.error(f"{args.variable}: no such user") self.parser.error(f"{args.variable}: no such user")
print( console.print(
f" - {Fore.GREEN}{args.variable}{Fore.RESET} -> {Fore.RED}{repr(args.value)}{Fore.RESET}" f" - [green]{args.variable}[/green] -> [red]{repr(args.value)}[/red]"
) )
pwncat.victim.users[args.variable].password = args.value pwncat.victim.users[args.variable].password = args.value
else: else:
@ -58,9 +58,9 @@ class Command(CommandDefinition):
and args.value is not None and args.value is not None
): ):
try: try:
pwncat.victim.state = util.State._member_map_[args.value.upper()] pwncat.victim.state = State._member_map_[args.value.upper()]
except KeyError: except KeyError:
util.error(f"{args.value}: invalid state") console.log(f"[red]error[/red]: {args.value}: invalid state")
elif args.variable is not None and args.value is not None: elif args.variable is not None and args.value is not None:
try: try:
pwncat.victim.config[args.variable] = args.value pwncat.victim.config[args.variable] = args.value
@ -79,17 +79,15 @@ class Command(CommandDefinition):
) )
pwncat.victim.session = pwncat.victim.session_maker() pwncat.victim.session = pwncat.victim.session_maker()
except ValueError as exc: except ValueError as exc:
util.error(str(exc)) console.log(f"[red]error[/red]: {exc}")
elif args.variable is not None: elif args.variable is not None:
value = pwncat.victim.config[args.variable] value = pwncat.victim.config[args.variable]
print( console.print(
f" {Fore.CYAN}{args.variable}{Fore.RESET} = " f" [cyan]{args.variable}[/cyan] = [yellow]{repr(value)}[/yellow]"
f"{Fore.YELLOW}{repr(value)}{Fore.RESET}"
) )
else: else:
for name in pwncat.victim.config: for name in pwncat.victim.config:
value = pwncat.victim.config[name] value = pwncat.victim.config[name]
print( console.print(
f" {Fore.CYAN}{name}{Fore.RESET} = " f" [cyan]{name}[/cyan] = [yellow]{repr(value)}[/yellow]"
f"{Fore.YELLOW}{repr(value)}{Fore.RESET}"
) )

View File

@ -1,7 +1,7 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import pwncat import pwncat
from pwncat.commands.base import CommandDefinition, Complete, Parameter from pwncat.commands.base import CommandDefinition, Complete, Parameter
from pwncat import util from pwncat.util import console
import os import os
@ -25,7 +25,9 @@ class Command(CommandDefinition):
TERM = os.environ.get("TERM", None) TERM = os.environ.get("TERM", None)
if TERM is None: if TERM is None:
if not args.quiet: if not args.quiet:
util.warn("no local TERM set. falling back to 'xterm'") console.log(
"[yellow]warning[/yellow]: no local [blue]TERM[/blue]; falling back to 'xterm'"
)
TERM = "xterm" TERM = "xterm"
# Get the width and height # Get the width and height
@ -33,8 +35,11 @@ class Command(CommandDefinition):
# Update the state # Update the state
pwncat.victim.run( pwncat.victim.run(
f"stty rows {rows};" f"stty columns {columns};" f"export TERM='{TERM}'" f"stty rows {rows}; stty columns {columns}; export TERM='{TERM}'"
) )
if not args.quiet: if not args.quiet:
util.success("terminal state synchronized") console.log(
"[green]:heavy_check_mark:[/green] terminal state synchronized",
emoji=True,
)

View File

@ -1,6 +1,10 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
from typing import List
from rich.progress import Progress, BarColumn
import pwncat import pwncat
from pwncat import util from pwncat.util import console
from pwncat.commands.base import ( from pwncat.commands.base import (
CommandDefinition, CommandDefinition,
Complete, Complete,
@ -8,7 +12,7 @@ from pwncat.commands.base import (
StoreConstOnce, StoreConstOnce,
StoreForAction, StoreForAction,
) )
from pwncat.tamper import RevertFailed from pwncat.tamper import RevertFailed, Tamper
class Command(CommandDefinition): class Command(CommandDefinition):
@ -49,29 +53,36 @@ class Command(CommandDefinition):
if args.action == "revert": if args.action == "revert":
if args.all: if args.all:
removed_tampers = [] tampers = list(pwncat.victim.tamper)
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: else:
if args.tamper not in range(len(pwncat.victim.tamper)):
self.parser.error("invalid tamper id")
tamper = pwncat.victim.tamper[args.tamper]
try: try:
tampers = [pwncat.victim.tamper[args.tamper]]
except KeyError:
console.log("[red]error[/red]: invalid tamper id")
return
self.revert(tampers)
else:
for ident, tamper in enumerate(pwncat.victim.tamper):
console.print(f" [cyan]{ident}[/cyan] - {tamper}")
def revert(self, tampers: List[Tamper]):
""" Revert the list of tampers with a nice progress bar """
with Progress(
"[bold]reverting[/bold]",
"",
"{task.fields[tamper]}",
BarColumn(bar_width=None),
"[progress.percentage]{task.percentage:>3.1f}%",
console=console,
) as progress:
task = progress.add_task("reverting", tamper="init", total=len(tampers))
for tamper in tampers:
try:
progress.update(task, tamper=str(tamper))
tamper.revert() tamper.revert()
pwncat.victim.tamper.remove(tamper) pwncat.victim.tamper.remove(tamper)
except RevertFailed as exc: except RevertFailed as exc:
util.error(f"revert failed: {exc}") progress.log(f"[yellow]warning[/yellow]: revert failed: {exc}")
pwncat.victim.session.commit() progress.update(task, advance=1)
else: progress.update(task, tamper="complete")
for id, tamper in enumerate(pwncat.victim.tamper):
print(f" {id} - {tamper}")

View File

@ -630,14 +630,14 @@ class Finder:
privkey = None privkey = None
elif writers: elif writers:
# TODO this needs to be updated to work in the middle of a rich progress # TODO this needs to be updated to work in the middle of a rich progress
util.warn( console.log(
"no readers found for {Fore.GREEN}{techniques[0].user}{Fore.RESET}" f"[yellow]warning[/yellow]: no readers found for [green]{techniques[0].user}[/green] "
f"however, we do have a writer."
) )
util.warn(f"however, we do have a writer.") response = console.input(
response = confirm( "Would you like to clobber their authorized keys? (y/N) "
"would you like to clobber their authorized keys? ", suffix="(y/N) " ).lower()
) if response != "y":
if not response:
raise PrivescError("user aborted key clobbering") raise PrivescError("user aborted key clobbering")
# If we don't already know a private key, then we need a writer # If we don't already know a private key, then we need a writer

View File

@ -42,7 +42,7 @@ class CreatedFile(Tamper):
raise RevertFailed(str(exc)) raise RevertFailed(str(exc))
def __str__(self): def __str__(self):
return f"{Fore.RED}Created{Fore.RESET} file {Fore.CYAN}{self.path}{Fore.RESET}" return f"[red]Created[/red] file [cyan]{self.path}[/cyan]"
class ModifiedFile(Tamper): class ModifiedFile(Tamper):
@ -90,7 +90,7 @@ class ModifiedFile(Tamper):
raise RevertFailed(str(exc)) raise RevertFailed(str(exc))
def __str__(self): def __str__(self):
return f"{Fore.RED}Modified{Fore.RESET} {Fore.CYAN}{self.path}{Fore.RESET}" return f"[red]Modified[/red] [cyan]{self.path}[/cyan]"
def __repr__(self): def __repr__(self):
return f"ModifiedFile(path={self.path})" return f"ModifiedFile(path={self.path})"