📜 ⬆️ ⬇️

Microsoft did not isolate Windows Defender in the sandbox, so I did

Microsoft put its users at considerable risk when it released Windows Defender outside the sandbox. It surprised me. The sandbox is one of the most effective security enhancement techniques. Why did Microsoft isolate other highly probable attack targets in the sandbox, such as JIT code in Microsoft Edge, but left Windows Defender unprotected?

As a PoC (proof-of-concept), I isolated Windows Defender, and now I post my code in open access as Flying Sandbox Monster . The foundation of the Flying Sandbox Monster is AppJailLauncher-rs , a framework for Rust for placing unreliable apps in AppContainers . It also allows you to take out an I / O application for a TCP server so that the application in the sandbox runs on a completely different machine. This is an additional level of isolation.

In the article I will describe the process and the results of creating this tool, and also express my thoughts about Rust on Windows.


Flying Sandbox Monster launched Windows Defender in the sandbox to scan the WannaCry binary
')

Plan


Windows Defender's unhindered access to system resources and the reception of any malicious file formats make it an ideal target for intruders. The key process of the MsMpEng program works as a service with SYSTEM privileges. Scanning component MpEngine supports the analysis of an astronomical number of file formats. It also includes system emulators for different architectures and interpreters for different languages. All this together, executed at the highest level of privilege in Windows. Op-pa.

All this made me think. How difficult will it be to isolate MpEngine using the same technique that I used in the CTF community sandboxing competition two years ago?

The first step to isolating Windows Defender is the ability to launch AppContainers. I would like to use AppJailLauncher again, but there was a problem here. The original AppJailLauncher was written as a demo. If I had known then, I would have written it in C ++ Core , so as not to suffer with memory management. Over the past two years, I tried to rewrite it in C ++, but failed (why are dependencies always such a headache?).

But then I was inspired . Why not rewrite the AppContainer launch code to Rust?

Creating a sandbox


A few months later, after a cursory study of Rust textbooks and writing my first code, I had three main pillars for running AppContainers on Rust: these are SimpleDacl , Profile and WinFFI .


Next, it was necessary to understand how to install the interface with the scanning component Windows Defender. An example of implementation on C and instructions for starting scanning MsMpEng were in the Tavis Ormandi loadlibrary repository. Porting structures and prototypes of functions on Rust is easy to automate, although I initially forgot about array fields and function pointers, which caused many different problems; however, thanks to the testing functionality built into Rust, I quickly resolved all my porting errors - and soon I had a minimal test case that scanned the EICAR test file .


Flying Sandbox Monster Base Architecture

Our example of the Flying Sandbox Monster consists of a sandbox wrapper and the Malware Protection Engine (MpEngine). The only executable file has two modes: parent and child processes. The mode is determined by the presence of environment variables that contain HANDLEs for the file being scanned and the communication between processes. The parent process sets these two HANDLEs before creating the child AppContainer process. Now the isolated child process loads the anti-virus engine library and scans the incoming file for viruses.

This is not enough for the full-fledged work of PoC, because the Malware Protection Engine did not want to be initialized inside AppContainer. At first, I thought it was an access control issue. But after carefully checking the differences in ProcMon (comparing the differences in performance in AppContainer and not in AppContainer), I realized that the problem may be in determining the version of Windows. The Tavis code is always reported as a version of Windows XP. My code reported the real version of the host system: in my case, this is Windows 10. Checking in WinDbg proved that the initialization problem is really in that. It was necessary to lie MpEngine about the host version of Windows. In C / C ++, I would use function interception with Detours. Unfortunately, for Rust under Windows there is no equivalent library for intercepting functions (several libraries for intercepting functions turned out to be much "heavier" than I need). Naturally, I implemented a simple library to intercept functions on Rust (only for 32-bit Windows PE).

AppJailLauncher-rs view


Since I have already implemented the key components of AppJailLauncher on Rust, why not finish the work and wrap it all into a Rust TCP server? I did it, and now I am glad to present you the “second version” of AppJailLauncher - AppJailLauncher-rs .

