In Part 2 of this series on iOS Deep Link attacks, we will explore how to identify different vulnerabilities on iOS Deep Link and the technical demonstration to exploit them.
If you haven’t yet checked out Part 1, you can view it here.
Agenda
In this blog we will cover the deep link vulnerabilities as shown below.
- DeepLink Phishing
- Insufficient URL validation
- HTML injection via deeplink
- CSRF via DeepLink
Scenario – 1: DeepLink Phishing
Deep Link Phishing is a commonly overlooked issue when it comes to iOS deep link attacks/testing. Phishing usually happens when someone creates a fake website or app that looks exactly like a trusted one, trying to trick people into giving away important personal information.
Deep-linking using URL schemas, can allow two different apps to register and use the same URL. For example, one could register the same URL schema that Twitter uses for their own app. So, when a deep-link intended for Twitter is triggered, their app can have an option to open along with the Twitter app, if the application Twitter is not installed on the device, then the app can be opened directly.
Imagine there is an iOS app called 8ksec that allows users to log in. This app has a custom deep link schema, eightksec://login
which directly takes users to their login panel.
The attacker has the capability to create a login panel that looks similar and can use the same deep link schema, eightksec://login
as shown below.
The victim assumes that by clicking on the deep link schema, they will open the legitimate application.
If the genuine application is not installed, the malicious application will open. If the genuine application is installed, the user will be presented with two options to choose from, allowing them to navigate into either the genuine application or the malicious application.
Mitigation
Prioritizing the adoption of universal links over custom URL schemes may be the optimal means to prevent such attacks.
Scenario – 2: Insufficient URL validation
When it comes to Deep Linking, inadequate URL validation typically refers to the situation where a deep link is utilized to load a web URL within a web view, but the proper validation of that URL is omitted. Numerous applications implements Deep Link schemas to integrate web views into their functionality, but they often neglect to perform URL validation. This oversight can allow these schemas to be exploited, causing the application to load arbitrary and potentially unsafe URLs.
We have observed this issue commonly occurring in React-based iOS applications. In these cases, a web application is initially developed and later integrated into the mobile app using various SDKs. Deep links are then utilized to load these web applications within the mobile app.
Additionally, another possible application of loading URLs through deep links is in the context of displaying Terms and Conditions, Help, and Support sections within a mobile app. This is often advantageous as these sections are typically already defined and available on the web application. Example,
dvia://navigate/help?url=
dvia://navigate/termsandcondition?url=
We have made some changes on the AppDelegate.swift file of DVIA-v2 to experiment this scenario, if you are planning to follow along, replace your AppDelegate.swift file with below code and run the application again.
import UIKit
import WebKit
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, WKUIDelegate {
var window: UIWindow?
var webView: WKWebView!
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Initialize the window
window = UIWindow(frame: UIScreen.main.bounds)
let webConfiguration = WKWebViewConfiguration()
webView = WKWebView(frame: window!.bounds, configuration: webConfiguration)
webView.uiDelegate = self
// Load the initial URL
let initialURL = URL(string: "ksec7.wpcomstaging.com")
let initialRequest = URLRequest(url: initialURL!)
webView.load(initialRequest)
// Set the web view as the root view controller's view
let viewController = UIViewController()
viewController.view = webView
window?.rootViewController = viewController
window?.makeKeyAndVisible()
return true
}
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
if url.scheme == "dvia", url.host == "openurl" {
if let urlString = url.queryParameters?["url"], let url = URL(string: urlString) {
let myRequest = URLRequest(url: url)
webView.load(myRequest)
return true
}
}
return false
}
}
extension URL {
var queryParameters: [String: String]? {
guard let components = URLComponents(url: self, resolvingAgainstBaseURL: true),
let queryItems = components.queryItems else {
return nil
}
var parameters = [String: String]()
for item in queryItems {
parameters[item.name] = item.value
}
return parameters
}
}
The code above contains potential issue with weak host validation. This means that the code does not perform thorough validation or verification of the host component of the deep link URL. The open url method checks if the scheme is dvia and the host is openurl to determine if it should handle the deep link. However, it does not perform any further validation on the host value or verify if it corresponds to a trusted or expected value.
To exploit it, we first need to identify the Deep Link schema for it, we can view it from Info.plist file, to identify it, please go through our previous blog. Once we identify the deep link schema as dvia://, we need to navigate into AppDelegate.swift file to identify the remaining portion of the schema.
The above code checks if the schema is dvia, host is opeurl and the parameter as url, if these all three conditions are satisfied, it loads the created request into the webView instance by calling webView.load(myRequest), which instructs the web view to load the specified URL which makes our schema as:
dvia://openurl?url=
The intention of an application is to load ksec7.wpcomstaging.com, which it does when the application is opened, but let’s try to open the arbitrary URL on it through DeepLink schema.
Click on the above deeplink.
It is evident that an unrestricted URL has been loaded within the application. Similarly, this vulnerability can be exploited to execute phishing attacks as well.
Mitigation
Maintain a whitelist of trusted domains or URLs that the application is allowed to load through deep links. Validate incoming URLs against this whitelist to ensure they originate from trusted sources.
Scenario – 3: HTML Injection
HTML injection occurs when a web application neglects to adequately validate and sanitize user input before presenting it on a webpage. This security vulnerability enables an attacker to insert and execute unauthorized code within the affected website or application.
When the values of the parameters from a deep link are passed to a web view, there is a potential risk of HTML injection. This vulnerability enables an attacker to create specific payloads that can redirect users to malicious websites or applications.
To demonstrate this scenario, replace your AppDelegate.swift file with below code.
import UIKit
import WebKit
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, WKUIDelegate {
var window: UIWindow?
var webView: WKWebView!
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Initialize the window
window = UIWindow(frame: UIScreen.main.bounds)
let webConfiguration = WKWebViewConfiguration()
webView = WKWebView(frame: window!.bounds, configuration: webConfiguration)
webView.uiDelegate = self
// Load the initial URL
let initialURL = URL(string: "ksec7.wpcomstaging.com")
let initialRequest = URLRequest(url: initialURL!)
webView.load(initialRequest)
// Set the web view as the root view controller's view
let viewController = UIViewController()
viewController.view = webView
window?.rootViewController = viewController
window?.makeKeyAndVisible()
return true
}
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
if url.scheme == "dvia", url.host == "display" {
if let urlString = url.queryParameters?["message"]?.removingPercentEncoding {
let webViewController = UIViewController()
let webView = WKWebView(frame: webViewController.view.bounds)
// Create a dummy baseURL with a file path
let dummyBaseURL = URL(fileURLWithPath: NSTemporaryDirectory())
webView.loadHTMLString(urlString, baseURL: dummyBaseURL)
webViewController.view.addSubview(webView)
// Add a "Close" button to dismiss the web view
let closeButton = UIBarButtonItem(barButtonSystemItem: .close, target: self, action: #selector(closeWebView))
webViewController.navigationItem.rightBarButtonItem = closeButton
// Create a navigation controller to embed the web view controller
let navigationController = UINavigationController(rootViewController: webViewController)
// Present the navigation controller
self.window?.rootViewController?.present(navigationController, animated: true, completion: nil)
return true
}
}
return false
}
@objc func closeWebView() {
self.window?.rootViewController?.dismiss(animated: true, completion: nil)
}
}
extension URL {
var queryParameters: [String: String]? {
guard let components = URLComponents(url: self, resolvingAgainstBaseURL: true),
let queryItems = components.queryItems else {
return nil
}
var parameters = [String: String]()
for item in queryItems {
parameters[item.name] = item.value
}
return parameters
}
}
In the above code, the message parameter is retrieved from the deep link and assigned to the urlString variable. However, the code does not perform any validation or sanitization on this input. Consequently, if an attacker crafts a deep link with a malicious message parameter containing HTML or JavaScript code, it will be directly loaded and executed within the web view.
To take advantage of this vulnerability, an attacker can create a specifically crafted HTML payload that includes a hyperlink designed to redirect users to a malicious web application as below:
dvia://display?message=%3Ca%20href%3D%22http%3A%2F%2Fdamnvulnerableiosapp.com%2F%22%3Eopen%3C%2Fa%3E
Click on the deeplink above.
When victim clicks on the link, the malicious URL will be opened.
Mitigation
Before loading the HTML content into the web view, it is important to consider performing output encoding or sanitization on the “message” parameter. This step involves the filtering or escaping of HTML tags or special characters that may have the potential to execute malicious code.
Scenario – 4: CSRF Attacks
CSRF, or Cross-Site Request Forgery, is a security flaw that occurs when a malicious website tricks a user’s browser into unknowingly sending unauthorized requests to another website where the user is authenticated. This vulnerability is not limited to web applications alone; it can also be exploited through deep links in iOS or other platforms.
In iOS applications, CSRF attacks often occur when actions are performed through deep links. A similar case can be found in this hackerone report, where an attacker was able to manipulate users to follow them via CSRF by supplying the deep link schema.
Financial applications that incorporate deep link functionality for repeating payment transactions can serve as another potential target for this vulnerability. In this scenario, attackers exploit the deep link feature by supplying victims with deep link schemas, enabling them to execute unauthorized and arbitrary transactions.
To demonstrate this scenario, replace your AppDelegate.swift file with below code.
import UIKit
import WebKit
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, WKUIDelegate {
var window: UIWindow?
var webView: WKWebView!
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Initialize the window
window = UIWindow(frame: UIScreen.main.bounds)
let webConfiguration = WKWebViewConfiguration()
webView = WKWebView(frame: window!.bounds, configuration: webConfiguration)
webView.uiDelegate = self
// Load the initial URL
let initialURL = URL(string: "ksec7.wpcomstaging.com")
let initialRequest = URLRequest(url: initialURL!)
webView.load(initialRequest)
// Set the web view as the root view controller's view
let viewController = UIViewController()
viewController.view = webView
window?.rootViewController = viewController
window?.makeKeyAndVisible()
return true
}
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
if url.scheme == "dvia", url.host == "payment" {
if let urlString = url.queryParameters?["user"]?.removingPercentEncoding {
let message = "Payment has been sent to \(urlString)"
let alertController = UIAlertController(title: "Payment", message: message, preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
window?.rootViewController?.present(alertController, animated: true, completion: nil)
return true
}
}
return false
}
@objc func closeWebView() {
self.window?.rootViewController?.dismiss(animated: true, completion: nil)
}
}
extension URL {
var queryParameters: [String: String]? {
guard let components = URLComponents(url: self, resolvingAgainstBaseURL: true),
let queryItems = components.queryItems else {
return nil
}
var parameters = [String: String]()
for item in queryItems {
parameters[item.name] = item.value
}
return parameters
}
}
The code above checks if the URL’s scheme is dvia and the host is payment. It takes an parameter for a user and initiates the payment for that user. Attacker can craft the deeplink schemas as below when executed the payment would be performed for an attacker.
dvia://payment?user=attacker-8ksec
Supply the above deeplink schema to the victim.
When the attacker activates the schema by clicking on it, the execution will take place, resulting in the completion of a payment.
Mitigation
Deeplink schemas should not be implemented to perform the sensitive actions on the application.
When required, it is important to validate whether the deeplink schemas have been triggered from within the application or by external components.
To learn how to identify deeplink schemas on an iOS application, you can navigate to our perious blog known as iOS Deep Link Attacks Part 1 – Introduction
In conclusion, we have investigated different deeplink schemas and methods of identification and its exploitation.
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.