We're hiring - Join Doyensec!

At Doyensec, we believe that quality is the natural product of passion and care. We love what we do and we routinely take on difficult engineering challenges to help our customers build with security.

We are a small highly focused team. We concentrate on application security and do fewer things better. We don’t care about your education, background and certifications. If you are really good and passionate at building and breaking complex software, you’re the right candidate.

Open Positions

:: Full-stack Security Automation Engineer (Six Months Collaboration, Remote Work) ::

We are looking for a full-stack senior software engineer that can help us build security automation tools. If you’ve ever built a fuzzer, played with static analysis and enhanced a web scanner engine, you probably have the right skillset for the job.

We offer a well-paid six-months collaboration, combined with an additional bonus upon successful completion of the project.


  • Full-stack development (front-end, back-end components) of web security testing tools
  • Solve technical challenges at the edge of web security R&D, together with Doyensec’s founders


  • Experience developing multi-tiered software applications or products. We generally use Node.js and Java, and require proficiency in those languages
  • Ability to work with standard dev tools and techniques (IDE, git, …)
  • You’re passionate about building great software and can have fun while doing it
  • Interested in web security, with good understanding of common software vulnerabilities
  • You’re self-driven and can focus on a project to make it happen
  • Eager to learn, adapt and perfect your work

Contact us at info@doyensec.com

:: Application Security Engineer (Full-time, Remote Work - Europe) ::

We are looking for an experienced security engineer to join our consulting team. We perform graybox security testing on complex web and mobile applications. We need someone who can hit the ground running. If you’re good at “crawling around in the ventilation ducts of the world’s most popular and important applications”, you probably have the right skillset for the job.

We offer a competitive salary in a supportive and dynamic environment that rewards hard work and talent. We are dedicated to providing research-driven application security and therefore invest 25% of your time exclusively to research where we build security testing tools, discover new attack techniques, and develop countermeasures.


  • Security testing of web, mobile (iOS, Android) applications
  • Vulnerability research activities, coordinated and executed with Doyensec’s founders
  • Partner with customers to ensure project’s objectives are achieved 


  • Ability to discover, document and fix security bugs
  • You’re passionate about understanding complex systems and can have fun while doing it
  • Top-notch in web security. Show us public research, code, advisories, etc.
  • Eager to learn, adapt, and perfect your work

Contact us at info@doyensec.com

Staring into the Spotlight

Spotlight is the all pervasive seeing eye of the OSX userland. It drinks from a spout of file events sprayed out of the kernel and neatly indexes such things for later use. It is an amalgamation of binaries and libraries, all neatly fitted together just to give a user oversight of their box. It presents interesting attack surface and this blog post is an explanation of how some of it works.

One day, we found some interesting looking crashes recorded in /Users/<name>/Library/Logs/DiagnosticReports

Yet the crashes weren’t from the target. In OSX, whenever a file is created, a filesystem event is generated and sent down from the kernel. Spotlight listens for this event and others to immediately parse the created file for metadata. While fuzzing a native file parser these Spotlight crashes began to appear from mdworker processes. Spotlight was attempting to index each of the mutated input samples, intending to include them in search results later.


The Spotlight system is overseen by mds. It opens and reads from /dev/fsevents, which streams down file system event information from the kernel. Instead of dumping the events to disk, like fseventsd, it dumps the events into worker processes to be parsed on behalf of Spotlight. Mds is responsible for delegating work and managing mdworker processes with whom it communicates through mach messaging. It creates, monitors, and kills mdworkers based on some light rules. The kernel does not block and the volume of events streaming through the fsevents device can be quite a lot. Mds will spawn more mdworker processes when handling a higher event magnitude but there is no guarantee it can see and capture every single event.

The kernel filters which root level processes can read from this device. fsevents filter

Each of the mdworker processes get spawned, parse some files, write the meta info, and die. Mdworker shares a lot of code with mdimport, its command line equivalent. The mdimport binary is used to debug and test Spotlight importers and therefore makes a great target for auditing and fuzzing. Much of what we talk about in regards to mdimport also applies to mdworker.


You can see what mdworkers are up to with the following: sudo fs_usage -w -f filesys mdworker

Importers are found in /Library/Spotlight, /System/Library/Spotlight, or in an application’s bundle within “/Contents/Library/Spotlight”. If the latter is chosen, the app typically runs a post install script with mdimport -r <importer> and/or lsregister. The following command shows the list of importers present on my laptop. It shows some third party apps have installed their own importers.

