1
0
mirror of https://github.com/calebstewart/pwncat.git synced 2024-11-27 19:04:15 +01:00

Added separate domain user and group enumerations

This commit is contained in:
Caleb Stewart 2021-06-05 01:55:22 -04:00
parent 9c522b6997
commit 686caba900
4 changed files with 126 additions and 147 deletions

View File

@ -60,7 +60,6 @@ class WindowsUser(User):
password_last_set: Optional[datetime],
last_logon: Optional[datetime],
principal_source: str,
domain: Optional[str] = None,
password: Optional[str] = None,
hash: Optional[str] = None,
):
@ -79,15 +78,14 @@ class WindowsUser(User):
self.password_last_set: Optional[datetime] = password_last_set
self.last_logon: Optional[datetime] = last_logon
self.principal_source: str = principal_source
self.domain: Optional[str] = domain
def __repr__(self):
if self.password is None and self.hash is None:
return f"""User(uid={self.id}, name={repr(self.name)}, domain={repr(self.domain)})"""
return f"""User(uid={self.id}, name={repr(self.name)})"""
elif self.password is not None:
return f"""User(uid={repr(self.id)}, name={repr(self.name)}, domain={repr(self.domain)}, password={repr(self.password)})"""
return f"""User(uid={repr(self.id)}, name={repr(self.name)}, password={repr(self.password)})"""
else:
return f"""User(uid={repr(self.id)}, name={repr(self.name)}, domain={repr(self.domain)}, hash={repr(self.hash)})"""
return f"""User(uid={repr(self.id)}, name={repr(self.name)}, hash={repr(self.hash)})"""
class WindowsGroup(Group):

View File

@ -0,0 +1,123 @@
#!/usr/bin/env python3
from typing import Any, Dict, List, Optional
from datetime import datetime
from collections import namedtuple
from pwncat.db import Fact
from pwncat.modules import Status, ModuleFailed
from pwncat.facts.windows import WindowsUser
from pwncat.platform.windows import Windows, PowershellError
from pwncat.modules.enumerate import Schedule, EnumerateModule
class DomainUser(WindowsUser):
""" Builds on Windows Groups to add domain specific information """
def __init__(
self,
source: str,
name: str,
uid: str,
account_expires: Optional[datetime],
description: str,
enabled: bool,
full_name: str,
password_changeable_date: Optional[datetime],
password_expires: Optional[datetime],
user_may_change_password: bool,
password_required: bool,
password_last_set: Optional[datetime],
last_logon: Optional[datetime],
principal_source: str,
domain: str,
data: Dict,
password: Optional[str] = None,
hash: Optional[str] = None,
):
super().__init__(
source=source,
name=name,
uid=uid,
account_expires=account_expires,
description=description,
enabled=enabled,
full_name=full_name,
password_changeable_date=password_changeable_date,
password_expires=password_expires,
user_may_change_password=user_may_change_password,
password_required=password_required,
password_last_set=password_last_set,
last_logon=last_logon,
principal_source=principal_source,
password=password,
hash=hash,
)
self.types.append("domain.user")
self.domain = domain
if "description" in data:
data["user_description"] = data.get("description")
del data["description"]
self.__dict__.update(data)
def __repr__(self):
if self.password is None and self.hash is None:
return f"""DomainUser(uid={self.id}, name={repr(self.name)}, domain={repr(self.domain)})"""
elif self.password is not None:
return f"""DomainUser(uid={repr(self.id)}, name={repr(self.name)}, domain={repr(self.domain)}, password={repr(self.password)})"""
else:
return f"""DomainUser(uid={repr(self.id)}, name={repr(self.name)}, domain={repr(self.domain)}, hash={repr(self.hash)})"""
class Module(EnumerateModule):
""" Retrieve information on all domain computers """
PLATFORM = [Windows]
PROVIDES = ["domain.user", "user"]
SCHEDULE = Schedule.ONCE
def enumerate(self, session: "pwncat.manager.Session"):
""" Perform enumeration """
# Ensure we have PowerView loaded
yield Status("loading powersploit recon")
session.run("powersploit", group="recon")
try:
domain = session.run("enumerate.domain")[0]
except IndexError:
# Not a domain joined machine
return
try:
yield Status("requesting domain groups")
users = session.platform.powershell("Get-DomainUser")[0]
except (IndexError, PowershellError) as exc:
# Doesn't appear to be a domain joined user
return
if isinstance(users, dict):
users = [users]
for user in users:
yield DomainUser(
source=self.name,
name=user["samaccountname"],
uid=user["objectsid"],
account_expires=user.get("accountexpires"),
description=user.get("description") or "",
enabled=True,
full_name=user.get("name") or "",
password_changeable_date=None,
password_expires=None,
user_may_change_password=True,
password_required=True,
password_last_set=None,
last_logon=None,
principal_source="",
domain=domain["Name"],
data=user,
)

