Linux.Rekoobe.1 - троян, ориентированный на Linux-устройства


Linux.Rekoobe.1 - троян, ориентированный на заражение устройств с архитектурой SPARC и Intel x86, x86-64, работающих под управлением ОС Linux.

Вредонос способен по команде злоумышленников скачивать с управляющего сервера и загружать на него различные файлы. Кроме того, он может взаимодействовать с командным интерпретатором Linux на инфицированном устройстве.

Напомним, первые версии Linux.Rekoobe.1 были ориентированы на заражение работающих под управлением Linux устройств с архитектурой SPARC. Позже троян был модифицирован, в результате чего стал совместим с платформой Intel. Известны образцы Linux.Rekoobe.1 как для 32-, так и для 64-разрядных Intel-совместимых версий ОС Linux.

Для своей работы Linux.Rekoobe.1 использует зашифрованный с использованием алгоритма XOR конфигурационный файл, прочитав содержимое которого троян с определенной периодичностью обращается к управляющему серверу для получения команд. Известны следующие возможные места расположения конфигурационного файла:

/usr/lib/liboop-trl.so.0.0.0
/usr/lib/libhistory.so.5.7
/usr/lib/libsagented.so.1
/usr/lib/libXcurl
/usr/lib/llib-llgrpc


Этот файл имеет следующую структуру:

SECRET value
MAGIC value
PROXYHOST value
PROXYPORT value
USERNAME value
PASSWORD value
ENDPOINT value
SERVER_PORT value
CONNECT_BACK_DELAY value


Вместо переменной "value" в нем хранится значение соответствующего параметра. Получив данные из конфигурационного файла троян с интервалом, указанным в значении параметра CONNECT_BACK_DELAY, обращается для получения команд к управляющему серверу, адрес которого задан параметром ENDPOINT. При этом если значение параметра PROXYHOST отлично от "none", тогда соединение устанавливается с использованием прокси-сервера, данные для авторизации на котором также извлекаются из конфигурационного файла.

Сеанс связи с управляющим сервером начинается с отправки значения параметра MAGIC из конфигурационного файла и приема отклика длиной в 40 байт. Далее полученные 40 байт делятся на два блока по 20 байт и используются для инициализации контекстов AES, по одному для принимаемых и отправляемых данных:

int __cdecl AES_Init(st_aes_ctx *aes_ctx, char *data, char *salt)
{
  ...
  if ( RecvPacket(fd, buffer, 40, 0) != 1 )
    goto err_occured;
  *(_DWORD *)dec_salt = *(_DWORD *)buffer;
  *(_DWORD *)&dec_salt[4] = *(_DWORD *)&buffer[4];
  *(_DWORD *)&dec_salt[8] = *(_DWORD *)&buffer[8];
  *(_DWORD *)&dec_salt[12] = *(_DWORD *)&buffer[12];
  *(_DWORD *)&dec_salt[16] = *(_DWORD *)&buffer[16];
  *(_DWORD *)enc_salt = *(_DWORD *)&buffer[20];
  *(_DWORD *)&enc_salt[4] = *(_DWORD *)&buffer[24];
  *(_DWORD *)&enc_salt[8] = *(_DWORD *)&buffer[28];
  *(_DWORD *)&enc_salt[12] = *(_DWORD *)&buffer[32];
  *(_DWORD *)&enc_salt[16] = *(_DWORD *)&buffer[36];
  AES_Init(&aes_ctx_encrypt, secret, enc_salt);
  AES_Init(&aes_ctx_decrypt, secret, dec_salt);
  ...
}

где функция AES_Init генерирует ключ шифрования на основе значения SHA1 от параметра secret и переданного блока enc_salt или dec_salt:

int __cdecl AES_Init(st_aes_ctx *aes_ctx, char *data, char *salt)
{
  ...
  sha1_init(&ctx);
  sha1_update(&ctx, data, strlen(data));
  sha1_update(&ctx, salt, 0x14u);
  sha1_final(&ctx, hash);
  AES_InitKey(aes_ctx, hash, 128);
  ...
}


Функция AES_Init для каждого контекста AES также формирует два специальных блока по 40 байт каждый. В дальнейшем они будут использоваться в качестве подписи. Для этого массив verify_1 заполняется 40 байтами со значением 0x36, а массив verify_2 заполняется 40 байтами со значением 0x5C. После этого первые 20 байт каждого массива шифруются при помощи алгоритма XOR с соответствующими 20 байтами ключа AES.

Все последующие передаваемые в обе стороны данные в рамках установленной сессии будут отправляться специально сформированными посылками.

Первая принимаемая посылка содержит 16 байт идентификатора. Их троян сверяет с хранящимся в его теле идентификатором. В случае, если они совпадают, троян отправляет их серверу в качестве подтверждения.

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

В процессе приема посылки от управляющего сервера троян получает 16 байт, которые расшифровываются в режиме AES-CBC-128. Первый WORD (MSB) получившегося буфера является размером следующего блока данных (параметр size).

Затем троян вычисляет размер посылки по формуле

  • "packetsize = size + 2 байта + выравнивание"

и принимает данные объемом packetsize + 4 байта в тот же буфер по смещению 0x10 байт от его начала. Последние 20 байт являются подписью.

С целью проверки подписи первый DWORD подписи модифицируется по принципу - первые три байта замещаются нулями, а в четвертый записывается порядковый номер посылки (троян ведет учет количества принятых и посланных посылок в соответствующих контекстах AES). После этого буфер, в который принимались данные и где был модифицирован DWORD, используется для генерации хэша SHA1 (буфер обозначен именем buffer):

...
sha1_init(&sha1_ctx);
sha1_update(&sha1_ctx, aes_ctx_decrypt.verify_1, 0x40u);
sha1_update(&sha1_ctx, buffer, size + 4);
sha1_final(&sha1_ctx, &hash);
sha1_init(&sha1_ctx);
sha1_update(&sha1_ctx, aes_ctx_decrypt.verify_2, 0x40u);
sha1_update(&sha1_ctx, &hash, 0x14u);
sha1_final(&sha1_ctx, &hash);
...


При этом хэшируется только принятая полезная нагрузка и значение DWORD, в котором сохранен порядковый номер пакета. 4 DWORD-а подписи в хэшируемый объем данных не попадают.

Первые 20 байт полученного хэша сравниваются с подписью посылки. В случае их совпадения, посылка расшифровывается, а если нет – считается недействительной.

Отправка посылки на управляющий сервер осуществляется аналогичным образом.

Интересно, но троян Linux.Rekoobe.1 способен выполнять всего лишь три команды:

  • Reverse Shell (cmd == 0x03)
  • Скачать файл (cmd == 0x02)
  • Загрузить файл на управляющий сервер (cmd == 0x01)