WSL Antivirus and Firewall Compatibility

Introduction

The purpose of this post is to enlighten antivirus (AV) companies to new security considerations for correctly interfacing with Bash on Ubuntu on Windows powered by the Windows Subsystem for Linux (WSL). This post will provide a background description of WSL and its underlying technologies including Pico processes. This, in combination with guidance surrounding new Pico process APIs, will provide the means necessary to prepare AV companies to properly support Pico Processes and WSL.

The Windows Subsystem for Linux

The Windows Subsystem for Linux (WSL) is a collection of components that enables native Linux ELF64 binaries to run on Windows. It was released April 1st 2016 at the Microsoft //Build conference and included as an optional component in the Anniversary Update of Windows 10. It contains both user mode and kernel mode components and is primarily comprised of:

  1. User mode session manager service that handles the Linux instance life cycle
  2. Pico provider drivers (lxss.sys, lxcore.sys) that emulate a Linux kernel by translating Linux system calls
  3. Pico processes that host the unmodified user mode Linux (e.g. /bin/bash)

Pico Processes

As part of Project Drawbridge, the Windows kernel introduced the concept of Pico processes and Pico drivers. Pico processes are OS processes without the trappings of OS services associated with subystems like a Win32 Process Environment Block (PEB). Furthermore, for a Pico process, system calls and user mode exceptions are dispatched to a paired driver.

Pico processes and drivers provide the foundation for the Windows Subsystem for Linux, which runs native unmodified Linux binaries by loading executable ELF binaries into a Pico process’s address space and executes them atop a Linux-compatible layer of syscalls.

WSL File System Interactions

To bridge the gap between Linux and Windows WSL uses two different systems to provide users seamless access to their data. Lxfs allows access to Windows files by emulating full Linux behavior for the internal Linux file system within WSL. DrvFs allows full access to Windows drives and files on NTFS. DrvF enables some of the functionality of Linux file systems, such as case sensitivity and symbolic links, while still supporting interoperability with Windows.

file system graphic

Figure 1: WSL File System Architecture

Windows 10 WSL Antivirus/Firewall Deficiencies

Since WSL was released in the Windows Anniversary Update as a beta feature there was no work done to ensure smooth operation with external AV manufacturers. This has caused a variety of issues that has impacted user experience:

  • prevented users from accessing the internet
  • prevented installation of WSL
  • prevented access to certain directories
  • Caused unexpected file permissions behaviors within the Bash environment

Changes for Improved WSL Antivirus/Firewall Interactions

The Ps notifications exist so kernel mode drivers can run code when a state of the system changes. The expectation is that drivers use these callbacks to build up state about the system which is later used to perform some function. Vendors can use the notifications to develop firewalls or anti-virus software, but the APIs are generic enough that it could be applied to other scenarios.

For the next release of Windows, we are planning to introduce the following changes. We feel that with these additions 3rd party antivirus and firewall vendors should be able to properly interface with WSL without affecting existing threat detection logic.

  • Added the ability to query the subsystem type of processes and threads
  • Extended PS thread and process notifications for subsystems

Examples

Process Notify

Original registration code

Status = PsSetCreateProcessNotifyRoutineEx(Callback,FALSE);

New registration code

Type = PsCreateProcessNotifySubsystems; Status = PsSetCreateProcessNotifyRoutineEx2(Type, Callback,FALSE);

Callback Example

void Callback (_In_ HANDLE ParentId, _In_ HANDLE ProcessId, _Inout_opt_ PPS_CREATE_NOTIFY_INFO CreateInfo) {     if (CreateInfo->Flags.IsSubsystemProcess == 0) {         /* Original callback code goes here */     } else {         Type = ProcessSubsystemInformation;         Status = NtQueryInformationProcess(ProcessHandle, Type, &Subsystem, sizeof(Subsystem), NULL);         if (Subsystem == SubsystemInformationTypeWSL) {             /* New callback code to handle WSL processes goes here */         }     } }

Thread Notify

Original registration code

Status = PsSetCreateThreadNotifyRoutine(Callback);

New registration code

Type = PsCreateThreadNotifySubsystems; Status = PsSetCreateThreadNotifyRoutineEx(Type, Callback);

Callback Example

void Callback (_In_ HANDLE ProcssId, _In_ HANDLE ThreadId, _In_ BOOLEAN Create) {     Type = ThreadSubsystemInformation;     Status = NtQueryInformationThread(ThreadHandle, Type, &Subsystem, sizeof(Subsystem), NULL);     if (Subsystem == SubsystemInformationTypeWin32) {         /* Original callback code goes here */     } else if (Subsystem == SubsystemInformationTypeWSL) {         /* New callback code to handle WSL processes goes here */     } }

 

Windows Kernel-Mode Process and Thread Manager Process and Thread Manager Routines PsSetCreateProcessNotifyRoutineEx

Comments

  • Anonymous
    November 03, 2016
    There's an unfinished sentence just before Figure 1.
    • Anonymous
      November 07, 2016
      Thanks for pointing that out.
  • Anonymous
    February 19, 2017
    Can we expect pico to also work with the built in Windows Firewall soon, so that WSL processes are recognized by WF rules?
  • Anonymous
    April 19, 2017
    It would be mutch better if you spent some time integrating some Mm callbacks for filter drivers.
  • Anonymous
    October 18, 2017
    There's a mistake in the MSDN documentation about the PCREATE_PROCESS_NOTIFY_ROUTINE_EX.https://msdn.microsoft.com/en-us/library/windows/hardware/mt764086(v=vs.85).aspxAnd also in the code sample you gave, you can see from the WDK that PCREATE_PROCESS_NOTIFY_ROUTINE_EX should get PEPROCESS of the created process as first parameter, and not the parent id, which you will get only PCREATE_PROCESS_NOTIFY_ROUTINE (without the EX function). Therefore your callback example should look like:void Callback (Inout PEPROCESS Process, In HANDLE ProcessId, Inout_opt PPS_CREATE_NOTIFY_INFO CreateInfo)and notvoid Callback (In HANDLE ParentId, In HANDLE ProcessId, Inout_opt PPS_CREATE_NOTIFY_INFO CreateInfo)