Executive summary

Kaspersky Exploit Prevention is a component part of Kaspersky products that has successfully detected a number of zero-day attacks in the past. Recently, it caught a new unknown exploit for Google’s Chrome browser. We promptly reported this to the Google Chrome security team. After reviewing of the PoC we provided, Google confirmed there was a zero-day vulnerability and assigned it CVE-2019-13720. Google has released Chrome version 78.0.3904.87 for Windows, Mac, and Linux and we recommend all Chrome users to update to this latest version as soon as possible! You can read Google’s bulletin by clicking here.

Kaspersky endpoint products detect the exploit with the help of the exploit prevention component. The verdict for this attack is Exploit.Win32.Generic.

We are calling these attacks Operation WizardOpium. So far, we have been unable to establish a definitive link with any known threat actors. There are certain very weak code similarities with Lazarus attacks, although these could very well be a false flag. The profile of the targeted website is more in line with earlier DarkHotel attacks that have recently deployed similar false flag attacks.

More details about CVE-2019-13720 and recent DarkHotel false flag attacks are available to customers of Kaspersky Intelligence Reporting. For more information, contact: intelreports@kaspersky.com.

Technical details

The attack leverages a waterhole-style injection on a Korean-language news portal. A malicious JavaScript code was inserted in the main page, which in turn, loads a profiling script from a remote site.

The main index page hosted a small JavaScript tag that loaded a remote script from hxxp://code.jquery.cdn.behindcorona[.]com/.

The script then loads another script named .charlie.XXXXXXXX.js. This JavaScript checks if the victim’s system can be infected by performing a comparison with the browser’s user agent, which should run on a 64-bit version of Windows and not be a WOW64 process; it also tries to get the browser’s name and version. The vulnerability tries to exploit the bug in Google Chrome browser and the script checks if the version is greater or equal to 65 (current Chrome version is 78):

If the browser version checks out, the script starts performing a number of AJAX requests to the attacker’s controlled server (behindcorona[.]com) where a path name points to the argument that is passed to the script (xxxxxxx.php). The first request is necessary to obtain some important information for further use. This information includes several hex-encoded strings that tell the script how many chunks of the actual exploit code should be downloaded from the server, as well as a URL to the image file that embeds a key for the final payload and RC4 key to decrypt these chunks of the exploit’s code.

After downloading all the chunks, the RC4 script decrypts and concatenates all the parts together, which gives the attacker a new JavaScript code containing the full browser exploit. To decrypt the parts, the previously retrieved RC4 key is used.

The browser exploit script is obfuscated; after de-obfuscation we observed a few peculiar things:

Another check is made against the user agent’s string – this time it checks that the browser version is 76 or 77. It could mean that the exploit authors have only worked on these versions (a previous exploitation stage checked for version number 65 or newer) or that other exploits have been used in the past for older Chrome versions. There are a few functions that operate on the browser’s built-in BigInt class, which is useful for doing 64-bit arithmetic inside JavaScript code, for example, to work with native pointers in a 64-bit environment. Usually, exploit developers implements their own functions for doing this by working with 32-bit numbers. However, in this case, BigInt is used, which should be faster because it’s implemented natively in the browser’s code. The exploit developers don’t use all 64 bits here, but instead operate on a smaller range of numbers. This is why they implement a few functions to work with higher/lower parts of the number. There are many functions and variables that are not used in the actual code. This usually means that they were used for debugging code and were then left behind when the code was moved to production. The majority of the code uses several classes related to a certain vulnerable component of the browser. As this bug has still not been fixed, we are not including details about the specific vulnerable component here. There are a few big arrays with numbers that represent a shellcode block and an embedded PE image.

The analysis we have provided here is deliberately brief due to vulnerability disclosure principles. The exploit used a race condition bug between two threads due to missing proper synchronization between them. It gives an attacker an a Use-After-Free (UaF) condition that is very dangerous because it can lead to code execution scenarios, which is exactly what happens in our case.

The exploit first tries to trigger UaF to perform an information leak about important 64-bit addresses (as a pointer). This results in a few things: 1) if an address is leaked successfully, it means the exploit is working correctly; 2) a leaked address is used to know where the heap/stack is located and that defeats the address space layout randomization (ASLR) technique; 3) a few other useful pointers for further exploitation could be located by searching near this address.

After that it tries to create a bunch of large objects using a recursive function. This is done to make some deterministic heap layout, which is important for a successful exploitation. At the same time, it attempts to utilize a heap spraying technique that aims to reuse the same pointer that was freed earlier in the UaF part. This trick could be used to cause confusion and give the attacker the ability to operate on two different objects (from a JavaScript code perspective), though in reality they are located in the same memory region.

The exploit attempts to perform numerous operations to allocate/free memory along with other techniques that eventually give the attackers an arbitrary read/write primitive. This is used to craft a special object that can be used with WebAssembly and FileReader together to perform code execution for the embedded shellcode payload.

Payload description

The final payload is downloaded as an encrypted binary (worst.jpg) that is decrypted by the shellcode.

After decryption, the malware module is dropped as updata.exe to disk and executed. For persistence the malware installs tasks in Windows Task Scheduler.

The payload ‘installer’ is a RAR SFX archive, with the following information:

File size: 293,403

MD5: 8f3cd9299b2f241daf1f5057ba0b9054

SHA256: 35373d07c2e408838812ff210aa28d90e97e38f2d0132a86085b0d54256cc1cd

The archive contains two files:

File name: iohelper.exe

MD5: 27e941683d09a7405a9e806cc7d156c9

SHA256: 8fb2558765cf648305493e1dfea7a2b26f4fc8f44ff72c95e9165a904a9a6a48

File name: msdisp64.exe

MD5: f614909fbd57ece81d00b01958338ec2

SHA256: cafe8f704095b1f5e0a885f75b1b41a7395a1c62fd893ef44348f9702b3a0deb

Both files were compiled at the same time, which if we are to believe the timestamp, was “Tue Oct 8 01:49:31 2019”.

The main module (msdisp64.exe) tries to download the next stage from a hardcoded C2 server set. The next stages are located on the C2 server in folders with the victim computer names, so the threat actors have information about which machines were infected and place the next stage modules in specific folders on the C2 server.

More details about this attack are available to customers of Kaspersky Intelligence Reporting. For more information, contact: intelreports@kaspersky.com.

IoCs