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
|
||||
import dataclasses
|
||||
|
||||
import rich.markup
|
||||
|
||||
import pwncat
|
||||
from pwncat.db import Fact
|
||||
from pwncat.util import Init
|
||||
from pwncat.platform.linux import Linux
|
||||
from pwncat.modules.enumerate import Schedule, EnumerateModule
|
||||
|
||||
|
||||
@dataclasses.dataclass
|
||||
class ServiceData:
|
||||
class ServiceData(Fact):
|
||||
def __init__(self, source, name, uid, state, pid):
|
||||
super().__init__(source=source, types=["system.service"])
|
||||
|
||||
name: str
|
||||
""" The name of the service as given on the remote host """
|
||||
uid: int
|
||||
""" The user this service is running as """
|
||||
state: str
|
||||
""" Whether the service is running """
|
||||
pid: int
|
||||
self.name: str = name
|
||||
""" The name of the service as given on the remote host """
|
||||
self.uid: int = uid
|
||||
""" The user this service is running as """
|
||||
self.state: str = state
|
||||
""" Whether the service is running """
|
||||
self.pid: int = pid
|
||||
|
||||
def __str__(self):
|
||||
def title(self, session):
|
||||
if self.uid == 0:
|
||||
color = "red"
|
||||
else:
|
||||
color = "green"
|
||||
|
||||
try:
|
||||
user_name = pwncat.victim.find_user_by_id(self.uid).name
|
||||
user_name = session.find_user(uid=self.uid).name
|
||||
except KeyError:
|
||||
user_name = f"{self.uid} (unknown user)"
|
||||
color = "yellow"
|
||||
@ -42,18 +45,18 @@ class ServiceData:
|
||||
|
||||
|
||||
class Module(EnumerateModule):
|
||||
""" Enumerate systemd services on the victim """
|
||||
"""Enumerate systemd services on the victim"""
|
||||
|
||||
PROVIDES = ["system.service"]
|
||||
PLATFORM = [Linux]
|
||||
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
|
||||
):
|
||||
if fact.data.init != Init.SYSTEMD:
|
||||
if fact.init != Init.SYSTEMD:
|
||||
return
|
||||
break
|
||||
|
||||
@ -61,37 +64,28 @@ class Module(EnumerateModule):
|
||||
# For the generic call, we grab the name, PID, user, and state
|
||||
# of each process. If some part of pwncat needs more, it can
|
||||
# request it specifically.
|
||||
data = pwncat.victim.env(
|
||||
[
|
||||
"systemctl",
|
||||
"show",
|
||||
"--type=service",
|
||||
"--no-pager",
|
||||
"--all",
|
||||
"--value",
|
||||
"--property",
|
||||
"Id",
|
||||
"--property",
|
||||
"MainPID",
|
||||
"--property",
|
||||
"UID",
|
||||
"--property",
|
||||
"SubState",
|
||||
"\\*",
|
||||
],
|
||||
PAGER="",
|
||||
|
||||
data = session.platform.run(
|
||||
"systemctl show --type=service --no-pager --all --value --property Id --property MainPID --property UID --property SubState \\*",
|
||||
capture_output=True,
|
||||
text=True,
|
||||
check=True,
|
||||
)
|
||||
data = data.strip().decode("utf-8").split("\n")
|
||||
|
||||
for i in range(0, len(data), 5):
|
||||
if i >= (len(data) - 4):
|
||||
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()
|
||||
if data.stdout:
|
||||
data = data.stdout.split("\n\n")
|
||||
|
||||
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 pwncat
|
||||
from pwncat.db import Fact
|
||||
from pwncat import util
|
||||
from pwncat.platform.linux import Linux
|
||||
from pwncat.modules.enumerate import Schedule, EnumerateModule
|
||||
|
||||
|
||||
@dataclasses.dataclass
|
||||
class ArchData:
|
||||
class ArchData(Fact):
|
||||
"""
|
||||
Simply the architecture of the remote machine. This class
|
||||
wraps the architecture name in a nicely printable data
|
||||
class.
|
||||
"""
|
||||
|
||||
arch: str
|
||||
""" The determined architecture. """
|
||||
def __init__(self, source, arch):
|
||||
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"
|
||||
|
||||
|
||||
@dataclasses.dataclass
|
||||
class KernelVersionData:
|
||||
class HostnameData(Fact):
|
||||
"""
|
||||
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,
|
||||
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
|
||||
"""
|
||||
|
||||
major: int
|
||||
minor: int
|
||||
patch: int
|
||||
abi: str
|
||||
def __init__(self, source, major, minor, patch, abi):
|
||||
super().__init__(source=source, types=["system.kernel.version"])
|
||||
|
||||
def __str__(self):
|
||||
self.major: int = major
|
||||
self.minor: int = minor
|
||||
self.patch: int = patch
|
||||
self.abi: str = abi
|
||||
|
||||
def title(self, session):
|
||||
return (
|
||||
f"Running Linux Kernel [red]{self.major}[/red]."
|
||||
f"[green]{self.minor}[/green]."
|
||||
@ -49,8 +69,7 @@ class KernelVersionData:
|
||||
)
|
||||
|
||||
|
||||
@dataclasses.dataclass
|
||||
class KernelVulnerabilityData:
|
||||
class KernelVulnerabilityData(Fact):
|
||||
"""
|
||||
Data describing a kernel vulnerability which appears to be exploitable
|
||||
on the remote host. This is **not** guaranteed to be exploitable, however
|
||||
@ -59,21 +78,23 @@ class KernelVulnerabilityData:
|
||||
vulnerability.
|
||||
"""
|
||||
|
||||
name: str
|
||||
versions: List[str]
|
||||
link: Optional[str]
|
||||
cve: Optional[str]
|
||||
# All exploits are assumed working, but can be marked as not working
|
||||
working: bool = True
|
||||
def __init__(self, source, name, versions, link, cve):
|
||||
super().__init__(source=source, types=["system.kernel.vuln"])
|
||||
|
||||
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]"
|
||||
if self.cve is not None:
|
||||
line += f" ([cyan]CVE-{self.cve}[/cyan])"
|
||||
return line
|
||||
|
||||
@property
|
||||
def description(self):
|
||||
def description(self, title):
|
||||
line = f"Affected Versions: {repr(self.versions)}\n"
|
||||
if self.link:
|
||||
line += f"Details: {self.link}"
|
||||
@ -102,12 +123,15 @@ class Module(EnumerateModule):
|
||||
PLATFORM = [Linux]
|
||||
SCHEDULE = Schedule.ONCE
|
||||
|
||||
def enumerate(self):
|
||||
""" Run uname and organize information """
|
||||
def enumerate(self, session):
|
||||
"""Run uname and organize information"""
|
||||
|
||||
# Grab the uname output
|
||||
output = pwncat.victim.run("uname -s -n -r -m -o").decode("utf-8").strip()
|
||||
fields = output.split(" ")
|
||||
output = session.platform.run(
|
||||
"uname -s -n -r -m -o", capture_output=True, text=True, check=True
|
||||
)
|
||||
|
||||
fields = output.stdout.split(" ")
|
||||
|
||||
# Grab the components
|
||||
# kernel_name = fields[0] if fields else None
|
||||
@ -121,14 +145,14 @@ class Module(EnumerateModule):
|
||||
y_and_z = ".".join(y_and_z).split("-")
|
||||
y = y_and_z[0]
|
||||
z = "-".join(y_and_z[1:])
|
||||
version = KernelVersionData(int(w), int(x), int(y), z)
|
||||
yield "system.kernel.version", version
|
||||
version = KernelVersionData(self.name, int(w), int(x), int(y), z)
|
||||
yield version
|
||||
|
||||
# Handle arch
|
||||
yield "system.arch", ArchData(machine_name)
|
||||
yield ArchData(self.name, machine_name)
|
||||
|
||||
# Handle Hostname
|
||||
yield "system.hostname", hostname
|
||||
yield HostnameData(self.name, hostname)
|
||||
|
||||
# Handle Kernel vulnerabilities
|
||||
with open(
|
||||
@ -140,6 +164,10 @@ class Module(EnumerateModule):
|
||||
for name, vuln in vulns.items():
|
||||
if version_string not in vuln["vuln"]:
|
||||
continue
|
||||
yield "system.kernel.vuln", KernelVulnerabilityData(
|
||||
name, vuln["vuln"], vuln.get("mil", None), vuln.get("cve", None)
|
||||
yield KernelVulnerabilityData(
|
||||
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
|
||||
# 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("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)
|
||||
|
||||
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