AppJailLauncher was a TCP server that listened to a specific port and started the AppContainer process for each accepted TCP connection. I did not want to reinvent the wheel, but mio , the compact I / O library for Rust, simply did not fit. First, its TcpClient did not provide access to the original HANDLEs sockets for Windows. Secondly, these sockets were not inherited by the AppContainer child process. Because of this, we have to submit another “support” to support appjaillauncher-rs: TcpServer .

TcpServer is responsible for an asynchronous TCP server and a client socket compatible with STDIN/STDOUT/STDERR redirection. Sockets created by socket can not redirect standard I / O streams. For proper standard I / O redirection, native sockets are needed (like those created via WSASocket ). To allow redirection, TcpServer creates these “native” sockets and does not explicitly prohibit inheritance for them.

My experience with Rust


Overall, my experience with Rust turned out to be very pleasant, despite some slight rough edges. Let me mention some of the features of this programming language that I especially noticed during the development of AppJailLauncher.

Cargo . Managing dependencies in C ++ for Windows is a really tedious and complicated matter, especially with links to third-party libraries. Rust cleverly solves this problem with cargo package manager. There is a large set of packages that solve many common problems like sorting out arguments (clap-rs), Windows FFI (winapi-rs, etc.) and processing wide strings (widestrings).

Built-in testing . Unit tests for C ++ applications require the use of a third-party library and a lot of manual work. That is why they are rarely written for small projects, such as the first version of AppJailLauncher. In Rust, unit testing is built into the cargo system, where it exists along with the main functionality.

Macro system . In Rust, the macro system works at the level of an abstract syntax tree, unlike the simple replacement engine in C / C ++. Although you need to learn a little bit, Rust macros are completely devoid of the annoying properties of C / C ++ macros, such as naming and area collisions.

Debugging Debugging Rust under Windows works as it should. Rust generates WinDbg-compatible debugging symbols (PDB files) that enable smooth debugging of sources.

Interface of external functions . The Windows API is written in C / C ++ and it is understood that this is the way to access it. Other languages, like Rust, must use the external function interface (FFI) to access the Windows API. Rust FFI for Windows (winapi-rs) is mostly ready. There are key APIs, but some not-so-used subsystems like the API for changing the access control list are missing.

Attributes . Setting attributes is quite cumbersome, as they apply only to the next line.

Borrow Checker . The concept of ownership is how Rust achieves memory security. Attempts to understand how Borrow Checker works are accompanied by mysterious, unique errors and require hours of reading documentation and textbooks. In the end, it's worth it: when I "clicked" and I learned the concept, my programming skills advanced dramatically.

Vectors In C ++, the std::vector container can set its supporting buffer to other code. The original vector remains valid, even if the supporting buffer has been changed. In the case of Vec in Rust, this is not the case. Here Vec requires the creation of a new object from the “blanks” of the old Vec .

Types Option and Result . Native types Option and Result should have simplified error checking, but actually made it seem more detailed. You can pretend that there are no errors, and just call unwrap , but this will lead to a runtime failure when Error (or None ) inevitably comes out.

Other types and slices . It is necessary to get used to the owned types and related slices (for example, String/str , PathBuf/Path ). They come in pairs with similar names, but behave differently. In Rust, the type owned is an expandable, mutable object (usually a string). A slice is a kind of immutable character buffer (also usually a string).

Future


The Rust Ecosystem for Windows is still growing. You can also create new libraries Rust, simplifying the development of software for security under Windows. I made the initial versions of several Rust libraries for sandboxing in Windows, parsing PE, and intercepting IAT. I hope they will be useful to the emerging Rust community under Windows.

Using Rust and AppJailLauncher, I isolated the Windows Defender, Microsoft's flagship antivirus product, in the sandbox. My achievement is both wonderful and a bit shameful. Remarkably, a reliable Windows sandbox mechanism is available for third-party software. The shame is that Microsoft itself did not isolate the Defender. Microsoft bought what would later become Windows Defender , in 2004. In those days, such bugs and architectural blunders were unacceptable, but explainable. Over the years, Microsoft has created an excellent organization to develop security systems for routine testing and fuzzing. She sandboxed critical parts of Internet Explorer. Somehow, Windows Defender got stuck in 2004. Instead of using the Project Zero methods and continually pointing out the symptoms of this innate flaw, let's move Windows Defender back to the future.

Source: https://habr.com/ru/post/334792/


All Articles