mirror of
https://github.com/calebstewart/pwncat.git
synced 2024-11-27 10:54:14 +01:00
Merge branch 'master' of github.com:calebstewart/pwncat
This commit is contained in:
commit
37f04d4e16
@ -7,6 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
The Changelog starts with v0.4.1, because we did not keep one before that,
|
The Changelog starts with v0.4.1, because we did not keep one before that,
|
||||||
and simply didn't have the time to go back and retroactively create one.
|
and simply didn't have the time to go back and retroactively create one.
|
||||||
|
|
||||||
|
## [Unreleased]
|
||||||
|
### Fixed
|
||||||
|
- Fixed `shlex.join` use with non-str type objects (e.g. `RemotePath`)
|
||||||
|
- Fixed `set` command use with incorrect keys (e.g. `set invalid value`)
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- Added missed `PlatformError` for `upload` command (e.g. "no gtfobins writers available")
|
||||||
|
|
||||||
## [0.5.4] - 2022-01-27
|
## [0.5.4] - 2022-01-27
|
||||||
Bug fix for the `load` command.
|
Bug fix for the `load` command.
|
||||||
|
|
||||||
|
@ -12,7 +12,8 @@
|
|||||||
#
|
#
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
sys.path.insert(0, os.path.abspath('../..'))
|
|
||||||
|
sys.path.insert(0, os.path.abspath("../.."))
|
||||||
|
|
||||||
|
|
||||||
# -- Project information -----------------------------------------------------
|
# -- Project information -----------------------------------------------------
|
||||||
|
@ -68,12 +68,6 @@ class Command(CommandDefinition):
|
|||||||
|
|
||||||
if args.module is not None:
|
if args.module is not None:
|
||||||
manager.config.back()
|
manager.config.back()
|
||||||
except pwncat.modules.ModuleFailed as exc:
|
|
||||||
if args.traceback:
|
|
||||||
console.print_exception()
|
|
||||||
else:
|
|
||||||
console.log(f"[red]error[/red]: module failed: {exc}")
|
|
||||||
return
|
|
||||||
except pwncat.modules.ModuleNotFound:
|
except pwncat.modules.ModuleNotFound:
|
||||||
console.log(f"[red]error[/red]: {module_name}: not found")
|
console.log(f"[red]error[/red]: {module_name}: not found")
|
||||||
return
|
return
|
||||||
@ -86,6 +80,12 @@ class Command(CommandDefinition):
|
|||||||
except pwncat.modules.InvalidArgument as exc:
|
except pwncat.modules.InvalidArgument as exc:
|
||||||
console.log(f"[red]error[/red]: invalid argument: {exc}")
|
console.log(f"[red]error[/red]: invalid argument: {exc}")
|
||||||
return
|
return
|
||||||
|
except pwncat.modules.ModuleFailed as exc:
|
||||||
|
if args.traceback:
|
||||||
|
console.print_exception()
|
||||||
|
else:
|
||||||
|
console.log(f"[red]error[/red]: module failed: {exc}")
|
||||||
|
return
|
||||||
|
|
||||||
if isinstance(result, list):
|
if isinstance(result, list):
|
||||||
result = [r for r in result if not r.hidden]
|
result = [r for r in result if not r.hidden]
|
||||||
|
@ -80,9 +80,14 @@ class Command(CommandDefinition):
|
|||||||
try:
|
try:
|
||||||
if manager.sessions and args.variable == "db":
|
if manager.sessions and args.variable == "db":
|
||||||
raise ValueError("cannot change database with running session")
|
raise ValueError("cannot change database with running session")
|
||||||
|
if args.variable in manager.config:
|
||||||
manager.config.set(
|
manager.config.set(
|
||||||
args.variable, args.value, getattr(args, "global")
|
args.variable, args.value, getattr(args, "global")
|
||||||
)
|
)
|
||||||
|
else:
|
||||||
|
console.log(
|
||||||
|
f"[red]error[/red]: invalid choice {repr(args.variable)}"
|
||||||
|
)
|
||||||
if args.variable == "db":
|
if args.variable == "db":
|
||||||
# Ensure the database is re-opened, if it was already
|
# Ensure the database is re-opened, if it was already
|
||||||
manager.open_database()
|
manager.open_database()
|
||||||
@ -95,10 +100,15 @@ class Command(CommandDefinition):
|
|||||||
except ValueError as exc:
|
except ValueError as exc:
|
||||||
console.log(f"[red]error[/red]: {exc}")
|
console.log(f"[red]error[/red]: {exc}")
|
||||||
elif args.variable is not None:
|
elif args.variable is not None:
|
||||||
|
if args.variable in manager.config:
|
||||||
value = manager.config[args.variable]
|
value = manager.config[args.variable]
|
||||||
console.print(
|
console.print(
|
||||||
f" [cyan]{args.variable}[/cyan] = [yellow]{repr(value)}[/yellow]"
|
f" [cyan]{args.variable}[/cyan] = [yellow]{repr(value)}[/yellow]"
|
||||||
)
|
)
|
||||||
|
else:
|
||||||
|
console.log(
|
||||||
|
f"[red]error[/red]: invalid choice {repr(args.variable)}"
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
for name in manager.config:
|
for name in manager.config:
|
||||||
value = manager.config[name]
|
value = manager.config[name]
|
||||||
|
@ -14,6 +14,7 @@ from rich.progress import (
|
|||||||
import pwncat
|
import pwncat
|
||||||
from pwncat.util import console, copyfileobj, human_readable_size, human_readable_delta
|
from pwncat.util import console, copyfileobj, human_readable_size, human_readable_delta
|
||||||
from pwncat.commands import Complete, Parameter, CommandDefinition
|
from pwncat.commands import Complete, Parameter, CommandDefinition
|
||||||
|
from pwncat.platform import PlatformError
|
||||||
|
|
||||||
|
|
||||||
class Command(CommandDefinition):
|
class Command(CommandDefinition):
|
||||||
@ -77,5 +78,10 @@ class Command(CommandDefinition):
|
|||||||
f"uploaded [cyan]{human_readable_size(length)}[/cyan] "
|
f"uploaded [cyan]{human_readable_size(length)}[/cyan] "
|
||||||
f"in [green]{human_readable_delta(elapsed)}[/green]"
|
f"in [green]{human_readable_delta(elapsed)}[/green]"
|
||||||
)
|
)
|
||||||
except (FileNotFoundError, PermissionError, IsADirectoryError) as exc:
|
except (
|
||||||
|
FileNotFoundError,
|
||||||
|
PermissionError,
|
||||||
|
IsADirectoryError,
|
||||||
|
PlatformError,
|
||||||
|
) as exc:
|
||||||
self.parser.error(str(exc))
|
self.parser.error(str(exc))
|
||||||
|
@ -41,7 +41,7 @@ class AuthorizedKeyImplant(PrivateKey):
|
|||||||
user = session.find_user(uid=self.uid)
|
user = session.find_user(uid=self.uid)
|
||||||
|
|
||||||
if current_user.id != self.uid and current_user.id != 0:
|
if current_user.id != self.uid and current_user.id != 0:
|
||||||
raise ModuleFailed(f"must be root or {user.name}")
|
raise ModuleFailed(f"must be [blue]root[/blue] or [blue]{user.name}[/blue]")
|
||||||
|
|
||||||
# Ensure the directory exists
|
# Ensure the directory exists
|
||||||
homedir = session.platform.Path(user.home)
|
homedir = session.platform.Path(user.home)
|
||||||
@ -93,10 +93,12 @@ class Module(ImplantModule):
|
|||||||
yield Status("verifying user permissions")
|
yield Status("verifying user permissions")
|
||||||
current_user = session.current_user()
|
current_user = session.current_user()
|
||||||
if user != "__pwncat_current__" and current_user.id != 0:
|
if user != "__pwncat_current__" and current_user.id != 0:
|
||||||
raise ModuleFailed("only root can install implants for other users")
|
raise ModuleFailed(
|
||||||
|
"only [blue]root[/blue] can install implants for other users"
|
||||||
|
)
|
||||||
|
|
||||||
if not os.path.isfile(key):
|
if not os.path.isfile(key):
|
||||||
raise ModuleFailed(f"private key {key} does not exist")
|
raise ModuleFailed(f"private key [blue]{key}[/blue] does not exist")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
yield Status("reading public key")
|
yield Status("reading public key")
|
||||||
@ -119,7 +121,7 @@ class Module(ImplantModule):
|
|||||||
for implant in session.run("enumerate", types=["implant.*"]):
|
for implant in session.run("enumerate", types=["implant.*"]):
|
||||||
if implant.source == self.name and implant.uid == user_info.uid:
|
if implant.source == self.name and implant.uid == user_info.uid:
|
||||||
raise ModuleFailed(
|
raise ModuleFailed(
|
||||||
f"{self.name} already installed for {user_info.name}"
|
f"[blue]{self.name}[/blue] already installed for [blue]{user_info.name}[/blue]"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Ensure the directory exists
|
# Ensure the directory exists
|
||||||
|
@ -1111,7 +1111,7 @@ class Linux(Platform):
|
|||||||
)
|
)
|
||||||
|
|
||||||
if isinstance(args, list):
|
if isinstance(args, list):
|
||||||
command = shlex.join(args)
|
command = shlex.join(str(arg) for arg in args)
|
||||||
elif isinstance(args, str):
|
elif isinstance(args, str):
|
||||||
command = args
|
command = args
|
||||||
else:
|
else:
|
||||||
|
@ -146,7 +146,7 @@ def human_readable_delta(seconds):
|
|||||||
|
|
||||||
|
|
||||||
def join(argv: List[str]):
|
def join(argv: List[str]):
|
||||||
"""Join the string much line shlex.join, except assume that each token
|
"""Join the string much like shlex.join, except assume that each token
|
||||||
is expecting double quotes. This allows variable references within the
|
is expecting double quotes. This allows variable references within the
|
||||||
tokens."""
|
tokens."""
|
||||||
|
|
||||||
|
@ -8,9 +8,10 @@ import dataclasses
|
|||||||
from io import StringIO
|
from io import StringIO
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from pwncat.channel import ChannelError
|
|
||||||
from Crypto.PublicKey import RSA
|
from Crypto.PublicKey import RSA
|
||||||
|
|
||||||
|
from pwncat.channel import ChannelError
|
||||||
|
|
||||||
PLATFORM_MAP = {"ubuntu": "linux", "centos": "linux", "windows": "windows"}
|
PLATFORM_MAP = {"ubuntu": "linux", "centos": "linux", "windows": "windows"}
|
||||||
|
|
||||||
|
|
||||||
@ -37,13 +38,13 @@ def connection_details_for(name):
|
|||||||
|
|
||||||
@pytest.fixture(params=["ubuntu", "centos"])
|
@pytest.fixture(params=["ubuntu", "centos"])
|
||||||
def linux_details(request):
|
def linux_details(request):
|
||||||
""" Get available connection details for linux hosts """
|
"""Get available connection details for linux hosts"""
|
||||||
return connection_details_for(request.param)
|
return connection_details_for(request.param)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(params=["windows"])
|
@pytest.fixture(params=["windows"])
|
||||||
def windows_details(request):
|
def windows_details(request):
|
||||||
""" Get available connection details for windows hosts """
|
"""Get available connection details for windows hosts"""
|
||||||
return connection_details_for(request.param)
|
return connection_details_for(request.param)
|
||||||
|
|
||||||
|
|
||||||
@ -84,18 +85,18 @@ set -g db "memory://"
|
|||||||
|
|
||||||
@pytest.fixture(params=["windows", "ubuntu", "centos"])
|
@pytest.fixture(params=["windows", "ubuntu", "centos"])
|
||||||
def session(request):
|
def session(request):
|
||||||
""" Start a session with any platform """
|
"""Start a session with any platform"""
|
||||||
yield from session_for(request)
|
yield from session_for(request)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(params=["windows"])
|
@pytest.fixture(params=["windows"])
|
||||||
def windows(request):
|
def windows(request):
|
||||||
""" Start a windows session """
|
"""Start a windows session"""
|
||||||
yield from session_for(request)
|
yield from session_for(request)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(params=["ubuntu", "centos"])
|
@pytest.fixture(params=["ubuntu", "centos"])
|
||||||
def linux(request):
|
def linux(request):
|
||||||
""" Start a linux session """
|
"""Start a linux session"""
|
||||||
|
|
||||||
yield from session_for(request)
|
yield from session_for(request)
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from pwncat.modules import IncorrectPlatformError
|
from pwncat.modules import IncorrectPlatformError
|
||||||
|
|
||||||
|
|
||||||
@ -13,13 +14,13 @@ def test_session_iter_users(session):
|
|||||||
|
|
||||||
|
|
||||||
def test_session_find_user_name(session):
|
def test_session_find_user_name(session):
|
||||||
""" Test that locating a user by name works """
|
"""Test that locating a user by name works"""
|
||||||
|
|
||||||
assert session.find_user(name="john") is not None
|
assert session.find_user(name="john") is not None
|
||||||
|
|
||||||
|
|
||||||
def test_session_find_user_uid(linux):
|
def test_session_find_user_uid(linux):
|
||||||
""" Test locating a user by their UID (for linux only) """
|
"""Test locating a user by their UID (for linux only)"""
|
||||||
|
|
||||||
user = linux.find_user(uid=0)
|
user = linux.find_user(uid=0)
|
||||||
|
|
||||||
@ -28,7 +29,7 @@ def test_session_find_user_uid(linux):
|
|||||||
|
|
||||||
|
|
||||||
def test_session_find_user_sid(windows):
|
def test_session_find_user_sid(windows):
|
||||||
""" Test locating a user by their SID (for windows only) """
|
"""Test locating a user by their SID (for windows only)"""
|
||||||
|
|
||||||
# This is the SID of the Administrator in the windows servercore image...
|
# This is the SID of the Administrator in the windows servercore image...
|
||||||
# This will only work from the testing container, but I've decided that's fine.
|
# This will only work from the testing container, but I've decided that's fine.
|
||||||
@ -39,7 +40,7 @@ def test_session_find_user_sid(windows):
|
|||||||
|
|
||||||
|
|
||||||
def test_session_find_module(session):
|
def test_session_find_module(session):
|
||||||
""" Test that locating modules works """
|
"""Test that locating modules works"""
|
||||||
|
|
||||||
assert len(list(session.find_module("enumerate.*"))) > 0
|
assert len(list(session.find_module("enumerate.*"))) > 0
|
||||||
assert len(list(session.find_module("enumerate.user"))) == 1
|
assert len(list(session.find_module("enumerate.user"))) == 1
|
||||||
@ -47,7 +48,7 @@ def test_session_find_module(session):
|
|||||||
|
|
||||||
|
|
||||||
def test_session_run_module(session):
|
def test_session_run_module(session):
|
||||||
""" Test running a module within a session """
|
"""Test running a module within a session"""
|
||||||
|
|
||||||
# We should be able to enumerate a hostname
|
# We should be able to enumerate a hostname
|
||||||
facts = session.run("enumerate", types=["system.hostname"])
|
facts = session.run("enumerate", types=["system.hostname"])
|
||||||
@ -55,14 +56,14 @@ def test_session_run_module(session):
|
|||||||
|
|
||||||
|
|
||||||
def test_session_wrong_platform_linux(linux):
|
def test_session_wrong_platform_linux(linux):
|
||||||
""" Test that windows modules don't run in linux """
|
"""Test that windows modules don't run in linux"""
|
||||||
|
|
||||||
with pytest.raises(IncorrectPlatformError):
|
with pytest.raises(IncorrectPlatformError):
|
||||||
linux.run("windows.enumerate.user")
|
linux.run("windows.enumerate.user")
|
||||||
|
|
||||||
|
|
||||||
def test_session_wrong_platform_windows(windows):
|
def test_session_wrong_platform_windows(windows):
|
||||||
""" Test that linux modules don't run on windows """
|
"""Test that linux modules don't run on windows"""
|
||||||
|
|
||||||
with pytest.raises(IncorrectPlatformError):
|
with pytest.raises(IncorrectPlatformError):
|
||||||
windows.run("linux.enumerate.user")
|
windows.run("linux.enumerate.user")
|
||||||
|
Loading…
Reference in New Issue
Block a user