1
0
mirror of https://github.com/calebstewart/pwncat.git synced 2024-11-23 17:15:38 +01:00

Brought services and uname to new platform

This commit is contained in:
John Hammond 2021-05-12 00:17:25 -04:00
parent 23dc10c0a9
commit 2aa3aa79c7
7 changed files with 115 additions and 82 deletions

View File

@ -1,31 +1,34 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import dataclasses
import rich.markup
import pwncat import pwncat
from pwncat.db import Fact
from pwncat.util import Init from pwncat.util import Init
from pwncat.platform.linux import Linux from pwncat.platform.linux import Linux
from pwncat.modules.enumerate import Schedule, EnumerateModule from pwncat.modules.enumerate import Schedule, EnumerateModule
@dataclasses.dataclass class ServiceData(Fact):
class ServiceData: def __init__(self, source, name, uid, state, pid):
super().__init__(source=source, types=["system.service"])
name: str self.name: str = name
""" The name of the service as given on the remote host """ """ The name of the service as given on the remote host """
uid: int self.uid: int = uid
""" The user this service is running as """ """ The user this service is running as """
state: str self.state: str = state
""" Whether the service is running """ """ Whether the service is running """
pid: int self.pid: int = pid
def __str__(self): def title(self, session):
if self.uid == 0: if self.uid == 0:
color = "red" color = "red"
else: else:
color = "green" color = "green"
try: try:
user_name = pwncat.victim.find_user_by_id(self.uid).name user_name = session.find_user(uid=self.uid).name
except KeyError: except KeyError:
user_name = f"{self.uid} (unknown user)" user_name = f"{self.uid} (unknown user)"
color = "yellow" color = "yellow"
@ -42,18 +45,18 @@ class ServiceData:
class Module(EnumerateModule): class Module(EnumerateModule):
""" Enumerate systemd services on the victim """ """Enumerate systemd services on the victim"""
PROVIDES = ["system.service"] PROVIDES = ["system.service"]
PLATFORM = [Linux] PLATFORM = [Linux]
SCHEDULE = Schedule.ONCE SCHEDULE = Schedule.ONCE
def enumerate(self): def enumerate(self, session):
for fact in pwncat.modules.run( for fact in session.run(
"enumerate.gather", types=["system.init"], progress=self.progress "enumerate.gather", types=["system.init"], progress=self.progress
): ):
if fact.data.init != Init.SYSTEMD: if fact.init != Init.SYSTEMD:
return return
break break
@ -61,37 +64,28 @@ class Module(EnumerateModule):
# For the generic call, we grab the name, PID, user, and state # For the generic call, we grab the name, PID, user, and state
# of each process. If some part of pwncat needs more, it can # of each process. If some part of pwncat needs more, it can
# request it specifically. # request it specifically.
data = pwncat.victim.env(
[ data = session.platform.run(
"systemctl", "systemctl show --type=service --no-pager --all --value --property Id --property MainPID --property UID --property SubState \\*",
"show", capture_output=True,
"--type=service", text=True,
"--no-pager", check=True,
"--all",
"--value",
"--property",
"Id",
"--property",
"MainPID",
"--property",
"UID",
"--property",
"SubState",
"\\*",
],
PAGER="",
) )
data = data.strip().decode("utf-8").split("\n")
for i in range(0, len(data), 5): if data.stdout:
if i >= (len(data) - 4): data = data.stdout.split("\n\n")
break
name = data[i + 2].strip().rstrip(".service")
pid = int(data[i].strip())
if "[not set]" in data[i + 1]:
uid = 0
else:
uid = int(data[i + 1].strip())
state = data[i + 3].strip()
yield "system.service", ServiceData(name, uid, state, pid) for segment in data:
section = segment.split("\n")
try:
pid = int(section[0])
except ValueError as exc:
pwncat.console.log(repr(data), markup=False)
if section[1] == "[not set]":
uid = 0
else:
uid = int(section[1])
name = section[2].removesuffix(".service")
state = section[3]
yield ServiceData(self.name, name, uid, state, pid)

View File

