Protecting Files and Folders with CBFS Filter
Folder protection may serve several important goals, each aimed at safeguarding the integrity and confidentiality of the data stored within. These goals include:
- Securing files from unauthorized access: One of the primary objectives is to prevent unauthorized users from accessing sensitive information. This involves implementing strict access controls and permissions that restrict file visibility and usability to only those individuals who have been explicitly granted access rights.
- Protecting files from being damaged by malware and viruses: Another critical goal of folder protection is to shield files from potential threats posed by malware and viruses. This includes preventing malicious software from modifying, corrupting, or deleting significant files. By leveraging robust security measures, such as real-time monitoring and proactive threat detection, organizations can minimize the risk of data loss and maintain the integrity of their files.
- Establishing an audit of access to sensitive data stored in the folder: Establishing a comprehensive audit trail for access to sensitive data is essential for compliance and accountability. By tracking who accessed which files, when, and what actions were taken, organizations can create a detailed record that aids in investigations and ensures compliance with data protection regulations. This auditing capability helps identify potential security breaches and provides insights into user behavior.
These critical goals are achieved using the monitoring and control capabilities of CBFilter, which provides mechanisms to effectively manage access, detect threats, and maintain a comprehensive audit trail for sensitive data. CBFilter is a component of CBFS Filter, an SDK and library designed for monitoring and controlling various system activities, including filesystem operations, registry operations, and the lifecycle of processes and threads.
Setting Up Rules
In all cases, an application needs to add at least two rules: one for the directory being protected and another for file access. These rules are added using the AddFilterRule method.
The first rule aims to register and potentially deny applications' attempts to read the contents of the directory. For this purpose, an application can use the BeforeEnumerateDirectory event if access prevention is required:
filter.AddFilterRule("C:\\path\\to\\directory", Constants.ACCESS_NONE, Constants.FS_CE_BEFORE_ENUMERATE_DIRECTORY, Constants.FS_NE_NONE);
Alternatively, if only a notification is required (e.g., for audit purposes), the NotifyEnumerateDirectory
event can be used:
filter.AddFilterRule("C:\\path\\to\\directory", Constants.ACCESS_NONE, Constants.FS_CE_NONE, Constants.FS_NE_ENUMERATE_DIRECTORY);
If tracking each listed item isn’t necessary and you don't need specific parameters, you can utilize the fact that a directory must be opened before it can be enumerated. In this case, add a rule to track when the directory is opened and deny access if needed:
filter.AddFilterRule("C:\\path\\to\\directory", Constants.ACCESS_NONE, Constants.FS_CE_BEFORE_OPEN, Constants.FS_NE_NONE);
The second rule should cover files within the directory. Here, we take advantage of the system design in which any file operation can only occur when the file is opened. To control access to a file, simply add a corresponding rule to track file creation and opening. If only monitoring is required, you can use the notification events NotifyCreateFile and NotifyOpenFile:
filter.AddFilterRule("C:\\path\\to\\directory\\*.*", Constants.ACCESS_NONE, Constants.FS_CE_NONE, Constants.FS_NE_CREATE | Constants.FS_NE_OPEN);
If there is a need to deny access to a file—either unconditionally or based on certain criteria—you can use either the BeforeCreateFile/BeforeOpenFile or the AfterCreateFile/AfterOpenFile pair of events.
In Before* events, it is not yet known if a file or directory exists, whether it is a file or directory, or if an attempt to open it would succeed. Completing these events or denying the request in the corresponding event handlers prevents the request from reaching the filesystem.
A rule for Before*File events can be added with this call:
filter.AddFilterRule("C:\\path\\to\\directory\\*.*", Constants.ACCESS_NONE, Constants.FS_CE_BEFORE_CREATE | Constants.FS_CE_BEFORE_OPEN, Constants.FS_NE_NONE);
The corresponding rule for After* events can be added similarly:
filter.AddFilterRule("C:\\path\\to\\directory\\*.*", Constants.ACCESS_NONE, Constants.FS_CE_AFTER_CREATE | Constants.FS_CE_AFTER_OPEN, Constants.FS_NE_NONE);
If you need to exclude certain entries from filtering, you can define pass-through rules that negate the effect of filter rules. This is accomplished by utilizing the AddPassthroughRule and related methods.
Rules can be managed dynamically while filtering is active (even from event handlers if proper synchronization techniques are used). This allows an application to add and delete rules when removable media is plugged into 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 Notify* event handlers, an application can collect additional information about the request and log the event. 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 initiated the request
- the PID (process Id) of the process that initiated the request
- the Id of the thread that initiated the request
- 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.
Before* and After* events offer much greater flexibility. When handling Before* events, the application can complete the request with an error code, effectively denying it, or process the event without passing it to the filesystem. In the latter case, the handler is expected to fulfill the request by returning the requested information or performing the corresponding action.
In After* events, the application can change the status code returned by the filesystem, converting the request from successful to failed or vice versa. Also, for requests that expect the data to be returned by the filesystem, the handler can modify the data before they are returned.
For Before* and After* events, the scope of information available to a handler includes everything provided in Notify* events, plus the following additional details:
- 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
Things to Pay Attention To
If an application protects access to a directory, it should consider the possibility that an attacker may rename this directory or one of its parent directories, thereby moving it 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\directory being protected, and an attacker renames C:\path to C:\another_path, the rules set for C:\path\to\directory will not be triggered after renaming. The application must either set multiple rules to receive BeforeRenameOrMoveFile notifications for C:\path, C:\path\to, and C:\path\to\directory 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.
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, the "main" file and the hard link are treated equally, making it difficult to determine which one was the original.
By using a hard link, an attacker could gain access to your protected file, thereby bypassing the path-based rules you have implemented. To prevent this situation, your application should establish 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);
The BeforeCreateHardLink event should be handled by setting the Status parameter to the desired value, such as STATUS_ACCESS_DENIED (the numeric code is 0xC000 0022) and then setting the ProcessRequest parameter to false.
Cooperating with System Security
Most likely, the sensitive files you need to protect have certain security attributes set on them, utilizing the ACL (Access Control List) functionality of the OS. While these attributes effectively prevent unauthorized access, they can be challenging to manage and sometimes lack the necessary flexibility. In this context, CBFilter serves as an additional layer of defense that not only protects the file data but also supervises the security attributes. For instance, you may want to prevent alterations to the security attributes. This can be achieved by adding a rule and handling the BeforeSetFileSecurity event:
filter.AddFilterRule("C:\\path\\to\\directory\\*.*", Constants.ACCESS_NONE, Constants.FS_CE_BEFORE_SET_SECURITY, Constants.FS_NE_NONE);
The event handler can then inspect the originator of the request and act accordingly. For example, it is more likely that security changes are made by File Explorer rather than by an unknown process lacking a digital signature.
Securing File Access
When controlling file access simply requires denying access to a file or directory, you can complete the corresponding event (for which you set the rule) with an error code. To do this, set the Status parameter of the event to STATUS_ACCESS_DENIED and set the ProcessRequest parameter to false.
If you need to make files read-only, the handlers for the BeforeCreateFile and BeforeOpenFile events should inspect the DesiredAccess parameter of the event and block the request if the parameter indicates an intention to write to the file.
Of course, additional request parameters may also need to be inspected before making a decision. For example, to ensure that only certain users have access to a file, an event handler can call the GetOriginatorToken method of CBFilter and use the obtained security token to request further information about the user who opened the file or initiated the directory enumeration request. It’s important to remember to properly close the security token after use by calling the CloseHandle function from the Windows API.
The GetOriginatorProcessName and GetOriginatorProcessId methods can provide information about the process that made the request. The application may want to verify the digital signatures of the process modules, for instance, to ensure that the process is part of the operating system (and was signed by Microsoft). Additionally, the application can inspect the path where the executable is located; often, malware is downloaded to a subdirectory of a user’s home directory and executed from there.
Protecting File From Damage
The steps outlined in the previous sections can also be applied to protect files from being damaged by malware. Additionally, when it is not possible to distinguish between legitimate and unauthorized modifications to a file, creating backup copies of data may be a viable option. By saving a copy of a previous version of a file before it is changed, renamed, or deleted, your application can preserve the data in case such an action is unauthorized.
To make a copy of the data, the application must first detect a change and then access the existing file data for copying. This involves a two-step process.
The first step is tracking the file change. This requires handling several events, including: BeforeCreateFile, BeforeOpenFile, AfterCreateFile, AfterOpenFile, BeforeRenameOrMoveFile, BeforeDeleteFile, BeforeWriteFile, BeforeSetFileSize, BeforeSetAllocationSize, and BeforeSetFileSecurity.
The second step is copying the file data. For this, you need to obtain a separate handle to the file using the CreateFileDirect method (this is done in the AfterCreateFile and AfterOpenFile event handlers), and then copy the file data when the first change occurs. This procedure is covered in detail in a dedicated article.
Access And Default Rules
CBFilter provides mechanisms to protect files from unauthorized access without requiring the application to handle events (or even be active in the case of default rules). These mechanisms consist of access rules and default rules. Access rules are added using the previously mentioned AddFilterRule method, while default rules are added through the dedicated AddDefaultRule method. These two types of rules are discussed in detail in the article on Access Rules and Default Rules.
Other ways of handling access
It is possible to protect the contents of a directory by redirecting certain operations to another directory. This approach allows the original directory contents to remain intact while all changes occur in a different location. However, using redirection is a relatively complex strategy that requires tracking which files were redirected so that subsequent requests to those files can also be redirected.
Redirection can be achieved in several ways: by using redirection rules through the AddReparseRule method, by adding a filter rule and handling the ReparseFileName event for added flexibility, or even by employing the file isolation technique.
A detailed review of file redirection is provided in a separate article.
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.