1
0
mirror of https://github.com/calebstewart/pwncat.git synced 2024-11-27 10:54:14 +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
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
self.name: str = name
""" The name of the service as given on the remote host """
uid: int
self.uid: int = uid
""" The user this service is running as """
state: str
self.state: str = state
""" Whether the service is running """
pid: int
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"
@ -48,12 +51,12 @@ class Module(EnumerateModule):
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 = 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]:
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,
)
if data.stdout:
data = data.stdout.split("\n\n")
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(data[i + 1].strip())
state = data[i + 3].strip()
uid = int(section[1])
name = section[2].removesuffix(".service")
state = section[3]
yield "system.service", ServiceData(name, uid, state, pid)
yield ServiceData(self.name, name, uid, state, pid)

View File

@ -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
def __init__(self, source, arch):
super().__init__(source=source, types=["system.arch"])
self.arch: str = arch
""" The determined architecture. """
def __str__(self):
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]
def __init__(self, source, name, versions, link, cve):
super().__init__(source=source, types=["system.kernel.vuln"])
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 __str__(self):
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):
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
View File

@ -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 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.