mirror of
https://github.com/calebstewart/pwncat.git
synced 2024-11-27 19:04:15 +01:00
Migrated some enumeration code into the new module framework
This commit is contained in:
parent
9a855c409f
commit
1706213920
46
pwncat/modules/enumerate/arch.py
Normal file
46
pwncat/modules/enumerate/arch.py
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
from typing import List
|
||||||
|
import dataclasses
|
||||||
|
|
||||||
|
import pwncat
|
||||||
|
from pwncat import util
|
||||||
|
from pwncat.modules.enumerate import EnumerateModule, Schedule
|
||||||
|
|
||||||
|
|
||||||
|
@dataclasses.dataclass
|
||||||
|
class ArchData:
|
||||||
|
"""
|
||||||
|
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.
|
||||||
|
|
||||||
|
This explanation came from here:
|
||||||
|
https://askubuntu.com/questions/843197/what-are-kernel-version-number-components-w-x-yy-zzz-called
|
||||||
|
"""
|
||||||
|
|
||||||
|
arch: str
|
||||||
|
""" The determined architecture. """
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f"Running on a [cyan]{self.arch}[/cyan] processor"
|
||||||
|
|
||||||
|
|
||||||
|
class Module(EnumerateModule):
|
||||||
|
"""
|
||||||
|
Enumerate kernel/OS version information
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
|
||||||
|
PROVIDES = ["arch"]
|
||||||
|
|
||||||
|
def enumerate(self):
|
||||||
|
"""
|
||||||
|
Enumerate kernel/OS version information
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
result = pwncat.victim.env(["uname", "-m"]).decode("utf-8").strip()
|
||||||
|
except FileNotFoundError:
|
||||||
|
return
|
||||||
|
|
||||||
|
yield "arch", ArchData(result)
|
43
pwncat/modules/enumerate/aslr.py
Normal file
43
pwncat/modules/enumerate/aslr.py
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
from typing import List
|
||||||
|
import dataclasses
|
||||||
|
|
||||||
|
import pwncat
|
||||||
|
from pwncat import util
|
||||||
|
from pwncat.modules.enumerate import EnumerateModule, Schedule
|
||||||
|
|
||||||
|
|
||||||
|
@dataclasses.dataclass
|
||||||
|
class ASLRStateData:
|
||||||
|
|
||||||
|
state: int
|
||||||
|
""" the value of /proc/sys/kernel/randomize_va_space """
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
if self.state == 0:
|
||||||
|
return f"ASLR is [green]disabled[/green]"
|
||||||
|
return f"ASLR is [red]enabled[/red]"
|
||||||
|
|
||||||
|
|
||||||
|
class Module(EnumerateModule):
|
||||||
|
"""
|
||||||
|
Determine whether or not ASLR is enabled or disabled.
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
|
||||||
|
PROVIDES = ["aslr"]
|
||||||
|
|
||||||
|
def enumerate(self):
|
||||||
|
|
||||||
|
try:
|
||||||
|
with pwncat.victim.open("/proc/sys/kernel/randomize_va_space", "r") as filp:
|
||||||
|
value = filp.read()
|
||||||
|
try:
|
||||||
|
value = int(value)
|
||||||
|
except ValueError:
|
||||||
|
value = None
|
||||||
|
|
||||||
|
if value is not None:
|
||||||
|
yield "aslr", ASLRStateData(value)
|
||||||
|
except (FileNotFoundError, PermissionError):
|
||||||
|
pass
|
51
pwncat/modules/enumerate/container.py
Normal file
51
pwncat/modules/enumerate/container.py
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
from typing import List
|
||||||
|
import dataclasses
|
||||||
|
|
||||||
|
import pwncat
|
||||||
|
from pwncat import util
|
||||||
|
from pwncat.modules.enumerate import EnumerateModule, Schedule
|
||||||
|
|
||||||
|
|
||||||
|
@dataclasses.dataclass
|
||||||
|
class ContainerData:
|
||||||
|
|
||||||
|
type: str
|
||||||
|
""" what type of container? either docker or lxd """
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f"Running in a [yellow]{self.type}[/yellow] container"
|
||||||
|
|
||||||
|
|
||||||
|
class Module(EnumerateModule):
|
||||||
|
"""
|
||||||
|
Check if this system is inside a container
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
|
||||||
|
PROVIDES = ["container"]
|
||||||
|
|
||||||
|
def enumerate(self):
|
||||||
|
|
||||||
|
try:
|
||||||
|
with pwncat.victim.open("/proc/self/cgroup", "r") as filp:
|
||||||
|
if "docker" in filp.read().lower():
|
||||||
|
yield "container", ContainerData("docker")
|
||||||
|
return
|
||||||
|
except (FileNotFoundError, PermissionError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
with pwncat.victim.subprocess(
|
||||||
|
f'find / -maxdepth 3 -name "*dockerenv*" -exec ls -la {{}} \\; 2>/dev/null', "r"
|
||||||
|
) as pipe:
|
||||||
|
if pipe.read().strip() != b"":
|
||||||
|
yield "container", ContainerData("docker")
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
with pwncat.victim.open("/proc/1/environ", "r") as filp:
|
||||||
|
if "container=lxc" in filp.read().lower():
|
||||||
|
yield "container", ContainerData("lxc")
|
||||||
|
return
|
||||||
|
except (FileNotFoundError, PermissionError):
|
||||||
|
pass
|
75
pwncat/modules/enumerate/distro.py
Normal file
75
pwncat/modules/enumerate/distro.py
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
from typing import List
|
||||||
|
import dataclasses
|
||||||
|
|
||||||
|
import pwncat
|
||||||
|
from pwncat import util
|
||||||
|
from pwncat.modules.enumerate import EnumerateModule, Schedule
|
||||||
|
|
||||||
|
|
||||||
|
@dataclasses.dataclass
|
||||||
|
class DistroVersionData:
|
||||||
|
"""
|
||||||
|
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.
|
||||||
|
|
||||||
|
This explanation came from here:
|
||||||
|
https://askubuntu.com/questions/843197/what-are-kernel-version-number-components-w-x-yy-zzz-called
|
||||||
|
"""
|
||||||
|
|
||||||
|
name: str
|
||||||
|
ident: str
|
||||||
|
build_id: str
|
||||||
|
version: str
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return (
|
||||||
|
f"Running [blue]{self.name}[/blue] ([cyan]{self.ident}[/cyan]), "
|
||||||
|
f"Version [red]{self.version}[/red], "
|
||||||
|
f"Build ID [green]{self.build_id}[/green]."
|
||||||
|
)
|
||||||
|
|
||||||
|
class Module(EnumerateModule):
|
||||||
|
"""
|
||||||
|
Enumerate kernel/OS version information
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
|
||||||
|
PROVIDES = ["distro"]
|
||||||
|
|
||||||
|
def enumerate(self):
|
||||||
|
|
||||||
|
build_id = None
|
||||||
|
pretty_name = None
|
||||||
|
ident = None
|
||||||
|
version = None
|
||||||
|
|
||||||
|
try:
|
||||||
|
with pwncat.victim.open("/etc/os-release", "r") as filp:
|
||||||
|
for line in filp:
|
||||||
|
line = line.strip()
|
||||||
|
if line.startswith("PRETTY_NAME="):
|
||||||
|
pretty_name = line.split("=")[1].strip('"')
|
||||||
|
elif line.startswith("BUILD_ID="):
|
||||||
|
build_id = line.split("=")[1].strip('"')
|
||||||
|
elif line.startswith("ID="):
|
||||||
|
ident = line.split("=")[1].strip('"')
|
||||||
|
elif line.startswith("VERSION_ID="):
|
||||||
|
version = line.split("=")[1].strip('"')
|
||||||
|
except (PermissionError, FileNotFoundError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
if version is None:
|
||||||
|
try:
|
||||||
|
with pwncat.victim.open("/etc/lsb-release", "r") as filp:
|
||||||
|
for line in filp:
|
||||||
|
if line.startswith("LSB_VERSION="):
|
||||||
|
version = line.split("=")[1].strip('"')
|
||||||
|
break
|
||||||
|
except (PermissionError, FileNotFoundError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
if pretty_name is None and build_id is None and ident is None and version is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
yield "distro", DistroVersionData(pretty_name, ident, build_id, version)
|
38
pwncat/modules/enumerate/hostname.py
Normal file
38
pwncat/modules/enumerate/hostname.py
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
from typing import List
|
||||||
|
import dataclasses
|
||||||
|
|
||||||
|
import pwncat
|
||||||
|
from pwncat import util
|
||||||
|
from pwncat.modules.enumerate import EnumerateModule, Schedule
|
||||||
|
|
||||||
|
|
||||||
|
class Module(EnumerateModule):
|
||||||
|
"""
|
||||||
|
Enumerate system hostname facts
|
||||||
|
:return: A generator of hostname facts
|
||||||
|
"""
|
||||||
|
|
||||||
|
PROVIDES = ["hostname"]
|
||||||
|
|
||||||
|
def enumerate(self):
|
||||||
|
|
||||||
|
try:
|
||||||
|
hostname = pwncat.victim.env(["hostname", "-f"]).decode("utf-8").strip()
|
||||||
|
yield "hostname", hostname
|
||||||
|
return
|
||||||
|
except FileNotFoundError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
try:
|
||||||
|
hostname = pwncat.victim.env(["hostnamectl"]).decode("utf-8").strip()
|
||||||
|
hostname = hostname.replace("\r\n", "\n").split("\n")
|
||||||
|
for name in hostname:
|
||||||
|
if "static hostname" in name.lower():
|
||||||
|
hostname = name.split(": ")[1]
|
||||||
|
yield "hostname", hostname
|
||||||
|
return
|
||||||
|
except (FileNotFoundError, IndexError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
return
|
50
pwncat/modules/enumerate/hosts.py
Normal file
50
pwncat/modules/enumerate/hosts.py
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
from typing import List
|
||||||
|
import dataclasses
|
||||||
|
import re
|
||||||
|
|
||||||
|
import pwncat
|
||||||
|
from pwncat import util
|
||||||
|
from pwncat.modules.enumerate import EnumerateModule, Schedule
|
||||||
|
|
||||||
|
@dataclasses.dataclass
|
||||||
|
class HostData:
|
||||||
|
|
||||||
|
address: str
|
||||||
|
hostnames: List[str]
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
joined_hostnames = ", ".join(self.hostnames)
|
||||||
|
return f"[cyan]{self.address}[/cyan] -> [blue]{joined_hostnames}[/blue]"
|
||||||
|
|
||||||
|
class Module(EnumerateModule):
|
||||||
|
"""
|
||||||
|
Enumerate hosts identified in /etc/hosts which are not localhost
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
|
||||||
|
PROVIDES = ["hosts"]
|
||||||
|
|
||||||
|
def enumerate(self):
|
||||||
|
|
||||||
|
try:
|
||||||
|
with pwncat.victim.open("/etc/hosts", "r") as filp:
|
||||||
|
for line in filp:
|
||||||
|
# Remove comments
|
||||||
|
line = re.sub(r"#.*$", "", line).strip()
|
||||||
|
line = line.replace("\t", " ")
|
||||||
|
# We don't care about localhost or localdomain entries
|
||||||
|
if (
|
||||||
|
line.endswith("localhost")
|
||||||
|
or line.endswith(".localdomain")
|
||||||
|
or line.endswith("localhost6")
|
||||||
|
or line.endswith(".localdomain")
|
||||||
|
or line.endswith("localhost4")
|
||||||
|
or line.endswith("localdomain4")
|
||||||
|
or line == ""
|
||||||
|
):
|
||||||
|
continue
|
||||||
|
address, *hostnames = [e for e in line.split(" ") if e != ""]
|
||||||
|
yield "hosts", HostData(address, hostnames)
|
||||||
|
except (PermissionError, FileNotFoundError):
|
||||||
|
pass
|
77
pwncat/modules/enumerate/init.py
Normal file
77
pwncat/modules/enumerate/init.py
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
from typing import List
|
||||||
|
import dataclasses
|
||||||
|
|
||||||
|
import pwncat
|
||||||
|
from pwncat import util
|
||||||
|
from pwncat.modules.enumerate import EnumerateModule, Schedule
|
||||||
|
|
||||||
|
@dataclasses.dataclass
|
||||||
|
class InitSystemData:
|
||||||
|
|
||||||
|
init: util.Init
|
||||||
|
version: str
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f"Running [blue]{self.init}[/blue]"
|
||||||
|
|
||||||
|
@property
|
||||||
|
def description(self):
|
||||||
|
return self.version
|
||||||
|
|
||||||
|
class Module(EnumerateModule):
|
||||||
|
"""
|
||||||
|
Enumerate system init service
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
|
||||||
|
PROVIDES = ["init"]
|
||||||
|
|
||||||
|
def enumerate(self):
|
||||||
|
|
||||||
|
init = util.Init.UNKNOWN
|
||||||
|
version = None
|
||||||
|
|
||||||
|
# Try to get the command name of the running init process
|
||||||
|
try:
|
||||||
|
with pwncat.victim.open("/proc/1/comm", "r") as filp:
|
||||||
|
comm = filp.read().strip()
|
||||||
|
if comm is not None:
|
||||||
|
if "systemd" in comm.lower():
|
||||||
|
init = util.Init.SYSTEMD
|
||||||
|
elif "sysv" in comm.lower():
|
||||||
|
init = util.Init.SYSV
|
||||||
|
elif "upstart" in comm.lower():
|
||||||
|
init = util.Init.UPSTART
|
||||||
|
except (PermissionError, FileNotFoundError):
|
||||||
|
comm = None
|
||||||
|
|
||||||
|
# Try to get the command name of the running init process
|
||||||
|
try:
|
||||||
|
with pwncat.victim.open("/proc/1/cmdline", "r") as filp:
|
||||||
|
comm = filp.read().strip().split("\x00")[0]
|
||||||
|
except (PermissionError, FileNotFoundError):
|
||||||
|
comm = None
|
||||||
|
|
||||||
|
if comm is not None:
|
||||||
|
if "systemd" in comm.lower():
|
||||||
|
init = util.Init.SYSTEMD
|
||||||
|
elif "sysv" in comm.lower():
|
||||||
|
init = util.Init.SYSV
|
||||||
|
elif "upstart" in comm.lower():
|
||||||
|
init = util.Init.UPSTART
|
||||||
|
|
||||||
|
with pwncat.victim.subprocess(f"{comm} --version", "r") as filp:
|
||||||
|
version = filp.read().decode("utf-8").strip()
|
||||||
|
if "systemd" in version.lower():
|
||||||
|
init = util.Init.SYSTEMD
|
||||||
|
elif "sysv" in version.lower():
|
||||||
|
init = util.Init.SYSV
|
||||||
|
elif "upstart" in version.lower():
|
||||||
|
init = util.Init.UPSTART
|
||||||
|
|
||||||
|
# No need to provide an empty version string. They apparently don't support "--version"
|
||||||
|
if version == "":
|
||||||
|
version = None
|
||||||
|
|
||||||
|
yield "init", InitSystemData(init, version)
|
65
pwncat/modules/enumerate/kernel.py
Normal file
65
pwncat/modules/enumerate/kernel.py
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
from typing import List
|
||||||
|
import dataclasses
|
||||||
|
|
||||||
|
import pwncat
|
||||||
|
from pwncat import util
|
||||||
|
from pwncat.modules.enumerate import EnumerateModule, Schedule
|
||||||
|
|
||||||
|
@dataclasses.dataclass
|
||||||
|
class KernelVersionData:
|
||||||
|
"""
|
||||||
|
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.
|
||||||
|
|
||||||
|
This explanation came from here:
|
||||||
|
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 __str__(self):
|
||||||
|
return (
|
||||||
|
f"Running Linux Kernel [red]{self.major}[/red]."
|
||||||
|
f"[green]{self.minor}[/green]."
|
||||||
|
f"[blue]{self.patch}[/blue]-[cyan]{self.abi}[/cyan]"
|
||||||
|
)
|
||||||
|
|
||||||
|
class Module(EnumerateModule):
|
||||||
|
"""
|
||||||
|
Enumerate kernel/OS version information
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
PROVIDES = ["kernel"]
|
||||||
|
|
||||||
|
def enumerate(self):
|
||||||
|
|
||||||
|
# Try to find kernel version number
|
||||||
|
try:
|
||||||
|
kernel = pwncat.victim.env(["uname", "-r"]).strip().decode("utf-8")
|
||||||
|
if kernel == "":
|
||||||
|
raise FileNotFoundError
|
||||||
|
except FileNotFoundError:
|
||||||
|
try:
|
||||||
|
with pwncat.victim.open("/proc/version", "r") as filp:
|
||||||
|
kernel = filp.read()
|
||||||
|
except (PermissionError, FileNotFoundError):
|
||||||
|
kernel = None
|
||||||
|
|
||||||
|
# Parse the kernel version number
|
||||||
|
if kernel is not None:
|
||||||
|
kernel = kernel.strip()
|
||||||
|
# We got the full "uname -a" style output
|
||||||
|
if kernel.lower().startswith("linux"):
|
||||||
|
kernel = kernel.split(" ")[2]
|
||||||
|
|
||||||
|
# Split out the sections
|
||||||
|
w, x, *y_and_z = kernel.split(".")
|
||||||
|
y_and_z = ".".join(y_and_z).split("-")
|
||||||
|
y = y_and_z[0]
|
||||||
|
z = "-".join(y_and_z[1:])
|
||||||
|
|
||||||
|
yield "kernel", KernelVersionData(int(w), int(x), int(y), z)
|
Loading…
Reference in New Issue
Block a user