diff --git a/pwncat/facts/windows.py b/pwncat/facts/windows.py index 124ec80..0041b5b 100644 --- a/pwncat/facts/windows.py +++ b/pwncat/facts/windows.py @@ -1,9 +1,11 @@ """ Windows-specific facts which are used in multiple places throughout the framework. """ -from typing import List, Optional +import functools +from typing import Any, List, Callable, Optional from datetime import datetime +from pwncat.db import Fact from pwncat.facts import User, Group @@ -122,3 +124,36 @@ class WindowsGroup(Group): self.group_description: str = description self.principal_source: str = principal_source self.domain: Optional[str] = domain + + +class PowershellFact(Fact): + """Powershell Object Wrapper Fact""" + + def __init__( + self, + source: str, + types: List[str], + obj: Any, + title: Callable, + description: Callable, + ): + super().__init__(source=source, types=types) + + self.obj = obj + + if description is not None: + self.description = functools.partial(description, self) + if title is not None: + self.title = functools.partial(title, self) + + def description(self, session): + return None + + def title(self, session): + return self.obj + + def __getattr__(self, key: str): + try: + return self.obj[key] + except KeyError: + return super().__getattr__(key) diff --git a/pwncat/modules/windows/enumerate/__init__.py b/pwncat/modules/windows/enumerate/__init__.py index e69de29..2eb8cec 100644 --- a/pwncat/modules/windows/enumerate/__init__.py +++ b/pwncat/modules/windows/enumerate/__init__.py @@ -0,0 +1,71 @@ +#!/usr/bin/env python3 +from typing import List, Callable + +from pwncat.facts.windows import PowershellFact +from pwncat.platform.windows import Windows, PowershellError +from pwncat.modules.enumerate import Schedule, EnumerateModule + + +def build_powershell_enumeration( + types: List[str], + schedule: Schedule, + command: str, + docstring: str, + title: Callable = None, + description: Callable = None, + single: bool = False, +): + """ + Build an enumeration module around a single powershell command. + This will construct and return an enumeration class which executes + the given powershell script and yields a fact with the given types + that exposes all properties of the returned powershell objects. This + is a helper to quickly develop basic powershell-based enumeration modules. + """ + + class Module(EnumerateModule): + + PROVIDES = types + PLATFORM = [Windows] + SCHEDULE = schedule + + def enumerate(self, session: "pwncat.manager.Session"): + + try: + result = session.platform.powershell(command) + + if not result: + return + + if isinstance(result[0], list): + results = result[0] + else: + results = [results[0]] + + if single: + yield PowershellFact( + source=self.name, + types=types, + data=results[0], + title=title, + description=description, + ) + else: + yield from [ + PowershellFact( + source=self.name, + types=types, + obj=obj, + title=title, + description=description, + ) + for obj in results + ] + + except PowershellError as exc: + pass + + # Set the docstring + Module.__doc__ = docstring + + return Module diff --git a/pwncat/modules/windows/enumerate/system/startup.py b/pwncat/modules/windows/enumerate/system/startup.py new file mode 100644 index 0000000..3924583 --- /dev/null +++ b/pwncat/modules/windows/enumerate/system/startup.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python3 +from typing import Any, Dict + +import rich.markup + +from pwncat.modules.enumerate import Schedule +from pwncat.modules.windows.enumerate import build_powershell_enumeration + + +def startup_title(self, session: "pwncat.manager.Session"): + return f"[cyan]{rich.markup.escape(self.Caption)}[/cyan]: {repr(rich.markup.escape(self.Command))}" + + +Module = build_powershell_enumeration( + types=["system.startup.command"], + schedule=Schedule.ONCE, + command="Get-CimInstance -ClassName Win32_StartupCommand", + docstring="Locate all startup commands via WMI queries", + title=startup_title, + description=None, + single=False, +) diff --git a/pwncat/modules/windows/enumerate/system/wsus.py b/pwncat/modules/windows/enumerate/system/wsus.py index 862cdaa..4f6f5c4 100644 --- a/pwncat/modules/windows/enumerate/system/wsus.py +++ b/pwncat/modules/windows/enumerate/system/wsus.py @@ -4,40 +4,55 @@ import rich.markup from pwncat.db import Fact from pwncat.modules import ModuleFailed -from pwncat.modules.windows import Windows, PowershellError +from pwncat.platform.windows import Windows, PowershellError from pwncat.modules.enumerate import Schedule, EnumerateModule +from pwncat.modules.windows.enumerate import build_powershell_enumeration -class WindowsUpdateServer(Fact): - def __init__(self, source: str, server: str): - super().__init__(source=source, types=["system.wsus.server"]) - - self.server = server - - def is_secure(self) -> bool: - """Check if the given server is secure""" - return self.server.startswith("https://") - - def title(self, session): - return rich.markup.escape(self.server) +def wsus_title(self, session: "pwncat.manager.Session"): + return rich.markup.escape(self.server) -class Module(EnumerateModule): - """Locate all WSUS update servers""" +Module = build_powershell_enumeration( + types=["system.wsus.server"], + schedule=Schedule.ONCE, + command="Get-ItemPropertyValue -Path 'HKLM:\\SOFTWARE\\Policies\\Microsoft\\Windows\\WindowsUpdate' -Name WUServer", + docstring="Locate all WSUS update server", + title=wsus_title, + description=None, + single=True, +) - PROVIDES = ["system.wsus.server"] - PLATFORM = [Windows] - SCHEDULE = Schedule.ONCE - - def enumerate(self, session: "pwncat.manager.Session"): - - try: - result = session.platform.powershell( - "Get-ItemPropertyValue -Path 'HKLM:\\SOFTWARE\\Policies\\Microsoft\\Windows\\WindowsUpdate' -Name WUServer" - ) - if not result: - return - - yield WindowsUpdateServer(source=self.name, server=result[0]) - except PowershellError as exc: - pass +# class WindowsUpdateServer(Fact): +# def __init__(self, source: str, server: str): +# super().__init__(source=source, types=["system.wsus.server"]) +# +# self.server = server +# +# def is_secure(self) -> bool: +# """Check if the given server is secure""" +# return self.server.startswith("https://") +# +# def title(self, session): +# return rich.markup.escape(self.server) +# +# +# class Module(EnumerateModule): +# """Locate all WSUS update servers""" +# +# PROVIDES = ["system.wsus.server"] +# PLATFORM = [Windows] +# SCHEDULE = Schedule.ONCE +# +# def enumerate(self, session: "pwncat.manager.Session"): +# +# try: +# result = session.platform.powershell( +# "Get-ItemPropertyValue -Path 'HKLM:\\SOFTWARE\\Policies\\Microsoft\\Windows\\WindowsUpdate' -Name WUServer" +# ) +# if not result: +# return +# +# yield WindowsUpdateServer(source=self.name, server=result[0]) +# except PowershellError as exc: +# pass