pwncat.platform package¶
A platform is the pwncat abstraction for an OS or specific distribution. In general,
this abstraction allows pwncat to generically interact with targets at the OS level.
For example, a platform provides a pathlib.Path
implementation which provides
seamless file access. A platform also defines ways to query environment variables,
get the current user ID and name and generically start processes.
An individual platform must define a set of methods within it’s Platform
class
for file abstraction, process abstraction, and user abstraction. These methods are
then used by the generic Path
and Popen
classes to abstract interaction
with the target.
Normally, you can access a platform through a session. Every session has a platform property which returns a platform-specific implementation of the core methods outlined below.
pathlib-like File Abstraction¶
Each platform sets the Path
property to a class which glues our generic Path
class below to either PureWindowsPath
or PureLinuxPath
. You can construct
a session-specific path object by utilizing the session.platform.Path
property.
path = session.platform.Path("/etc/passwd")
print(path.read_text())
- pwncat.platform.PLATFORM_TYPES = {'linux': <class 'pwncat.platform.linux.Linux'>, 'windows': <class 'pwncat.platform.windows.Windows'>}¶
A dictionary of platform names mapping to their class objects. This drives the
pwncat.platform.create
factory function.
- class pwncat.platform.Path¶
Bases:
object
A Concrete-Path. An instance of this class is bound to a specific victim, and supports all semantics of a standard pathlib concrete Path.
- chmod(mode: int)¶
Modify file unix permissions
- Parameters
mode (int) – unix permission bits
- classmethod cwd() pwncat.platform.Path ¶
Return a new concrete path referencing the current directory
- exists() bool ¶
Test if the path exists on the target system
- expanduser() pwncat.platform.Path ¶
Return a new path object which represents the full path to the file expanding any
~
or~user
components.
- glob(pattern: str) Generator[pwncat.platform.Path, None, None] ¶
Glob the given relative pattern in the directory represented by this path, yielding Path objects for any matching files/directories.
- group() str ¶
Returns the name of the group owning the file. KeyError is raised if the file’s GID isn’t found in the system database.
- classmethod home() pwncat.platform.Path ¶
Return a new concrete path referencing the current user home directory
- is_block_device() bool ¶
Returns True if the path points to a block device
- is_char_device() bool ¶
Returns True if the path points to a character device
- is_dir() bool ¶
Returns True if the path points to a directory (or a symbolic link pointing to a directory). False if it points to another kind of file.
- is_fifo() bool ¶
Returns True if the path points to a FIFO
- is_file() bool ¶
Returns True if the path points to a regular file
- is_mount() bool ¶
Returns True if the path is a mount point.
- is_socket() bool ¶
Returns True if the path points to a Unix socket
- is_symlink() bool ¶
Returns True if the path points to a symbolic link, False otherwise
- iterdir() bool ¶
When the path points to a directory, yield path objects of the directory contents.
- lchmod(mode: int)¶
Modify a symbolic link’s mode (same as chmod for non-symbolic links)
- link_to(target)¶
Create a hard link pointing to a path named target
- lstat() os.stat_result ¶
Same as stat except operate on the symbolic link file itself rather than the file it points to.
- mkdir(mode: int = 511, parents: bool = False, exist_ok: bool = False)¶
Create a new directory at this given path.
- open(mode: str = 'r', buffering: int = - 1, encoding: Optional[str] = None, errors: Optional[str] = None, newline: Optional[str] = None)¶
Open the file pointed to by the path, like Platform.open
- owner() str ¶
Return the name of the user owning the file. KeyError is raised if the file’s uid is not found in the System database
- parts = []¶
- read_bytes() bytes ¶
Return the binary contents of the pointed-to file as a bytes object
- read_text(encoding: Optional[str] = None, errors: Optional[str] = None) str ¶
Return the decoded contents of the pointed-to file as a string
- readable() bool ¶
Test if this file is readable based on the stat results and the sessions’ current user/group ID.
- readlink() pwncat.platform.Path ¶
Return the path to which the symbolic link points
- rename(target) pwncat.platform.Path ¶
Rename the file or directory to the given target (str or Path).
- replace(target) pwncat.platform.Path ¶
Same as rename for Linux
- resolve(strict: bool = False)¶
Resolve the current path into an absolute path
- rglob(pattern: str) Generator[pwncat.platform.Path, None, None] ¶
This is like calling Path.glob() with “**/” added to in the front of the given relative pattern
- rmdir()¶
Remove this directory. The directory must be empty.
- samefile(otherpath: pwncat.platform.Path)¶
Return whether this path points to the same file as other_path which can be either a Path object or a string.
- stat() os.stat_result ¶
Request file stat details
- symlink_to(target, target_is_directory: bool = False)¶
Make this path a symbolic link to target.
- touch(mode: int = 438, exist_ok: bool = True)¶
Create a file at this path. If the file already exists, function succeeds if exist_ok is true (and it’s modification time is updated). Otherwise FileExistsError is raised.
- unlink(missing_ok: bool = False)¶
Remove the file or symbolic link.
- writable() bool ¶
Test if this file is writable based on the stat results and the sessions current user/group ID.
- write_bytes(data: bytes)¶
Open the file pointed to in bytes mode and write data to it.
- write_text(data: str, encoding: Optional[str] = None, errors: Optional[str] = None)¶
Open the file pointed to in text mode, and write data to it.
- class pwncat.platform.Platform(session: pwncat.manager.Session, channel: pwncat.channel.Channel, log: Optional[str] = None, verbose: bool = False)¶
Bases:
abc.ABC
Abstracts interactions with a target of a specific platform. This includes running commands, changing directories, locating binaries, etc.
During construction, the channel
connect
method is called to complete any outstanding requirements for connecting the channel. If the channel is not connected after this, aPlatformError
is raised.Platform’s are not created directly, but can be instantiated through the manager
create_session
method.- Parameters
session – a session object to which this platform is bound
channel (pwncat.channel.Channel) – an open a channel with the specified platform
log (str) – path to a log file which logs each command executed for this platform
- Path¶
A concrete Path object for this platform conforming to pathlib.Path
- abstract Popen(args, stdin=None, stdout=None, stderr=None, shell=False, cwd=None, encoding=None, errors=None, text=None, env=None, universal_newlines=None, **other_popen_kwargs) pwncat.subprocess.Popen ¶
Execute a process on the remote host with an interface similar to that of the python standard
subprocess.Popen
. The returned object behaves much like a standardPopen
object and conforms to the interface defined bypwncat.subprocess.Popen
. For an explanation of arguments, seepwncat.subprocess.Popen
.
- abstract abspath(path: str) str ¶
Attempt to resolve a path to an absolute path
- abstract chdir(path: Union[str, pwncat.platform.Path])¶
Change directories to the given path. This method returns the current working directory prior to the change.
- Parameters
path (Union[str, Path]) – a relative or absolute path to change to
- Returns
current working directory prior to the change
- Raises
FileNotFoundError: the specified path doesn’t exist NotADirectoryError: the specified path is not a directory
- abstract chmod(path: str, mode: int, link: bool = False)¶
Update the file permissions
- compile(sources: List[Union[str, BinaryIO]], output: Optional[str] = None, suffix: Optional[str] = None, cflags: Optional[List[str]] = None, ldflags: Optional[List[str]] = None) str ¶
Attempt to compile the given C source files into a binary suitable for the remote host. If a compiler exists on the remote host, prefer compilation locally. If no compiler exists on the remote remote host, check the cross global config variable for the path to a local compiler capable of generating binaries for the remote host. If the binary is compiled locally, it is automatically uploaded to the remote host. The path to the new binary on the victim is returned.
- Parameters
sources (List[Union[str, io.IOBase]]) – list of source file paths or IO streams used as source files
output (str) – base name of the output file. If not specified, a name is randomly generated.
suffix (str) – a suffix to add to the output name.
cflags (List[str]) – a list of flags to pass to the compiler
ldflags (List[str]) – a list of flags to pass to the linker
- Returns
str
- Raises
NotImplementedError – this platform does not support c compilation
PlatformError – no local or cross compiler detected or compilation failed
- abstract exit()¶
Exit this session
- abstract get_host_hash() str ¶
Retrieve a string which uniquely identifies this victim host. On Unix-like platforms, this retrieves the hostname and MAC addresses of any available network interfaces and computes a hash, which should be unique regardless of connection method.
- Returns
a unique string (normally a hash) identifying this host
- Return type
str
- abstract getenv(name: str) str ¶
Get the value of an environment variable.
- Parameters
name (str) – the name of the environment variable
- Return type
str
- abstract getuid() Union[int, str] ¶
Get the current user ID. This should not query the target, but should return the current cached UID as found with refresh_uid.
- interactive_loop(interactive_complete: threading.Event)¶
Handles interactive piping of data between victim and attacker. If the platform you are implementing does not support raw mode, you must override this method to support interactivity. A working example with the python readline module exists in the windows platform. Linux uses this default implementation.
- abstract link_to(source: str, target: str)¶
Create a filesystem hard link.
- abstract listdir(path=None) Generator[str, None, None] ¶
List the contents of a directory. If
path
is None, then the contents of the current directory is listed. The list is not guaranteed to be sorted in any way.- Parameters
path (str or Path-like) – the directory to list
- Raises
FileNotFoundError – When the requested directory is not a directory, does not exist, or you do not have execute permissions.
- abstract lstat(path: str) os.stat_result ¶
Run stat on the symbolic link and return a stat result object. This has the same semantics as the stat method.
- property manager¶
Shortcut to accessing the manager
- abstract mkdir(path: str, mode: int = 511, parents: bool = False)¶
Create a new directory
- name = None¶
Name of this platform (e.g. “linux”)
- abstract open(path: Union[str, pwncat.platform.Path], mode: str)¶
Open a remote file for reading or writing. Normally, only one of read or write modes are allowed for a remote file, but this may change with future platforms. It is recommended to only use one mode when opening remote files. This method attempts to replicate the built-in
open
function and returns a file-like object. The b mode is honored and if not present, a TextIOWrapper is used to wrap the file object to ensure text data is returned.- Parameters
path (Union[str, Path]) – path to the file
mode (str) – the open-mode (see built-in
open
)
- Returns
a file-like object
- Raises
FileNotFoundError: the specified file does not exist IsADirectoryError: the specified path refers to a directory
- process_output(data)¶
Called during interactivity to handle output from the victim. It can mutate the output and return a changed value if needed. It does nothing by default.
- abstract readlink(path: str)¶
Attempt to read the target of a link
- abstract refresh_uid() Union[int, str] ¶
Refresh the cached UID of the current session.
- abstract rename(source: str, target: str)¶
Rename a file from the source to the target. This should replace the target if it exists.
- abstract rmdir(target: str)¶
Remove the specified directory. It must be empty.
- run(args, stdin=None, input=None, stdout=None, stderr=None, capture_output=False, shell=False, cwd=None, timeout=None, check=False, encoding=None, errors=None, text=None, env=None, universal_newlines=None, popen_class=None, **other_popen_kwargs) pwncat.subprocess.Popen ¶
Run the given command utilizing the
self.popen
method and return apwncat.subprocess.CompletedProcess
instance.
- set_verbose(verbose: bool)¶
Enable or disable verbose output If enabled, commands that are executed by pwncat are logged in the output for the user otherwise pwncat do not show them
- abstract stat(path: str) os.stat_result ¶
Run stat on a path on the remote system and return a stat result This is mainly used by the concrete Path type to fill in a majority of it’s methods. If the specified path does not exist or cannot be accessed, a FileNotFoundError or PermissionError is raised respectively
- Parameters
path (str) – path to a remote file
- su(user: str, password: Optional[str] = None)¶
Attempt to switch users in the running shell. This normally executes a new sub-shell as the requested user. On unix-like systems, this is simply a wrapper for the
su
command. Implementations may differ on other systems. If a password isn’t provided, the database will be consulted for a matching username and password.- Parameters
user (str) – the name of the new user
password (str) – the password for the new user
- Raises
PermissionError: the provided password was incorrect
- sudo(command: Union[str, List[str]], user: Optional[str] = None, group: Optional[str] = None, **popen_kwargs)¶
Run the specified command as the specified user and group. On unix-like systems the normally translates to the
sudo
command. The command is executed using theself.popen
method. All arguments not documented here are passed directly toself.popen
. The process is executed and if a password is required, it is sent from the database. If a password is not available, the process is killed and a PermissionError is raised. If the password is incorrect, a PermissionError is also raised.
- abstract symlink_to(source: str, target: str)¶
Create a symbolic link to source from target
- abstract tempfile(mode: str, length: Optional[int] = None, suffix: Optional[str] = None)¶
Create a temporary file on the remote host and open it with the specified mode. Creating a new temporary file with a mode other than “w” is mostly useless, however
mode
can be used to specify a binary or text-mode file. The length argument is useful if you know the length of file you are about to read. This alleviates some situations which could be complicated on some platforms by not knowing the intended file length prior to opening. Optionally, a suffix can be added to the random file name. A file-like object is returned. The temporary file is not removed by pwncat itself. Unless explicitly removed, it will continue to exist until the remote operating system cleans up temporary files (possible at the next reboot).- Parameters
mode (str) – the open-mode for the new file-like object
length (int) – the intended length for the new file
suffix (str) – a suffix for the filename
- Returns
a file-like object
- abstract touch(path: str)¶
Update a file modification time and possibly create it
- abstract umask(mask: Optional[int] = None)¶
Set or retrieve the current umask value
- abstract unlink(target: str)¶
Remove a link to a file (similar to rm)
- which(name: str, **kwargs) str ¶
Locate the specified binary on the remote host. Normally, this is done through the local which command on the remote host (for unix-like hosts), but can be located by any means. The returned path string is guaranteed to exist on the remote host and provide the capabilities of the requested binary.
- Parameters
name (Union[list, str]) – name of the binary (e.g. “tar” or “dd”)
- Returns
full path to the requested binary
- Return type
str
- Raises
FileNotFoundError: the requested binary does not exist on this host
- abstract whoami()¶
Retrieve’s only name of the current user (may be faster depending on platform)
- exception pwncat.platform.PlatformError¶
Bases:
Exception
Generic platform error.
- pwncat.platform.create(platform: str, log: Optional[str] = None, channel: Optional[pwncat.channel.Channel] = None, **kwargs)¶
Create a new platform object with a registered platform type. If no channel is specified, then this will attempt to utilize the
pwncat.channel.create
factory function to create a channel. In this case, all keyword arguments are passed to the channel creation function and a platform is created around the channel.- Parameters
platform (str) – the name of the platform to construct
channel (pwncat.channel.Channel) – the C2 channel to use for communication
- Returns
A newly created platform around the specified channel
- Return type
- Raises
KeyError: if the specified platform does not exist ChannelError: if a channel could not be created
- pwncat.platform.find(name: str) Type[pwncat.platform.Platform] ¶
Retrieve the platform class for the specified name
- Parameters
name (str) – name of the platform
- Returns
the platform class
- Return type
Type[Platform]
- Raises
KeyError: if the specified platform does not exist
- pwncat.platform.register(platform: Type[pwncat.platform.Platform])¶
Register a platform class to be automatically constructed with the
create
factory function with the given name. This can be used to register new custom platforms in plugins.- Parameters
name (str) – the name of the new platform
platform (Type[Platform]) – the platform class