A Talk about Logic Vulnerabilities of Android Components - Android Security

本文关注Android平台上的逻辑漏洞,这些漏洞通常隐藏在大量业务代码中,稳定性好但难以发现。文章介绍了Activity、Service、Broadcast Receiver和ContentProvider四大组件的逻辑漏洞,包括隐式导出、权限问题、Intent重定向等,并探讨了Intent作为组件间通信桥梁的角色和潜在风险。
摘要由CSDN通过智能技术生成

https://tutorialboy24.blogspot.com/2022/11/a-talk-about-logic-vulnerabilities-of.htmlicon-default.png?t=M85Bhttps://tutorialboy24.blogspot.com/2022/11/a-talk-about-logic-vulnerabilities-of.html

 

Foreword

As society pays more and more attention to security, various defensive programming or vulnerability mitigation measures are gradually added to the operating system, such as code signature, pointer signature, address randomization, isolated table heap, etc. is often difficult under these mitigations. Therefore, attackers are gradually focusing more on logical loopholes. Logical loopholes usually have good stability and are not affected but at the same time, they are hidden deep and are difficult to find in a large number of business codes . Moreover, due to the variety of forms, it is not very versatile, and may not be a high-priority research direction from the perspective of the input-output ratio. Regardless, this is always an attack surface to watch. Therefore,This article introduces some common logic vulnerabilities targeting the Android platform.

The Major Components

Anyone who has been in contact with Android should have heard of the "major components". The first thing to learn when developing an application is the life cycle of each component. The so-called four major components refer to Activity, Service, Broadcast Receiver , and Content Provider respectively. For the implementation details of these components, please refer to the official document: Application Fundamentals.

In security research, the four major components deserve our special attention, because they are an important bridge for the application to communicate with the outside world, and even within the application, these components are used to build a loosely coupled relationship with each other. example, the application does not need to apply for camera permission, but the (system) camera application can open the camera and obtain the captured photos through mutual communication between components as if it was taking pictures by itself.

In the process of component interaction, the core data structure is Intent, which is the carrier of communication between most components.

Intent 101

According to the official statement, Intent is an "abstract description of an operation to be performed", which can also be called "intent" in literal translation, such as wanting to turn on the camera to take pictures, to open a browser to visit a website, to open the settings interface,... can be described by Intent.

There are two main forms of Intent, namely explicit Intent, and implicit Intent; the difference between the two is that the former explicitly specifies a Component, while the latter does not specify a Component, but it will use enough information to help the system understand the intent, such as Action, Category, etc.

The main function of Intent is to start Activity, so we take this scenario as an example to analyze the specific implementation of Intent from the source code. The general code snippet for starting an Activity is as follows:

Intent intent = new Intent(context, SomeActivity.class);
startActivity(intent);

An explicit Intent is used here, but that's not the point. It is generally called in an Activity, so the Activity.startActivitycode is called in frameworks/base/core/java/android/app/Activity.java, and there is no copying and pasting here. In short, the calling link is as follows:

  • Activity. startActivity()
  • Activity. startActivityForResult()
  • Instrumentation. execStartActivity()
  • ActivityTaskManager.getService().startActivity()
  • IActivityTaskManager. startActivity()

The last call is an interface, which is a very common pattern. The next step is to find its implementation. If there is no accident, this implementation should be in another process. In fact it is also system_serverin :

  • ActivityTaskManagerService. startActivity()
  • ActivityTaskManagerService.startActivityAsUser()
  • ActivityStarter. execute()

The last method prepares to start the Activity through the information passed in before, including caller, userId, flags, callingPackage and the most important intent information, as follows:

private int startActivityAsUser(...) {
    // ...
    return getActivityStartController()
            .obtainStarter(
                intent, "startActivityAsUser")
            .setCaller(caller)
            .setCallingPackage(callingPackage)
            .setCallingFeatureId(callingFeatureId)
            .setResolvedType(resolvedType)
            .setResultTo(resultTo)
            .setResultWho(resultWho)
            .setRequestCode(requestCode)
            .setStartFlags(startFlags)
            .setProfilerInfo(profilerInfo)
            .setActivityOptions(bOptions)
            .setUserId(userId)
            .execute();
}

The main logic of ActivityStarter. execute() is as follows:

int execute() {
    // ...
    if (mRequest.activityInfo == null) {
        mRequest.resolveActivity(mSupervisor);
    }
    res = resolveToHeavyWeightSwitcherIfNeeded();
    res = executeRequest(mRequest);

}

Among them, it resolveActivity is used to obtain the information of the Activity to be started. For example, in the case of implicit start, there may be multiple targets that meet the requirements, and a pop-up menu will ask the user which application to choose to open. executeRequestIn the middle, it mainly checks related permissions, and calls startActivityUncheckedto .

I have already introduced most of the processes in the Android12 application startup process analysis , and here more attention is paid to the role of Intent itself. From the above analysis, it can be seen as a message carrier in multi-process communication, and its source code definition can also be seen that Intent itself is a structure that can be serialized and passed between processes.

public class Intent implements Parcelable, Cloneable { ... }

IntentIt has many methods and attributes, which will not be expanded here for the time being, and will be analyzed later when specific vulnerabilities are introduced. The following article mainly starts with the four major components, and introduces some common vulnerability design traps and respecific .

Activity

