Na segunda parte desta série sobre ataques de deep link no iOS, vamos explorar como identificar diferentes vulnerabilidades em deep links do iOS e a demonstração técnica para explorá-las.
Caso ainda não tenha lido a Parte 1, você pode vê-la aqui.
Agenda
Neste blog, abordaremos as vulnerabilidades de deep link conforme mostrado abaixo.
- Fishing com Deep Link
- Validação insuficiente de URLs
- Injeção de HTML via deep link
- CSRF via DeepLink
Cenário - 1: Fishing com Deep Link
O fishing com deep link é um problema frequentemente negligenciado quando se trata de ataques/testes de deep links no iOS. O phishing geralmente acontece quando alguém cria um site ou aplicativo falso que parece exatamente como um confiável, tentando enganar as pessoas para fornecer informações pessoais importantes.
O uso de URL schemas para deep linking pode permitir que dois diferentes apps registrem e usem a mesma URL. Por exemplo, alguém poderia registrar o mesmo URL schema usado pelo Twitter em seu próprio app. Assim, quando um deep link destinado ao Twitter é acionado, o aplicativo do Twitter tem a opção de abrir junto com o aplicativo do Twitter, se este não estiver instalado no dispositivo.
Imagine que existe um aplicativo iOS chamado 8ksec que permite aos usuários fazer login. Este app tem um esquema de deep link personalizado, eightksec://login, que leva os usuários diretamente ao painel de login.
O atacante pode criar um painel de login semelhante e usar o mesmo esquema de URL, eightksec://login, conforme mostrado abaixo.

O usuário assume que ao clicar no esquema de deep link, abrirá o aplicativo legítimo.

Se o aplicativo genuíno não estiver instalado, o aplicativo malicioso abrirá. Se o aplicativo genuíno estiver instalado, o usuário será apresentado com duas opções para escolher entre o aplicativo genuíno ou o aplicativo malicioso.

Mitigação
A adoção priorizada de links universais em vez de esquemas de URL personalizados pode ser a maneira ótima para prevenir tais ataques.
Cenário - 2: Validade insuficiente de URLs
No que diz respeito ao Deep Linking, uma validação insuficiente de URL geralmente se refere à situação em que um deep link é utilizado para carregar uma URL web dentro de uma visualização web, mas a verificação adequada dessa URL é omitida. Muitos aplicativos implementam esquemas de URL personalizados para integrar visualizações web em suas funcionalidades, mas frequentemente negligenciam a realização da validação de URLs. Esta falha pode permitir que esses esquemas sejam explorados, fazendo com que o aplicativo carregue URLs arbitrárias e potencialmente inseguras.
Nós observamos esse problema ocorrendo frequentemente em aplicações iOS baseadas em React. Nesses casos, uma aplicação web é inicialmente desenvolvida e posteriormente integrada ao app móvel usando vários SDKs. Deep links são então utilizados para carregar essas aplicações web dentro do app móvel.
Além disso, outra possível aplicação de carregamento de URLs através de deep links é no contexto de exibir Termos e Condições, Ajuda e Suporte em um app móvel. Isso geralmente é vantajoso, pois essas seções são tipicamente já definidas e disponíveis na aplicação web.
Exemplo:
dvia://navigate/help?url=
dvia://navigate/termsandcondition?url=
Fizemos algumas alterações no arquivo AppDelegate.swift da DVIA-v2 para experimentar este cenário, se você planeja seguir junto, substitua seu arquivo AppDelegate.swift pelo código abaixo e execute o aplicativo novamente.
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: "8ksec.io")
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
}
}O código acima contém um problema potencial com validação fraca do host. Isso significa que o código não realiza uma verificação ou validação rigorosa da parte do host da URL de link profundo. O método open url verifica se o esquema é dvia e o host é openurl para determinar se deve lidar com o link profundo. No entanto, ele não realiza nenhuma verificação adicional no valor do host ou confirma se corresponde a um valor de confiança ou esperado.
Para explorá-lo, precisamos primeiro identificar o esquema de Deep Link para isso. Podemos visualizá-lo no arquivo Info.plist. Para identificá-lo, consulte nosso blog anterior. Uma vez que identificamos o esquema de deep link como dvia://, precisamos navegar até o arquivo AppDelegate.swift para identificar a porção restante do esquema.
O código acima verifica se o esquema é dvia, o host é opeurl e o parâmetro é url. Se todas essas três condições forem satisfeitas, ele carrega a solicitação criada no instância webView chamando webView.load(myRequest), o que instrui a web view a carregar a URL especificada. Isso faz com que nosso esquema seja:
dvia://openurl?url=
A intenção do aplicativo é carregar 8ksec.io, o que ele faz quando o aplicativo é aberto, mas vamos tentar abrir uma URL arbitrária nele através de DeepLink schema.

Clique no deep link acima.

