SHA1
- a11bda0acdb98972b3dec706d35f7fba59587f99 (SPARC)
- 04f691e12af2818015a8ef68c6e80472ae404fec (SPARC)
- 466d045c3db7c48b78c6bb95873b817161a96370 (SPARC)
- cd274e6b73042856e9eec98d258a96cfbe637f6f (Intel x86)
- 8e93cfbaaf7538f8965080d192df712988ccfc54 (Intel x86-64)
Троянец, ориентированный на заражение устройств с архитектурой SPARC и Intel x86, x86-64, работающих под управлением ОС Linux. Конфигурационные данные троянец хранит в одном файле, зашифрованном с использованием алгоритма 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 байт полученного хэша сравниваются с подписью посылки, и если они совпадают, посылка расшифровывается, а если нет – считается недействительной.
Отправка посылки на управляющий сервер осуществляется аналогичным образом.
Троянец способен выполнять три команды:
- Reverse Shell (cmd == 0x03)
- Скачать файл (cmd == 0x02)
- Загрузить файл на управляющий сервер (cmd == 0x01)