~/posts/mobile/cve-2025-24200-usb-lock-state-authorization.md

CVE-2025-24200: USB policy and lock-state authorization

Separate accessibility prompts from backend policy writes. Use build differences, Boolean conditions, and address models to distinguish patch evidence, debugging observations, and the hardware hypothesis.

date[31:24]
read[23:16]
8 min
cat[15:8]
Mobile
Contents
  1. 0x00Restricting data is not disabling the entire connector
  2. 0x01Pin the builds before comparing control flow
  3. 0x02One change protects interaction, the other protects the write
  4. 0x03Make the backend condition explicit
  5. 0x04The notification callback leads into assistivetouchd
  6. 0x05An active call establishes only part of reachability
  7. 0x06The hardware hypothesis needs an end-to-end observation
  8. 0x07Official references

CVE-2025-24200 is about who may change an accessory policy while an iPhone is locked, not merely whether a dialog appears. The iOS 18.3.1 changes involve both the accessibility interface and the settings service: one controls interaction, while the other controls the state change.

Apple released the fix on 2025-02-10, described an authorization issue addressed through state management, and credited Bill Marczak. The analysis below separates iOS 18.3 / 18.3.1 patch evidence from an iOS 16.7.10 debugging observation. Public build differences, offline models, and hardware tests answer different questions. Apple security advisory

Restricting data is not disabling the entire connector

USB Restricted Mode governs data connections. Apple's security guide describes a one-hour boundary after locking or termination of an accessory data connection, with separate treatment of recognized accessories, unknown accessories, and states requiring passcode re-entry. The one-hour rule has context; it is not the only timer governing every device state. Apple data-connection security

Restoring a data channel, bypassing a passcode, and decrypting user data are separate outcomes. The advisory establishes that a physical attack may disable USB Restricted Mode on a locked device. Later acquisition or data-access steps still have their own prerequisites.

Interface illustration comparing an accessory unlock notice with a Switch Control accessory dialog

Interface illustration: the left panel emphasizes unlocking, while the right describes a Switch Control accessory's potential effect on connections while locked. These are contrasting interface states, not a continuous experiment or evidence of a completed physical attack.

Pin the builds before comparing control flow

Two evidence sets, two device environments2 rows
Purpose Device System and build
Patch-localization record iPhone14,4 iOS 18.3 (22D63) and 18.3.1 (22D72)
Active method-call record iPhone10,3, iPhone X iOS 16.7.10 (20H350)

Keep the device, architecture slice, component path, and analyzer configuration fixed, and record each Mach-O UUID and file digest. A function address is not a cross-build constant. Function counts and basic-block deltas are leads, not vulnerability attribution.

A dictionary keyed only by function name also silently overwrites duplicate names. Retain all candidates, then compare names that occur exactly once on each side. The following function accepts sequences of (name, basic_block_count) pairs. A Binary Ninja collector can obtain them through function.name and len(function.basic_blocks) after analysis completes.

Unique-name matchingpython
from collections import defaultdict

def compare_unique(before, after):
    def collect(rows):
        out = defaultdict(list)
        for name, count in rows:
            if not name.startswith("sub_"):
                out[name].append(count)
        return out
    old, new = collect(before), collect(after)
    changed, ambiguous = [], []
    for name in sorted(old.keys() & new.keys()):
        if len(old[name]) != 1 or len(new[name]) != 1:
            ambiguous.append(name)
            continue
        a, b = old[name][0], new[name][0]
        if a != b:
            changed.append((name, a, b))
    return changed, ambiguous

This still misses renamed, stripped, inlined, or misidentified functions. It ranks candidates rather than determining patch semantics. Local tests use synthetic records; the two firmware images were not reanalyzed in Binary Ninja.

One change protects interaction, the other protects the write

The patch-localization record identifies two entries worth following:

Function-level leads2 rows
Component Method Basic-block delta
AXSpringBoardServerInstance -[AXSpringBoardServerHelper _handleDisallowUSBRestrictedModeSCInformativeOnly:] +4
profiled -[MCProfileServicer setParametersForSettingsByType:configurationUUID:toSystem:user:passcode:credentialSet:completion:] +6

These counts belong to that particular analysis record and were not recomputed from complete binaries. An independent check of the ipsw project's build report confirms two more direct string additions:

Strings added in the build comparison+2 −0
Not showing USB restricted mode alert because device is locked
SETTINGS_DEVICE_IS_LOCKED_P_SETTING

The first appears in AXSpringBoardServerInstance; the second appears in profiled, alongside the new _MCSettingsErrorDomain symbol. They support the interface-check plus write-check interpretation, but strings alone do not reconstruct the entire control flow. This is a tool project's report, not Apple source code. ipsw build differences

The interface method checks the unlocked state before presenting its notice, whose title key is sc.disallow.usb.restricted.mode.alert.title. A single OK action and the InformativeOnly name suggest a notification role, but names alone do not establish every side effect. Follow the final settings write.

Make the backend condition explicit

