Skip to main content

Wipe and Rise: How Deleting Folder on Windows Enables LPE

Illustration of Wipe and Rise: How Deleting Folder on Windows Enables LPE
Mateusz Lewczak

Introduction

Time-of-check-to-time-of-use (TOCTOU) race conditions have plagued Windows software for decades, yet they still surface in modern code. During a recent audit of TestedAPP we uncovered a textbook example: the application’s background service first checks whether a cache directory exists and, milliseconds later, deletes it without re-validating the path. Because every non-privileged user can create files and folders inside the application tree, an attacker can win the race, swap the legitimate directory for an NTFS mount point, and redirect the deletion to any location on the system drive. The easiest and most damaging target is the hidden C:\Config.Msi staging folder that Windows Installer treats as fully trusted during install-rollback operations [1, 2].

Exploiting this primitive is straightforward. By combining SetOpLock (to freeze the service just after it issues the delete request) with CreateMountPoint and CreateSymlink from Google Project Zero’s symbolic-link-toolkit, a low-privilege user turns the innocent folder purge into an arbitrary-folder-delete primitive.

Once C:\Config.Msi is wiped, the attacker recreates it as a junction pointing elsewhere and drops a rogue rollback script plus a DLL payload. When Windows Installer resumes and runs the rollback, that DLL is invoked as NT AUTHORITY\SYSTEM, launching a SYSTEM-level command prompt and handing the attacker full control of the machine.

Beyond the immediate privilege escalation impact, the finding underscores two issues: Windows still grants standard users broad write access to many application directories, and developers continue to rely on non atomic file system operations that can be subverted with object manager tricks. The research therefore serves as both an exploitation case study and a practical checklist for hardening installer style code paths against modern race condition attacks.

PROOF OF CONCEPT

Module Behavior and Attack Vector

Tested application module includes a command line utility, downloader.exe, that any local (non-privileged) user can invoke to prefetch or “cache” packages simply by supplying a URL. Because the tool does not validate or restrict the URL parameter, an attacker can run, for example:

Downloader command

Upon execution of the cache command, the package download process is initiated by sending a request for the .MANI manifest, which serves as the package descriptor. Two HTTP GET requests are issued:

  1. The first request is sent to the manifest generation endpoint to produce the .MANI descriptor for the specified package:

Manifest generation request

  1. The second request is then sent to retrieve that .MANI file’s contents, downloading the manifest into the local cache folder for parsing and subsequent file downloads:

Manifest retrieval request

A sample .MANI manifest is provided below, listing file names, sizes and checksums:

Sample MANI manifest

The manifest is saved in the central cache directory:

Cache directory

A subdirectory named after the CONTENTID (for example, ABC123) is then created beneath that path:

Content ID directory

Within this staging folder, each file declared in the manifest is downloaded and its integrity is verified against the provided checksum before final placement. Subsequently, the files listed in the .MANI manifest are fetched via an HTTP GET request. For the Windows entry shown above, the request takes the following form:

File download request

When the server fails to return the requested file or the downloaded file’s checksum does not match the declared value, all files associated with that package are purged. Critical to this vulnerability, is that the TestedApp.exe service attempts to access a non-existent CachePKG[CONTENTID]_0_1 directory. As can be seen in the following screenshot from the Procmon64.exe tool (from the SysInternals Suite):

Procmon screenshot 1

Otherwise, if that directory is present, it is deleted by the service:

Procmon screenshot 2

Furthermore, at the very start of the file deletion sequence, a nonexistent file is referenced by the service behavior that can be leveraged during exploitation:

Procmon screenshot 3

A less significant, but noteworthy, behavior is that the application repeatedly loops through the entire HTTP request sequence, generating the .MANI manifest, fetching the .MANI file, attempting to download each file declared within it and purging package data.

Exploitation – prerequisites

Windows restricts NTFS junction creation to privileged accounts, making traditional symbolic-link attacks infeasible for standard users. However, Google Project Zero research demonstrated that a mount point can be created against the \RPC Control object directory an object manager namespace to which unprivileged processes often have write access effectively emulating a symbolic link without requiring SeCreateSymbolicLinkPrivilege.

