Apple released iOS 18.3.1 (build 22D72) to patch a vulnerability tied to the Accessibility framework and reported by Citizen Lab. Let's analyze it!

The vulnerability advisory can be found here. Here is an overview directly took from Apple's website:

Accessibility

Available for: iPhone XS and later, iPad Pro 13-inch, iPad Pro 12.9-inch 3rd generation and later, iPad Pro 11-inch 1st generation and later, iPad Air 3rd generation and later, iPad 7th generation and later, and iPad mini 5th generation and later

Impact: A physical attack may disable USB restricted mode on a locked device. Apple is aware of a report that this issue may have been exploited in an extremely sophisticated attack against specific targeted individuals.

Description: An authorization issue was addressed with improved state management.

CVE-2025-24200: Bill Marczak of The Citizen Lab at The University of Toronto’s Munk School

In this blog post, we explain what we've understood from the patch so far.

🚧 USB Restricted Mode

Before analyzing the update, let's briefly mention the USB restricted mode and why it is important.

If an iPhone stays locked for over an hour while the feature is activated, data connections through its port will be disabled until the user unlocks the device and explicitly grants permission for the accessory to connect:

This is essential to mitigate sophisticated attacks involving external devices such as forensic extractors e.g. this one or this one.

Being able to bypass this mechanism from the lock screen would restore the possibility to use such devices. This recent tweet shows the importance of this capability for threat actors:

In next sections, we provide a first analysis of CVE-2025-24200, which allowed to bypass the USB restricted mode under some conditions.

📦 Setup

Before finding the actual patch, the first step is to download the iOS builds before and after it:

# pre-patch
ipsw download ipsw --device iPhone14,4 --build 22D63

# post-patch
ipsw download ipsw --device iPhone14,4 --build 22D72

We can then extract all binaries and libraries from both releases to investigate the changes.

🔬 Finding the patch

We first looked at the diff report generated with ipsw diff and published by Blacktop for each iOS release.

From this, we can inspect every notable change. It is worth noting that in the advisory, Apple refers to an improved state management, which likely entails additional checks and, consequently, the introduction of new basic blocks.

With the following (headless) Binary Ninja script, we can list the basic blocks count diff for two symbolized versions of the same binary:

import binaryninja

binaryninja.disable_default_log()

def build_table(bv: binaryninja.BinaryView):
    """Builds a table of function names to their basic block count."""
    table = {}
    for f in bv.functions:
        if not f.name.startswith('sub_'):
            table[f.name] = [len(f.basic_blocks)]
    return table

def diff_tables(original, patched):
    """Compares two basic block tables and prints changes."""
    for fn in original.keys() & patched.keys():
        if original[fn] != patched[fn]:
            print(f'{fn}: {original[fn]} -> {patched[fn]}')

t1 = build_table(binaryninja.load("/path/to/original"))
t2 = build_table(binaryninja.load("/path/to/patched"))

diff_tables(t1, t2)

In the next sub-sections, we highlight notable changes identified in two of these components.

Changes to AXSpringBoardServerInstance

In this framework, the following function appears to have 4 new basic blocks.

-[AXSpringBoardServerHelper _handleDisallowUSBRestrictedModeSCInformativeOnly:]

Inspecting it more closely, it seems Apple added a check at the very beginning of the function to ensure the device is unlocked before presenting an alert. This alert has the localized string with id sc.disallow.usb.restricted.mode.alert.title as its title and a single clickable action, which is OK.

As its name suggests, this function is only informative, i.e. it only affects the user interface. We still need to find where the USB restricted mode is effectively disabled after the alert is presented.

Changes to profiled

profiled is an important iOS daemon handling a lot of things, among which accessibility features, remote device management (MDM) and more...

In this release, the following function was patched and gained 6 basic blocks:

-[MCProfileServicer setParametersForSettingsByType:configurationUUID:toSystem:user:passcode:credentialSet:completion:]

Here is the patch we managed to recover by reverse-engineering both versions:

diff --git a/original.m b/patched.m
index cd2b537..7fe1bd4 100644
--- a/original.m
+++ b/patched.m
@@ -8,6 +8,13 @@ BOOL has_ent = [self

if (has_some_entitlement == NO) {
    // Unchanged
} else {
+       NSDict *rb = [settingsByType objectForKeyedSubscript: @"RestrictedBool"];
+       BOOL is_device_locked [[MCDependencyManager sharedManager] isDeviceLocked];
+       BOOL restricted_mode_allowed = [
+               rb objectForKeyedSubscript: @"FeatureUSBRestrictedModeAllowed"
+       ]
+
+       if (restricted_mode_allowed == NO || is_device_locked == NO) {

               [[[MCProfileServiceServer sharedServer]]
                       setParametersForSettingsByType: type
                       sender: [self remoteProcessBundleID]
                       completion: completion
               ]
+
+       } else {
+               NSError *error = [
+                       MCErrorWithDomain: _MCSettingsErrorDomain
+                       code: 0x6d66
+                       descriptionArray: _MCErrorArray
+                       errorType: _MCErrorTypeFatal
+               ];
+
+               if (completion) {
+                       completion.completionHandler(completion, error);
+               }
+       }
 }

As we can see, before calling the setParametersForSettingsByType inner method that effectively sets the desired settings (i.e. the restricted mode state in our case 👀), the function checks that the device is unlocked.

This seems to be the patch that effectively mitigates CVE-2025-24200.

⚔️ Attack vector

Now that we have a greater understanding of the patch, we need to find a code path that lets us exploit the underlying vulnerability.

The first step is to find from where the USB restricted mode can be disabled through one of the two previously identified patched functions.

After some research, we found out that the assistivetouchd daemon contains code to disable USB restricted mode through the following function:

-[SCATScannerManager _setUSBRMPreferenceDisabled]

This function contains reference to several AX[...] classes. This is a hint it could be tied to the first part of the patch we discussed (raising an alert). This prompted us to pay closer attention to this daemon.

The assistivetouchd daemon

The assistivetouchd daemon can be found at the following location on iOS devices:

System/Library/CoreServices/AssistiveTouch.app/assistivetouchd

It continuously operates in the background whenever certain accessibility features are enabled on the device. The most noticeable example is the Assistive Touch feature, which allows you to perform some actions from a new menu, always present on the screen:

Looking at cross references in assistivetouchd to find where _setUSBRMPreferenceDisabled could be called, we can find the following function:

-[SCATScannerManager handleUSBMFiDeviceConnected]

Based on its name, we can expect that simply connecting an MFi (Made for iPhone certified) device would be sufficient to activate it.

Below is the code we successfully reversed for this function (though it may contain inaccuracies):

@implementation SCATScannerManager

- (void)handleUSBMFiDeviceConnected {
    AXSettings btm = [SCATBluetoothManager sharedInstance];

    bool did_read_rm_alert = [btm switchControlUserDidReadUSBRestrictedModeAlert];
    bool has_disabled_rm = [self userHasDisabledUSBRM];
    bool should_disallow_rm = [btm switchControlShouldDisallowUSBRestrictedMode];

    if (!did_read_rm_alert && !has_disabled_rm && should_disallow_rm) {
        [btm setSwitchControlShouldDisallowUSBRestrictedMode:NO];

        // Forwards a call to the AXSettings framework that is responsible
        // for showing the alert discussed earlier in the patch section

        // The -[SCATScannerManager _setUSBRMPreferenceDisabled] function that
        // disables USB restricted mode is passed as the callback handler for
        // this alert.
    }
}

@end

Some basic checks are performed to determine if an alert should be displayed. Basically, if the alert hasn't been presented yet and the USB restricted mode is enabled, it should appear. When it happens, the only option is to click OK, which triggers the provided callback.

In this case, the callback is the _setUSBRMPreferenceDisabled function.

Manually triggering the function for testing purpose

In order to test our hypothesis, we wanted to arbitrarily call the handleUSBMFiDeviceConnected method on an iOS device that we can debug with Frida.

Unfortunately, we were not able to get the function through the Module API. We thus decided to do it another way and extracted the assistivetouch binary from the device using the following command:

frida-pull -U /System/Library/CoreServices/AssistiveTouch.app/assistivetouchd

From this binary, we can manually compute offsets between a function that we can trigger and the target one.

In our case, we will hook -[HNDRocker _shakePressed] because we can trigger it from the assistive touch menu while the device is locked. Here is the onEnter handler:

onEnter(log, args, state) {
    // Following addresses need to be adapted for each build
    shakePressedBaseAddr = 0x100042858;
    handleUSBMFiDeviceConnectedBaseAddr = 0x1000ad1c8;

    off = handleUSBMFiDeviceConnectedBaseAddr - shakePressedBaseAddr

    shakePressedAddr = this.context.pc;
    log('ShakePressed:', shakePressedAddr)

    handleUSBMFiDeviceConnectedAddr = shakePressedAddr.add(off)
    log('handleUSBMFiDeviceConnected:', handleUSBMFiDeviceConnectedAddr)

    handle = new NativeFunction(handleUSBMFiDeviceConnectedAddr, 'long', []);
    handle()
},

Here is how to trace it:

frida-trace -U assistivetouchd -m '-[HNDRocker _shakePressed]'

Finally, we can trigger the traced function by clicking on Shake; Which thanks to our hook calls the handleUSBMFiDeviceConnected function and makes the popup appear:

As said earlier, at this point an attacker can simply click OK to disable USB restricted mode from the lock screen.

The tests described in this section were made with an iPhone X (iPhone10,3) running iOS 16.7.10 (build 20H350).

How to trigger this in real conditions

So far we only managed to make the alert appear by arbitrarily calling the -[SCATScannerManager handleUSBMFiDeviceConnected] method, but we don't know yet how it is supposed to be called in real conditions.

One thing we didn't mention is that the Assistive Touch feature isn't the only one that makes the assistivetouchd daemon run in the background. Another accessiblity feature that does so is Switch Control.

This feature allows one to plug an external device into the iPhone to control it. From the class names of various methods, we looked at up until now, which all start with SC (probably for Switch Control), we can deduce this is the legitimate way to go: Plug an MFi-certified switch control device to the iPhone.

Looking at available switch control devices available on the market, it seems most of them are Bluetooth devices. However, we found one that used to operate over lightning.

Here is a screenshot showing this product from its vendor website. Note that this product isn't available anymore:

While the device is in restricted mode, the USB protocol is completely disabled. However, other protocols can be used freely over the lightning port. This is for instance the case of the iAP2 protocol that can be used by MFi devices.

That is why we think plugging this kind of device to an iPhone with Switch Control enabled may be enough to make the popup appear and disable USB restricted mode from the lock screen before iOS 18.3.1.

Disclaimer:

Although we believe this could work, we currently lack the necessary hardware to test it. We are also aware restricted mode isn't the only mitigation when it comes to physical accessories, and an actual exploit may be more complex.

Furthermore, we have only explored one possible attack vector for this vulnerability, but others may exist. It is advisable to update your devices to the latest version, even if you do not use accessibility features.


References


If you would like to learn more about our security audits and explore how we can help you, get in touch with us!