mirror of
https://github.com/calebstewart/pwncat.git
synced 2024-11-27 19:04:15 +01:00
Added persistence documentation
This commit is contained in:
parent
fce965c0c8
commit
a1e819d06d
@ -12,4 +12,5 @@ prompt commands or more complicated privilege escalation or persistence methods.
|
||||
|
||||
commandparser.rst
|
||||
privesc.rst
|
||||
persist.rst
|
||||
victim.rst
|
||||
|
78
docs/source/api/persist.rst
Normal file
78
docs/source/api/persist.rst
Normal file
@ -0,0 +1,78 @@
|
||||
Persistence Methods
|
||||
===================
|
||||
|
||||
Persistence methods are implemented through an abstract ``PersistenceMethod`` base class which defines methods
|
||||
for installing and removing various persistence methods. The persistence module and associated base classes
|
||||
are defined in the ``pwncat/persist/__init__.py`` script.
|
||||
|
||||
Persistence methods are loaded using the ``pkgutil`` python module automatically from the ``pwncat/persist``
|
||||
subdirectory. Any module implementing a ``Method`` class which inherits from the ``PersistenceMethod``
|
||||
base class will be imported as an available persistence method.
|
||||
|
||||
Implementing Persistence Methods
|
||||
--------------------------------
|
||||
|
||||
A persistence method is implemented by creating a new script under the ``pwncat/persist`` directory
|
||||
which implements a ``Method`` class. This class must inherit from the ``PersistenceMethod`` base
|
||||
class and implement the ``install`` and ``remove`` functions.
|
||||
|
||||
A privilege escalation method also defines a few properties as class variables which govern how
|
||||
the method is utilized by ``pwncat``. The ``system`` variable is a boolean defining whether this
|
||||
persistence method allows access only as ``root`` or is installed on a per-user basis. If this
|
||||
item is true, all ``user`` options in further methods are ignored.
|
||||
|
||||
The ``name`` variable is used to create user-readable formatted strings representing this persistence
|
||||
method. Lastly, the ``local`` variable is a boolean defining whether the persistence method allows
|
||||
local escalation to the specified user.
|
||||
|
||||
Three methods can be overridden within the ``PersistenceMethod`` base class. The first is the ``install``
|
||||
method. This method takes a ``user`` parameter (if not a system method), and should install persistence
|
||||
as the specified user. If there is a problem or error during installation, ``PersistenceError`` should
|
||||
be raised with a description of the error.
|
||||
|
||||
Next, the ``remove`` method must be implemented to undo the actions of the ``install`` method. It takes
|
||||
the same ``user`` argument, and upon error should also raise ``PersistenceError``.
|
||||
|
||||
Lastly, the ``escalate`` method is only required if ``local`` is true. It should leverage this
|
||||
persistence method to gain access shell access as the specified user (again, user should be ignored
|
||||
for system methods). This is used as a shortcut in the implementation of the ``privesc`` command
|
||||
to utilize local persistence methods to escalate to different users.
|
||||
|
||||
Locating and Installing Persistence Methods
|
||||
-------------------------------------------
|
||||
|
||||
If you would like to programmatically install, remove or locate privilege escalation methods,
|
||||
you can use the ``pwncat.victim.persist`` module. This module provides a generic interface
|
||||
to enumerate available methods, list installed methods, and locate methods by name.
|
||||
|
||||
The ``install`` method takes a method name and an optional user. This will locate the identified
|
||||
method and call it's ``install`` routine. If installation is successful, it will register the
|
||||
method in the database as installed and also register a corresponding tamper object to track
|
||||
the installation. If the method does not exist or failed to install, a ``PersistenceError``
|
||||
exception is raised.
|
||||
|
||||
The ``register`` method takes the same parameters as the ``install`` method. It will register the
|
||||
specified method as being installed but not perform the installation routine. This is useful
|
||||
when a module installs a persistence method in a non-standard way and needs to register this
|
||||
installation with the framework. For example, the ``privesc`` module may install SSH authorized
|
||||
keys via a privesc file writer. If this happens, it will register this persistence with the ``persist``
|
||||
module for tracking.
|
||||
|
||||
The ``remove`` is the inverse of the ``install`` method, and will completely remove the given
|
||||
persistence method.
|
||||
|
||||
To find a persistence method by name, you can use the ``find`` method which returns an iterator
|
||||
of known persistence methods. The ``persist`` module is also an iterator which will yield all
|
||||
known persistence methods.
|
||||
|
||||
The Persistence Module Class
|
||||
----------------------------
|
||||
|
||||
.. autoclass:: pwncat.persist.Persistence
|
||||
:members:
|
||||
|
||||
The Base Persistence Method Class
|
||||
---------------------------------
|
||||
|
||||
.. autoclass:: pwncat.persist.PersistenceMethod
|
||||
:members:
|
@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python3
|
||||
import functools
|
||||
import pkgutil
|
||||
from typing import Optional, Dict, Iterator
|
||||
from typing import Optional, Dict, Iterator, Tuple
|
||||
from colorama import Fore
|
||||
|
||||
import pwncat
|
||||
@ -20,6 +20,13 @@ def persistence_tamper_removal(name: str, user: Optional[str] = None):
|
||||
|
||||
|
||||
class Persistence:
|
||||
"""
|
||||
This class abstracts the management of persistence methods and is accessible at runtime
|
||||
via ``pwncat.victim.persist``. It provides methods of enumerating available persistence
|
||||
methods, enumerating installed persistence methods, installing methods, and removing
|
||||
methods.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
|
||||
self.methods: Dict[str, "PersistenceMethod"] = {}
|
||||
@ -29,8 +36,17 @@ class Persistence:
|
||||
self.methods[method.name] = method
|
||||
|
||||
def install(self, name: str, user: Optional[str] = None):
|
||||
""" Add persistence as the specified user. If the specified persistence
|
||||
method is system method, the "user" argument is ignored. """
|
||||
"""
|
||||
Install the specified method by name. If the specified method is not a system
|
||||
method, ``user`` specifies the user to install this method as. Otherwise, the
|
||||
``user`` parameter is ignored.
|
||||
|
||||
This method raises a PersistenceError if installation failed or the given method
|
||||
does not exist.
|
||||
|
||||
:param name: the name of the persistence method to install
|
||||
:param user: the user to install persistence as
|
||||
"""
|
||||
try:
|
||||
method = next(self.find(name))
|
||||
except StopIteration:
|
||||
@ -45,9 +61,17 @@ class Persistence:
|
||||
self.register(name, user)
|
||||
|
||||
def register(self, name: str, user: Optional[str] = None):
|
||||
""" Register a persistence method as pre-installed. This is useful for some privilege escalation
|
||||
"""
|
||||
Register a persistence method as pre-installed. This is useful for some privilege escalation
|
||||
which automatically adds things equivalent to persistent, but without the
|
||||
persistence module itself (e.g. backdooring /etc/passwd or SSH keys). """
|
||||
persistence module itself (e.g. backdooring /etc/passwd or SSH keys).
|
||||
|
||||
This method raises a PersistenceError if the given persistence method
|
||||
does not exist.
|
||||
|
||||
:param name: the method to register as pre-installed
|
||||
:param user: the user the method was installed as
|
||||
"""
|
||||
|
||||
method = next(self.find(name))
|
||||
|
||||
@ -61,47 +85,54 @@ class Persistence:
|
||||
)
|
||||
|
||||
@property
|
||||
def installed(self) -> Iterator["PersistenceMethod"]:
|
||||
""" Retrieve a list of installed persistence methods """
|
||||
def installed(self) -> Iterator[Tuple[str, "PersistenceMethod"]]:
|
||||
"""
|
||||
Enumerate all installed persistence methods.
|
||||
|
||||
:return: An iterator of tuples of (username,PeristenceMethod)
|
||||
"""
|
||||
for persist in pwncat.victim.host.persistence:
|
||||
yield persist.user, self.methods[persist.method]
|
||||
|
||||
@property
|
||||
def available(self) -> Iterator[str]:
|
||||
""" Yield all the known methods """
|
||||
def available(self) -> Iterator["PersistenceMethod"]:
|
||||
"""
|
||||
Enumerate all available persistence methods
|
||||
|
||||
:return: Iterator of available persistence methods
|
||||
"""
|
||||
yield from self.methods.values()
|
||||
|
||||
def find(
|
||||
self,
|
||||
name: Optional[str] = None,
|
||||
user: Optional[str] = None,
|
||||
installed: bool = False,
|
||||
local: Optional[bool] = None,
|
||||
system: Optional[bool] = None,
|
||||
) -> Iterator["PersistenceMethod"]:
|
||||
def find(self, name: Optional[str] = None,) -> Iterator["PersistenceMethod"]:
|
||||
"""
|
||||
Locate persistence methods matching the given name.
|
||||
|
||||
:param name: the name of the persistence module to locate
|
||||
:return: Iterator of persistence methods matching the name
|
||||
"""
|
||||
for method in self.methods.values():
|
||||
if name is not None and method.name != name:
|
||||
# not the requested method
|
||||
continue
|
||||
if installed:
|
||||
if user is not None or system is None or method.system == system:
|
||||
if not method.installed(user):
|
||||
continue
|
||||
else:
|
||||
# the user was not specified and this module is not a
|
||||
# system module. We can't check install state, so we
|
||||
# err on the side of caution here.
|
||||
continue
|
||||
if local is not None and method.local != local:
|
||||
continue
|
||||
# All checks passed. Yield the method.
|
||||
yield method
|
||||
|
||||
def remove(self, name: str, user: Optional[str] = None, from_tamper: bool = False):
|
||||
""" Remove the specified persistence method from the remote victim
|
||||
"""
|
||||
Remove the specified persistence method from the remote victim
|
||||
if the given persistence method is a system method, the "user"
|
||||
argument is ignored. """
|
||||
argument is ignored.
|
||||
|
||||
Raises a ``PersistenceError`` if the given method doesn't exist or removal
|
||||
failed.
|
||||
|
||||
The ``from_tamper`` parameter should not be used and is only used
|
||||
for internal removal from within the tamper subsystem.
|
||||
|
||||
:param name: the name of the method to remove
|
||||
:param user: the user which was used to install this method
|
||||
:param from_tamper: whether we are removing from the tamper removal system
|
||||
"""
|
||||
try:
|
||||
method = next(self.find(name))
|
||||
except StopIteration:
|
||||
@ -138,7 +169,9 @@ class Persistence:
|
||||
|
||||
|
||||
class PersistenceMethod:
|
||||
""" Base persistence method class """
|
||||
""" Base persistence method class. The docstring for your method class will
|
||||
become the long-form help for this method (viewable with ``persist -l -m {method-name}``)
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
@ -162,9 +195,21 @@ class PersistenceMethod:
|
||||
raise NotImplementedError
|
||||
|
||||
def install(self, user: Optional[str] = None):
|
||||
"""
|
||||
Install this method of persistence as the given user. Raise a
|
||||
``PersistenceError`` if installation fails.
|
||||
|
||||
:param user: the user to install persistence as
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def remove(self, user: Optional[str] = None):
|
||||
"""
|
||||
Remove this method of persistence as the given user. Raise a
|
||||
``PersistenceError`` if removal fails.
|
||||
|
||||
:param user: the user to remove persistence as
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def installed(self, user: Optional[str] = None) -> bool:
|
||||
|
Loading…
Reference in New Issue
Block a user