9 minute read

HelloKitty ransomware represents a sophisticated strain of malicious software strategically designed for targeted attacks, demonstrating an evolved and nuanced approach within the realm of cybersecurity threats. First identified in November 2020, this ransomware variant distinguishes itself through its utilization of robust encryption algorithms, rendering victim files inaccessible and underscoring the formidable technical proficiency of its operators.

hellokitty

NTRUEncrypt

The NTRUEncrypt SDK for C/C++ implements the NTRUEncrypt algorithm, a replacement for legacy public-key encryption algorithms such as RSA. NTRUEncrypt gives incredible performance gains at no loss in security.

Installation

First of all, the interesting file is INSTALL file with instructions:

hellokitty

So, you can build libntruencrypt with simple commands and bash scripts.

Another option is in the vs2012 folder:

hellokitty

Where you can find Microsoft Visual Studio Solution File Ntrubuild.sln and project files.

include

The NTRUEncrypt SDK offers a wide range of “parameter sets” to cater to different security requirements. The parameter sets can be specified using the NTRU_ENCRYPT_PARAM_SET_ID found in the header file crypto_ntru.h. The parameter set selected is specified during key generation. When the private and public keys are created, the parameter set is encoded into the key blob:

hellokitty

The parameter set you choose depends on several factors:

  • What level of security is required?
  • Do you need to ensure compatibility with ASC Standard X9.98-2010? For instance, is this a requirement for an RFP you are responding to?
  • What is the primary focus: reducing operation time, conserving bandwidth, or a combination of both?

It’s important to note that this SDK does not offer a parameter set at the widely used RSA-1024 security level, which is comparable to 80-bit symmetric ciphers or 163-bit Elliptic Curve cryptography. This is because RSA-1024 is no longer recommended by NIST, Microsoft, and the CA/Browser Forum, and certificates at that security level will no longer be issued after December 31, 2010. You can find more information about this on the following link: https://learn.microsoft.com/en-us/previous-versions//cc751157(v=technet.10).

src

So the main and most interesting folder is src:

hellokitty

As you can see, here the encryption logic.

The most important implementations here are Deterministic Random Byte Generator (DRBG) and Entropy source (Seeding the DRBG).

What is DRBG?

Prior to using NTRUEncrypt for key-generation or encryption, it is necessary to create an instance of a deterministic random byte generator (DRBG). The DRBG in this release follows the ANS X9.82 Part 3-2007 standard and utilizes HMAC_DRBG. The DRBG instantiation function provides a handle that can be passed to the key-generation and encryption functions.

When setting up a DRBG, you need to specify a security level. This should be at least as high as the security level of the NTRU parameter set that will be employed. Refer to the following table for the security levels linked to the different NTRUEncrypt parameter sets. You can have a maximum of four DRBGs running concurrently, with each one operating at a different security level: 112-bits, 128-bits, 192-bits, and 256-bits. Once set up, a single DRBG can be utilized for all tasks at a specific security level:

Security Level Best bandwidth and speed, no X9.98 compatibility Need X9.98 compatibility (best speed / best bandwidth / balance speed and bandwidth)
112-bit symmetric / RSA-2048 / ECC-224 NTRU_EES401EP2 NTRU_EES659EP1 / NTRU_EES401EP1 / NTRU_EES541EP1
128-bit symmetric / RSA-4096 / ECC-256 NTRU_EES439EP1 NTRU_EES761EP1 / NTRU_EES449EP1 / NTRU_EES613EP1
192-bit symmetric / RSA-7680 / ECC-384 NTRU_EES593EP1 NTRU_EES1087EP1 / NTRU_EES677EP1 / NTRU_EES887EP1
256-bit symmetric / RSA-15360 / ECC-512 NTRU_EES743EP1 NTRU_EES1499EP1 / NTRU_EES1087EP2 / NTRU_EES1171EP1

Table 2 gives the bandwidth and performance at each security level for NTRUEncrypt keys for the appropriate parameter sets, and for Elliptic Curve Cryptography (ECC) and RSA:

Name Strength Sizes (CText/Pub/Priv) Enc / Dec Time Pat. Until
EES401EP1 112 bits 552 / 556 / 264 2.5 / 2.7 8/19/2017
EES541EP1 112 bits 744 / 748 / 132 1.5 / 1.9 8/19/2017
EES659EP1 112 bits 907 / 911 / 104 1.5 / 2.0 8/19/2017
EES401EP2 112 bits 552 / 556 / 67 1.0 / 1.2 8/24/2021
NTRU_DEFAULT_PARAMS_112_BITS 112 bits Synonym for EES401EP2 or EES401EP1    
EES449EP1 128 bits 618 / 622 / 311 2.7 / 3.4 8/19/2017
EES613EP1 128 bits 843 / 847 / 147 1.6 / 2.2 8/19/2017
EES761EP1 128 bits 1047 / 1051 / 114 1.6 / 2.2 8/19/2017
EES439EP1 128 bits 604 / 608 / 68 1.1 / 1.4 8/24/2021
EES443EP1 128 bits 610 / 614 / 68 1.1 / 1.3 8/24/2021
NTRU_DEFAULT_PARAMS_128_BITS 128 bits Synonym for EES443EP1 or EES449EP1    
EES677EP1 192 bits 931 / 935 / 402 4.4 / 5.5 8/19/2017
EES887EP1 192 bits 1220 / 1224 / 212 2.8 / 3.9 8/19/2017
EES1087EP1 192 bits 1495 / 1499 / 183 3.0 / 4.0 8/19/2017
EES593EP1 192 bits 816 / 820 / 87 1.7 / 2.1 8/24/2021
EES587EP1 192 bits 808 / 812 / 87 1.9 / 2.3 8/24/2021
NTRU_DEFAULT_PARAMS_192_BITS 192 bits Synonym for EES587EP1 or EES677EP1    
EES1087EP2 256 bits 1495 / 1499 / 339 4.5 / 6.1 8/19/2017
EES1171EP1 256 bits 1611 / 1615 / 301 4.3 / 6.0 8/19/2017
EES1499EP1 256 bits 2062 / 2066 / 227 4.3 / 6.0 8/19/2017
EES743EP1 256 bits 1022 / 1026 / 111 2.2 / 2.9 8/24/2021
NTRU_DEFAULT_PARAMS_256_BITS 256 bits Synonym for EES743EP1 or EES1087EP2    

Running times were obtained on a single core of a 2 GHz Intel Duo T7250 processor running Linux.

What about seeding DRBG?

Seeding is a crucial step for any DRBG, as the security of the DRBG relies heavily on the randomness of the seed. The X9.82 DRBG included in this release acquires the seed through an entropy function. The caller is responsible for providing the entropy function and passing a pointer to it to the DRBG instantiation function. The code provided in this release includes a function that calculates sample entropy.

The entropy function prototype is in ntru_crypto_drbg.h:

hellokitty

As we can see, the source code has a lot of comments and is well documented.

It has two arguments: a one-byte command, and an address for one byte of data to be passed back. The function returns one byte of status, with 1 indicating success and 0 indicating failure. The three commands that must be implemented are INIT, GET_NUM_BYTES_PER_BYTE_OF_ENTROPY, and GET_BYTE_OF_ENTROPY:

hellokitty

The INIT command is used to perform whatever initialization the entropy function requires.

The GET_NUM_BYTES_PER_BYTE_OF_ENTROPY command returns the required number of bytes to obtain 8 bits of entropy. This value should range from 1 to 8 bytes. This value relies on the unpredictability of the entropy source. It is important to consider that the level of entropy needed to set up a DRBG is 1.5 times the security level of the DRBG. As an illustration, creating a DRBG with 112-bit security necessitates 21 bytes of entropy.

The GET_BYTE_OF_ENTROPY command returns a byte with a certain amount of entropy, ranging from 1 to 8 bits. The specific amount of entropy is indicated in the response to the GET_NUM_BYTES_PER_BYTE_OF_ENTROPY command.

The caller-supplied entropy function may be a true random source, an already properly-seeded pseudo-random number generator, or a seed that has been obtained from a random source.