To implement this technique, the symboliclink-testing-tools suite was utilized, that can be found under this URL address on GitHub [4]. From this repository, the following utilities were employed:

  • SetOpLock.exe was used to establish an opportunistic lock on a file (or directory), preventing its deletion until the lock is released.

  • CreateMountPoint.exe was used to bind an otherwise empty filesystem directory to the \RPC Control namespace (or any other writable object directory), thereby redirecting subsequent filesystem operations through the object manager path.

  • CreateSymlink.exe was used to generate a file level symbolic link within the object manager namespace, allowing redirection of open, read, write, or delete operations to arbitrary targets without administrative privileges.

By combining these tools pausing the deletion routine with an oplock, mounting an empty folder onto \RPC Control, and then creating an object-manager symlink, an unprivileged user gains an arbitrary-file-delete primitive capable of targeting protected system paths.

Additionally, an .MANI manifest must be provided during the process, upon which the TestedApp service will attempt to cache the package and thereby trigger the application behavior that permits the folder’s deletion. The following Python script, built on the Flask framework, was employed to serve the contents of the requested .MANI manifest:

Flask script

The directory containing the Flask application must also include an mani_file with the following contents:

MANI file contents

Execution of the script can occur locally or on a remote, attacker controlled machine.

Exploitation steps – folder deletion

To successfully exploit the vulnerability, the following steps must be carried out:

  1. The .MANI serving endpoint must first be launched by executing:

Launch Flask server

This command starts the Flask-based server, which will listen for incoming requests and return the .MANI manifest when queried.

  1. The file EXPLOIT_1.SPARSEMAP is created, then an opportunistic lock is set to pause the file-deletion routine.

Set oplock

  1. The package-download process is initiated using Downloader.exe.

Initiate download

  1. A mount point is established at the path of the non-existent CachePKG folder.

Create mount point

  1. A symbolic link is created from CachePKG\EXPLOIT_0_1 to the directory targeted for deletion.

Create symbolic link

  1. The opportunistic lock is released by pressing ENTER.

Upon completion of these steps, the TestedApp.exe service deletes the specified folder, as illustrated in the following Procmon64.exe screenshot:

Folder deletion confirmation

Exploitation steps – privilege escalation

Windows Installer (MSI) performs all file staging and rollback-script generation inside a hidden C:\Config.Msi folder on the system drive, treating everything there as fully trusted installer data before any changes are committed. During a normal installation, new or replaced files are first copied into Config.Msi, and corresponding rollback scripts (.rbs/.rbf) are built there. Only once all staging steps complete successfully are the files moved into their final locations; if staging fails or an explicit rollback/uninstall is invoked, the rollback scripts stored in Config.Msi are executed under the SYSTEM account to undo or finalize all changes.

By exploiting an arbitrary-folder-delete primitive immediately after Config.Msi is created, an attacker can remove the folder and replace it with an NTFS junction. When Windows Installer subsequently issues its cleanup or rollback delete operation against C:\Config.Msi, the junction causes deletion of the protected target directory instead of the original staging area. After deletion of system files, a malicious Config.Msi directory (or junction) can be recreated containing attacker controlled .rbs/.rbf rollback scripts. Upon the next rollback invocation, these scripts run with SYSTEM privileges, yielding arbitrary code execution and full local privilege escalation. A full proof of concept exploit has been published by the Zero Day Initiative on GitHub [3].

It implements a two-phase attack that leverages Windows Installer’s rollback mechanism together with an arbitrary folder delete primitive. During the second phase, an MSI install is launched and then aborted mid-rollback. After C:\Config.Msi has been deleted and recreated with a permissive DACL (Discretionary Access Control List), the installer’s generated rollback script (.rbs) and rollback file (.rbf) are replaced: the .rbf payload is a DLL crafted to launch a command line, and the malicious .rbs directs Windows Installer to copy that DLL file into:

DLL placement path

When the rollback resumes, these files are executed under the SYSTEM account, causing the DLL to be dropped into the ink directory. Thereafter, each invocation of the On-Screen Keyboard (osk.exe) on the secure desktop (for example, via Ctrl + Alt + Delete) results in the loading of the hijacked HID.dll, which spawns a SYSTEM privileged command prompt via DLL hijacking.

