11 KiB
iOS App Extensions
Content copied form https://mobile-security.gitbook.io/mobile-security-testing-guide/ios-testing-guide/0x06h-testing-platform-interaction#app-extensions
App extensions let apps offer custom functionality and content to users while they’re interacting with other apps or the system. Some notable ones are:
- Custom Keyboard: replaces the iOS system keyboard with a custom keyboard for use in all apps.
- Share: post to a sharing website or share content with others.
- Today: also called widgets, they offer content or perform quick tasks in the Today view of Notification Center.
For example, the user selects text in the host app, clicks on the "Share" button and selects one "app" or action from the list. This triggers the app extension of the containing app. The app extension displays its view within the context of the host app and uses the items provided by the host app, the selected text in this case, to perform a specific task (post it on a social network, for example). See this picture from the Apple App Extension Programming Guide which pretty good summarizes this:
Security Considerations
From the security point of view it is important to note that:
- An app extension does never communicate directly with its containing app (typically, it isn’t even running while the contained app extension is running).
- An app extension and the host app communicate via inter-process communication.
- An app extension’s containing app and the host app don’t communicate at all.
- A Today widget (and no other app extension type) can ask the system to open its containing app by calling the
openURL:completionHandler:
method of theNSExtensionContext
class. - Any app extension and its containing app can access shared data in a privately defined shared container.
- App extensions cannot access some APIs, for example, HealthKit.
- They cannot receive data using AirDrop but do can send data.
- No long-running background tasks are allowed but uploads or downloads can be initiated.
- App extensions cannot access the camera or microphone on an iOS device (except for iMessage app extensions).
Static analysis
Verifying if the App Contains App Extensions
If you have the original source code you can search for all occurrences of NSExtensionPointIdentifier
with Xcode (cmd+shift+f) or take a look into "Build Phases / Embed App extensions":
There you can find the names of all embedded app extensions followed by .appex
, now you can navigate to the individual app extensions in the project.
If not having the original source code:
Grep for NSExtensionPointIdentifier
among all files inside the app bundle (IPA or installed app):
$ grep -nr NSExtensionPointIdentifier Payload/Telegram\ X.app/
Binary file Payload/Telegram X.app//PlugIns/SiriIntents.appex/Info.plist matches
Binary file Payload/Telegram X.app//PlugIns/Share.appex/Info.plist matches
Binary file Payload/Telegram X.app//PlugIns/NotificationContent.appex/Info.plist matches
Binary file Payload/Telegram X.app//PlugIns/Widget.appex/Info.plist matches
Binary file Payload/Telegram X.app//Watch/Watch.app/PlugIns/Watch Extension.appex/Info.plist matches
You can also access per SSH, find the app bundle and list all inside PlugIns (they are placed there by default) or do it with objection:
ph.telegra.Telegraph on (iPhone: 11.1.2) [usb] # cd PlugIns
/var/containers/Bundle/Application/15E6A58F-1CA7-44A4-A9E0-6CA85B65FA35/
Telegram X.app/PlugIns
ph.telegra.Telegraph on (iPhone: 11.1.2) [usb] # ls
NSFileType Perms NSFileProtection Read Write Name
------------ ------- ------------------ ------ ------- -------------------------
Directory 493 None True False NotificationContent.appex
Directory 493 None True False Widget.appex
Directory 493 None True False Share.appex
Directory 493 None True False SiriIntents.appex
We can see now the same four app extensions that we saw in Xcode before.
Determining the Supported Data Types
This is important for data being shared with host apps (e.g. via Share or Action Extensions). When the user selects some data type in a host app and it matches the data types define here, the host app will offer the extension. It is worth noticing the difference between this and data sharing via UIActivity
where we had to define the document types, also using UTIs. An app does not need to have an extension for that. It is possible to share data using only UIActivity
.
Inspect the app extension's Info.plist
file and search for NSExtensionActivationRule
. That key specifies the data being supported as well as e.g. maximum of items supported. For example:
<key>NSExtensionAttributes</key>
<dict>
<key>NSExtensionActivationRule</key>
<dict>
<key>NSExtensionActivationSupportsImageWithMaxCount</key>
<integer>10</integer>
<key>NSExtensionActivationSupportsMovieWithMaxCount</key>
<integer>1</integer>
<key>NSExtensionActivationSupportsWebURLWithMaxCount</key>
<integer>1</integer>
</dict>
</dict>
Only the data types present here and not having 0
as MaxCount
will be supported. However, more complex filtering is possible by using a so-called predicate string that will evaluate the UTIs given. Please refer to the Apple App Extension Programming Guide for more detailed information about this.
Checking Data Sharing with the Containing App
Remember that app extensions and their containing apps do not have direct access to each other’s containers. However, data sharing can be enabled. This is done via "App Groups" and the NSUserDefaults
API. See this figure from Apple App Extension Programming Guide:
As also mentioned in the guide, the app must set up a shared container if the app extension uses the NSURLSession
class to perform a background upload or download, so that both the extension and its containing app can access the transferred data.
Verifying if the App Restricts the Use of App Extensions
It is possible to reject a specific type of app extension by using the following method:
However, it is currently only possible for "custom keyboard" app extensions (and should be verified when testing apps handling sensitive data via the keyboard like e.g. banking apps).
Dynamic Analysis
For the dynamic analysis we can do the following to gain knowledge without having the source code:
- Inspecting the items being shared
- Identifying the app extensions involved
Inspecting the Items Being Shared
For this we should hook NSExtensionContext - inputItems
in the data originating app.
Following the previous example of Telegram we will now use the "Share" button on a text file (that was received from a chat) to create a note in the Notes app with it:
If we run a trace, we'd see the following output:
(0x1c06bb420) NSExtensionContext - inputItems
0x18284355c Foundation!-[NSExtension _itemProviderForPayload:extensionContext:]
0x1828447a4 Foundation!-[NSExtension _loadItemForPayload:contextIdentifier:completionHandler:]
0x182973224 Foundation!__NSXPCCONNECTION_IS_CALLING_OUT_TO_EXPORTED_OBJECT_S3__
0x182971968 Foundation!-[NSXPCConnection _decodeAndInvokeMessageWithEvent:flags:]
0x182748830 Foundation!message_handler
0x181ac27d0 libxpc.dylib!_xpc_connection_call_event_handler
0x181ac0168 libxpc.dylib!_xpc_connection_mach_event
...
RET: (
"<NSExtensionItem: 0x1c420a540> - userInfo:
{
NSExtensionItemAttachmentsKey = (
"<NSItemProvider: 0x1c46b30e0> {types = (\n \"public.plain-text\",\n \"public.file-url\"\n)}"
);
}"
)
Here we can observe that:
- This occurred under-the-hood via XPC, concretely it is implemented via a
NSXPCConnection
that uses thelibxpc.dylib
Framework. - The UTIs included in the
NSItemProvider
arepublic.plain-text
andpublic.file-url
, the latter being included inNSExtensionActivationRule
from theInfo.plist
of the "Share Extension" of Telegram.
Identifying the App Extensions Involved
You can also find out which app extension is taking care of your the requests and responses by hooking NSExtension - _plugIn
:
We run the same example again:
(0x1c0370200) NSExtension - _plugIn
RET: <PKPlugin: 0x1163637f0 ph.telegra.Telegraph.Share(5.3) 5B6DE177-F09B-47DA-90CD-34D73121C785
1(2) /private/var/containers/Bundle/Application/15E6A58F-1CA7-44A4-A9E0-6CA85B65FA35
/Telegram X.app/PlugIns/Share.appex>
(0x1c0372300) -[NSExtension _plugIn]
RET: <PKPlugin: 0x10bff7910 com.apple.mobilenotes.SharingExtension(1.5) 73E4F137-5184-4459-A70A-83
F90A1414DC 1(2) /private/var/containers/Bundle/Application/5E267B56-F104-41D0-835B-F1DAB9AE076D
/MobileNotes.app/PlugIns/com.apple.mobilenotes.SharingExtension.appex>
As you can see there are two app extensions involved:
Share.appex
is sending the text file (public.plain-text
andpublic.file-url
).com.apple.mobilenotes.SharingExtension.appex
which is receiving and will process the text file.
If you want to learn more about what's happening under-the-hood in terms of XPC, we recommend to take a look at the internal calls from "libxpc.dylib". For example you can use frida-trace
and then dig deeper into the methods that you find more interesting by extending the automatically generated stubs.