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
|
commandparser.rst
|
||||||
privesc.rst
|
privesc.rst
|
||||||
|
persist.rst
|
||||||
victim.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
|
#!/usr/bin/env python3
|
||||||
import functools
|
import functools
|
||||||
import pkgutil
|
import pkgutil
|
||||||
from typing import Optional, Dict, Iterator
|
from typing import Optional, Dict, Iterator, Tuple
|
||||||
from colorama import Fore
|
from colorama import Fore
|
||||||
|
|
||||||
import pwncat
|
import pwncat
|
||||||
@ -20,6 +20,13 @@ def persistence_tamper_removal(name: str, user: Optional[str] = None):
|
|||||||
|
|
||||||
|
|
||||||
class Persistence:
|
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):
|
def __init__(self):
|
||||||
|
|
||||||
self.methods: Dict[str, "PersistenceMethod"] = {}
|
self.methods: Dict[str, "PersistenceMethod"] = {}
|
||||||
@ -29,8 +36,17 @@ class Persistence:
|
|||||||
self.methods[method.name] = method
|
self.methods[method.name] = method
|
||||||
|
|
||||||
def install(self, name: str, user: Optional[str] = None):
|
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:
|
try:
|
||||||
method = next(self.find(name))
|
method = next(self.find(name))
|
||||||
except StopIteration:
|
except StopIteration:
|
||||||
@ -45,9 +61,17 @@ class Persistence:
|
|||||||
self.register(name, user)
|
self.register(name, user)
|
||||||
|
|
||||||
def register(self, name: str, user: Optional[str] = None):
|
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
|
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))
|
method = next(self.find(name))
|
||||||
|
|
||||||
@ -61,47 +85,54 @@ class Persistence:
|
|||||||
)
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def installed(self) -> Iterator["PersistenceMethod"]:
|
def installed(self) -> Iterator[Tuple[str, "PersistenceMethod"]]:
|
||||||
""" Retrieve a list of installed persistence methods """
|
"""
|
||||||
|
Enumerate all installed persistence methods.
|
||||||
|
|
||||||
|
:return: An iterator of tuples of (username,PeristenceMethod)
|
||||||
|
"""
|
||||||
for persist in pwncat.victim.host.persistence:
|
for persist in pwncat.victim.host.persistence:
|
||||||
yield persist.user, self.methods[persist.method]
|
yield persist.user, self.methods[persist.method]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def available(self) -> Iterator[str]:
|
def available(self) -> Iterator["PersistenceMethod"]:
|
||||||
""" Yield all the known methods """
|
"""
|
||||||
|
Enumerate all available persistence methods
|
||||||
|
|
||||||
|
:return: Iterator of available persistence methods
|
||||||
|
"""
|
||||||
yield from self.methods.values()
|
yield from self.methods.values()
|
||||||
|
|
||||||
def find(
|
def find(self, name: Optional[str] = None,) -> Iterator["PersistenceMethod"]:
|
||||||
self,
|
"""
|
||||||
name: Optional[str] = None,
|
Locate persistence methods matching the given name.
|
||||||
user: Optional[str] = None,
|
|
||||||
installed: bool = False,
|
:param name: the name of the persistence module to locate
|
||||||
local: Optional[bool] = None,
|
:return: Iterator of persistence methods matching the name
|
||||||
system: Optional[bool] = None,
|
"""
|
||||||
) -> Iterator["PersistenceMethod"]:
|
|
||||||
|
|
||||||
for method in self.methods.values():
|
for method in self.methods.values():
|
||||||
if name is not None and method.name != name:
|
if name is not None and method.name != name:
|
||||||
# not the requested method
|
# not the requested method
|
||||||
continue
|
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.
|
# All checks passed. Yield the method.
|
||||||
yield method
|
yield method
|
||||||
|
|
||||||
def remove(self, name: str, user: Optional[str] = None, from_tamper: bool = False):
|
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"
|
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:
|
try:
|
||||||
method = next(self.find(name))
|
method = next(self.find(name))
|
||||||
except StopIteration:
|
except StopIteration:
|
||||||
@ -138,7 +169,9 @@ class Persistence:
|
|||||||
|
|
||||||
|
|
||||||
class PersistenceMethod:
|
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):
|
def __init__(self):
|
||||||
pass
|
pass
|
||||||
@ -162,9 +195,21 @@ class PersistenceMethod:
|
|||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
def install(self, user: Optional[str] = None):
|
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
|
raise NotImplementedError
|
||||||
|
|
||||||
def remove(self, user: Optional[str] = None):
|
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
|
raise NotImplementedError
|
||||||
|
|
||||||
def installed(self, user: Optional[str] = None) -> bool:
|
def installed(self, user: Optional[str] = None) -> bool:
|
||||||
|
Loading…
Reference in New Issue
Block a user