@ -6,28 +6,45 @@ from typing import List, Optional
import pkg_resources import pkg_resources
import pwncat import pwncat
from pwncat.db import Fact
from pwncat import util from pwncat import util
from pwncat.platform.linux import Linux from pwncat.platform.linux import Linux
from pwncat.modules.enumerate import Schedule, EnumerateModule from pwncat.modules.enumerate import Schedule, EnumerateModule
@dataclasses.dataclass class ArchData(Fact):
class ArchData:
""" """
Simply the architecture of the remote machine. This class Simply the architecture of the remote machine. This class
wraps the architecture name in a nicely printable data wraps the architecture name in a nicely printable data
class. class.
""" """
arch: str def __init__(self, source, arch):
""" The determined architecture. """ super().__init__(source=source, types=["system.arch"])
def __str__(self): self.arch: str = arch
""" The determined architecture. """
def title(self, session):
return f"Running on a [cyan]{self.arch}[/cyan] processor" return f"Running on a [cyan]{self.arch}[/cyan] processor"
@dataclasses.dataclass class HostnameData(Fact):
class KernelVersionData: """
The hostname of this machine.
"""
def __init__(self, source, hostname):
super().__init__(source=source, types=["system.hostname"])
self.hostname: str = hostname
""" The determined architecture. """
def title(self, session):
return f"Hostname [cyan]{self.hostname}[/cyan]"
class KernelVersionData(Fact):
""" """
Represents a W.X.Y-Z kernel version where W is the major version, Represents a W.X.Y-Z kernel version where W is the major version,
X is the minor version, Y is the patch, and Z is the ABI. X is the minor version, Y is the patch, and Z is the ABI.
@ -36,12 +53,15 @@ class KernelVersionData:
https://askubuntu.com/questions/843197/what-are-kernel-version-number-components-w-x-yy-zzz-called https://askubuntu.com/questions/843197/what-are-kernel-version-number-components-w-x-yy-zzz-called
""" """
major: int def __init__(self, source, major, minor, patch, abi):
minor: int super().__init__(source=source, types=["system.kernel.version"])
patch: int
abi: str
def __str__(self): self.major: int = major
self.minor: int = minor
self.patch: int = patch
self.abi: str = abi
def title(self, session):
return ( return (
f"Running Linux Kernel [red]{self.major}[/red]." f"Running Linux Kernel [red]{self.major}[/red]."
f"[green]{self.minor}[/green]." f"[green]{self.minor}[/green]."
@ -49,8 +69,7 @@ class KernelVersionData:
) )
@dataclasses.dataclass class KernelVulnerabilityData(Fact):
class KernelVulnerabilityData:
""" """
Data describing a kernel vulnerability which appears to be exploitable Data describing a kernel vulnerability which appears to be exploitable
on the remote host. This is **not** guaranteed to be exploitable, however on the remote host. This is **not** guaranteed to be exploitable, however
@ -59,21 +78,23 @@ class KernelVulnerabilityData:
vulnerability. vulnerability.
""" """
name: str def __init__(self, source, name, versions, link, cve):
versions: List[str] super().__init__(source=source, types=["system.kernel.vuln"])
link: Optional[str]
cve: Optional[str]
# All exploits are assumed working, but can be marked as not working
working: bool = True
def __str__(self): self.name: str = name
self.versions: List[str] = versions
self.link: Optional[str] = link
self.cve: Optional[str] = cve
# All exploits are assumed working, but can be marked as not working
working: bool = True
def title(self, title):
line = f"[red]{self.name}[/red]" line = f"[red]{self.name}[/red]"
if self.cve is not None: if self.cve is not None:
line += f" ([cyan]CVE-{self.cve}[/cyan])" line += f" ([cyan]CVE-{self.cve}[/cyan])"
return line return line
@property def description(self, title):
def description(self):
line = f"Affected Versions: {repr(self.versions)}\n" line = f"Affected Versions: {repr(self.versions)}\n"
if self.link: if self.link:
line += f"Details: {self.link}" line += f"Details: {self.link}"
@ -102,12 +123,15 @@ class Module(EnumerateModule):
PLATFORM = [Linux] PLATFORM = [Linux]
SCHEDULE = Schedule.ONCE SCHEDULE = Schedule.ONCE
def enumerate(self): def enumerate(self, session):
""" Run uname and organize information """ """Run uname and organize information"""
# Grab the uname output # Grab the uname output
output = pwncat.victim.run("uname -s -n -r -m -o").decode("utf-8").strip() output = session.platform.run(
fields = output.split(" ") "uname -s -n -r -m -o", capture_output=True, text=True, check=True
)
fields = output.stdout.split(" ")
# Grab the components # Grab the components
# kernel_name = fields[0] if fields else None # kernel_name = fields[0] if fields else None
@ -121,14 +145,14 @@ class Module(EnumerateModule):
y_and_z = ".".join(y_and_z).split("-") y_and_z = ".".join(y_and_z).split("-")
y = y_and_z[0] y = y_and_z[0]
z = "-".join(y_and_z[1:]) z = "-".join(y_and_z[1:])
version = KernelVersionData(int(w), int(x), int(y), z) version = KernelVersionData(self.name, int(w), int(x), int(y), z)
yield "system.kernel.version", version yield version
# Handle arch # Handle arch
yield "system.arch", ArchData(machine_name) yield ArchData(self.name, machine_name)
# Handle Hostname # Handle Hostname
yield "system.hostname", hostname yield HostnameData(self.name, hostname)
# Handle Kernel vulnerabilities # Handle Kernel vulnerabilities
with open( with open(
@ -140,6 +164,10 @@ class Module(EnumerateModule):
for name, vuln in vulns.items(): for name, vuln in vulns.items():
if version_string not in vuln["vuln"]: if version_string not in vuln["vuln"]:
continue continue
yield "system.kernel.vuln", KernelVulnerabilityData( yield KernelVulnerabilityData(
name, vuln["vuln"], vuln.get("mil", None), vuln.get("cve", None) self.name,
name,
vuln["vuln"],
vuln.get("mil", None),
vuln.get("cve", None),
) )

14
test.py
View File

@ -17,7 +17,17 @@ manager = pwncat.manager.Manager("data/pwncatrc")
# Establish a session # Establish a session
# session = manager.create_session("windows", host="192.168.56.10", port=4444) # session = manager.create_session("windows", host="192.168.56.10", port=4444)
# session = manager.create_session("windows", host="192.168.122.11", port=4444) # session = manager.create_session("windows", host="192.168.122.11", port=4444)
session = manager.create_session("linux", host="127.0.0.1", port=4444) session = manager.create_session("linux", host="127.0.0.1", port=9999)
# session = manager.create_session("windows", host="0.0.0.0", port=4444) # session = manager.create_session("windows", host="0.0.0.0", port=4444)
print(session.current_user()) for _ in range(30):
data = session.platform.run(
"cat /tmp/dummy",
capture_output=True,
text=True,
check=True,
)
print(data.stdout.split("\n\n")[0])
print("===================================================")

BIN
test.zodb Normal file

Binary file not shown.

BIN
test.zodb.index Normal file

Binary file not shown.

1
test.zodb.lock Normal file
View File

@ -0,0 +1 @@
2639

BIN
test.zodb.tmp Normal file

Binary file not shown.