mp_js
mp_js
Windows Defender’s
JavaScript Engine
Alexei Bulazel
@0xAlexei
REcon Brussels 2018
About Me
Twitter:
@0xAlexei
Outline
1. Introduction
2. Tooling & Process
3. Reverse Engineering
4. Vulnerability Discussion
5. Conclusion
Motivation
● Tavis and Natalie at P0 dropped
some awesome bugs
1. Introduction
2. Tooling & Process
3. Reverse Engineering
4. Vulnerability Discussion
5. Conclusion
Binaries ● 5/23 (P0 bugs fixed) ● 11/1
● 6/20 ● 12/6 (UK NCSC
mpam-fe.exe released monthly:
● 7/19 bugs fixed)
● 8/23 ● 1/18 (latest)
● mpengine.dll
“Microsoft Malware Protection Engine” ● 9/27
● MPSigStub.exe
“Microsoft Malware Protection
Signature Update Stub”
● mpasbase.vdm
● mpasdlta.vdm
● mpavbase.vdm
● mpavdlta.vdm
32 & 64-bit builds
Tools
Solution:
● Custom loader
● Call directly into
functions that
initiate scanning
“Repeated vs. single-round games in security”
Halvar Flake, BSides Zurich Keynote
Loader and Shell
Windows Binary
Loader and Shell
Windows Binary
MpEngine.dll
Loader and Shell
Windows Binary
MpEngine.dll
JS Emulator
Loader and Shell
Windows Binary
MpEngine.dll
JS Emulator
JavaScriptInterpreter::eval
Loader and Shell
Windows Binary
MpEngine.dll
JS Emulator
JavaScriptInterpreter::eval
JsRuntimeState::
JsRuntimeState()
Loader and Shell
Windows Binary
MpEngine.dll
Add Custom
VTable
JS Emulator
VTable JavaScriptInterpreter::eval
Windows Binary
MpEngine.dll
Add Custom
VTable
JS Emulator
VTable JavaScriptInterpreter::eval
JS Input
(function (){
for (var i = 0; i < 10;
i++){
log(i);
}
})()
WinDbg
Tavis Ormandy’s loadlibrary
● PE loader for Linux
○ Shim out implementations for Windows API imported functions
○ Go through full initialization process
● mpscript tool exposes the JS engine
● Hook the __rsignal function that tells MpEngine to scan
something
○ Scan a buffer, it gets detected as JS and subsequently analyzed
● Hook _strtod to get output
○ function log(msg){ parseFloat('__log: ' + msg);}
https://github.com/taviso/loadlibrary
Taviso’s loadlibrary https://github.com/taviso/loadlibrary
Linux mpscript
Binary
Taviso’s loadlibrary https://github.com/taviso/loadlibrary
Linux mpscript
MpEngine.dll
Binary
Taviso’s loadlibrary https://github.com/taviso/loadlibrary
Linux mpscript
MpEngine.dll
Binary
WinAPI
Emulation IAT
Taviso’s loadlibrary https://github.com/taviso/loadlibrary
Linux mpscript
MpEngine.dll
Binary
WinAPI
Emulation IAT
JS Emulator
Taviso’s loadlibrary https://github.com/taviso/loadlibrary
Linux mpscript
MpEngine.dll
Binary
WinAPI
Emulation IAT
JsDelegateObject_Global::
parseFloat
_strtod
JS Emulator
Taviso’s loadlibrary https://github.com/taviso/loadlibrary
Linux mpscript
MpEngine.dll
Binary
WinAPI
Emulation IAT
JsDelegateObject_Global::
parseFloat
Print to stdout
_strtod
JS Emulator
Taviso’s loadlibrary https://github.com/taviso/loadlibrary
Linux mpscript
MpEngine.dll
Binary
WinAPI
Emulation IAT
JsDelegateObject_Global::
parseFloat
Print to stdout
JS Input
function log(msg){
parseFloat('__log: ' + _strtod
msg);
JS Emulator
}
Linux mpscript
MpEngine.dll
Binary
WinAPI
Emulation IAT
JsDelegateObject_Global::
parseFloat
Print to stdout
JS Input
function log(msg){
parseFloat('__log: ' + _strtod
msg);
JS Emulator
}
Initialization None, just LoadLibrary the DLL Full, requires corresponding VDM files
Scanning Direct call into JS engine Call main entry point for any AV scan
Various ::eval
functions implement
the interpretation of
JS statements
(function() {
String(parseFloat([1, 2, 3].join())).toString()
})()
Type Checking
Explicit Checking
● m_type
● m_class
Type Checking
Explicit Checking
● m_type
● m_class
Type Checking Casting
Explicit Checking
● m_type
● m_class
Type Checking Casting
Explicit Checking
● m_type
● m_class
Duck Typing
Type Checking Casting
Explicit Checking
● m_type Redefinition?
● m_class (function x(){
var x = new Array();
x.foo = (new Date()).getMonth;
x.foo();
})()
triggerEvent(): err_typeerror
triggerEvent(): error_tostring
Log(): <NA>: 0: uncaught exception: TypeError:
Date.prototype.getMonth() must be called only for Dates
Duck Typing
DOM Emulation dt mpengine!HtmlDocument::Impl::Node
+0x00 type : HtmlDocument::NodeType
+0x04 name : std::pair<char const *,unsigned int>
+0x0c data : std::pair<char const *,unsigned int>
+0x14 html : std::pair<char const *,unsigned int>
● Objects may have m_html pointer to +0x1c parent : Uint4B
+0x20 nextSibling : Uint4B
HtmlDocument::Iterator for +0x24 firstKid : Uint4B
+0x28 lastKid : Uint4B
iteration over std::vector of
HtmlDocument::Impl::Node
objects
+0x0c m_propertyNames
● std::map of hash(property) → name
● Stores property names
● hash(“foo”) → “foo”
Property Hashing
Property Hashing
Property Hashing
Brute Forcing a Hash Collision
Brute Forcing a Hash Collision
hash(“length”) == hash(“fyW093”)
JsArrayObject::putUpdateLengthThrows
● Fires on any
update of Array
object properties,
checks for update
of
hash(“length”)
● Manages arrays
when “length”
property is
changed
○ Erases
elements from
the array if
indexed above
the new length
Demo 3
(function() {
var x = [1, 2, 3];
print(x.length); // 3
print(x); // "1,2,3"
x.fyW093 = 5;
print(x.fyW093); // 5
print(x.length); // 5
print(x); // "1,2,3,,"
x.fyW093 = 1;
print(x); // "1"
print(x.length) // 1
})()
Outline
1. Introduction
2. Tooling & Process
3. Reverse Engineering
a. JS Language
b. Types
c. Memory Management
d. Fingerprinting
4. Vulnerability Discussion
5. Conclusion
Memory Management
Allocations stored in
std::vector<JsHeapObject*>
Garbage Collection
Garbage Collection
Garbage Collection
Mark-and-Sweep
Garbage Collection
Garbage Collection
Mark-and-Sweep
Garbage Collection
JsRuntimeState::~JsRuntimeState
destructor tears down the JS heap with
JsHeap::~JsHeap
JsBufString char Array Backing
(function() {
print(escape("%a"));
print(escape("%"));
})()
Outline
1. Introduction
2. Tooling & Process
3. Reverse Engineering
a. JS Language
b. Types
c. Memory Management
d. Fingerprinting
4. Vulnerability Discussion
5. Conclusion
Fingerprinting
function (){
if (DetectDefender())
{
return;
}
else
{
MaliciousCode();
}
}
Fingerprinting
> log(document.referrer)
http://www.google.com/url?sa=t&rct=j&q=&esrc=s&source=web
JavaScriptLog():
TypeError: collectGarbage() is not a constructor
(function x(){
var x = new Object();
x.foo = (new String()).valueOf;
x.foo()
})()
triggerEvent(): err_typeerror
triggerEvent(): error_tostring
Log(): uncaught exception: TypeError: String.prototype.toString and String.prototype.valueOf
must be called only for Strings
(function x(){
var x = new navigator.javaEnabled();
})()
JavaScriptLog(): TypeError: Navigator.javaEnabled() and Navigator.taintEnabled() are not a
constructors
BUG, should never happen
triggerEvent(): err_typeerror
triggerEvent(): error_tostring
Log(): uncaught exception: TypeError: node.insertBefore()
‘this' object must be DOM Object (BUG, should never happen)
(function (){
var myFunction = function namedFunction(){};
print(myFunction.name);
})()
print(): undefined
(function x(){
var x = new Array();
x.getTimezoneOffset = (new Date()).getTimezoneOffset;
print(x.getTimezoneOffset())
})()
JavaScriptLog(): 0
Outline
1. Introduction
2. Tooling & Process
3. Reverse Engineering
4. Vulnerability Discussion
5. Conclusion
May 2017
Initial validation
Understanding P0’s Vulnerability
JsDelegateObject_Error::
toString
Initial validation
Get this.message
Understanding P0’s Vulnerability
JsDelegateObject_Error::
toString
Initial validation
Get this.message
Pass this.message to
function expecting JsString
Understanding P0’s Vulnerability
JsRuntimeState::triggerShortStrEvent
is an AV monitoring callback
Data Structures:
● Few controlled allocations
○ Integer overflow checked
● One array implementation
● Capped array lengths
● std::vector backing JS arrays
Attack Surface Reduction
Language:
● Complex ECMAScript features avoided
○ eg: Array.prototype.sort
Data Structures:
● Few controlled allocations
○ Integer overflow checked
● One array implementation
● Capped array lengths
● std::vector backing JS arrays
Implementation:
● Callbacks into JS runtime avoided
● Single threaded
● Little DOM implementation
● Extensive type checking (other
than P0’s bug)
● No JIT
● No GC
Attack Surface Reduction
Language:
● Complex ECMAScript features avoided
○ eg: Array.prototype.sort
Data Structures:
● Few controlled allocations
○ Integer overflow checked
● One array implementation
● Capped array lengths
● std::vector backing JS arrays
Implementation: Overall:
● Callbacks into JS runtime avoided ● Simplicity and ease of implementation
● Single threaded
● Little DOM implementation
● Take advantage of being inside an AV
● Extensive type checking (other ● Break the runtime in the interest of
than P0’s bug) security
● No JIT
● No GC ● Soon to be sandboxed...
Outline
1. Introduction
2. Tooling & Process
3. Reverse Engineering
4. Vulnerability Discussion
5. Conclusion
The Remaining 98%
43,000+ Functions
The Remaining 98%
43,000+ Functions
Unpackers
The Remaining 98%
43,000+ Functions
Unpackers
Parsers
The Remaining 98% Windows Emulator
43,000+ Functions ● x86, x64, & ARM
○ Lifted to IL for
emulation
● WinAPI & NT
Kernel emulation
Unpackers
Parsers
The Remaining 98% Windows Emulator
43,000+ Functions ● x86, x64, & ARM
○ Lifted to IL for
emulation
● WinAPI & NT
Kernel emulation
Unpackers
mpscript> (function(){
var x = new Array(1,2,"A","B");
var y = new String(x);
print(y);
})()
triggerEvent(): array_join
triggerEvent(): str_valueof
print(): 1,2,A,B
print(): undefined
Log(): <NA>: 0: execution took 68 ticks
Log(): <NA>: 0: final memory used 9KB
Log(): <NA>: 0: total of 0 GCs performed
allocate a buffer of
size of the sum of
lengths
JsString::initByVector
allocate a buffer of
size of the sum of
lengths
allocate a buffer of
size of the sum of
lengths
TOCTOU?
JsString::initByVector
TOCTOU?
JsString::initByVector
TOCTOU?
JsString::initByVector
Js[Buf,
Concat,
Ref,
Sub]String:: TOCTOU?
localCopyToBuffer