1
0
mirror of https://github.com/calebstewart/pwncat.git synced 2024-11-24 01:25:37 +01:00

Replaced systemd service enumeration

This version is compatible with update-to-date and older systemd as seen
in CentOS 7.9. Fixes #103.
This commit is contained in:
Caleb Stewart 2021-06-05 16:10:07 -04:00
parent 2de02baee9
commit 377f948a93

View File

@ -1,10 +1,13 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import subprocess
import rich.markup import rich.markup
import pwncat import pwncat
from pwncat.db import Fact from pwncat.db import Fact
from pwncat.util import Init from pwncat.util import Init
from pwncat.modules import Status, ModuleFailed
from pwncat.subprocess import CalledProcessError
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
@ -50,6 +53,37 @@ class ServiceData(Fact):
return line return line
def build_service_data(session, source, service):
""" Build a service data object from a dictionary """
# Grab the user name if available
user = service.get("User", None).strip()
# Resolve to user object
if user is not None:
user = session.find_user(name=user)
# If the user existed, grab the ID
if user is not None:
uid = user.id
else:
# Otherwise, assume it was root
uid = 0
try:
pid = int(service.get("MainPID", None))
except ValueError:
pid = None
return ServiceData(
source=source,
name=service["Id"].strip(),
uid=uid,
state=service.get("SubState", "unknown").strip(),
pid=pid,
)
class Module(EnumerateModule): class Module(EnumerateModule):
"""Enumerate systemd services on the victim""" """Enumerate systemd services on the victim"""
@ -66,32 +100,38 @@ class Module(EnumerateModule):
return return
break break
# Request the list of services # Ensure we build the user cache
# For the generic call, we grab the name, PID, user, and state session.find_user(uid=0)
# of each process. If some part of pwncat needs more, it can
# request it specifically.
data = session.platform.run( try:
"systemctl show --type=service --no-pager --all --value --property Id --property MainPID --property UID --property SubState \\*", # List all services and grab the details
capture_output=True, proc = session.platform.Popen(
text=True, "systemctl list-units --type=service --no-pager --all --no-legend --plain | cut -d' ' -f1 | xargs systemctl show --no-pager --all --property Id --property User --property MainPID --property SubState",
check=True, shell=True,
) stdout=subprocess.PIPE,
text=True,
)
if data.stdout: service = {}
data = data.stdout.split("\n\n")
for segment in data: for line in proc.stdout:
section = segment.split("\n") if line.strip() == "":
try: # We can only build a service structure if we know the name
pid = int(section[0]) if "Id" in service and service["Id"].strip() != "":
except ValueError as exc: yield build_service_data(session, self.name, service)
# Reset service dict
service = {}
continue continue
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) # Store the key-value pair in the dict
name, *value = line.split("=")
value = "=".join(value)
service[name] = value
finally:
try:
proc.wait(2)
except TimeoutError:
proc.kill()
proc.wait()