View File

@ -40,63 +40,4 @@ class Module(EnumerateModule):
password_last_set=None,
last_logon=None,
principal_source=user["PrincipalSource"],
domain=None,
)
try:
result = session.platform.powershell(
"(Get-WmiObject Win32_ComputerSystem).PartOfDomain"
)[0]
except (KeyError, PowershellError):
# Unable to grab domain status
return
# Not in a domain
if not result:
return
# We are in a domain, load powerview
session.run("powersploit", group="recon")
try:
results = session.platform.powershell("Get-DomainUser")[0]
except (KeyError, PowershellError):
# We coudln't retrieve domain users :(
return
if isinstance(results, dict):
results = [results]
for user in results:
dn = user.get("distinguishedname")
if dn is None:
domain = "unknown"
else:
dn = dn.split(",")
domain = []
for element in dn[::-1]:
if element.startswith("DC="):
domain.insert(0, element.split("=")[1])
else:
break
domain = ".".join(domain)
yield WindowsUser(
source=self.name,
name=user["samaccountname"],
uid=user["objectsid"],
account_expires=None,
description=user.get("description") or "",
enabled=True,
full_name=user.get("name") or "",
password_changeable_date=None,
password_expires=None,
user_may_change_password=True,
password_required=True,
password_last_set=None,
last_logon=None,
principal_source="",
domain=domain,
)

View File

@ -1,83 +0,0 @@
#!/usr/bin/env python3
import dataclasses
from enum import IntFlag
from rich.table import Table
from pwncat.facts import Fact
from pwncat.platform.windows import Windows
from pwncat.modules.enumerate import Schedule, EnumerateModule
class LuidAttributes(IntFlag):
DISABLED = 0x00
SE_PRIVILEGE_ENABLED_BY_DEFAULT = 0x01
SE_PRIVILEGE_ENABLED = 0x02
SE_PRIVILEGE_REMOVE = 0x04
SE_PRIVILEGE_USED_FOR_ACCESS = 0x80000000
class TokenPrivilegeData(Fact):
def __init__(
self,
source: str,
privilege: str,
attributes: LuidAttributes,
token_handle: int,
pid: int,
):
super().__init__(source=source, types=["user.privs"])
self.privilege: str = privilege
self.attributes: LuidAttributes = attributes
# self.token_handle: int = token_handle
# self.pid: int = pid
def title(self, session: "pwncat.manager.Session"):
if self.attributes == LuidAttributes.DISABLED:
color = "red"
else:
color = "green"
attrs = str(self.attributes)
attrs = attrs.replace("LuidAttributes.", "")
attrs = attrs.replace("|", "[/cyan]|[cyan]")
attrs = "[cyan]" + attrs + "[/cyan]"
return f"[{color}]{self.privilege}[/{color}] -> {attrs}"
class Module(EnumerateModule):
"""Enumerate user privileges using PowerView's Get-ProcessTokenPrivilege"""
PROVIDES = ["user.privs"]
PLATFORM = [Windows]
SCHEDULE = Schedule.ALWAYS
def enumerate(self, session: "pwncat.manager.Session"):
# Ensure that powerview is loaded
session.run(
"manage.powershell.import",
path="PowerShellMafia/PowerSploit/Privesc/PowerUp.ps1",
)
# Grab our current token privileges
results = session.platform.powershell("Get-ProcessTokenPrivilege")
if len(results) == 0:
session.log("[red]error[/red]: Get-ProcessTokenPrivilege failed")
return
# They end up in an array in an array
privs = results[0]
# Create our enumeration data types
for priv in privs:
yield TokenPrivilegeData(
source=self.name,
privilege=priv["Privilege"],
attributes=LuidAttributes(priv["Attributes"]),
token_handle=priv["TokenHandle"],
pid=priv["ProcessId"],
)