Activity , also known as the active window, is a graphical interface that directly interacts with the user. One of the main development tasks of APP is to design each activity and plan the jumps and links between them. Usually an activity represents a full-screen active window, but it can also exist in other forms, such as floating windows, multi-windows, etc. As a UI window, it generally uses XML files for layout, and inherits the Activity class to implement its life cycle functions onCreate and onPauseother life cycle methods.

If the Activity defined by the developer wants to be Context.startActivity started by , it must be declared in the manifest file of the APP, namely AndroidManifest.xml. When the application is installed, it PackageManager will parse the relevant information in its manifest file and register it in the system, so that it can be searched when resolved.

In the adb shell, you can use am start-activity to open the specified Activity, and start it by specifying the Intent:

am start-activity [-D] [-N] [-W] [-P <FILE>] [--start-profiler <FILE>]
        [--sampling INTERVAL] [--streaming] [-R COUNT] [-S]
        [--track-allocation] [--user <USER_ID> | current] <INTENT>

As the carrier of the user interface, Activity carries many tasks such as user input/processing, external data reception/display, etc., so it is a main attack surface of the application. Several common attack scenarios are introduced below.

The Life Cycle

The classic life cycle diagram of Activity is as follows:

Usually developers only need to implement the onCreatemethod , but for some complex business scenarios, it is necessary to correctly understand its life cycle. Take an application that the author encountered in the internal test as an example. , such as turning on the camera streaming, or turning on the recording, onDestroybut the streaming/recording was only turned off in . This will cause these operations to still run in the background when the APP enters the background, and the attacker can construct a task stack so that the victim still executes the background functions of the target application when facing the phishing interface of the malicious application, thus forming a special phishing scenario.The correct approach should be to close sensitive operations in the onPausedcallback.

In fact, the attacker can precisely control the triggering timing of the target Activity lifecycle callback function by continuously sending different Intents. If no attention is paid during development, the state machine of the application function will be abnormal or even a security problem.

Implicit Exported

As mentioned earlier, if the Activity defined by the developer wants to use startActivity to start, it must <activity>be declared in AndroidManifest.xml. An example of a declaration is as follows:

<activity xmlns:android="http://schemas.android.com/apk/res/android" android:theme="@android:01030055" android:name="com.evilpan.RouterActivity">
  <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="demo" android:host="router"/>
  </intent-filter>
</activity>

There are many properties supported in activity 。 One of the important attributes is android:exported to indicate whether the current Activity can be started by other application components. This attribute has several characteristics.

  • The attribute can be defaulted, and the default value defaults to false.
  • If the Activity does not explicitly set this property and it is defined in the Activity <intent-filter>, then the default value is true.

That is to say, the developer may not explicitly specify the Activity export, but because it is specified intent-filter, it is actually exported, that is, the corresponding Activity can be invoked by other applications. This situation was very common in the early days. For example, APP designed a set of interfaces for changing passwords. You need to enter the old password first and then jump to the interface for entering the new password. If the latter is exported, the attacker can directly evoke the input of the new password. The password interface, thus bypassing the verification logic of the old password.

Google has been deeply aware of this problem, so it stipulates that after Android 12, if the application's Activity contains intent-filter, it must be explicitly specified android:exported as true or false, and the default is not allowed. In Android 12, an application that does not explicitly specify the exported attribute and has an intent-filter Activity will be directly rejected by the PackageManager during installation.

Fragment Injection

Activity, as the core component of the UI, also supports modular development, such as displaying several reusable sub-interfaces in the same interface. The Fragments component, or "fragment", was born with this design idea . FragmentActivityOne or more fragments can be combined in an Activity to facilitate code reuse. The life cycle of a fragment is affected by the host Activity.

The Fragment Injection vulnerability first broke out in 2013. Here we only introduce its principle. The original articles and papers are attached at the end of this section. The core of the vulnerability is the PreferenceActivityclass . Developers can inherit it to implement convenient settings. The onCreate function of this class has the following functions:

protected void onCreate() {
    // ...
    String initialFragment = getIntent().getStringExtra(EXTRA_SHOW_FRAGMENT);
    Bundle initialArguments = getIntent().getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS);
    // ...
    if (initialFragment != null) {
        switchToHeader(initialFragment, initialArguments);
    }
}

private void switchToHeaderInner(String fragmentName, Bundle args) {
    getFragmentManager().popBackStack(BACK_STACK_PREFS,
            FragmentManager.POP_BACK_STACK_INCLUSIVE);
    if (!isValidFragment(fragmentName)) {
        throw new IllegalArgumentException("Invalid fragment for this activity: "
                + fragmentName);
    }

    Fragment f = Fragment.instantiate(this, fragmentName, args);
}

It can be seen that a string and a Bundle parameter are obtained from the Intent, and finally passed switchToHeaderInnerinto to instantiate the specific one Fragment. The instantiation process is as follows:

public static Fragment instantiate(Context context, String fname, Bundle args) {
    // ...
    Class clazz = sClassMap.get(fname);
    if (clazz == null) {
            // Class not found in the cache, see if it's real, and try to add it
            clazz = context.getClassLoader().loadClass(fname);
            sClassMap.put(fname, clazz);
    }
    Fragment f = (Fragment)clazz.newInstance();
    if (args != null) {
            args.setClassLoader(f.getClass().getClassLoader());
            f.mArguments = args;
    }
    return f;
}

A classic reflection call, instantiates the incoming string as a Java class and sets its parameters. What is this, this is deserialization! And the actual vulnerability comes from here. Since the incoming parameter is controllable by the attacker, the attacker can set it as an internal class, thus touching the functions that the developer did not expect. In the original report, the author used

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值