Skip to content

Commit 6ac5afd

Browse files
committed
fix: add compatibility with Python 3.13 (fix distutils, find_module, crypt)
- Replaced deprecated `find_module` usage in `CommandParser` and `Manager` by `importlib.util.module_from_spec` and `exec_module` (fixes AttributeError: 'FileFinder' object has no attribute 'find_module') - Replaced `crypt.crypt(...)` with `passlib.hash.sha512_crypt` for password hashing (fixes ModuleNotFoundError: No module named 'crypt') - Added `passlib` as a dependency in `pyproject.toml` under [tool.poetry.dependencies] (fixes ModuleNotFoundError: No module named 'passlib')
1 parent 37f04d4 commit 6ac5afd

File tree

5 files changed

+25
-14
lines changed

5 files changed

+25
-14
lines changed

pwncat/commands/__init__.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ def run(self, manager: "pwncat.manager.Manager", args: "argparse.Namespace"):
4343
import pkgutil
4444
import termios
4545
import argparse
46+
import importlib.util
4647
from io import TextIOWrapper
4748
from enum import Enum, auto
4849
from typing import Dict, List, Type, Callable, Iterable
@@ -432,11 +433,14 @@ def __init__(self, manager: "pwncat.manager.Manager"):
432433
for loader, module_name, is_pkg in pkgutil.walk_packages(__path__):
433434
if module_name == "base":
434435
continue
435-
self.commands.append(
436-
loader.find_module(module_name)
437-
.load_module(module_name)
438-
.Command(manager)
439-
)
436+
437+
spec = importlib.util.find_spec(f"{__name__}.{module_name}")
438+
if spec is None:
439+
raise ImportError(f"Could not find spec for module {module_name}")
440+
441+
module = importlib.util.module_from_spec(spec)
442+
spec.loader.exec_module(module)
443+
self.commands.append(module.Command(manager))
440444

441445
self.prompt: PromptSession = None
442446
self.toolbar: PromptSession = None

pwncat/manager.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import tempfile
3636
import threading
3737
import contextlib
38+
import importlib.util
3839
from io import TextIOWrapper
3940
from enum import Enum, auto
4041
from typing import Dict, List, Tuple, Union, Callable, Optional, Generator
@@ -932,9 +933,12 @@ def load_modules(self, *paths):
932933
paths, prefix="pwncat.modules."
933934
):
934935

935-
# Why is this check *not* part of pkgutil??????? D:<
936936
if module_name not in sys.modules:
937-
module = loader.find_module(module_name).load_module(module_name)
937+
spec = importlib.util.find_spec(module_name)
938+
if spec is None:
939+
continue
940+
module = importlib.util.module_from_spec(spec)
941+
spec.loader.exec_module(module)
938942
else:
939943
module = sys.modules[module_name]
940944

pwncat/modules/linux/enumerate/escalate/append_passwd.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#!/usr/bin/env python3
2-
import crypt
2+
3+
from passlib.hash import sha512_crypt
34

45
import pwncat
56
from pwncat.util import console
@@ -31,7 +32,7 @@ def escalate(self, session: "pwncat.manager.Session"):
3132
shell = session.platform.getenv("SHELL")
3233

3334
# Hash the backdoor password
34-
backdoor_hash = crypt.crypt(backdoor_pass, crypt.METHOD_SHA512)
35+
backdoor_hash = sha512_crypt.using(salt_size=16).hash(backdoor_pass)
3536

3637
if not any(line.startswith(f"{backdoor_user}:") for line in passwd_contents):
3738

@@ -85,4 +86,4 @@ def enumerate(self, session):
8586
if ability.uid != 0:
8687
continue
8788

88-
yield AppendPasswd(self.name, ability)
89+
yield AppendPasswd(self.name, ability)

pwncat/modules/linux/implant/passwd.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#!/usr/bin/env python3
2-
import crypt
2+
3+
from passlib.hash import sha512_crypt
34

45
import pwncat
56
from pwncat.facts import Implant, ImplantType
@@ -93,7 +94,7 @@ def install(
9394

9495
# Hash the password
9596
yield Status("hashing password")
96-
backdoor_hash = crypt.crypt(backdoor_pass, crypt.METHOD_SHA512)
97+
backdoor_hash = sha512_crypt.using(salt_size=16).hash(backdoor_pass)
9798

9899
# Store the new line we are adding
99100
new_line = f"""{backdoor_user}:{backdoor_hash}:0:0::/root:{shell}\n"""

pyproject.toml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ addopts = "-v"
1111

1212
[tool.poetry]
1313
name = "pwncat-cs"
14-
version = "0.5.4"
14+
version = "0.5.5"
1515
description = "Reverse and bind shell automation framework"
1616
authors = ["Caleb Stewart <[email protected]>", "John Hammond"]
1717
readme = "README.md"
@@ -32,7 +32,8 @@ pwncat-cs = "pwncat.__main__:main"
3232
[tool.poetry.dependencies]
3333
python = "^3.9"
3434
netifaces = "^0.11.0"
35-
packaging = "^20.9"
35+
packaging = ">=23.0"
36+
passlib = "^1.7.4"
3637
prompt-toolkit = "^3.0.19"
3738
pycryptodome = "^3.10.1"
3839
requests = "^2.25.1"

0 commit comments

Comments
 (0)