$ mdimport -L
2017-07-30 00:36:15.518 mdimport[40541:1884333] Paths: id(501) (
    "/Library/Spotlight/Microsoft Office.mdimporter",
    "/Applications/Xcode.app/Contents/Applications/Application Loader.app/Contents/Library/Spotlight/MZSpotlight.mdimporter",

These .mdimporter files are actually just packages holding a binary. These binaries are what we are attacking.

Using mdimport is simple - mdimport <file>. Spotlight will only index metadata for filetypes having an associated importer. File types are identified through magic. For example, mdimport reads from the MAGIC environment variable or uses the “/usr/share/file/magic” directory which contains both the compiled .mgc file and the actual magic patterns. The format of magic files is discussed at https://developer.apple.com/legacy/library/documentation/Darwin/Reference/ManPages/man5/magic.5.html.

Crash File

crash logging

One thing to notice is that the crash log will contain some helpful information about the cause. The following message gets logged by both mdworker and mdimport, which share much of the same code:

Application Specific Information:
import fstype:hfs fsflag:480D000 flags:40000007E diag:0 isXCode:0 uti:com.apple.truetype-datafork-suitcase-font plugin:/Library/Spotlight/Font.mdimporter - find suspect file using: sudo mdutil -t 2682437

The 2682437 is the iNode reference number for the file in question on disk. The -t argument to mdutil will ask it to lookup the file based on volume ID and iNode and spit out the string. It performs an open and fcntl on the pseudo directory /.vol/<Volume ID>/<File iNode>. You can see this info with the stat syscall on a file.

$ stat /etc
16777220 418395 lrwxr-xr-x 1 root wheel 0 11 "Dec 10 05:13:41 2016" "Dec 10 05:13:41 2016" "Dec 10 05:15:47 2016" "Dec 10 05:13:41 2016" 4096 8 0x88000 /etc

$ ls /.vol/16777220/418395
afpovertcp.cfg    fstab.hd            networks          protocols
aliases           ftpd.conf           newsyslog.conf    racoon
aliases.db        ftpd.conf.default   newsyslog.d       rc.common

The UTI registered by the importer is also shown “com.apple.truetype-datafork-suitcase-font”. In this case, the crash is caused by a malformed Datafork TrueType suitcase (.dfont) file.

When we find a bug, we can study it under lldb. Launch mdimport under the debugger with the crash file as an argument. In this particular bug it breaks with an exception in the /System/Library/Spotlight/Font.mdimporter importer.

crash logging

The screenshot below shows the problem procedure with the crashing instruction highlighted for this particular bug.

The rsi register points into the memory mapped font file. A value is read out and stored in rax which is then used as an offset from rcx which points to the text segment of the executable in memory. A lookup is done on a hardcoded table and parsing proceeds from there. The integer read out of the font file is never validated.

When writing or reversing a Spotlight importer, the main symbol to first look at will be GetMetadataForFile or GetMetadataForURL. This function receives a path to parse and is expected to return the metadata as a CFDictionary.

We can see, from the stacktrace, how and where mdimport jumps into the GetMetadataForFile function in the Font importer. Fuzzing mdimport is straightforward, crashes and signals are easily caught.

The variety of importers present on OSX are sometimes patched alongside the framework libraries, as code is shared. However, a lot of code is unique to these binaries and represents a nice attack surface. The Spotlight system is extensive, including its own query language and makes a great target where more research is needed.

When fuzzing in general on OSX, disable Spotlight oversight of the folder where you generate and remove your input samples. The folder can be added in System Preferences->Spotlight->Privacy. You can’t fuzz mdimport from this folder, instead disable Spotlight with “mdutil -i off” and run your fuzzer from a different folder.


Modern Alchemy: Turning XSS into RCE


At the recent Black Hat Briefings 2017, Doyensec’s co-founder Luca Carettoni presented a new research on Electron security. After a quick overview of Electron’s security model, we disclosed design weaknesses and implementation bugs that can be leveraged to compromise any Electron-based application. In particular, we discussed a bypass that would allow reliable Remote Code Execution (RCE) when rendering untrusted content (for example via Cross-Site Scripting) even with framework-level protections in place.

In this blog post, we would like to provide insight into the bug (CVE-2017-12581) and remediations.

What’s Electron?

While you may not recognize the name, it is likely that you’re already using Electron since it’s running on millions of computers. Slack, Atom, Visual Studio Code, WordPress Desktop, Github Desktop, Basecamp3, Mattermost are just few examples of applications built using this framework. Any time that a traditional web application is ported to desktop, it is likely that the developers used Electron.

Electron Motto

Understanding the nodeIntegration flag

While Electron is based on Chromium’s Content module, it is not a browser. Since it facilitates the construction of complex desktop applications, Electron gives the developer a lot of power. In fact, thanks to the integration with Node.js, JavaScript can access operating system primitives to take full advantage of native desktop mechanisms.

It is well understood that rendering untrusted remote/local content with Node integration enabled is dangerous. For this reason, Electron provides two mechanisms to “sandbox” untrusted resources:


mainWindow = new BrowserWindow({  
	"webPreferences": { 
		"nodeIntegration" : false,  
		"nodeIntegrationInWorker" : false 



<webview src="https://www.doyensec.com/"></webview>

In above examples, the nodeIntegration flag is set to false. JavaScript running in the page won’t have access to global references despite having a Node.js engine running in the renderer process.

Hunting for nodeIntegration bypasses

It should now be clear why nodeIntegration is a critical security-relevant setting for the framework. A vulnerability in this mechanism could lead to full host compromise from simply rendering untrusted web pages. As modern alchemists, we use this type of flaws to turn traditional XSS into RCE. Since all Electron applications are bundled with the framework code, it is also complicated to fix these issues across the entire ecosystem.

During our research, we have extensively analyzed all project code changes to uncover previously discovered bypasses (we counted 6 before v1.6.1) with the goal of studying Electron’s design and weaknesses. Armed with that knowledge, we went for a hunt.

By studying the official documentation, we quickly identified a significant deviation from standard browsers caused by Electron’s “glorified” JavaScript APIs.

When a new window is created, Electron returns an instance of BrowserWindowProxy. This class can be used to manipulate the child browser window, thus subverting the Same-Origin Policy (SOP).

SOP Bypass #1

const win = window.open("https://www.doyensec.com"); 
win.location = "javascript:alert(document.domain)"; 

SOP Bypass #2

const win = window.open("https://www.doyensec.com"); 

The eval mechanism used by the SOP Bypass #2 can be explained with the following diagram:

BrowserWindowProxy's Eval

Additional source code review revealed the presence of privileged URLs (similar to browsers’ privileged zones). Combining the SOP-bypass by design with a specific privileged url defined in lib/renderer/init.js, we realized that we could override the nodeIntegration setting.

Chrome DevTools in Electron, prior to 1.6.8

A simple, yet reliable, proof-of-concept of the nodeIntegration bypass affecting all Electron releases prior to 1.6.7 is hereby included:

<!DOCTYPE html>
    <title>nodeIntegration bypass (SOP2RCE)</title>
    	document.write("Current location:" + window.location.href + "<br>");

    	const win = window.open("chrome-devtools://devtools/bundled/inspector.html");
    	win.eval("const {shell} = require('electron'); 

On May 10, 2017 we reported this issue to the maintainers via email. In a matter of hours, we received a reply that they were already working on a fix since the privileged chrome-devtools:// was discovered during an internal security activity just few days before our report. In fact, while the latest release on the official website at that time was 1.6.7, the git commit that fixes the privileged url is dated April 24, 2017.

The issue was fixed in 1.6.8 (officially released around the 15th of May). All previous versions of Electron and consequently all Electron-based apps were affected. Mitre assigned CVE-2017-12581 for this issue.

Mitigating nodeIntegration bypass vulnerabilities

  • Keep your application in sync with the latest Electron framework release. When releasing your product, you’re also shipping a bundle composed of Electron, Chromium shared library and Node. Vulnerabilities affecting these components may impact the security of your application. By updating Electron to the latest version, you ensure that critical vulnerabilities (such as nodeIntegration bypasses) are already patched and cannot be exploited to abuse your application.

  • Adopt secure coding practices. The first line of defense for your application is your own code. Common web vulnerabilities, such as Cross-Site Scripting (XSS), have a higher security impact on Electron hence it is highly recommend to adopt secure software development best practices and perform periodic security testing.

  • Know your framework (and its limitations). Certain principles and security mechanisms implemented by modern browsers are not enforced in Electron (e.g. SOP enforcement). Adopt defense in depth mechanisms to mitigate those deficiencies. For more details, please refer to our Electronegativity, A study of Electron Security presentation and Electron Security Checklist white-paper.

  • Use the recent “sandbox” experimental feature. Even with nodeIntegration disabled, the current implementation of Electron does not completely mitigate all risks introduced by loading untrusted resources. As such, it is recommended to enable sandboxing which leverages the native Chromium sandbox. A sandboxed renderer does not have a Node.js environment running (with the exception of preload scripts) and the renderers can only make changes to the system by delegating tasks to the main process via IPC. While still not perfect at the time of writing (there are known security issues, sandbox is not supported for the <webview> tag, etc.) this option should be enabled to provide additional isolation.