Mar 19, 2026 • Mathieu Farrell
Intego X9: Never trust my updates
This blog post dives into the most common classes of macOS Local Privilege Escalation vulnerabilities, from insecure XPC communications and time-of-check to...
Summary
This blog post dives into the most common classes of macOS Local Privilege Escalation vulnerabilities, from insecure XPC communications and time-of-check to time-of-use (TOCTOU) Race Conditions to a range of implementation and configuration oversights. We will explore how attackers can exploit these weaknesses to escalate privileges, and highlight real-world examples to illustrate recurring patterns. This post ends the series on Intego products on macOS by revealing vulnerabilities that can lead to Local Privilege Escalation, as well as a surprise bonus.
Published Analysis
This blog post dives into the most common classes of macOS Local Privilege Escalation vulnerabilities, from insecure XPC communications and time-of-check to time-of-use (TOCTOU) Race Conditions to a range of implementation and configuration oversights. We will explore how attackers can exploit these weaknesses to escalate privileges, and highlight real-world examples to illustrate recurring patterns. This post ends the series on Intego products on macOS by revealing vulnerabilities that can lead to Local Privilege Escalation, as well as a surprise bonus. Introduction In this final chapter of our series on vulnerabilities in Intego's macOS products, we pick up where part 2 left off. We previously showed how a TOCTOU PID reuse Race Condition could be used to bypass XPC authentication checks in all Intego's privileged processes. Here, we revisit that scenario to highlight the broader architectural issues it exposes and the importance of stronger validation within macOS XPC mechanisms. We will show how the XPC authentication bypass can be chained with an additional flaw, and how the combination leads to a Local Privilege Escalation as root . Our goal is to underline why even small issues in privileged services can become critical when paired with authentication lapses, and to emphasize the defensive lessons for designing secure macOS security software. Targetting com.intego.netupdated We will focus out analysis on the daemon com.intego.netupdated (running as root ), whose associated configuration is defined in the file /Library/LaunchDaemons/com.intego.netupdate.daemon.plist . Label com.intego.netupdate.daemon RunAtLoad KeepAlive ProgramArguments /Library/Intego/netupdated.bundle/Contents/MacOS/com.intego.netupdated MachServices com.intego.netupdate.daemon.agent AssociatedBundleIdentifiers com.intego.NetUpdate As it can be seen above, it exposes the following MachServices service: com.intego.netupdate.daemon.agent So let's reverse engineer the binary /Library/Intego/netupdated.bundle/Contents/MacOS/com.intego.netupdated with Binary Ninja. Figure 1 - XPC listener vulnerable to PID reuse attack. In our previous blog post we explained how relying on the process ID ( PID ) for XPC request authorization could be abused by an unprivileged user to communicate with exposed methods in privileged services. Let's switch from the "High Level IL" view to the "Disassembly" in order to get more information and explore the different structures to see what we can interact with. Figure 2 - Exploration of structures. Which leads us to identify some of the exposed methods. Figure 3 - List of methods exposed by the XPC service. To have a clearer view of what is exposed we can draw a graph of all exposed methods and their interfaces: From a security viewpoint, the update mechanism of a software product is a critical component because it is exposed to external input (from the vendor's update service) and by design has the ability to modify the entire product, so any flaw in this attack surface may have disastrous consequences. Among the various XPC methods exposed we can identify some that seem related to the update mechanism. The follow two are interesting: requestNetUpdateSettingsWithCompletionHandler: setNetUpdateSettings:completionHandler: Let's start by decompiling and analyzing requestNetUpdateSettingsWithCompletionHandler: Figure 4 - Decompilation of method requestNetUpdateSettingsWithCompletionHandler: . Method requestNetUpdateSettingsWithCompletionHandler: from class NUDaemonAgent processes a request by wrapping the caller's completion handler inside a block. The block fetches NetUpdate settings, converts them via a call to representation , and then calls the completion handler with either the resulting data or a failure indicator. Now let's look into setNetUpdateSettings:completionHandler: Figure 5 - Decompilation of method setNetUpdateSettings:completionHandler: . Method setNetUpdateSettings:completionHandler: from class NUDaemonAgent handles the process of applying new NetUpdate settings. It first validates that the provided settings object is a proper NSDictionary . Inside a block, a NUNetUpdateSettings instance is created, the dictionary's representation is applied, and the settings are synchronized ( synchronize ). Once the update is complete, the caller's completion handler is invoked to report the status. The method also triggers an internal notifySettingsDidChange (from NUNetUpdateSettings ) call so the rest of the system is aware of the new configuration. Exploring NetUpdate settings To explore the different settings that can be modified, we used a Frida hook. The goal was to understand which setting would allow us to escalate our privileges. File: hook.js if ( ! ObjC . available ) { console . log ( "[x] Objective-C runtime not available." ); throw "ObjC not available" ; } else { console . log ( "[*] Objective-C runtime available." ); } var hooks = [ { className : "NTGCodeSigningVerifier" , selector : "+...