The remaining files are source codes of different logics, among them I would like to note the following:

  • File ntru_crypto_hash.c - Routines implementing the hash object abstraction.
  • File ntrue_crypto_hmac.c - Routines implementing the HMAC hash calculation.
  • File ntru_crypto_msbyte_uint32.c - Routines to convert between an array of bytes in network byte order (most-significant byte first) and an array of uint32 words.
  • File ntru_crypto_ntru_convert.c - Conversion routines for NTRUEncrypt, including packing, unpacking, and others.
  • File ntru_crypto_ntru_encrypt_key.c - Routines for exporting and importing public and private keys for NTRUEncrypt.
  • File ntru_crypto_ntru_encrypt_param_sets.c - Defines the NTRUEncrypt parameter sets:

hellokitty

  • File: ntru_crypto_ntru_mgf1.c - Routines implementing MGF-TP-1 and MGF-1.
  • File: ntru_crypto_ntru_poly.c - Routines for generating and operating on polynomials in the NTRU algorithm.
  • File: ntru_crypto_sha1.c - Routines implementing the SHA-1 hash calculation.
  • File: ntru_crypto_sha2.c - Routines implementing the SHA-2 hash calculation.
  • File: ntru_crypto_sha256.c - Routines implementing the SHA-256 hash calculation.
  • And the last one is file: ntru_crypto_ntru_encrypt.c - Routines implementing NTRUEncrypt encryption and decryption and key generation:

hellokitty

sample

The sample code included in this release demonstrates how the function works and provides working code with a fixed seed. Providing entropy in this manner is clearly not suitable for a real-world application:

hellokitty

For example, static uint8_t get_entropy code snippet defines an example entropy function that simulates the generation of entropy for cryptographic purposes. The function get_entropy is designed to handle different commands (ENTROPY_CMD) to initialize, return the number of bytes required to get a byte of entropy, and return a byte of entropy.

hellokitty

This function takes a command and a pointer to an 8-bit integer and returns 1 for success and 0 for failure.

static uint8_t seed[28] = {
    'P','l','e','a','s','e',' ','u','s','e',' ','a',' ',
    'd','i','f','f','e','r','e','n','t',' ','s','e','e','d','!'
};
static size_t index;
  • seed - This is a static array containing 28 bytes of fixed seed data. This data is not truly random and should be replaced with actual entropy in a real-world application.
  • index - This static variable keeps track of the position in the seed array from which the next byte of entropy will be delivered.
if (cmd == INIT) {
    index = 0;
    return 1;
}
  • INIT - This command initializes the entropy function, setting the index to 0. This means the next request for a byte of entropy will start from the beginning of the seed array.

Checking for null output pointer:

if (out == NULL)
    return 0;

This check ensures that the out pointer is not NULL. If it is NULL, the function returns 0, indicating failure.

if (cmd == GET_NUM_BYTES_PER_BYTE_OF_ENTROPY) {
    *out = 1;
    return 1;
}
  • GET_NUM_BYTES_PER_BYTE_OF_ENTROPY command requests the number of bytes needed to produce a byte of entropy. Here, it returns 1, indicating that 1 byte of input gives 1 byte of entropy, implying a perfectly random source.
if (cmd == GET_BYTE_OF_ENTROPY) {
    if (index == sizeof(seed))
        return 0;                   /* used up all our entropy */

    *out = seed[index++];           /* deliver an entropy byte */
    return 1;
}
  • GET_BYTE_OF_ENTROPY command requests a single byte of entropy. If the index has reached the size of the seed array, it returns 0, indicating that all the entropy has been used up. Otherwise, it copies a byte from the seed array to out and increments the index.

Finally, if the command is not recognized, the function returns 0, indicating failure.

That’s all today.

The purpose of these articles was to improve the reader’s code analysis skills, gain insight into the techniques used by modern malware, and improve artifact detection. In addition, these blog posts in our laboratory were written with the intention of publishing an article during a long break, inspired by various presentations by our specialists at international conferences in Europe and the Middle East like BlackHat, Standoff, Security BSides Zagreb, Sofia and Prishtina. Although there is a lack of complete understanding of some parts of the source code, the goal of the article was to provide detailed and eloquent explanations.

By Cyber Threat Hunters from MSSPLab:

References

https://malpedia.caad.fkie.fraunhofer.de/details/win.hellokitty
Malware source code investigation: HelloKitty - part 1
https://en.wikipedia.org/wiki/NTRUEncrypt
Original NTRUEncrypt paper

Thanks for your time happy hacking and good bye!
All drawings and screenshots are MSSPLab’s