From 57d1c645d3bc65cff0db1ec9980d88d1092156bc Mon Sep 17 00:00:00 2001 From: John Hammond Date: Wed, 12 May 2021 19:41:12 -0400 Subject: [PATCH] All enumeration modules are now brought to the new framework --- pwncat/db/user.py | 3 - pwncat/facts/__init__.py | 3 - pwncat/facts/linux.py | 4 +- .../modules/linux/enumerate/creds/__init__.py | 5 +- pwncat/modules/linux/enumerate/file/caps.py | 23 +++--- .../modules/linux/enumerate/software/cron.py | 3 +- .../modules/linux/enumerate/system/process.py | 1 + .../linux/enumerate/system/services.py | 8 +- pwncat/modules/linux/enumerate/user/privs.py | 73 ------------------- pwncat/platform/linux.py | 2 +- 10 files changed, 28 insertions(+), 97 deletions(-) delete mode 100644 pwncat/modules/linux/enumerate/user/privs.py diff --git a/pwncat/db/user.py b/pwncat/db/user.py index bcb6d12..3ad7dee 100644 --- a/pwncat/db/user.py +++ b/pwncat/db/user.py @@ -22,9 +22,6 @@ class Group(Fact): def __repr__(self): return f"""Group(gid={self.id}, name={repr(self.name)}, members={repr(self.members)})""" - def __str__(self): - return f"""{rich.markup.escape(self.name)}, gid={self.id}, members={rich.markup.escape(",".join((m for m in self.members)))}""" - class User(Fact): """Basic representation of a user on the target system. Individual platform diff --git a/pwncat/facts/__init__.py b/pwncat/facts/__init__.py index 54fc34f..0ab5b70 100644 --- a/pwncat/facts/__init__.py +++ b/pwncat/facts/__init__.py @@ -24,9 +24,6 @@ class Group(Fact): def __repr__(self): return f"""Group(gid={self.id}, name={repr(self.name)}, members={repr(self.members)})""" - def __str__(self): - return f"""{rich.markup.escape(self.name)}, gid={self.id}, members={rich.markup.escape(",".join((m for m in self.members)))}""" - class User(Fact): """Basic representation of a user on the target system. Individual platform diff --git a/pwncat/facts/linux.py b/pwncat/facts/linux.py index 5b47dbf..86f6820 100644 --- a/pwncat/facts/linux.py +++ b/pwncat/facts/linux.py @@ -5,7 +5,7 @@ from pwncat.facts import User, Group class LinuxUser(User): - """ Linux-specific user definition """ + """Linux-specific user definition""" def __init__( self, @@ -33,7 +33,7 @@ class LinuxUser(User): class LinuxGroup(Group): - """ Linux-specific group definition """ + """Linux-specific group definition""" def __init__(self, source, group_name, hash, gid, members, password=None): diff --git a/pwncat/modules/linux/enumerate/creds/__init__.py b/pwncat/modules/linux/enumerate/creds/__init__.py index 6bab4ab..d46e83e 100644 --- a/pwncat/modules/linux/enumerate/creds/__init__.py +++ b/pwncat/modules/linux/enumerate/creds/__init__.py @@ -48,13 +48,12 @@ class PrivateKeyData(Fact): self.encrypted: bool = encrypted """ Is this private key encrypted? """ - def __str__(self): + def title(self, session): if self.uid == 0: color = "red" else: color = "green" return f"Potential private key for [{color}]{self.uid}[/{color}] at [cyan]{rich.markup.escape(self.path)}[/cyan]" - @property - def description(self) -> str: + def description(self, session) -> str: return self.content diff --git a/pwncat/modules/linux/enumerate/file/caps.py b/pwncat/modules/linux/enumerate/file/caps.py index b5a3ee1..915acf4 100644 --- a/pwncat/modules/linux/enumerate/file/caps.py +++ b/pwncat/modules/linux/enumerate/file/caps.py @@ -32,12 +32,6 @@ class FileCapabilityData(Fact): line += "]" return line - def __str__(self): - line = f"[cyan]{rich.markup.escape(self.path)}[/cyan] -> [" - line += ",".join(f"[blue]{rich.markup.escape(c)}[/blue]" for c in self.caps) - line += "]" - return line - class Module(EnumerateModule): """Enumerate capabilities of the binaries of the remote host""" @@ -57,10 +51,19 @@ class Module(EnumerateModule): # Process the standard output from the command with proc.stdout as stream: - for path in stream: + for line in stream: # Parse out path and capability list - path, caps = [x.strip() for x in path.strip().split(" ")] - caps = caps.split(",") - fact = FileCapabilityData(self.name, path, caps) + # getcap is inconsistent in how it displays output. + # We can try and handle both cases. + if " = " in line: + # /usr/bin/mtr-packet = cap_net_raw+ep + path, caps = [x.strip() for x in line.strip().split("=")] + caps = caps.split(",") + fact = FileCapabilityData(self.name, path, caps) + else: + path, caps = [x.strip() for x in line.strip().split(" ")] + caps = caps.split(",") + fact = FileCapabilityData(self.name, path, caps) + yield fact diff --git a/pwncat/modules/linux/enumerate/software/cron.py b/pwncat/modules/linux/enumerate/software/cron.py index 5294c81..8f5a13a 100644 --- a/pwncat/modules/linux/enumerate/software/cron.py +++ b/pwncat/modules/linux/enumerate/software/cron.py @@ -9,6 +9,7 @@ import pwncat from pwncat.db import Fact from pwncat.modules import Status from pwncat.platform.linux import Linux +from pwncat.subprocess import CalledProcessError from pwncat.modules.enumerate import Schedule, EnumerateModule @@ -92,7 +93,7 @@ class Module(EnumerateModule): ) user_entries = proc.stdout - except FileNotFoundError: + except CalledProcessError as exc: # The crontab command doesn't exist :( return diff --git a/pwncat/modules/linux/enumerate/system/process.py b/pwncat/modules/linux/enumerate/system/process.py index 083083c..ea3ab8e 100644 --- a/pwncat/modules/linux/enumerate/system/process.py +++ b/pwncat/modules/linux/enumerate/system/process.py @@ -64,6 +64,7 @@ class Module(EnumerateModule): "ps -eo pid,ppid,uid,user,command --no-header -ww", capture_output=True, text=True, + check=True, ) if proc.stdout: diff --git a/pwncat/modules/linux/enumerate/system/services.py b/pwncat/modules/linux/enumerate/system/services.py index 94c0145..4947a50 100644 --- a/pwncat/modules/linux/enumerate/system/services.py +++ b/pwncat/modules/linux/enumerate/system/services.py @@ -8,6 +8,12 @@ from pwncat.util import Init from pwncat.platform.linux import Linux from pwncat.modules.enumerate import Schedule, EnumerateModule +""" +TODO: This is weirdly inconsistent. Sometimes it works, other times it misses +like more than half of the services. We don't know why. systemctl might be +doing something weird? +""" + class ServiceData(Fact): def __init__(self, source, name, uid, state, pid): @@ -80,7 +86,7 @@ class Module(EnumerateModule): try: pid = int(section[0]) except ValueError as exc: - pwncat.console.log(repr(data), markup=False) + continue if section[1] == "[not set]": uid = 0 else: diff --git a/pwncat/modules/linux/enumerate/user/privs.py b/pwncat/modules/linux/enumerate/user/privs.py deleted file mode 100644 index d60dcda..0000000 --- a/pwncat/modules/linux/enumerate/user/privs.py +++ /dev/null @@ -1,73 +0,0 @@ -#!/usr/bin/env python3 -import dataclasses -from enum import IntFlag - -from rich.table import Table - -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 - - -@dataclasses.dataclass -class TokenPrivilegeData: - - privilege: str - attributes: LuidAttributes - token_handle: int - pid: int - - def __str__(self): - - 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] - - 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 "user.privs", TokenPrivilegeData( - privilege=priv["Privilege"], - attributes=LuidAttributes(priv["Attributes"]), - token_handle=priv["TokenHandle"], - pid=priv["ProcessId"], - ) diff --git a/pwncat/platform/linux.py b/pwncat/platform/linux.py index 4e77c6a..72a910a 100644 --- a/pwncat/platform/linux.py +++ b/pwncat/platform/linux.py @@ -1299,7 +1299,7 @@ class Linux(Platform): " stty sane", f" stty rows {rows} columns {columns}", f" export TERM='{TERM}'", - """export PS1='$(command printf "\\033[01;31m(remote)\\033[0m \\033[01;33m$(whoami)@$(hostname)\\033[0m:\\033[1;36m$PWD\\033[0m$ ")'""", + """export PS1='$(command printf "\\033[01;31m(remote)\\033[0m \\033[01;33m$(whoami)@$(hostname)\\033[0m:\\033[1;36m$PWD\\033[0m\\$ ")'""", ] ) + "\n"