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:
parent
23dc10c0a9
commit
2aa3aa79c7
@ -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)
|
||||||
|
@ -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
14
test.py
@ -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.index
Normal file
BIN
test.zodb.index
Normal file
Binary file not shown.
1
test.zodb.lock
Normal file
1
test.zodb.lock
Normal file
@ -0,0 +1 @@
|
|||||||
|
2639
|
BIN
test.zodb.tmp
Normal file
BIN
test.zodb.tmp
Normal file
Binary file not shown.
Loading…
Reference in New Issue
Block a user