In recent years, disputes over territorial issues between Ukraine and Russia never stops, including the disputes over Crimean Peninsula, Russia-Ukraine gas disputes, and the conflict in eastern Ukraine. As the crisis between the two countries upgrades, security incidents in cyberspace may be more intense than the reality. During the Christmas Holiday of 2015, the Ukrainian power grid was attacked by a Russia-based cybergang, causing 1.4 million residents in western Ukraine to suffer from an outage in the cold. The whole city suffered a great panic and heavy losses. On the contrary, there were much fewer disclosures on the APT attacks against Russia.
On November 25, 2018, an international incident occurred between Ukraine and Russia when the Ukrainian Navy vessels attempted to pass from the Black Sea into the Sea of Azov through the Kerch Strait while on their way to the port of Mariupol. This, again, drew the attention from all the globe.
On the evening of November 29, 2018, shortly after the break-out of the Kerch Strait Incident, 360 Advanced Threat Response Team was the first security team to discover the APT attack against the FSBI “Polyclinic No.2” affiliated to the Presidential Administration of Russia. The lure document used to initiate the attack was a carefully forged employee questionnaire, which exploited the latest Flash 0day vulnerability CVE-2018-15982 and a customized Trojan with self-destruction function. All the technical details indicate that the APT group is determined to compromise the target at any price, but at the same time, it is also very cautious.
Fig 1: Lure document
What’s noteworthy is that according to the introduction on the compromised website of the polyclinic (http://www.p2f.ru), the institution was established in 1965 and it was founded by the Presidential Administration of Russia. The multidisciplinary outpatient institution mainly serves the civil servants of the highest executive, legislative, judicial authorities of the Russian Federation, as well as famous figures of science and art.
Since it is the first detection of this APT attack by 360 Security on a global scale, we code-named it as “Operation Poison Needles”, considering that the target was a medical institution. Currently, the attribution of the attacker is still under investigation. However, the special background of the polyclinic and the sensitiveness of the group it served both indicate the attack is highly targeted. Simultaneously, the attack occurred at a very sensitive timing of the Kerch Strait Incident, so it also aroused the assumption on the political attribution of the attack.
Fig 2: The introduction of the Polyclinic (from its website)
The attacker launched the attack through a rar compressed package. Once the victim opens the bait document in the compressed package, the vulnerability will be triggered. The complete attack procedure is as follows:
Fig 3: Attack procedure with the vulnerability
When the lure document (a questionnaire) is clicked to open, the embedded Flash 0day vulnerability file will be played:
Fig 4: When the Flash 0day vulnerability tries to play on the victim’s computer(The pop-up window alert reads: The embedded content contained in this document may be harmful to your computer. Choose one of the following options: - Don’t allow the content to be played (recommended) - I know the content, allow it to play.)
After the vulnerability is triggered, the winrar decompressor will operate on the packaged file and execute the final PE payload backup.exe.
Fig 5: The executing process of the vulnerability
Our analysis verified that this 0day is a UAF vulnerability in the Flash’s file package com.adobe.tvsdk.mediacore.metadata. SetObject does not use DRCWB (Deferred Reference Counted, with Write Barrier) when saving a String (belonging to RCObject) object to the keySet member of the Metadata class object. The attacker took advantage of this to obtain a dangling pointer through brute GC (Garbage Collection). Based on this, multiple type obfuscations were performed through several UAFs. Then the arbitrary address reading and writing was realized by the interaction of two custom classes. Based on this, ASLR was bypassed by revealing the ByteArray's virtual table pointer. Finally, by means in the leaked codes from HackingTeam, the attackers bypassed DEP/CFG to execute the shellcode.
In the 0day exploit, the instantiation object address of Metadata in Flash is as follows:
(Red remarks: It can be seen from the Command that 07660fd0 is the instantiation object of Metadata.)
After recursive calling the Metadata's setObject method, the keySet member of the Metadata object is shown in the figure below:
Red remarks: 1. The keySet member structure of Metadata object 2. The actual data area of keySet is brim with strings
Part of the values of keySet member:
Below is the released memory of the keyset member after brute GC:
The memory is reused by a new class Class5, causing type obfuscation:(Note - left: 09171508 was originally a String object, but was occupied by Class5 object. Note – right: here, 09171508 is a Class5 object. Where the offset is 0x10, the value is 24 – this address is originally the string length of the Class5 object. )
Afterwards, the attacker determined whether the exploit was successful by checking whether the length of the String object is 24. (If the exploit is successful, it will cause type obfuscation. To obtain the length property of the String object is actually to obtain the value of the first member variable of Class5 which is 24).
Through decompiling, we found that the Native function corresponding to the setObject of the Metadata is shown in the figure below, and the actual function is in setObject_impl.
In Object_impl, the incoming key (String object) is directly saved to the keySet member of Metadata.
(Red remarks: arg0 is the incoming String object, and esi is a Metadata object. The successive 16 bytes of esi + 0x1C resembles the Buffer structure.)
The definition of Buffer structure is as below (the structure of one keySet member may vary from another).
(Red remarks: the keySet member of Metadata indicates the index for the next value added.)
The incoming keys (String object) were saved in the below code of add_keySet:
(Red remarks: 1. esi is the initial value of keySet member, the offset 4 is the initial address of the actual data area. Esi is the index value. 2. arg_4 is the incoming key (String object), here it is saved to keySet directly, rather than using DRCWB.)
At this time, the GC mechanism recognizes that the incoming key is not referenced, thus reclaiming the corresponding memory. However, the pointer to the reclaimed memory remains in the keySet member of the Metadata object. Subsequently, reuse of the reclaimed memory described in step 4 by new Class 5 causes the UAF.
The original exploit code first applied for 0x1000 String objects, and then immediately released half of them, resulting in a large number of memory holes of String object which were set ready for later attack.
Subsequently, the exploit defied a Metadata object and the key-value pair was saved to the object by means of setObject. The keySet member of the Metadata object holds a pointer to a memory area containing all the keys (stored as String). Right after that, GC was triggered brutally. As the keySet member does not use DRCWB, the keySet member holds a dangling pointer pointing to the above memory area. Then array arr_key is read and saved to keySet for later use.
After getting the dangling pointer, the exploit immediately requested 256 Class5 objects and saved to vec5 (vec5 is a vector object). Because the memory size of the Class5 object is the same as that of String objects (0x18 bytes in 32-bit) and the related objects are allocated in the same heap, there will be the possibility that Class5 objects will occupy the memory space of the previously released String objects, according to the MMgc memory allocation algorithm.
The Class5 object is defined as follows. It can be seen that the Class5 has two uint members, which are initialized to 0x18 and 2200 (0x898), respectively.
Then traverse the array key_arr to find the String object whose length becomes 0x18 (in memory, the length field of the String object coincides with the m_1 member of Class5). On this basis, the environment will be checked whether it is currently in 32-bit or 64-bit, thus entering different exploit.
As shown in the figure above, after locating the String index occupied by the Class5 object, the exploit cleared up the related attribute of arr_key, which causes the reference count of the elements in arr_key (including the occupied Class5 object) to decrease to 0. In MMgc, the object immediately enters ZCT (zero count table) after the reference count is reduced to zero. Then the exploit brutally start GC to reclaim the memory in the ZCT to enter the subsequent exploitation phases. Below we mainly analyzed the exploitation in the 32-bit environment.
In 32-bit environment, after releasing the placeholder Class5 object, the exploit immediately applied for 256 Class3 objects and saved them to another vector object vec3. This process reused the memory space of one (or several) Class5 object(s) that were previously released.
The Class3 object is defined as follows and resembles Class5, both occupy 0x18 bytes in memory.
You can see that Class3 has an m_ba member and an m_Class1 member. m_ba was initialized to a ByteArray object while m_Class1 was initialized to a Class1 object. The Class1 object is defined as follows:
After the Class3 occupying the memory, the exploit immediately traversed vec5 to find an original Class5 object that had been occupied by the Class3 object. Once found, the Class5 object’s index would be saved to this.index_1 and the m_Class1 member of the Class5 object (which had become a Class3 object) to this.ori_cls1_addr for subsequent recovery.
After two rounds of UAF, the exploit then used the index_1 saved in above step and modify the m_Class1 member of the reused Class3 object with vec5[index_1]. Then immediately traverse vec3 to find the modified Class3 object and save the index of the object in vec3 to this.index_2.
So far, the exploit has got two vectors (vec5 and vec3) that can manipulate the same object and the corresponding index of the object in their respective vec (index_1 and index_2). Next, the exploit will construct arbitrary address reading and writing primitives based on this.
Let's take a look at the implementation of arbitrary address reading and writing primitives in 32-bit. As you can see from the following code, we can implement arbitrary address reading and writing primitives by means of two obfuscated Class objects. The relevant code has been elaborated in the above figure and the following figure and will not be described here anymore. For an instruction of the offset by 0x10, please refer to our previous analysis of the vulnerability CVE-2018-5002.
Arbitrary address reading and writing primitive in 64-bit is similar to that in 32-bit, except that the class object that is obfuscated with Class5 is replaced by Class2 and Class4 in 64-bit. In addition, a Class0 is also constructed for address reading in 64-bit environment.
The following are definitions of these three classes.
The following are arbitrary address reading and writing primitives in 64-bit. The reading and writing primitives in 64-bit can only read and write 32 bits at a time, so a 64-bit address needs to be read and written twice.
The exploit code also constructs a series of performance functions with arbitrary address reading primitive, and uses these functions to finally read the VirtualProtect function address of kernel32.dll for bypassing DEP.
The exploit code finally adopted HackingTeam's method to bypass DEP/CFG. Since the related process has been published on the Internet, I will not go into details here anymore.
In original exploit code, the shellcode 32-bit and 64-bit are placed in Class6 and Class7 separately. The shellcode finally calls cmd to start the WinRAR process. The relevant command parameters are as follows:
Adobe has fixed the 0day vulnerability mentioned here in its officially release of Flash 18.104.22.168 on December 5th. Our dynamic analysis results show that the patch uses an Array object to store all the keys to eliminate reference issues instead of using a Buffer-like structure.
1．A Metadata instantiation object is shown in the figure below, and the address is 0x7409540.
- After the patch, you can see that the offset 0x1C of the Metadata object is no longer similar to the Buffer structure, but an Array object. The Array object stores the key values without the previous problem. (Same as what the red remarks on the screenshot describes.)
- The key value that set by the message loop calling setObjest is as follows.
- After Brute garbage collection, it is found that the pointer in the saved ketSet still points to a valid string, indicating that brute garbage collection does not reclaim the key-value object.
(Red remarks: the actual data area to save keySet whne the value is 0x77670a0)
The PE payload backup.exe disguised itself as an NVIDIA control panel application with detailed file descriptions and version numbers.
(The properties of the backup.exe)
The exe file is digitally signed using a certificate that has been revoked (valid from 2018/3/16 to 2019/3/17).
After the backup.exe is started, an NVIDIAControlPanel.exe will be released in the local user's program data directory. This file and the backup.exe file have the same file information and digital signature, but their file sizes are different.
After a series of analysis, we found that the PE payload is a backdoor program that is strongly protected by VMP encryption. Through decryption and restoration, we found that the main function of the master program is to create a window message loop and 8 function threads, as follows:
The thread functions:
|0||Anti-analysis||Check if the name of the program itself conforms to the HASH naming convention; if yes, set self-destroying flag.|
|1||Resume||Monitor user activity and send a 0x401 message (create a thread for registering scheduled task) if the user types on the keyboard or moves the mouse|
|2||Sleep||Enter the sleep mode randomly, taking the current system time for comparison, randomly send out message WM_COPYDATA (the main Message loop will sleep for some time after receiving this instruction).|
|3||Self-destroying Timer||Compare the time string in the program with the system time. If the current system time is later, set the flag bit and send message 0x464 to the main window (execute self-destruction); if the self-destroying flag is set, send the 0x464 directly.|
|4||Communication||Collect machine information and send it to C&C; execute shellcode, load PE in memory and download file execution code.|
|5||Register for auto-start||Check whether the current program path is the same as the previously saved path, and add a startup item to the registry.|
|6||Register for scheduled tasks||Check if there is AV software in the device, perform self-destruction if detected; copy itself to another directory and add a scheduled task disguised to be a NVIDIA control panel to start.|
|7||Self-destroying||Stop the scheduled task of disguising as a NVIDIA control panel, clean up related files, and perform self-destruction.|
|WM_CREATE||Create resume, sleep and timed self-destruct threads and create global Event objects|
|WM_DESTRO, WM_SIZE, WM_PAINT||Default Message Loop|
|WM_CLOSE||Close the window|
|WM_QUIT||Close the handle and call the thread to register auto-start|
|WM_ENDSESSION||Call the thread to register auto-start|
|WM_COPYDATA||Execute main thread, sleep|
|WM_USER（0x400）||Create communication thread|
|WM_USER + 1 （0x401）||Create the thread for registering scheduled tasks|
|WM_USER + 2 （0x402）||Call MessageBox|
|WM_USER + 3 （0x403）||Sleep for a certain time|
|WM_USER + 4 （0x404）||Default Message Loop|
|WM_USER + 5 （0x405）||Create the startup setting thread|
|WM_USER + 0x63 （0x463）||Notify the global Event, then wait for the previous Thread 3 to be finished (0.5s). Re-create Thread 3|
|WM_USER + 0x64 （0x464）||Create self-destruction thread|
0 Anti-analysis thread
Check if the name of the program itself conforms to the HASH naming convention; if yes, set self-destroying flag.
1 Resume thread
Monitor user activity and send a 0x401 message to the main thread if the user types on the keyboard or moves the mouse. Then resume the thread to register scheduled tasks.
2 Sleep thread
Take the current TickCount as a comparison. When the number of low word is less than 100, then dispatch WM_COPYDATA. The main message loop will sleep for a while when it receives the message.
3 Timed self-destroying thread
Decrypt the encrypted time string and compare it with the current system time. If the current system time is later, the self-destruction flag will be set and a 0x464 message is sent to the main window (execute self-destruction).
4 Communication thread
Get machine information, including CPU model, memory usage, hard disk usage, system version, system language, time zone, user name, SID, installer list, etc..
Send data to 22.214.171.124 via POST method:
Continue to send data to the server when the connection is successful.
When the conditions are met, call the RunPayload function (in fact, no case under the conditions has been captured)
5 Register auto-start thread
- First, get the NVIDIAControlPanel file path in the AppData\Local directory saved in Thread 6. Use the path or the short path to check whether it is the same as the current file module path.
- It attepmts to open the folder SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\StartupApproved\StartupFolder under the registry HKEY_CURRENT_USER.
- Look up for the key NVIDIAControlPanel in the current registry path. If it does not exist or is disabled, set the key value to the enabled key value 02,00,00,00,00,00,00,00,00,00. 00,00.
6 Register scheduled tasks thread
Check if the current process id is 4. If the answer is yes, then it pops up Aborting message window, quit the thread and clean up the environment.
In the same time, it will also dispatch “quite” message to the Windows Update.
Three ways to copy data
The malware uses three ways to copy data:
- If there is AV software and monitored by the malware, the malware will use the Bits_IBackgroundCopyManager method for self-copying.
- If there is no related AV detection process, iFileOperation will be used for self-copying.
- If at the end of the above data-copy process, there are still files that were not copied to the target directory, then performance data-copy through the BAT method.
Process file release in a batch
Create a process to run bat file and copy itself to release the file.
At the end, the malware will release a backdoor: F951362DDCC37337A70571D6EAE8F122
AV software bypass
The AV software it checks including F-Secure, Panda, ESET, Avira, Bitdefender, Norton, Kaspersky by searching for the name and specific driver file.
If any AV software is detected, the malware will destruct itself.
Add scheduled tasks
7 Self-destruction thread
Use ITask and ITaskService to stop the scheduled task NVIDIA ControlPanel after checking the system version.
Versions before Win7 should use interface ITask:
Win7 and versions after Win7 should use interface ITaskService:
Once finished, clean up all the files.