diff --git a/CHANGELOG.md b/CHANGELOG.md index d0384348..89f0e42b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ and simply didn't have the time to go back and retroactively create one. ### Added - Added missed `PlatformError` for `upload` command (e.g. "no gtfobins writers available") +- Addes Python 3.13 support ## [0.5.4] - 2022-01-27 Bug fix for the `load` command. diff --git a/pwncat/commands/__init__.py b/pwncat/commands/__init__.py index 17fc7cdc..6d25d471 100644 --- a/pwncat/commands/__init__.py +++ b/pwncat/commands/__init__.py @@ -34,46 +34,43 @@ class Command(CommandDefinition): def run(self, manager: "pwncat.manager.Manager", args: "argparse.Namespace"): manager.log("we ran a custom command!") """ + +import argparse +import fcntl +import importlib import os +import pkgutil import re -import sys -import tty -import fcntl import shlex -import pkgutil +import sys import termios -import argparse -from io import TextIOWrapper +import tty from enum import Enum, auto -from typing import Dict, List, Type, Callable, Iterable from functools import partial +from io import TextIOWrapper +from typing import Callable, Dict, Iterable, List, Type import rich.text -from pygments import token from prompt_toolkit import ANSI, PromptSession -from pygments.lexer import RegexLexer -from pygments.styles import get_style_by_name -from prompt_toolkit.lexers import PygmentsLexer -from prompt_toolkit.styles import Style, merge_styles -from prompt_toolkit.history import History +from prompt_toolkit.application.current import get_app +from prompt_toolkit.auto_suggest import AutoSuggestFromHistory +from prompt_toolkit.completion import (CompleteEvent, Completer, Completion, + WordCompleter, merge_completers) from prompt_toolkit.document import Document -from prompt_toolkit.completion import ( - Completer, - Completion, - CompleteEvent, - WordCompleter, - merge_completers, -) +from prompt_toolkit.history import History from prompt_toolkit.key_binding import KeyBindings -from prompt_toolkit.auto_suggest import AutoSuggestFromHistory +from prompt_toolkit.lexers import PygmentsLexer from prompt_toolkit.patch_stdout import patch_stdout +from prompt_toolkit.styles import Style, merge_styles from prompt_toolkit.styles.pygments import style_from_pygments_cls -from prompt_toolkit.application.current import get_app +from pygments import token +from pygments.lexer import RegexLexer +from pygments.styles import get_style_by_name import pwncat import pwncat.db -from pwncat.util import console from pwncat.channel import ChannelClosed +from pwncat.util import console class Complete(Enum): @@ -432,11 +429,9 @@ def __init__(self, manager: "pwncat.manager.Manager"): for loader, module_name, is_pkg in pkgutil.walk_packages(__path__): if module_name == "base": continue - self.commands.append( - loader.find_module(module_name) - .load_module(module_name) - .Command(manager) - ) + full_module_name = f"{__name__}.{module_name}" + module = importlib.import_module(full_module_name) + self.commands.append(module.Command(manager)) self.prompt: PromptSession = None self.toolbar: PromptSession = None @@ -444,8 +439,7 @@ def __init__(self, manager: "pwncat.manager.Manager"): self.aliases: Dict[str, CommandDefinition] = {} self.shortcuts: Dict[str, CommandDefinition] = {} self.found_prefix: bool = False - # Saved terminal state to support switching between raw and normal - # mode. + # Saved terminal state to support switching between raw and normal mode. self.saved_term_state = None def setup_prompt(self): @@ -788,7 +782,8 @@ def restore_term(self, new_line=True): class CommandLexer(RegexLexer): """Implements a Regular Expression based pygments lexer for dynamically highlighting - the pwncat prompt during typing. The tokens are generated from command definitions.""" + the pwncat prompt during typing. The tokens are generated from command definitions. + """ tokens = {} diff --git a/pwncat/manager.py b/pwncat/manager.py index 393cb012..9c97f6ed 100644 --- a/pwncat/manager.py +++ b/pwncat/manager.py @@ -23,43 +23,46 @@ callback which receives the new session as an argument. """ + +import contextlib +import datetime +import fnmatch +import importlib +import importlib.util import os -import ssl -import sys +import pkgutil import queue import signal import socket -import fnmatch -import pkgutil -import datetime +import ssl +import sys import tempfile import threading -import contextlib -from io import TextIOWrapper from enum import Enum, auto -from typing import Dict, List, Tuple, Union, Callable, Optional, Generator +from io import TextIOWrapper +from typing import Callable, Dict, Generator, List, Optional, Tuple, Union +import persistent.list +import rich.progress import ZODB import zodburi -import rich.progress -import persistent.list from cryptography import x509 -from cryptography.x509.oid import NameOID -from prompt_toolkit.shortcuts import confirm from cryptography.hazmat.primitives import hashes, serialization from cryptography.hazmat.primitives.asymmetric import rsa +from cryptography.x509.oid import NameOID +from prompt_toolkit.shortcuts import confirm import pwncat.db import pwncat.facts import pwncat.modules import pwncat.modules.enumerate -from pwncat.util import RawModeExit, console -from pwncat.config import Config -from pwncat.target import Target -from pwncat.channel import Channel, ChannelError, ChannelClosed +from pwncat.channel import Channel, ChannelClosed, ChannelError from pwncat.commands import CommandParser -from pwncat.platform import Platform, PlatformError +from pwncat.config import Config from pwncat.modules.enumerate import Scope +from pwncat.platform import Platform, PlatformError +from pwncat.target import Target +from pwncat.util import RawModeExit, console class InteractiveExit(Exception): @@ -388,7 +391,8 @@ def _open_socket(self) -> socket.socket: def _ssl_wrap(self, server: socket.socket) -> ssl.SSLSocket: """Wrap the given server socket in an SSL context and return the new socket. - If the ``ssl`` option is not set, this method simply returns the original socket.""" + If the ``ssl`` option is not set, this method simply returns the original socket. + """ if not self.ssl: return server @@ -925,16 +929,18 @@ def load_modules(self, *paths): """Dynamically load modules from the specified paths If a module has the same name as an already loaded module, it will - take it's place in the module list. This includes built-in modules. + take its place in the module list. This includes built-in modules. """ for loader, module_name, _ in pkgutil.walk_packages( paths, prefix="pwncat.modules." ): - # Why is this check *not* part of pkgutil??????? D:< if module_name not in sys.modules: - module = loader.find_module(module_name).load_module(module_name) + try: + module = importlib.import_module(module_name) + except ImportError: + continue # Skip modules that can't be loaded else: module = sys.modules[module_name] @@ -942,11 +948,11 @@ def load_modules(self, *paths): continue # Create an instance of this module - module_name = module_name.split("pwncat.modules.")[1] - self.modules[module_name] = module.Module() + short_name = module_name.split("pwncat.modules.")[1] + self.modules[short_name] = module.Module() - # Store it's name so we know it later - setattr(self.modules[module_name], "name", module_name) + # Store its name so we know it later + setattr(self.modules[short_name], "name", short_name) def log(self, *args, **kwargs): """Output a log entry"""