Arbitrary File Delete to SYSTEM (Emsisoft Antivirus)
Introduction
Back in August last year, I decided to take a closer look at lesser-known antivirus vendors after reading an excellent ZDI post discussing the rise of link-following vulnerabilities. That article made me wonder: if this bug class is so easy to find and exploit, surely major AV vendors are well-aware and protected, right?
Turns out, that’s not always the case. In this blog post, I’ll walk through a Local Privilege Escalation (LPE) vulnerability I discovered in Emsisoft Antivirus, which allowed me to escalate to NT AUTHORITY\SYSTEM
via arbitrary file deletion.
Finding Targets
My initial step was simple: map out antivirus vendors using search engine and shortlist those I hadn’t encountered much before.
Google results when searching for antivirus vendors
Before doing the setup of installation, trials, and VMs, I did a quick CVE search to see which vendors had the fewest public disclosures. Emsisoft had very few, so I decided to start my testing there.
Hunting for Link-Following Vulnerabilities
To identify potential link-following vulnerabilities, I followed this process:
- Target applications with elevated privileges — ideally, those performing file operations.
- Monitor file activity using Procmon to log file I/O and behavior.
- Check access control — ensure the file operations occur in user-writable locations.
- Look for sensitive actions, especially:
- File create, copy, move, rename, delete
- Modifying folder DACLs
- Analyze security checks — determine if the application blindly trusts symlinks or junctions.
What Are Symbolic Links and Junctions in Windows?
Windows supports multiple types of file system redirection mechanisms. These are useful for things like backward compatibility or file organization but they can also be dangerous when misused, especially in privilege escalation scenarios.
Symbolic Links (Symlinks)
Symbolic links are file system objects that point to another file or directory. They’re similar to shortcuts, but behave like actual files or directories at the system level.
- Can point to files or directories
- Created using
mklink
:mklink link target
(file)mklink /D link target
(directory)
- Can point across volumes (e.g.,
C:\
toD:\
) - Require SeCreateSymbolicLinkPrivilege, normally only available to administrators
When a privileged process follows a symlink, it can be tricked into reading from or writing to an attacker-controlled location.
Junctions
Junctions are similar to directory symlinks, but:
- Only work for directories
- Can only point to paths on the same volume
- Created using:
mklink /J link target
- or programmatically via
CreateMountPoint
or reparse tags
- Do not require special privileges — any user can create them in writable directories
This makes junctions particularly attractive for exploitation, as even standard users can create them to redirect operations into sensitive paths.
Local Privilege Escalation in Emsisoft Antivirus
Like most AV software, Emsisoft allows users to quarantine or permanently delete detected files via the GUI. Interestingly, after a while I found that when a file is deleted from the UI, it’s not moved to a temporary directory, nor is it locked — the software simply references its full path and deletes it.
Naturally, I fired up Procmon to validate this behavior.
Procmon log showing
a2service.exe
deleting files as SYSTEM
As shown above, a2service.exe
— running as NT AUTHORITY\SYSTEM
— is responsible for the deletion.
I tested what would happen if I deleted the flagged file and replaced it with a junction pointing to another location (e.g., C:\
), using the same filename. The result? Arbitrary file deletion as SYSTEM — a perfect candidate for privilege escalation.
Exploitation Flow
Here’s the exploit chain:
- Create a decoy folder on the user’s Desktop — a subfolder named
Testing
is created. - Place a fake “malicious” file inside the folder using a critical system-like name (e.g.,
Config.Msi
) to trick Emsisoft into detecting it as a threat. - Trigger detection by manually scanning the file using Emsisoft Anti-Malware. Once detected, do not delete it yet.
- Replace the folder with a junction that points
Testing
to the root directoryC:\
. - In the Emsisoft UI, select the detected file (which now maps to
C:\Config.Msi
) and choose “Delete or Quarantine”. Behind the scenes, the following race condition is triggered:
- An oplock (opportunistic lock) is set on
C:\Config.Msi
to detect when Emsisoft attempts to delete the folder. Once the deletion triggers the oplock, a race begins to:
- Move the target path.
- Install a custom MSI package.
- The goal is to overwrite the rollback script
.rbs
file during the install process.
- An oplock (opportunistic lock) is set on
- If the race is won, the overwritten
.rbs
file will be executed as SYSTEM, allowing for arbitrary code execution at the highest privilege level.
This technique is based on research by Abdelhamid Naceri.
Exploit Demo
Here’s the exploit in action:
Preventing Arbitrary File Deletion via Junctions
- Use HANDLEs Instead of Path Strings
- Open the parent directory or file using CreateFile() with flags like
FILE_FLAG_BACKUP_SEMANTICS
andFILE_FLAG_OPEN_REPARSE_POINT
. - Work with file handles directly, rather than re-parsing paths, to avoid TOCTOU-style races and junction attacks.
- Open the parent directory or file using CreateFile() with flags like
- Avoid Following Reparse Points (Junctions, Symlinks)
- Use the
FILE_FLAG_OPEN_REPARSE_POINT
flag when opening paths to inspect whether they are reparse points, rather than following them blindly. - After opening, call DeviceIoControl with
FSCTL_GET_REPARSE_POINT
to inspect whether the target is a junction/symlink.
- Use the
- Verify File Location Before Deleting
- After resolving the full path, call GetFinalPathNameByHandle() to obtain the real, canonical path of the file or directory.
- Ensure the final path lies within expected boundaries
- Use Proper Security Descriptors
- Lock down file/directory access so low-priv users cannot replace directories or inject junctions during runtime.
- Use NtDeleteFile Carefully
- If you’re using low-level APIs, be cautious with NtDeleteFile, as it can follow reparse points unless properly mitigated with attributes like
OBJ_DONT_REPARSE
.
- If you’re using low-level APIs, be cautious with NtDeleteFile, as it can follow reparse points unless properly mitigated with attributes like
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
void safe_delete_file(const std::wstring& path) {
if (is_any_parent_reparse_point(path)) {
abort_delete("Parent is a junction or symlink");
}
HANDLE hFile = CreateFileW(path, DELETE | READ_CONTROL,
0,
NULL, OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, NULL);
if (hFile == INVALID_HANDLE_VALUE) {
abort_delete("Failed to open file");
}
if (is_reparse_point(hFile)) {
CloseHandle(hFile);
abort_delete("Target is a reparse point");
}
std::wstring ntPath = get_nt_path_from_handle(hFile); // e.g., \Device\HarddiskVolume4\Windows\System32
std::wstring osRootNtPath = get_os_volume_nt_path(); // Resolved from %SystemRoot%
if (ntPath.starts_with(osRootNtPath + L"\\Windows") ||
ntPath.starts_with(osRootNtPath + L"\\System Volume Information")) {
CloseHandle(hFile);
abort_delete("Target is in sensitive system area");
}
FILE_DISPOSITION_INFO info = { TRUE };
SetFileInformationByHandle(hFile, FileDispositionInformation, &info, sizeof(info));
CloseHandle(hFile);
}
Safer alternative
The most critical aspect is minimizing the window between our checks and the actual delete operation. Working with a file HANDLE as much as possible is generally more safer as it prevents path-based TOCTOU attacks after the handle has been opened.
Conclusion
This vulnerability highlights how even lesser-known AV products can harbor serious privilege escalation bugs — especially when link handling is done naively. If you’re working in vulnerability research, AV software remains a rich hunting ground, and it’s worth checking out what happens when SYSTEM blindly trusts user-controlled paths.
Disclosure was made responsibly to Emsisoft in August 2024, and the issue has been addressed in a recent update.