From 9dda88c91c52f9fd73bf644d1e603ae799d50e5d Mon Sep 17 00:00:00 2001 From: John Hammond Date: Sun, 2 May 2021 14:07:40 -0400 Subject: [PATCH 1/3] Added ZODB 5.6.0 to requirements.txt --- requirements.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index e9cd708..cf4f650 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -git+https://github.com/JohnHammond/base64io-python +-git+https://github.com/JohnHammond/base64io-python bcrypt==3.1.7 certifi==2020.6.20 cffi==1.14.0 @@ -36,3 +36,4 @@ typing-extensions==3.7.4.2 urllib3==1.25.9 wcwidth==0.1.9 python-rapidjson==0.9.1 +ZODB==5.6.0 \ No newline at end of file From 2f39c976a6b9daf7c588ef833a00356cd8879513 Mon Sep 17 00:00:00 2001 From: John Hammond Date: Sun, 2 May 2021 14:57:44 -0400 Subject: [PATCH 2/3] Updated requirements, README and manager things --- README.md | 2 + pwncat/manager.py | 28 +++++------ pwncat/platform/__init__.py | 96 ++++++++++++++++++------------------- requirements.txt | 3 +- 4 files changed, 66 insertions(+), 63 deletions(-) diff --git a/README.md b/README.md index 14fd985..fd986e1 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,8 @@ refer to the documentation for up to date usage and API documentation! pwncat [documentation] is being built out on Read the Docs. Head there for the latest usage and development documentation! +**pwncat requires Python 3.9+.** + ## Modules Recently, the architecture of the pwncat framework was redesigned to diff --git a/pwncat/manager.py b/pwncat/manager.py index 4f8a7d3..b7a28e6 100644 --- a/pwncat/manager.py +++ b/pwncat/manager.py @@ -26,7 +26,7 @@ from pwncat.target import Target class InteractiveExit(Exception): - """ Indicates we should exit the interactive terminal """ + """Indicates we should exit the interactive terminal""" class Session: @@ -97,7 +97,7 @@ class Session: @property def target(self) -> Target: - """ Retrieve the target object for this session """ + """Retrieve the target object for this session""" try: # Find target object @@ -121,7 +121,7 @@ class Session: self.log("registered new host w/ db") def run(self, module: str, **kwargs): - """ Run a module on this session """ + """Run a module on this session""" module_name = module module = self.manager.modules.get(module_name) @@ -180,12 +180,12 @@ class Session: @contextlib.contextmanager def task(self, *args, **kwargs): - """ Get a new task in this session's progress instance """ + """Get a new task in this session's progress instance""" # Ensure the variable exists even if an exception happens # prior to task creation task = None - started = self._progress._started + started = self._progress.live.is_started if "status" not in kwargs: kwargs["status"] = "..." @@ -211,7 +211,7 @@ class Session: self._progress.stop() def update_task(self, task, *args, **kwargs): - """ Update an active task """ + """Update an active task""" self._progress.update(task, *args, **kwargs) @@ -226,7 +226,7 @@ class Session: self.manager.target = None def close(self): - """ Close the session and remove from manager tracking """ + """Close the session and remove from manager tracking""" self.platform.channel.close() @@ -315,12 +315,12 @@ class Manager: pass def __enter__(self): - """ Begin manager context tracking """ + """Begin manager context tracking""" return self def __exit__(self, _, __, ___): - """ Ensure all sessions are closed """ + """Ensure all sessions are closed""" while self.sessions: self.sessions[0].close() @@ -348,7 +348,7 @@ class Manager: self.parser = CommandParser(self) def create_db_session(self): - """ Create a new SQLAlchemy database session and return it """ + """Create a new SQLAlchemy database session and return it""" # Initialize a fallback database if needed if self.db is None: @@ -380,7 +380,7 @@ class Manager: setattr(self.modules[module_name], "name", module_name) def log(self, *args, **kwargs): - """ Output a log entry """ + """Output a log entry""" if self.target is not None: self.target._progress.log(*args, **kwargs) @@ -396,7 +396,7 @@ class Manager: @property def target(self) -> Session: - """ Retrieve the currently focused target """ + """Retrieve the currently focused target""" return self._target @target.setter @@ -428,7 +428,7 @@ class Manager: pwnlib.term.term_mode = False def interactive(self): - """ Start interactive prompt """ + """Start interactive prompt""" self.interactive_running = True @@ -525,7 +525,7 @@ class Manager: return session def _process_input(self, data: bytes, has_prefix: bool): - """ Process stdin data from the user in raw mode """ + """Process stdin data from the user in raw mode""" for byte in data: byte = bytes([byte]) diff --git a/pwncat/platform/__init__.py b/pwncat/platform/__init__.py index 0c3413b..87a5621 100644 --- a/pwncat/platform/__init__.py +++ b/pwncat/platform/__init__.py @@ -24,7 +24,7 @@ function. """ class PlatformError(Exception): - """ Generic platform error. """ + """Generic platform error.""" class Path: @@ -42,16 +42,16 @@ class Path: @classmethod def cwd(cls): - """ Return a new concrete path referencing the current directory """ + """Return a new concrete path referencing the current directory""" return cls(".").resolve() @classmethod def home(cls): - """ Return a new concrete path referencing the current user home directory """ + """Return a new concrete path referencing the current user home directory""" return cls("~").resolve() def writable(self) -> bool: - """ This is non-standard, but is useful """ + """This is non-standard, but is useful""" user = self._target.current_user() mode = self.stat().st_mode @@ -73,7 +73,7 @@ class Path: return False def stat(self) -> os.stat_result: - """ Run `stat` on the path and return a stat result """ + """Run `stat` on the path and return a stat result""" if self._stat is not None: return self._stat @@ -83,12 +83,12 @@ class Path: return self._stat def chmod(self, mode: int): - """ Execute `chmod` on the remote file to change permissions """ + """Execute `chmod` on the remote file to change permissions""" self._target.chmod(str(self), mode) def exists(self) -> bool: - """ Return true if the specified path exists on the remote system """ + """Return true if the specified path exists on the remote system""" try: self.stat() @@ -97,7 +97,7 @@ class Path: return False def expanduser(self) -> "Path": - """ Return a new path object with ~ and ~user expanded """ + """Return a new path object with ~ and ~user expanded""" if not self.parts[0].startswith("~"): return self.__class__(self) @@ -136,7 +136,7 @@ class Path: return False def is_file(self) -> bool: - """ Returns True if the path points to a regular file """ + """Returns True if the path points to a regular file""" try: return stat.S_ISREG(self.stat().st_mode) @@ -144,7 +144,7 @@ class Path: return False def is_mount(self) -> bool: - """ Returns True if the path is a mount point. """ + """Returns True if the path is a mount point.""" if str(self) == "/": return True @@ -155,7 +155,7 @@ class Path: return False def is_symlink(self) -> bool: - """ Returns True if the path points to a symbolic link, False otherwise """ + """Returns True if the path points to a symbolic link, False otherwise""" try: return stat.S_ISLNK(self.stat().st_mode) @@ -163,7 +163,7 @@ class Path: return False def is_socket(self) -> bool: - """ Returns True if the path points to a Unix socket """ + """Returns True if the path points to a Unix socket""" try: return stat.S_ISSOCK(self.stat().st_mode) @@ -171,7 +171,7 @@ class Path: return False def is_fifo(self) -> bool: - """ Returns True if the path points to a FIFO """ + """Returns True if the path points to a FIFO""" try: return stat.S_ISFIFO(self.stat().st_mode) @@ -179,7 +179,7 @@ class Path: return False def is_block_device(self) -> bool: - """ Returns True if the path points to a block device """ + """Returns True if the path points to a block device""" try: return stat.S_ISBLK(self.stat().st_mode) @@ -187,7 +187,7 @@ class Path: return False def is_char_device(self) -> bool: - """ Returns True if the path points to a character device """ + """Returns True if the path points to a character device""" try: return stat.S_ISCHR(self.stat().st_mode) @@ -207,7 +207,7 @@ class Path: yield self.__class__(*self.parts, name) def lchmod(self, mode: int): - """ Modify a symbolic link's mode (same as chmod for non-symbolic links) """ + """Modify a symbolic link's mode (same as chmod for non-symbolic links)""" self._target.chmod(str(self), mode, link=True) @@ -223,7 +223,7 @@ class Path: return self._lstat def mkdir(self, mode: int = 0o777, parents: bool = False, exist_ok: bool = False): - """ Create a new directory at this given path. """ + """Create a new directory at this given path.""" if not exist_ok and self.exists(): raise FileExistsError(str(self)) @@ -241,7 +241,7 @@ class Path: errors: str = None, newline: str = None, ): - """ Open the file pointed to by the path, like Platform.open """ + """Open the file pointed to by the path, like Platform.open""" return self._target.open( self, @@ -259,24 +259,24 @@ class Path: return self._target.find_user(id=self.stat().st_uid).name def read_bytes(self) -> bytes: - """ Return the binary contents of the pointed-to file as a bytes object """ + """Return the binary contents of the pointed-to file as a bytes object""" with self.open("rb") as filp: return filp.read() def read_text(self, encoding: str = None, errors: str = None) -> str: - """ Return the decoded contents of the pointed-to file as a string """ + """Return the decoded contents of the pointed-to file as a string""" with self.open("r", encoding=encoding, errors=errors) as filp: return filp.read() def readlink(self) -> "Path": - """ Return the path to which the symbolic link points """ + """Return the path to which the symbolic link points""" return self._target.readlink(str(self)) def rename(self, target) -> "Path": - """ Rename the file or directory to the given target (str or Path). """ + """Rename the file or directory to the given target (str or Path).""" self._target.rename(str(self), str(target)) @@ -286,12 +286,12 @@ class Path: return target def replace(self, target) -> "Path": - """ Same as `rename` for Linux """ + """Same as `rename` for Linux""" return self.rename(target) def resolve(self, strict: bool = False): - """ Resolve the current path into an absolute path """ + """Resolve the current path into an absolute path""" return self.__class__(self._target.abspath(str(self))) @@ -302,7 +302,7 @@ class Path: return self.glob("**/" + pattern) def rmdir(self): - """ Remove this directory. The directory must be empty. """ + """Remove this directory. The directory must be empty.""" if not self.is_dir(): raise NotADirectoryError(str(self)) @@ -322,7 +322,7 @@ class Path: return os.path.samestat(stat1, stat2) def symlink_to(self, target, target_is_directory: bool = False): - """ Make this path a symbolic link to target. """ + """Make this path a symbolic link to target.""" if not isinstance(target, self.__class__): target = self.__class__(target) @@ -354,7 +354,7 @@ class Path: self.chmod(mode) def unlink(self, missing_ok: bool = False): - """ Remove the file or symbolic link. """ + """Remove the file or symbolic link.""" if not missing_ok and not self.exists(): raise FileNotFoundError(str(self)) @@ -369,7 +369,7 @@ class Path: raise def link_to(self, target): - """ Create a hard link pointing to a path named target """ + """Create a hard link pointing to a path named target""" if not isinstance(target, self.__class__): target = self.__class__(target) @@ -386,13 +386,13 @@ class Path: raise OSError(exc.stdout) from exc def write_bytes(self, data: bytes): - """ Open the file pointed to in bytes mode and write data to it. """ + """Open the file pointed to in bytes mode and write data to it.""" with self.open("wb") as filp: filp.write(data) def write_text(self, data: str, encoding: str = None, errors: str = None): - """ Open the file pointed to in text mode, and write data to it. """ + """Open the file pointed to in text mode, and write data to it.""" with self.open("w", encoding=encoding, errors=errors) as filp: filp.write(data) @@ -501,7 +501,7 @@ class Platform(ABC): return data def __str__(self): - """ Retrieve a string describing the platform connection """ + """Retrieve a string describing the platform connection""" return str(self.channel) @abstractmethod @@ -521,7 +521,7 @@ class Platform(ABC): if a lookup for a specific user or group ID fails.""" def iter_users(self) -> Generator["pwncat.db.User", None, None]: - """ Iterate over all users on the remote system """ + """Iterate over all users on the remote system""" with self.session.db as db: users = db.query(pwncat.db.User).filter_by(host_id=self.session.host).all() @@ -570,12 +570,12 @@ class Platform(ABC): return user def update_user(self): - """ Force an update of the current user the next time it is requested. """ + """Force an update of the current user the next time it is requested.""" self._current_user = None def current_user(self): - """ Retrieve a user object for the current user """ + """Retrieve a user object for the current user""" if self._current_user is not None: return self._current_user @@ -585,7 +585,7 @@ class Platform(ABC): return self._current_user def iter_groups(self) -> Generator["pwncat.db.Group", None, None]: - """ Iterate over all groups on the remote system """ + """Iterate over all groups on the remote system""" with self.session.db as db: groups = ( @@ -650,11 +650,11 @@ class Platform(ABC): @abstractmethod def abspath(self, path: str) -> str: - """ Attempt to resolve a path to an absolute path """ + """Attempt to resolve a path to an absolute path""" @abstractmethod def readlink(self, path: str): - """ Attempt to read the target of a link """ + """Attempt to read the target of a link""" @abstractmethod def whoami(self): @@ -929,31 +929,31 @@ class Platform(ABC): @abstractmethod def lstat(self, path: str) -> os.stat_result: - """ Perform the equivalent of the lstat syscall """ + """Perform the equivalent of the lstat syscall""" @abstractmethod def abspath(self, path: str) -> str: - """ Attempt to resolve a path to an absolute path """ + """Attempt to resolve a path to an absolute path""" @abstractmethod def readlink(self, path: str): - """ Attempt to read the target of a link """ + """Attempt to read the target of a link""" @abstractmethod def umask(self, mask: int = None): - """ Set or retrieve the current umask value """ + """Set or retrieve the current umask value""" @abstractmethod def touch(self, path: str): - """ Update a file modification time and possibly create it """ + """Update a file modification time and possibly create it""" @abstractmethod def chmod(self, path: str, mode: int, link: bool = False): - """ Update the file permissions """ + """Update the file permissions""" @abstractmethod def mkdir(self, path: str, mode: int = 0o777, parents: bool = False): - """ Create a new directory """ + """Create a new directory""" @abstractmethod def rename(self, source: str, target: str): @@ -962,19 +962,19 @@ class Platform(ABC): @abstractmethod def rmdir(self, target: str): - """ Remove the specified directory. It must be empty. """ + """Remove the specified directory. It must be empty.""" @abstractmethod def symlink_to(self, source: str, target: str): - """ Create a symbolic link to source from target """ + """Create a symbolic link to source from target""" @abstractmethod def link_to(self, source: str, target: str): - """ Create a filesystem hard link. """ + """Create a filesystem hard link.""" @abstractmethod def unlink(self, target: str): - """ Remove a link to a file (similar to `rm`) """ + """Remove a link to a file (similar to `rm`)""" def register(platform: Type[Platform]): diff --git a/requirements.txt b/requirements.txt index cf4f650..973eb8a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -36,4 +36,5 @@ typing-extensions==3.7.4.2 urllib3==1.25.9 wcwidth==0.1.9 python-rapidjson==0.9.1 -ZODB==5.6.0 \ No newline at end of file +ZODB==5.6.0 +zodburi==2.4.0 \ No newline at end of file From 929c4bf225a9fdad30da969805455caf1f840319 Mon Sep 17 00:00:00 2001 From: John Hammond Date: Fri, 7 May 2021 18:48:40 -0400 Subject: [PATCH 3/3] Fixed requirements dash --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 973eb8a..ad781ed 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ --git+https://github.com/JohnHammond/base64io-python +git+https://github.com/JohnHammond/base64io-python bcrypt==3.1.7 certifi==2020.6.20 cffi==1.14.0