É evidente que uma URL não restrita foi carregada dentro do aplicativo. Da mesma forma, essa vulnerabilidade pode ser explorada para executar ataques de phishing.
Mitigação
- Mantenha uma lista branca de domínios ou URLs confiáveis que o aplicativo é permitido carregar através de deep links. Valide as URLs recebidas contra essa lista para garantir que elas sejam provenientes de fontes confiáveis.
Scenario - 3: Injeção de HTML
A injeção de HTML ocorre quando uma aplicação web negligencia a validação e sanitização adequadas da entrada do usuário antes de apresentá-la em uma página. Essa vulnerabilidade de segurança permite que um atacante insira e execute código não autorizado dentro do site ou aplicativo afetado.
Quando os valores dos parâmetros de um deep link são passados para uma web view, há o risco potencial de injeção de HTML. Essa vulnerabilidade permite que um atacante crie payloads específicos que podem redirecionar usuários para sites ou aplicativos maliciosos.
Para demonstrar essa situação, substitua seu arquivo AppDelegate.swift pelo código abaixo.
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: "8ksec.io")
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)
// Adicione um botão "Fechar" para fechar a visualização da web
let closeButton = UIBarButtonItem(barButtonSystemItem: .close, target: self, action: #selector(fecharWebView))
webViewController.navigationItem.rightBarButtonItem = closeButton
// Crie um controlador de navegação para embutir o controlador da visualização da web
let navigationController = UINavigationController(rootViewController: webViewController)
// Apresente o controlador de navegação
self.window?.rootViewController?.present(navigationController, animated: true, completion: nil)
return true
}
}
return false
}
@objc func fecharWebView() {
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
}
}No código acima, o parâmetro message é recuperado do deep link e atribuído à variável urlString. No entanto, o código não realiza nenhuma validação ou sanitização nesta entrada. Consequentemente, se um atacante criar um deep link com um parâmetro message malicioso contendo HTML ou código JavaScript, ele será carregado e executado diretamente no web view.
Para explorar essa vulnerabilidade, um atacante pode criar uma carga útil HTML específica que inclui um hyperlink projetado para redirecionar usuários para um aplicativo web malicioso conforme abaixo:
dvia://display?message=%3Ca%20href%3D%22http%3A%2F%2Fdamnvulnerableiosapp.com%2F%22%3Eopen%3C%2Fa%3E

Clique no deep link acima.

Quando a vítima clica no link, o URL malicioso será aberto.

Cenário - 4: Ataques CSRF (Cross-Site Request Forgery)
CSRF, ou Cross-Site Request Forgery, é uma falha de segurança que ocorre quando um site malicioso engana o navegador do usuário para enviar solicitações não autorizadas a outro site onde o usuário está autenticado. Esta vulnerabilidade não se limita apenas às aplicações web; pode ser explorada através de deep links em iOS ou outras plataformas.
Em aplicativos iOS, ataques CSRF geralmente ocorrem quando ações são realizadas por meio de deep links. Um caso semelhante pode ser encontrado neste relatório do HackerOne, onde um atacante foi capaz de manipular usuários para segui-los via CSRF fornecendo o esquema de deep link.
Aplicações financeiras que incorporam funcionalidade de deep links para transações de pagamento repetidas podem ser outro alvo potencial para esta vulnerabilidade. Neste cenário, os atacantes exploram a funcionalidade do deep link fornecendo esquemas de deep link aos usuários vítimas, permitindo-lhes executar transações não autorizadas e arbitrariamente.
Para demonstrar este cenário, substitua seu arquivo AppDelegate.swift pelo código abaixo.
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 {
// Inicialize a janela
window = UIWindow(frame: UIScreen.main.bounds)
let webConfiguration = WKWebViewConfiguration()
webView = WKWebView(frame: window!.bounds, configuration: webConfiguration)
webView.uiDelegate = self
// Carregue a URL inicial
let initialURL = URL(string: "8ksec.io")
let initialRequest = URLRequest(url: initialURL!)
webView.load(initialRequest)
// Defina a web view como a vista raiz do controlador de exibição
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 = "Pagamento enviado para (urlString)"
let alertController = UIAlertController(title: "Pagamento", 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
}
}
O código acima verifica se o esquema da URL é dvia** e o host é payment. Ele recebe um parâmetro para um usuário e inicia o pagamento para esse usuário. O atacante pode criar os esquemas de deep link conforme abaixo, quando executado, o pagamento será realizado para o atacante.
dvia://payment?user=atacante-8ksec
Fornecer o esquema de deep link acima ao vítima.

Quando o atacante ativa o esquema clicando nele, a execução ocorrerá, resultando na conclusão de um pagamento.

Mitigação
- Os esquemas de deep link não devem ser implementados para realizar ações sensíveis no aplicativo.
- Caso necessário, é importante validar se os esquemas de deep link foram acionados dentro do aplicativo ou por componentes externos.
Para aprender como identificar esquemas de deep link em um aplicativo iOS, você pode navegar para nosso blog anterior conhecido como iOS Deep Link Attacks Part 1 – Introdução
Em conclusão, investigamos diferentes esquemas de deep link e métodos de identificação e sua exploração.

