Introduction
In Part 1 of this series on iOS Deep link attacks, we will explore how to recognize various types of deep link schemas used in iOS apps and identify potential vulnerabilities associated with them. The focus of this part will be to describe the different types of schemas and explain methods for identifying them.
What is a deep link?
A deep link in mobile application is like a special link that takes you directly to a specific part of a mobile app instead of a website. This means you can easily switch between apps or go from a website to an app without having to click a lot of buttons. It makes it quicker and easier to find what you’re looking for in the app. By just tapping on the defined links i.e. URIs (Uniform Resource Identifiers), users can seamlessly transition from Safari, Mail, Notes, or other applications to access your app’s content.
There are various kind of vulnerabilities which can be found on Deep link which are discussed below. To identify and exploit such vulnerabilities, we first need to understand the different types of Deep Links schemas. Let’s explore through them at first.
iOS Deep Link
There are mainly two types of Deep Link available for iOS applications
URL Scheme
URL schemes are a way to define your own protocol or scheme for handling URLs within your app. By creating a unique URL scheme, such as 8ksec://, you can specify a custom action to be taken when a user clicks on a link with that scheme. For example, 8ksec://profile could be used to open the profile page within your app.
In order to make the above URI recognizable to iOS as a deep link, it must adhere to the format of a URL scheme: scheme://resource. Therefore, to make this link work, you would need to set up URIs like 8ksec://profile. Once this is done and the link is clicked, iOS will recognize it as a valid link and prompt the user with a pop-up asking for permission to open the 8ksec app. URL Schemes also supports the query parameter like if a application have a fuctionality to navigate into a specific user then it can be configured on the deep link as 8ksec://profile?userID=1337 formatting it as scheme://resource?param=value.
Taking an example of DViA which is Damn Vulnerable iOS App, we can look into the configuration of Deep Link as below.
Universal Links
Universal Links looks like a regular urls, the only difference between it and a regular URL is that, Universal Links are configured inside the application and they are used to navigate into the specific components of the application. Example, let’s say you have an app called 8ksec with a Universal Link configured for the URL ksec7.wpcomstaging.com. If a user clicks on this link and has the 8ksec app installed on their device, the link will open the app directly. If the user doesn’t have the app installed, the link will instead open in Safari.
In case of universal links, when a user installs the iOS app, the system checks the app’s manifest to identify its associated domains, specifically domains that begin with 8ksec which indicates the domain is for deep linking. The system then accesses the /.well-known/apple-app-site-association path for each matching domain. This path should contain a JSON file provided by you, which specifies the valid deep link paths for that domain.
Universal Links were introduced in iOS to address the issues that arose with URL Schemes. URL Schemes allowed any application to use the same deep link, even if it belonged to another app, leading to confusion and potential security concerns like phishing. For instance, if the Reddit app has a URL scheme of reddit://, other apps can use the same scheme, leading to ambiguity. However, Universal Links use App Links or Deep Link URIs, such as https://reddit.com, which must be defined on the app’s domain in the Application Site Association, such as https://www.reddit.com/.well-known/apple-app-site-association. The App Site Association is an iOS feature that enables your app to handle incoming deep links associated with your website.
Now that we have a clear understanding of the various types of Deep Links in iOS, we can explore the different attack vectors on iOS Deep Links and demonstrate them technically. To identify the vulnerabilities on the Deep Link, we must first identify the Deep Link schemas on the iOS application. So, let’s get started.
Deep Link Identification: URL Schemes
To identify the Deep Link schemas, the first step is to extract an unencrypted iPA from an iOS device, which can be achieved using the following tools:
Unc0ver: An application to Jailbreak iOS
Cydia: Used for installing packages on the device
FridaiOSDump: Dumps unencrypted iPA file from iOS
Let’s take an example of DVIA-v2 and identify the Deep Link Schemas on it. – Download the DVIA-v2-swift.ipa from here
wget https://github.com/prateek147/DVIA-v2/raw/master/DVIA-v2-swift.ipa
Rename the iPA file and unzip it.
cp DVIA-v2-swift.ipa DVIA-v2-swift.zip
unzip DVIA-v2-swift.zip
There will be a folder called Payload. The “Payload” folder in iOS is a directory that is generated when an app is compiled for distribution via the App Store or ad-hoc distribution. This folder contains bundles, documents, a library directory, and a plist file that provides information about the app’s configuration.
When a deep link is configured on the application, it is stored into the Info.plist file which is located inside the Payload directory. A Property List (plist) file is a type of file in iOS that stores key-value pairs representing various types of app configuration data, including settings, preferences, and information about the app. Plist files can hold different types of information, such as app-specific settings, user preferences, and metadata, making them a useful tool for managing and storing configuration data for iOS apps.
Navigate inside the Payload directory. There will be another subdirectory formed as the name of application called DVIA-v2.app.
cd Payload
ls DVIA-v2.app
Navigate inside DVIA-v2.app and list download only the plist files.
cd DVIA-v2.app
ls | grep plist Info.plist
The Info.plist file is a structured document written in XML format that contains key-value pairs representing different attributes of an app. By changing the values of these keys, developers can configure various aspects of the app, such as its name and version number. – If we try to open the plist file using a basic text editor or the cat command, we will only see encoded data that appears as gibberish characters, as illustrated in the image below.
We need to change the format into XML using plutil
plutil -convert xml1 Info.plist
And we can view the plist file and identify the deep link schemas.
Before identifying the schemas, let’s first understand its structure. There are components like
CFBBundleURLComponents, CFBundleComponentPath and CFBunndleURLComponentQueryItems responsible for containing and handling deep link schemas on the Info.plist file. The CFBundleURLComponents key is used to define the components of the URL scheme. The CFBundleURLComponentPath key is used to specify the path of the URL, while the CFBundleURLComponentQueryItems key is used to define any query parameters that should be included in the URL. We can see the example below.
CFBundleURLTypes
CFBundleURLName
[APP_URL_SCHEME]
CFBundleURLSchemes
[UNIQUE_URL_SCHEME_IDENTIFIER]
CFBundleURLComponents
CFBundleURLComponentPath
[PATH]
CFBundleURLComponentQueryItems
name
[PARAMETER_NAME]
value
[PARAMETER_VALUE]
Let’s explore the Info.plist file of DVIA. We have already converted plist into XML using plutil
CFBundleURLName
com.highaltitudehacks.DVIAswiftv2
CFBundleURLSchemes
dvia
dviaswift
We can see the component CFBundleURLSchemes contains two values dvia and dviaswift which makes our deep link schemas as dvia:// and dviaswift://
Also, lets identify the deep link schemas on another test application called OVIA. – Clone the repository and build an iPA from Xcode.
git clone https://github.com/oversecured/OversecuredVulnerableiOSApp
Rename and unzip the iPA file.
cp OVIA.ipa OVIA.zip
unzip OVIA.zip
Navigate into the Payload directory and convert
cd Payload/OVIA.app
plutil -convert xml1 Info.plist
Open the Info.plist file to identify the schemas
CFBundleURLTypes
CFBundleTypeRole
Editor
CFBundleURLName
deeplink
CFBundleURLSchemes
ovia
Here in this application, we can see the component CFBundleURLSchemes contains one value ovia which makes our deep link shemas as ovia://
We can also use some tools like GrapeFruit to identify the deep link schemas as shown in the image below. However the scope of GrapheFruit is beyond this blog.
Deep Link Identification: Universal Links
In case of universal links, there are located on the .entitlements and each domain would start with applinks: and having the full source code access of the application, we can either go to the Capabilities tab and look for the option labeled Associated Domains or can check the .entitlements file for the key com.apple.developer.associated-domains. We will take an example of telegram for this scanrio because it’s source code is available on the github. – Download the Telegram-iOS repository.
Follow along the github README.md file to build the Xcode Project. – Open the project and search for com.apple.developer.associated-domains, you can have your Universal Links configured there
Or you can grep the keywords ‘applinks:’ from the bundle.
grep -iRn 'applinks:'
./Telegram/BUILD:489: applinks:telegram.me
./Telegram/BUILD:490: applinks:t.me
./Telegram/BUILD:491: applinks:*.t.me
When we have the compiled iPA file, it won’t be possible to find the .entitlements file that contains the necessary information. However, we can use a tool called radare2 to help us locate the necessary data. By searching for the keyword PropertyList in the app binary files, we can extract the required applinks information.
r2 -qc 'izz~PropertyList' ./Telegram
Let’s take another example for Reddit as well.
So What’s Next?
When performing a Security Review of an iOS app’s deep links, simply collecting the URL schemes from the Info.plist file is not always sufficient. This is because deep links can be more complex than just a scheme, and may also include paths and parameters. As a result, it is necessary to identify the complete deep link schema, including any associated paths and parameters, to properly identify any issues that may arise on it. Therefore, while examining an iOS app’s deep links, it is essential to identify the full deep link schemas with any relevant paths and parameters if they have been configured in the app.
How do we identify that then?
It depends upon how you are provided to test the application, usually you would be required to test the application on three approaches, White Box, Grey Box and Black Box. For this blog, let us merge the Black Box and Grey Box into one since we would not be provided with any source code on this.
White Box: Having Full Source code of the iOS application
If you have the whole source code of the application, after identifying the deep link schema from Info.plit file, you can navigate into AppDelegate.swift file in the root directory of an iOS project in Xcode. When you create a new iOS project, Xcode automatically generates an AppDelegate.swift file for you.
The AppDelegate.swift file serves as the primary starting point for the app and manages incoming deep links. In particular, the application(_:open:options:) function included in the AppDelegate.swift file is responsible for handling deep links that open the app. Within this function, developers can obtain information from the deep link URL to determine the appropriate action to take, such as displaying particular content or directing the user to a specific screen.
So let’s navigate to the application(_:open:options:) code in our DVIA-v2 application and see if we could find any additional information there
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
let splitUrl = url.absoluteString.components(separatedBy: "/phone/call_number/")
if ((Int(splitUrl[1])) != nil){
//Valid URL, since the argument is a number
let alertController = UIAlertController(title: "Success!", message: "Calling \(splitUrl[1]). Ring Ring !!!", preferredStyle: .alert)
let okAction = UIAlertAction(title: "OK", style: UIAlertAction.Style.default, handler: nil)
alertController.addAction(okAction)
window?.rootViewController?.present(alertController, animated: true, completion: nil)
}
return true
}
As seen on the above code, it contains three arguments – app UIApplication: An instance of the UIApplication class. – url URL: The deep link URL that launched the app. – options UIApplication.OpenURLOptionsKey: A dictionary of launch options for the app.
Also the above code uses the components(separatedBy: ) method to split the deep link URL string by the /phone/call_number/ string and creates an array of substrings. It then retrieves the second element of the array, which is expected to be the phone number which makes our full deep link schemas as:
dvia://phone/call_number/12345
dviaswift://phone/call_numer/12345
So this is the approach to identify the deep link schemas when source code of the application is provided. If you do not have Xcode installed, you can find and view the AppDelegate.swift file using terminal as well.
Black and Grey Box: Having iPA or App Store Link
When performing security review of iOS applications, it is more common to receive either a compiled iPA file or an App Store link instead of the source code of the application. In such cases, it is not possible to view the source code of the AppDelegate.swift file or any other file. In these scenarios, a different approach is required to identify the deep link schema, which will be explained below.
Once we get the iPA file we need to rename it to zip and decompile it using unzip command to view the internal compontents of an iPA file but in the case of App Store link, you can use FridaiOSDump or a GUI based application called iMazing to extract an unencrypted iPA file from iOS device.
Assuming that you currently have an iPA file available in your current directory. We will unzip the iPA file and find the binary file called DVIA-v2 which is present inside the Payload directory. This binary file consists of most of the source code written which is compiled during build.
# The entered commands are
cp DVIA-v2-swift.ipa DVIA-v2-swift.zip
unzip DVIA-v2-swift.zip > /dev/null
ls Payload/DVIA-v2.app
Identifying deep link paths using regex
As previously mentioned, all the source code is compiled into a binary file known as DVIA-v2. Since we know that the deeplink paths follow the format of /something/something, we can generate a list of string literals from the binary file and apply regex to identify the paths, as demonstrated below.
cd Payload/DVIA-v2.app
strings DVIA-v2 | grep -E '.*\/.*'
We can observe that the output displays the deep link paths that we were searching for, which are formatted as /something/something and specifically include /phone/call_number/.
We can further refine the output by excluding or filtering irrelevant information.
strings DVIA-v2 | grep -E '\/.*\/.*' | grep -v 'https://\|/Users/\|/Volumes/\|http://\|BuildRoot/'
This method may not always be successful since it requires determining the format of the deep link paths, but it is still worth attempting.
Identifying deep link paths using Frida
In earlier versions of iOS inorder to handle the Deep link schemas, application:handleOpenURL was used, but this method could not determine the source that loaded the URL. To address this limitation, Apple introduced application:openURL:sourceApplication:annotation:. However, both of these methods have been deprecated, and the current supported method is application:openURL:options:, which provides information about the source of the URL load in the options.
Let’s see an example about how Frida can trace the mentioned method. The tracer will monitor all Objective-C method calls that match the pattern [ application:openURL:], which includes any methods in the “application” class of the app that have “openURL” in their name.
$8kSec: frida-ps -Uai | grep -i dvia
3535 DVIA-v2 com.8ksec.DVIAswiftv2
$8kSec: frida-trace -U -m "*[* application:*openURL:*]" DVIA-v2
Instrumenting...
-[DVIA_v2.AppDelegate application:openURL:options:]: Loaded handler at "/Users/8ksec/__handlers__/DVIA_v2.AppDelegate/application_openURL_options_.js"
Started tracing 1 function. Press Ctrl+C to stop.
/* TID 0x103 */
5489 ms -[DVIA_v2.AppDelegate application:0x102506910 openURL:0x28104eed0 options:0x283a81ac0]
Run the above command open the Deep Link scheme to trigger the handlers that we are tracing using Frida.
The frida has logged a method invocation which confirms that we have identified the URL scheme handler used by the iOS application.
To use Universal Links in an iOS app, the application:continueUserActivity:restorationHandler:method must be implemented to handle the incoming links. A blog post by Grepharder provides a detailed guide to intercepting Universal Links using Frida’s Interceptor. You can find the detailed explanation here. – Download the Frida Script from here
wget https://gist.githubusercontent.com/grepharder/7a6c1b64f5e8eecf78ca076140ac09ec/raw/b7f8fe3cd5b02242d2ee6a4f07ebe6a9671c658b/universal_links.js
For this demonstration, we will use a Telegram application. List the application name and package name using Frida
$8kSec: frida-ps -Uai | grep -i telegram
3529 Telegram ph.telegra.Telegraph
Run the above downloaded Frida Script on the telegram application
frida -U -f ph.telegra.Telegraph -l universal_links.js
Open the Deep Link schema on the iOS application. For the demonstration, we are triggering the deep link schemas through Notes.
The above script will show how the before mentioned methods are being called on the application and will list down the Deep Link URL.
Another method to identify deep link schemas and their associated paths and parameters is by using the Apple App Site Association. This involves checking the parameters and paths listed in /.well-known/apple-app-site-association, which contains all the allowed or defined parameters and paths. For instance, if you found the deep link schema to be https://www.reddit.com from the Plist file, you can navigate to https://www.reddit.com/.well-known/apple-app-site-association to find the associated paths and parameters.
In addition, we can also identify deep link schemas and their related paths and parameters from Android applications. This is easier than on iOS applications. We can use the same schemas on iOS applications to check if they are configured or not. To learn how to identify deep link schemas on an Android application, you can navigate to our another blog.
In conclusion, we have investigated different deep link schemas and methods of identification. Moving forward, we will explore potential vulnerabilities and provide practical demonstrations of how to exploit them in the next part of our Attacking iOS Deep Link series.
Looking to elevate your expertise in Mobile Application Security?
Practical Mobile Application Exploitation Training
365 Days of Access | Hands-On Learning | Self-Paced Training
GET IN TOUCH
Visit our training page if you’re interested in learning more about these techniques and developing your abilities further. Additionally, you may look through our Events page and sign up for our upcoming Public trainings.
Check out our Certifications Program and get Certified today.
Please don’t hesitate to reach out to us through out Contact Us page or through the Button below if you have any questions or need assistance with Penetration Testing or any other Security-related Services. We will answer in a timely manner within 1 business day.
We are always looking for talented people to join our team. Visit out Careers page to look at the available roles. We would love to hear from you.