To successfully exploit the vulnerability, the following steps must be carried out:

  1. The .MANI–serving endpoint must first be launched by executing:

Launch Flask server for privilege escalation

This command starts the Flask-based server, which will listen for incoming requests and return the .MANI manifest when queried.

  1. The FolderOrFileDeleteToSystem.exe binary previously compiled from the GitHub repository must be executed as shown:

Execute privilege escalation tool

  1. The file EXPLOIT_1.SPARSEMAP is created, then an opportunistic lock is set to pause the file-deletion routine.

Set oplock for privilege escalation

  1. The package download process is initiated using downloader.exe.

Initiate download for privilege escalation

  1. A mount point is established at the path of the non-existent CachePKG folder.

Create mount point for privilege escalation

  1. A symbolic link is created from CachePKG\EXPLOIT_0_1 to the directory targeted for deletion.

Create symbolic link for privilege escalation

  1. The opportunistic lock is released by pressing ENTER.

After completion of the above steps, the malicious HID.dll is dropped into:

Malicious DLL placement

At that point, commands may be executed as NT AUTHORITY\SYSTEM by triggering the On-Screen Keyboard on the secure desktop:

  1. Switch to the secure desktop (for example, via Ctrl + Alt + Delete).

  2. Run On-Screen Keyboard:

On-Screen Keyboard

  1. The hijacked HID.dll is loaded by osk.exe, spawning a SYSTEM-privileged command shell.

SYSTEM command shell

Summary

A classic time-of-check-to-time-of-use race condition in TestedAPP’s file-cleanup routine lets an unprivileged user replace the soon-to-be-deleted cache folder with an NTFS mount point, turning the benign deletion into an arbitrary-folder-remove that targets the trusted C:\Config.Msi directory. Once that folder is wiped, the attacker recreates it as a junction containing a rogue rollback script and a tainted DLL; when Windows Installer later runs this content under NT AUTHORITY\SYSTEM, the payload launches with full machine privileges. The episode shows that non-atomic file operations in user-writable paths still pose a serious risk and underscores the importance of keeping sensitive installer data in directories ordinary users cannot modify, re-validating paths right before use, and eliminating unsupervised deletion logic that mount points or junctions can hijack.

References

[1] https://www.thezdi.com/blog/2022/3/16/abusing-arbitrary-file-deletes-to-escalate-privilege-and-other-great-tricks-archive

[2] https://cloud.google.com/blog/topics/threat-intelligence/arbitrary-file-deletion-vulnerabilities/

[3] https://github.com/thezdi/PoC/tree/main/FilesystemEoPs/FolderOrFileDeleteToSystem

[4] https://github.com/googleprojectzero/symboliclink-testing-tools

Other Insights

Illustration of IAM – Privilege Escalation in Azure Cloud

IAM – Privilege Escalation in Azure Cloud

Adam Borczyk

In my recent analysis of Identity and Access Management (IAM) controls within Azure Cloud environments, I identified a significant risk related to improper role assignments. Specifically, the use of broad built-in roles, such as Owner and Contributor, without granular permission management can lead to unrestricted access. Although the tested environment contained a limited number of accounts, this misconfiguration represents a substantial security risk that can compromise cloud infrastructure and sensitive data.

READ article
Illustration of Inter-process Communication Vulnerability – Unrestricted Write Permissions in VPN Service

Inter-process Communication Vulnerability – Unrestricted Write Permissions in VPN Service

Mateusz Lewczak

Inter-Process Communication (IPC) is simply the set of mechanisms that let two or more processes on the same machine exchange data or signals. Across Windows, Linux and macOS you'll find pipes or FIFOs, shared memory regions, message queues and—crucially for many modern services—sockets (named pipes on Windows, UNIX Domain Sockets on Unix-like systems, and Mach ports/XPC on macOS). These primitives differ in performance and complexity, but they all serve the same goal: enable a less-privileged component (for example, a user-facing GUI) to invoke functionality in a more-privileged daemon (like a VPN manager).

READ article
A professional cybersecurity consultant ready to assist with your inquiry.

Any questions?

Happy to get a call or email
and help!