After the existing permission check, the recovered profiled logic reads the value associated with FeatureUSBRestrictedModeAllowed under RestrictedBool and queries isDeviceLocked. Abstracting away object lookup and Boolean conversion, the recorded branch becomes:

Boolean model of the settings entrypython
def write_allowed(base_permission, setting_value, locked):
    return base_permission and (not setting_value or not locked)
After the existing permission check succeeds4 rows
setting_value locked Modeled branch
false false Call the internal settings interface
false true Call the internal settings interface
true false Call the internal settings interface
true true Return error 0x6d66

Here, setting_value is an input to the recovered condition, not a product-switch meaning inferred from the word Allowed. Missing keys, unexpected object types, boolValue conversion, and the internal setter's interpretation need separate tracing. This model is not Objective-C source suitable for replacing a system implementation.

flowchart TD
  A["Receive settings request"] --> B{"Existing permission check passes?"}
  B -->|No| C["Keep the original error path"]
  B -->|Yes| D["Read target setting and lock state"]
  D --> E{"Value is false, or device is unlocked?"}
  E -->|Yes| F["Internal settings write"]
  E -->|No| G["Error 0x6d66 / completion"]

Checking at the backend write entry covers callers that do not pass through the same dialog. A state query alone does not establish atomicity between the check and the write, however. The diagram explains the observed patch boundary; it does not assert a new race vulnerability.

The notification callback leads into assistivetouchd

The daemon resides at /System/Library/CoreServices/AssistiveTouch.app/assistivetouchd. Cross-references involving SCATScannerManager connect these actions:

flowchart TD
  A["Accessory event enters handleUSBMFiDeviceConnected"] --> B["Check prompted, disabled, and current-need state"]
  B --> C["Update prompt flag and request notification"]
  C --> D["OK callback"]
  D --> E["_setUSBRMPreferenceDisabled"]
  E --> F["profiled settings entry"]

The complete method names are -[SCATScannerManager handleUSBMFiDeviceConnected] and -[SCATScannerManager _setUSBRMPreferenceDisabled]. This relationship explains why a notice can be associated with a sensitive setting. It does not prove that any USB accessory naturally produces the entry event.

AssistiveTouch and Switch Control are different accessibility features. A daemon name, floating menu, and SCAT class names may all occur in the same investigation without making "AssistiveTouch is enabled" equivalent to "every prerequisite for the Switch Control accessory path is satisfied."

An active call establishes only part of reachability

The iPhone X debugging record uses -[HNDRocker _shakePressed] as a triggerable address anchor and actively enters the target handler. The corresponding notice appears in the record. That supports interface reachability, not independent confirmation of a persisted policy change or a restored data channel under restriction.

For one image under a common load slide, the address relationship is:

Address relationship within one imagetext
target_runtime = anchor_runtime + (target_static - anchor_static)

The two recorded static addresses are 0x100042858 and 0x1000ad1c8, separated by 0x6a970, or 436592 bytes. The pair alone does not identify which is the target and which is the anchor. Establish each symbol and instruction position before assigning a signed difference, and do not transfer an iOS 16 difference to iOS 18.

An Objective-C instance-method IMP normally also receives self and _cmd. Check the instance, selector, argument and return types, thread requirements, and current implementation address. Frida's method wrappers and Objective-C runtime information help with that check. A no-argument native wrapper plus a visible dialog does not establish a correct calling convention. Frida Objective-C API

Offline checks cover name collisions, all three-variable Boolean combinations, and both address directions under four load slides:

Offline checkslog
PASS: 8 unique-name matching cases; 8 Boolean states; 8 relocation cases
Static address distance: 0x6a970 (436592 bytes)
No device methods called; no USB policy changed

These are local Python-model results, not a new device experiment. The Boolean model checks the expression; the address model checks cancellation of a common slide.

The hardware hypothesis needs an end-to-end observation

AbleNet's Hook+ setup documentation confirms the connection between a Lightning accessory and Switch Control, including configurations with one to four switches. The physical information in the older product guide is better represented as structured data than as a full-page product screenshot. AbleNet setup instructions

Physical interfaces shown in the product guide3 rows
Part Purpose
Lightning connector Connect to the iOS device
Switch jacks 1-4 Accept 3.5 mm mono TS switches
Micro USB port External charging connection; not equivalent to an arbitrary USB host channel

Those connection facts justify an investigation; they do not establish Hook+ as a complete trigger. MFi authentication, enabled features, accessory-event transport, user interaction, time spent locked, and system build can all matter. The analysis record contains no end-to-end test with that hardware.

Record natural event arrival, notice presentation, backend request success, policy changes, and successful new data connections separately. Even the last step remains distinct from decrypting user data. Use Apple's fixed release for the relevant device as the baseline, and examine constraints at the backend state change instead of substituting a dialog or cable for the full evidence chain.

Official references

NORMAL~/posts/mobile/cve-2025-24200-usb-lock-state-authorization.md§--
0%en