mirror of
https://github.com/calebstewart/pwncat.git
synced 2024-11-23 17:15:38 +01:00
Merge branch 'platforms' of https://github.com/calebstewart/pwncat into platforms
This commit is contained in:
commit
a60131edb8
@ -7,7 +7,7 @@ set -g privkey "data/pwncat"
|
||||
# Set the pwncat backdoor user and password
|
||||
set -g backdoor_user "pwncat"
|
||||
set -g backdoor_pass "pwncat"
|
||||
set -g db "memory://"
|
||||
set -g db "file://test.zodb"
|
||||
|
||||
set -g on_load {
|
||||
# Run a command upon a stable connection
|
||||
|
@ -108,7 +108,6 @@ def resolve_blocks(source: str):
|
||||
class DatabaseHistory(History):
|
||||
""" Yield history from the host entry in the database """
|
||||
|
||||
# NOTE - This is nerfed because of ZODB changes...
|
||||
def __init__(self, manager):
|
||||
super().__init__()
|
||||
self.manager = manager
|
||||
@ -116,23 +115,14 @@ class DatabaseHistory(History):
|
||||
def load_history_strings(self) -> Iterable[str]:
|
||||
""" Load the history from the database """
|
||||
|
||||
if False:
|
||||
with self.manager.new_db_session() as session:
|
||||
for history in (
|
||||
session.query(pwncat.db.History)
|
||||
.order_by(pwncat.db.History.id.desc())
|
||||
.all()
|
||||
):
|
||||
yield history.command
|
||||
with self.manager.db.transaction() as conn:
|
||||
yield from reversed(conn.root.history)
|
||||
|
||||
def store_string(self, string: str) -> None:
|
||||
""" Store a command in the database """
|
||||
|
||||
if False:
|
||||
history = pwncat.db.History(command=string)
|
||||
|
||||
with self.manager.new_db_session() as session:
|
||||
session.add(history)
|
||||
with self.manager.db.transaction() as conn:
|
||||
conn.root.history.append(string)
|
||||
|
||||
|
||||
class CommandParser:
|
||||
|
@ -2,7 +2,6 @@
|
||||
|
||||
import pwncat
|
||||
from pwncat.db.binary import Binary
|
||||
from pwncat.db.history import History
|
||||
from pwncat.db.persist import Persistence
|
||||
from pwncat.db.suid import SUID
|
||||
from pwncat.db.tamper import Tamper
|
||||
|
@ -1,13 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import persistent
|
||||
from typing import Optional
|
||||
|
||||
|
||||
class History(persistent.Persistent):
|
||||
"""Store history of ran commands on the target."""
|
||||
|
||||
def __init__(self, command):
|
||||
|
||||
# The command ran on the target (e.g., "whoami")
|
||||
self.command: Optional[str] = command
|
@ -120,6 +120,18 @@ class Session:
|
||||
|
||||
self.log("registered new host w/ db")
|
||||
|
||||
def current_user(self) -> pwncat.db.User:
|
||||
""" Retrieve the current user object """
|
||||
|
||||
return self.find_user(uid=self.platform.getuid())
|
||||
|
||||
def find_user(self, uid=None, name=None):
|
||||
""" Locate a user object by name or ID """
|
||||
|
||||
for user in self.run("enumerate.gather", types=["user"]):
|
||||
if (uid is None or user.id == uid) and (name is None or user.name == name):
|
||||
return user
|
||||
|
||||
def run(self, module: str, **kwargs):
|
||||
"""Run a module on this session"""
|
||||
|
||||
@ -341,8 +353,12 @@ class Manager:
|
||||
|
||||
if not hasattr(conn.root, "targets"):
|
||||
conn.root.targets = persistent.list.PersistentList()
|
||||
conn.transaction_manager.commit()
|
||||
conn.close()
|
||||
|
||||
if not hasattr(conn.root, "history"):
|
||||
conn.root.history = persistent.list.PersistentList()
|
||||
|
||||
conn.transaction_manager.commit()
|
||||
conn.close()
|
||||
|
||||
# Rebuild the command parser now that the database is available
|
||||
self.parser = CommandParser(self)
|
||||
|
@ -504,6 +504,10 @@ class Platform(ABC):
|
||||
"""Retrieve a string describing the platform connection"""
|
||||
return str(self.channel)
|
||||
|
||||
@abstractmethod
|
||||
def getuid(self):
|
||||
"""Get the current user ID"""
|
||||
|
||||
@abstractmethod
|
||||
def getenv(self, name: str) -> str:
|
||||
"""Get the value of an environment variable.
|
||||
@ -513,128 +517,6 @@ class Platform(ABC):
|
||||
:rtype: str
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def reload_users(self):
|
||||
"""Reload the user and group cache. This is automatically called
|
||||
if the cache hasn't been built yet, but may be called manually
|
||||
if you know the users have changed. This method is also called
|
||||
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"""
|
||||
|
||||
with self.session.db as db:
|
||||
users = db.query(pwncat.db.User).filter_by(host_id=self.session.host).all()
|
||||
|
||||
if users is None:
|
||||
self.reload_users()
|
||||
|
||||
users = (
|
||||
db.query(pwncat.db.User).filter_by(host_id=self.session.host).all()
|
||||
)
|
||||
|
||||
if users is not None:
|
||||
for user in users:
|
||||
_ = user.groups
|
||||
yield user
|
||||
|
||||
return
|
||||
|
||||
def find_user(
|
||||
self,
|
||||
name: Optional[str] = None,
|
||||
id: Optional[int] = None,
|
||||
_recurse: bool = True,
|
||||
) -> "pwncat.db.User":
|
||||
"""Locate a user by name or UID. If the user/group cache has not
|
||||
been built, then reload_users is automatically called. If the
|
||||
lookup fails, reload_users is called automatically to ensure that
|
||||
there has not been a user/group update remotely. If the user
|
||||
still cannot be found, a KeyError is raised."""
|
||||
|
||||
with self.session.db as db:
|
||||
user = db.query(pwncat.db.User).filter_by(host_id=self.session.host)
|
||||
|
||||
if name is not None:
|
||||
user = user.filter_by(name=name)
|
||||
if id is not None:
|
||||
user = user.filter_by(id=id)
|
||||
|
||||
user = user.first()
|
||||
if user is None and _recurse:
|
||||
self.reload_users()
|
||||
return self.find_user(name=name, id=id, _recurse=False)
|
||||
elif user is None:
|
||||
raise KeyError
|
||||
|
||||
return user
|
||||
|
||||
def update_user(self):
|
||||
"""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"""
|
||||
|
||||
if self._current_user is not None:
|
||||
return self._current_user
|
||||
|
||||
self._current_user = self.find_user(name=self.whoami())
|
||||
|
||||
return self._current_user
|
||||
|
||||
def iter_groups(self) -> Generator["pwncat.db.Group", None, None]:
|
||||
"""Iterate over all groups on the remote system"""
|
||||
|
||||
with self.session.db as db:
|
||||
groups = (
|
||||
db.query(pwncat.db.Group).filter_by(host_id=self.session.host).all()
|
||||
)
|
||||
|
||||
if groups is None:
|
||||
self.reload_users()
|
||||
|
||||
groups = (
|
||||
db.query(pwncat.db.Group).filter_by(host_id=self.session.host).all()
|
||||
)
|
||||
|
||||
if groups is not None:
|
||||
for group in groups:
|
||||
_ = group.members
|
||||
yield group
|
||||
|
||||
return
|
||||
|
||||
def find_group(
|
||||
self,
|
||||
name: Optional[str] = None,
|
||||
id: Optional[int] = None,
|
||||
_recurse: bool = True,
|
||||
) -> "pwncat.db.Group":
|
||||
"""Locate a group by name or GID. If the user/group cache has not
|
||||
been built, then reload_users is automatically called. If the
|
||||
lookup fails, reload_users is called automatically to ensure that
|
||||
there has not been a user/group update remotely. If the group
|
||||
still cannot be found, a KeyError is raised."""
|
||||
|
||||
with self.session.db as db:
|
||||
group = db.query(pwncat.db.Group).filter_by(host_id=self.session.host)
|
||||
|
||||
if name is not None:
|
||||
group = group.filter_by(name=name)
|
||||
if id is not None:
|
||||
group = group.filter_by(id=id)
|
||||
|
||||
group = group.first()
|
||||
if group is None and _recurse:
|
||||
self.reload_users()
|
||||
return self.find_group(name=name, id=id, _recurse=False)
|
||||
elif group is None:
|
||||
raise KeyError
|
||||
|
||||
return group
|
||||
|
||||
@abstractmethod
|
||||
def stat(self, path: str) -> os.stat_result:
|
||||
"""Run stat on a path on the remote system and return a stat result
|
||||
|
@ -547,11 +547,12 @@ class Linux(Platform):
|
||||
return
|
||||
|
||||
pty_command = None
|
||||
shell = self.getenv("SHELL")
|
||||
|
||||
if pty_command is None:
|
||||
script_path = self.which("script")
|
||||
if script_path is not None:
|
||||
pty_command = f""" exec {script_path} -qc /bin/sh /dev/null 2>&1\n"""
|
||||
pty_command = f""" exec {script_path} -qc {shell} /dev/null 2>&1\n"""
|
||||
|
||||
if pty_command is None:
|
||||
python_path = self.which(
|
||||
@ -566,7 +567,7 @@ class Linux(Platform):
|
||||
]
|
||||
)
|
||||
if python_path is not None:
|
||||
pty_command = f"""exec {python_path} -c "import pty; pty.spawn('/bin/sh')" 2>&1\n"""
|
||||
pty_command = f"""exec {python_path} -c "import pty; pty.spawn('{shell} -i')" 2>&1\n"""
|
||||
|
||||
if pty_command is not None:
|
||||
self.logger.info(pty_command.rstrip("\n"))
|
||||
@ -708,6 +709,15 @@ class Linux(Platform):
|
||||
except CalledProcessError:
|
||||
return None
|
||||
|
||||
def getuid(self):
|
||||
""" Retrieve the current user ID """
|
||||
|
||||
try:
|
||||
proc = self.run(["id", "-ru"], capture_output=True, text=True, check=True)
|
||||
return int(proc.stdout.rstrip("\n"))
|
||||
except CalledProcessError as exc:
|
||||
raise PlatformError(str(exc)) from exc
|
||||
|
||||
def getenv(self, name: str):
|
||||
|
||||
try:
|
||||
|
16
test.py
16
test.py
@ -12,7 +12,7 @@ manager = pwncat.manager.Manager("data/pwncatrc")
|
||||
|
||||
# Tell the manager to create verbose sessions that
|
||||
# log all commands executed on the remote host
|
||||
manager.config.set("verbose", True, glob=True)
|
||||
# manager.config.set("verbose", True, glob=True)
|
||||
|
||||
# Establish a session
|
||||
# session = manager.create_session("windows", host="192.168.56.10", port=4444)
|
||||
@ -20,16 +20,4 @@ manager.config.set("verbose", True, glob=True)
|
||||
session = manager.create_session("linux", host="127.0.0.1", port=4444)
|
||||
# session = manager.create_session("windows", host="0.0.0.0", port=4444)
|
||||
|
||||
proc = session.platform.sudo(
|
||||
["env"],
|
||||
env={"TEST": "hello world"},
|
||||
password="Super Secret Squirrel",
|
||||
text=True,
|
||||
stdout=subprocess.PIPE,
|
||||
)
|
||||
print(proc.stdout.readline())
|
||||
|
||||
proc.wait()
|
||||
|
||||
|
||||
manager.interactive()
|
||||
print(session.current_user())
|
||||
|
Loading…
Reference in New Issue
Block a user