1
0
mirror of https://github.com/calebstewart/pwncat.git synced 2024-11-30 20:34:15 +01:00
pwncat/docs/source/api/gtfobins.rst
2020-05-21 01:12:38 -04:00

230 lines
10 KiB
ReStructuredText

GTFOBins Abstraction Layer
==========================
``pwncat`` implements an abstraction of the fantastic GTFOBins_ project. This project catalogs
known methods of file read, file write and shell access with commonly accessible binaries.
The ``pwncat.gtfobins`` module along with the ``data/gtfobins.json`` database provides a
programmatic way of enumerating and searching for known GTFObins techniques for performing
various capabilities. It is able to generate payloads for gaining a shell, file read, and
file write in standard, SUID or sudo modes.
For the standard mode, ``gtfobins`` provides ``pwncat`` a way to generically refer to file
read and write operations without depending on specific remote binaries being available.
The likelihood of no methods of file read being available on a remote system is very low,
however the probability of something like ``dd`` to be missing (however odd that would be)
is much higher. In this way, things like ``pwncat.victim.open`` can operate in a generic
way without resulting in dependencies on specific remote binaries.
Further, the ``gtfobins`` modules has abstracted away the idea of SUID and sudo to provide a
uniform interface for generating payloads which gain file read/write or shell with known
SUID or sudo privileges. The ``gtfobins`` module knows how and where to insert special options
to enable taking advantage of SUID binaries and also knows how to parse sudo command
specifications to enumerate available binaries and produce payloads compatible with the given
sudo specification.
Module Organization
-------------------
The GTFObins module is at it's core a database lookup. Currently, this database is a JSON
file which generically describes a large subset of the greater GTFObins project and
describes how to build payloads for each binaries different capabilities.
The top-level module (the ``GTFOBins`` class) provides access to this database. It is
initialized with a path to the database file (``data/gtfobins.json``) and callable
which represents the ``which`` application for the target system. It should resolve
binary names into their fullpaths on the remote system. It also takes a second boolean
parameter which indicates where the returned string should be quoted as with ``shlex.quote``.
Payloads are generated from individual methods, which are all an implementation of the
``pwncat.gtfobins.Method`` class. A method is an implementation of a specific capability
for a specific binary. They contain the payload, command arguments, input and exit command
needed to execute a specific capability with a specific binary. These methods are defined
in the database, which will be described further down.
A ``pwncat.gtfobins.Binary`` object is instantiated for every binary described in the
database. Each binary is described simply by it's name and a list of methods taken
from the database. At a generic level, the binary doesn't know the path on the remote
system, which it will need to build a payload with any given method.
When enumerating methods, the ``Binary`` and ``GTFOBins`` objects will both return
instances of the ``MethodWrapper`` class. This class provides the actual payload
building mechanism. It is the glue that puts a specific binary path, SUID state and
sudo specification together with a specific ``Method`` object. You will not interact
with ``Method`` objects directly when using this module.
Retrieving a Method Wrapper
---------------------------
Method wrappers are created in three two ways. They can be built automatically by the
``GTFOBins`` object by iterating through all known binaries and using the provided
``which`` callable to locate valid remote binaries. This is done through the
``iter_methods`` function:
.. code-block:: python
for method in pwncat.victim.gtfo.iter_methods(Capability.READ, Stream.ANY):
print("We could read a file with {method.binary_path}!")
This works well when you don't need any special permissions, but just need to generate
a payload for a specific capability. You have no requirements beyond your capability.
However, sometimes you know a specific binary that you can use, but you're not sure
what you can do with it. This can happen when performing privilege escalation. Perhaps
you can run a specific binary as another user, but you'd like to leverage this for
more access. In this case, you can provide the binary path to the ``iter_binary``
method to iterate methods for that specific binary. In this case, the ``GTFOBins``
module will not utilize the ``which`` callable. It trusts you that the given binary
path you provided exists, and yields method wrappers for the capabilities you requested,
if any.
.. code-block:: python
for method in pwncat.victim.gtfo.iter_binary("/bin/bash", Capability.ALL, Stream.ANY):
print(f"We could perform {method.cap} with /bin/bash!")
The last way of generating a method wrapper is used when you know that a user can
run commands via sudo with a specific specification. You'd like to know if GTFObins can
provide any useful capabilities with this command. For this, you can use the
``iter_sudo`` method which will iterator over all methods which are capable of being
executed under the given sudo specification.
.. code-block:: python
for method in pwncat.victim.gtfo.iter_sudo("/usr/bin/git log*", caps=Capability.ALL):
print(f"You could perform {method.cap} with /usr/bin/git!")
``GTFOBins`` is able to parse the sudo command specification and identify if the allowed
parameters to the command overlap with the needed parameters for different methods. If
the specification is ``ALL`` or ends with an asterisk, this is often possible. If it doesn't,
then it will try to make the parameters fit the specification and decide if the
capabilitiy is feasible.
Generating a Payload by Capability
----------------------------------
Once you have identified a specific method (and have a method wrapper), generating a payload is
easy. The ``MethodWrapper`` class provides the ``build`` function which will be all components
of the payload. Each payload consists of three items:
* The base payload
* The input sent to the application
* The command used to exit the application
The base payload is the command sent to the target host which will trigger the action specified
by the method capability. The input is the a bytes object which is sent to the standard input
of the application to trigger the action. The command used to exit is a bytes object which when
sent to the applications standard input should cleanly exit the application and return the user
to a normal shell. The last two are optional, but may be required and should always be sent
if returned from ``build``. If a method doesn't need them, they will be empty bytes objects
and you can safely send them to the application anyway.
The ``build`` function takes variable arguments because the specific parameters required
for each capability are different:
* A SHELL capability requires the following arguments:
- shell: the shell to execute
* A READ capability requires the following arguments:
- lfile: the path to the local file to read
* A WRITE capability with a RAW stream requires the following arguments:
- lfile: the path to the local file to write to
- length: the number of bytes of data which will be written
* A WRITE capability with any other stream type requires:
- lfile: the path to the local file to write to
In the case of a read payload, the content of the file is assumed to be sent to standard output
of the command executed via the base payload. For write payloads, the new content for the file
is sent to the standard input of the base payload command **after** any input data returned from
the ``build`` function and **before** sending the exit bytes.
Putting It All Together
-----------------------
There's a lot of information up above, so here's an example of using the GTFOBins module. For
file read and file write. First up, we will read the ``/etc/passwd`` file and print the name
of all users on the remote system:
.. code-block:: python
from pwncat import victim
try:
# Find a reader from GTFObins
method = next(victim.gtfo.iter_methods(caps=Capability.READ, stream=Stream.ANY))
except StopIteration:
raise RuntimeError("no available gtfobins readers!")
# Build the payload
payload, input_data, exit_cmd = method.build(lfile="/etc/passwd")
# Run the payload on the remote host.
pipe = self.subprocess(
payload,
"r",
data=input_data.encode("utf-8"),
exit_cmd=exit_cmd.encode("utf-8"),
name=path,
)
# Wrap the pipe in the decoder for this method (possible base64)
with method.wrap_stream(pipe) as pipe:
for line in pipe:
line = line.decode("utf-8").strip()
print("Found user:", line.split(":")[0])
This might seem long and laberous, but it is infinitely better than depending on a specific
file read method or attempting to account for multiple read methods each time you want to read
a file (although, luckily ``pwncat.victim.open`` already wraps this for you ;). Next, we'll
take a look at writing a file.
.. code-block:: python
from pwncat import victim
# The data we will write
data = b"Hello from a new file!"
try:
# Find a writer from GTFObins
method = next(victim.gtfo.iter_methods(caps=Capability.WRITE, stream=Stream.RAW))
except StopIteration:
raise RuntimeError("no available gtfobins readers!")
# Build the payload
payload, input_data, exit_cmd = method.build(lfile="/tmp/new-file", length=len(data))
# Run the payload on the remote host.
pipe = self.subprocess(
payload,
"w",
data=input_data.encode("utf-8"),
exit_cmd=exit_cmd.encode("utf-8"),
name=path,
)
with method.wrap_stream(pipe) as pipe:
pipe.write(data)
GTFOBins Utility Classes
------------------------
.. autoclass:: pwncat.gtfobins.Capability
:members:
.. autoclass:: pwncat.gtfobins.Stream
:members:
The GTFOBins Object
-------------------
.. autoclass:: pwncat.gtfobins.GTFOBins
:members:
The MethodWrapper Object
------------------------
.. autoclass:: pwncat.gtfobins.MethodWrapper
:members:
.. _GTFOBins: https://gtfobins.github.io