Hiding Files and Folders with CBFS Filter
In the article about file and folder protection, we discussed the importance of folder and file protection, but there's always room for improvement. While the concept of "security through obscurity" often receives criticism, adding an extra layer of defense by hiding files and folders from plain sight can be beneficial. In this article, we will explore how to implement this hiding mechanism using CBFilter.
There are various scenarios where an application may need to either hide an entire directory along with its contents or keep the directory visible while hiding only the contents. This article will address both cases.
Setting Up Rules
An application must add at least two rules: one for directory enumeration and another for file access. These rules are added using the AddFilterRule method.
The first rule is used to register and potentially deny applications' attempts to read the contents of the directory. For this purpose, the AfterEnumerateDirectory event is used. If the application needs to hide the directory, the rule should be added for the directory’s parent:
filter.AddFilterRule("C:\\path\\to\\parent", Constants.ACCESS_NONE, Constants.FS_CE_AFTER_ENUMERATE_DIRECTORY, Constants.FS_NE_NONE);
To hide the contents, you need to add a rule specifically for the directory itself:
filter.AddFilterRule("C:\\path\\to\\parent\\hidden", Constants.ACCESS_NONE, Constants.FS_CE_AFTER_ENUMERATE_DIRECTORY, Constants.FS_NE_NONE);
Tracking the enumeration of the parent directory is essential to exclude the hidden directory from listings.
The second rule should cover files and subdirectories within the hidden directory, preventing direct access to any files or subdirectories that need to be hidden. This leverages the system design, which requires that any file operation occurs only when the file is opened. To prevent access, simply add a corresponding rule that tracks file creation and opening using the BeforeCreateFile and BeforeOpenFile events.
If you are hiding the “hidden” directory, add a rule for this specific directory:
filter.AddFilterRule("C:\\path\\to\\parent\\hidden", Constants.ACCESS_NONE, Constants.FS_CE_BEFORE_CREATE | Constants.FS_CE_BEFORE_OPEN, Constants.FS_NE_NONE);
Then, add a separate rule to control access to the directory’s contents:
filter.AddFilterRule("C:\\path\\to\\parent\\hidden\\*.*", Constants.ACCESS_NONE, Constants.FS_CE_BEFORE_CREATE | Constants.FS_CE_BEFORE_OPEN, Constants.FS_NE_NONE);
Denying the request in the corresponding event handlers prevents it from reaching the filesystem.
If you need to exclude certain entries from filtering, you can define pass-through rules that negate the effects of filter rules. This is done using the AddPassthroughRule and related methods. Keep in mind that if the directory is hidden, its contents, even if explicitly excluded from hiding, will not be easily accessible.
Rules can be managed dynamically while filtering is active (even from event handlers if proper synchronization techniques are applied). This flexibility allows an application to add or delete rules when removable media is plugged in or removed from the system.
Handling of Events
When handling an event, the application can take action unconditionally or evaluate various pieces of information related to the filesystem request and respond accordingly.
Within the event handlers, an application can collect additional information about the request. The information available to an event handler includes the parameters received in the event (such as the path to the file or directory and operation-specific parameters), as well as the following supplementary details that can be obtained by calling the corresponding methods of CBFilter:
- the name of the process that opened the file handle related to the request
- the PID (Process ID) of the process that opened the file handle related to the request
- the ID of the thread that opened the file handle associated with the request
- the security token of the process that opened the file handle; this token can be used to obtain the SID of the user account, along with the user name and other related information
- the name of the process that initiated the request (which may differ from the process which opened the handle)
- the PID (process Id) of the process that initiated the request (which may differ from the process that opened the handle)
- the Id of the thread that initiated the request (which may differ from the thread which opened the handle)
- the security token of the process that initiated the request; this token can be used to obtain the SID of the user account, along with the user name and other related information
- the time when the request reached the filter
- the information about remote access if a local file was accessed over the network.
Handling directory enumeration
When handling a directory enumeration request using the AfterEnumerateDirectory event, our objective is to hide entries from the listing.
During the enumeration of "C:\path\to\parent", the application needs to check the FileName parameter. If its value is "hidden" (the name of the directory that we are hiding), the event handler should set the ProcessRequest parameter of the event to false. This action instructs the driver to exclude the entry from the listing.
During the enumeration of "C:\path\to\parent\hidden", ProcessRequest should be set to false unconditionally, as all contents of the hidden directory need to remain concealed. However, if selective hiding is required, additional checks can be implemented to hide only specific files.
To determine which directory triggered the event, the application can inspect the DirectoryName parameter of the event.
Handling directory enumeration
An application may know (or infer) the full path to a file or subdirectory within a hidden directory. Therefore, it is essential to deny any file open and file create requests that may arise. To achieve this, the application should handle the BeforeCreateFile and BeforeOpenFile events and complete them with an error instead of passing them to the filesystem.
This is done by setting the Status parameter to STATUS_NO_SUCH_FILE (numeric code 0xC000000F) and then setting the ProcessRequest parameter to false. This tells the driver not to forward the request, but to complete it with the specified status.
Strengthening the defense
If an attacker knows the full path to a file, they may attempt to create a hard link to it. A hard link is a file entry in a directory (on the same volume) that points to the same file data as the original file. When a hard link is created, both the original file and the hard link are treated equally, making it difficult to determine which one is the primary file.
By using a hard link, an attacker could gain access to your hidden file. To prevent this, your application should set a rule for the BeforeCreateHardLink event:
filter.AddFilterRule("C:\\path\\to\\parent\\hidden\\*.*", Constants.ACCESS_NONE, Constants.FS_CE_BEFORE_CREATE_HARD_LINK, Constants.FS_NE_NONE);
This event should be handled similarly to how file opening requests are processed — by setting the Status parameter to STATUS_NO_SUCH_FILE (numeric code 0xC000000F) and then setting the ProcessRequest parameter to false.
Things to Pay Attention To
When an application protects access to a directory by hiding it, the developer should consider the possibility that an attacker may rename one of the parent directories, thereby moving the hidden directory outside the scope of monitoring. The decision of how to respond in this situation — whether to update the rules or prevent the renaming — lies with the application developers. Regardless, it is essential to have a rule in place that notifies the application when the directory is about to be renamed.
Special attention must be paid to this aspect: if you have a directory at C:\path\to\parent\hidden being protected, and an attacker renames C:\path to C:\another_path, the rules set for C:\path\to\parent and C:\path\to\parent\hidden will not be triggered. The application must either set multiple rules to receive BeforeRenameOrMoveFile notifications for C:\path, C:\path\to, and C:\path\to\parent individually, or establish two broader rules — one for C:\path and another for C:\path\*.* — to cover the same event.
The BeforeRenameOrMoveFile event handler should then analyze which file or directory is being renamed and take the necessary action accordingly.
Getting Started with CBFilter
You can find an evaluation version of the SDK for your platform and programming language in the Download Center. After downloading and installing the SDK, you will receive a library, sample code, and comprehensive documentation on your system. The documentation includes a "Getting Started" section with programming instructions. Additionally, free technical support is available during the evaluation phase.
We appreciate your feedback. If you have any questions, comments, or suggestions about this article please contact our support team at support@callback.com.