Привет^3
Сегодня покажу вам разбор несложной малвари под
Linux.Реверс-инжиниринг помогает разобраться в том, как работает ВПО (вредоносное программное обеспечение).
Cl0p. Часть 1. Cтатический анализ
При анализе малвары мне нравится извелкать интересные приемы, которые в ней используются. А далее воспроизводить их для дальнейшего детекта и забавы ради. Обычно проводится статический и динамический анализ для полного понимания алгоритма работы программы.
Первым делом
Обычно я смотрю файл через утилиту
Detect It Easy. Вот ее вывод:
Bash:
Info:
Size: 1028096
File type: ELF32
String: ELF(386)
Extension: elf
Operation system: Red Hat Linux(ABI: 2.6.9)
Architecture: 386
Mode: 32-bit
Type: EXEC
Endianness: LE
Выглядит так, что таблица символов не удалена, поэтому можно сразу найти
main. Закидываю бинарь в IDA Pro.main
В функции
main происходит создание процесса-демона с вызовом chroot() в корневую директорию /.Далее в бесконечном цикле вызывается функция
do_heartbeat().do_heartbeat()
Из функции можно извлечь все директории, с которыми малвара взимодействует.
/u01, /u02, /u03, /u04 — это часто используемые точки монтирования в Linux при работе с базами данных Oracle.find
Конечно же следом пойду смотреть фунцию
find.Алгоритм функции достаточно простой:
1. Открывает директорию и проходится по всем файлам в ней. Если это
. или .., то их пропускает;2. Вызвает функцию
CreatRadMe (да-да, это не опечатка). По названию понятно, что создается README файл;3. Далее происходит вызов
EncrFile, если не находит подстроку C_I_0P в пути до файла. Если же это директория, то рекурсивно вызывается find;CreatRadMe
Ничего особо интересного тут нет. Просто создается файл со следующим содержанием:
EncrFile
Думаю, что ключевая функция во всей малваре -
EncrFile.По сути для файла изменяются права с помощью
chmod() на rw-------. Эти же права используются далее для работы с файлами.Файл шифруется с помощью алгоритма
rc4. Ключ генерируется для каждого файла отдельно, и, что интересно, записывается в файл рядом с раширением .C_I_0P с помощью функции CreateKey. Это означает, что получится эти файлы расшифровать) При этом шифруется максимум 100'000'000 байт файла.Итог статического анализа
Я провел статический анализ не сложного шифровальщика-демона (такие бываю не часто, но об этом напишу позже). Так как ключ хранится рядом с зашифрованным файлом, то мы можем написать дешифровшик.
Cl0p. Часть 2. Динамический анализ
Все файлы для скачивания находятся внизу.
Думаю, что весело будет запустить малварь на виртуальной машине, чтобы увидеть его в работе. Так как таблица символов не была удалена, то все понятно по статическому анализу. Но хочу попробовать собрать нужные данные, чтобы написать для него декриптор)
[!] Обязательно сделайте снапшот перед запуском. Так же рекомендую выключить интернет, общие папки и отключить все USB-устройства.
Запуск
На виртуальной машине я создал файл с неким таинственным содержимым) Вам самим предстоит узнать что там) Этот файл называется
important_file.txt. После запуска малваря рядом с ним появились 2 файла: important_file.txt.C_I_0P и README_C_I_0P.TXT. Как и ожидалось))С помощью утилиты
hexdump посмотрю содержимое этих файлов:important_file.txt:
Bash:
hexdump -C important_file.txt
00000000 4a be 11 18 a9 c4 e9 2e b8 fe 88 cb 4e a0 df 2e |J...........N...|
00000010 e5 4f dd c9 71 91 01 9e 24 d9 f0 cf |.O..q...$...|
0000001c
important_file.txt.C_I_0P:
Bash:
hexdump -C important_file.txt.C_I_0P
00000000 00 ae 8b b7 4f d2 8f 7d f5 15 f5 fa a6 cc a5 76 |....O..}.......v|
00000010 d5 7d 41 cc 2d c1 be 2e de 9b c0 a9 98 d6 8b d4 |.}A.-...........|
00000020 3d e0 23 68 0c 44 71 71 35 81 e5 3b 54 35 e9 6d |=.#h.Dqq5..;T5.m|
00000030 f6 07 83 e3 54 3d 6f 22 3f ce 5f b0 36 fb e5 96 |....T=o"?._.6...|
00000040 8a 51 5a 40 e4 ef c5 d6 9c 1e 1f 9e 02 07 bd 8f |.QZ@............|
00000050 21 8b 2c 2a c9 c8 31 77 b5 90 38 3e e0 5d 85 92 |!.,*..1w..8>.]..|
00000060 59 fc 26 70 4b 97 05 fd 01 68 f3 3a 2b b1 29 79 |Y.&pK....h.:+.)y|
00000070 ec 96 da 9e 74 2d 00 00 00 00 00 00 00 00 00 00 |....t-..........|
00000080 00 ae 7e 03 00 80 81 00 00 01 00 00 00 00 00 00 |..~.............|
00000090 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
000000a0 00 1c 00 00 00 00 00 00 00 00 10 00 00 08 00 00 |................|
000000b0 00 00 00 00 00 d1 e8 4a 68 13 67 a3 17 d1 e8 4a |.......Jh.g....J|
000000c0 68 13 67 a3 17 fd e8 4a 68 64 1c 3c 2d ae 7e 03 |h.g....Jhd.<-.~.|
000000d0 00 00 00 00 00 01 00 00 00 00 40 80 40 1c 00 00 |..........@.@...|
000000e0 00 00 00 00 00 41 00 00 00 75 00 00 00 00 00 00 |.....A...u......|
000000f0 00 2f ff 7f 40 88 03 80 40 35 8a 04 08 18 ff 7f |./..@...@5......|
00000100
Ничего не понятно, но очень интересно. Файл зашифрован, поэтому он так и выглядит. Убиваю все признаки малвары через
kill, сохраняю эти файлы себе на основную тачку)P.S: Я пропатчил функцию
do_heartbeat(), чтобы она шифровала файлы только в одной директории. Безопасность прежде всего)Написание декриптора
Начну разбираться, как расшифровать мой файл. Ранее я уже понял, что для шифрования используется алгоритм
RC4. О нем я нашел следующую информацию:Это потоковый шифр с переменным размером ключа. Работает в режиме OFB. Для шифрования использует S-блок размером 8x8. Его элементы представляют собой
перестановку чисел от 0 до 255, а перестановка зависит от ключа переменной длины. В алгоритме используется два счетчика i и j, которые инициализируются нулями.
Так же в прошлый раз из кода я уже вытащил ключ:
Python:
master_key = 'Jfkdskfku2ir32y7432uroduw8y7318i9018urewfdsZ2Oaifwuieh~~cudsffdsd'
Второй ключ для файла я возьму из
important_file.txt.C_I_0P. Через wc посмотрю его длину:
Bash:
$ wc important_file.txt.C_I_0P
0 3 256 important_file.txt.C_I_0P
Можно увидеть, что всего в файле 256 символов. Но стоп, если вернуться к функции
EncrFile, то можно заметить, что малвара генерирует 117 символов ключа:Как часто шутят у меня в отделе — произошел киберобман) При первичном просмотре я не особо обратил внимание на функцию
CreateKey, но вот сейчас она пригодится)Действительно, пишет она 256 байт, из которых 117 — это ключ. Ключ хранится в стеке, в функцию передается его адрес. Значит в файл записывается часть стека. К интересному можно отнести
len, так как это длина зашифрованной части файла.Значит из файла
important_file.txt.C_I_0P по смещению 0x75 + 0x58 + 0x8 + 0x4 + 0x4 можно вытащить len. Так же можно заметить, что перед вызовом функции CreateKey ключ к файлу шифруется с помощью ssKeyKey:
C:
_rc4Full(szKeyKey, v3 - 1, rc4_key, 117u);
Createkey(pathname, rc4_key);
Обобщив всю информацию, можно составить следующий алгоритм для дешифрования:
1. Взять имя зашифрованного файла;
2. Прочитать файл с названием, состоящим из имени этого файла и расширения
.C_I_0P;3. Расшифровать первые 117 байт с помощью алгоритма
RC4, чтобы получить ключ для дешифровки файла;4. По смещению
0x75 + 0x58 + 0x8 + 0x4 + 0x4 получить длину зашифрованной части файла len;5. Расшифровать
len байт зашифрованного файла, добавить оставшиеся байты и сохранить на диск;Напишу код на
Python:
Python:
from arc4 import ARC4
import sys
import struct
def get_file_key(file: str) -> bytes:
key_file_name = file + '.C_I_0P'
print(f'[*] Read encrypted file key from {key_file_name}')
with open(key_file_name, 'rb') as f:
key = f.readline()
return key
def main(file: str) -> None:
master_key = (b'Jfkdskfku2ir32y7432uroduw8y7318i90'
b'18urewfdsZ2Oaifwuieh~~cudsffdsd')
gen_len = 0x75
file_key = get_file_key(file)
print('[*] Extract encrypetd file len')
# 0x58 + 0x75 + 0x8 + 0x4 + 0x4
file_len_offset = 0x58 + 0x75 + 0x8 + 0x4 + 0x4
try:
encr_file_size = struct.unpack(
'Q',
file_key[file_len_offset: file_len_offset + 0x8]
)[0]
except struct.error:
print('[-] Wrong encrypted file len')
print('[*] Decrypt file key')
cryptor = ARC4(master_key)
file_rc4_key = cryptor.decrypt(file_key)[:gen_len]
with open(file, 'rb') as f:
crypted_file = f.read()
cryptor = ARC4(file_rc4_key)
decrypted_file = cryptor.decrypt(crypted_file[:encr_file_size])
decrypted_file += crypted_file[encr_file_size:]
decrypted_file_path = file + '.decrypted'
print(f'[*] Save decrypted file to {decrypted_file_path}')
with open(file + '.decrypted', 'wb') as f:
f.write(decrypted_file)
print('[*] Done')
if __name__ == '__main__':
if len(sys.argv) != 2:
print(f'Usage {sys.argv[0]} <encrypted file>')
else:
main(sys.argv[1])
Протестировал на своем файле. Предлагаю и вам)
Сделано для @coffee_cube
Файлы для скачивания
Malware Link:
Ссылка скрыта от гостей
important_file.txt: Скачать
important_file.txt.C_I_0P: Скачать