🔒
There are new articles available, click to refresh the page.
Before yesterdayResearch - Companies

Social Network Account Stealers Hidden in Android Gaming Hacking Tool

19 October 2021 at 13:02

Authored by: Wenfeng Yu

McAfee Mobile Research team recently discovered a new piece of malware that specifically steals Google, Facebook, Twitter, Telegram and PUBG game accounts. This malware hides in a game assistant tool called “DesiEsp” which is an assistant tool for PUBG game available on GitHub. Basically, cyber criminals added their own malicious code based on this DesiEsp open-source tool and published it on Telegram. PUBG game users are the main targets of this Android malware in all regions around the world but most infections are reported from the United States, India, and Saudi Arabia. 

What is an ESP hack? 

ESP Hacks, (short for Extra-Sensory Perception) are a type of hack that displays player information such as HP (Health Points), Name, Rank, Gun etc. It is like a permanent tuned-up KDR/HP Vision. ESP Hacks are not a single hack, but a whole category of hacks that function similarly and are often used together to make them more effective. 

How can you be affected by this malware? 

After investigation, it was found that this malware was spread in the channels related to PUBG game on the Telegram platform. Fortunately, this malware has not been found on Google Play. 

Figure 1. Re-packaged hacking tool distributed in Telegram
Figure 1. Re-packaged hacking tool distributed in Telegram

Main dropper behavior 

This malware will ask the user to allow superuser permission after running: 

Figure 2. Initial malware requesting root access. 
Figure 2. Initial malware requesting root access.

If the user denies superuser request the malware will say that the application may not work: 

Figure 3. Error message when root access is not provided 
Figure 3. Error message when root access is not provided

When it gains root permission, it will start two malicious actions. First, it will steal accounts by accessing the system account database and application database.  

Figure 4. Get google account from android system account database.
Figure 4. Get a Google account from the Android system account database.

Second, it will install an additional payload with package name com.android.google.gsf.policy_sidecar_aps” using the “pm install” command. The payload package will be in the assets folder, and it will disguise the file name as “*.crt” or “*.mph”. 

Figure 5. Payload disguised as a certificate file (crt extension) 
Figure 5. Payload disguised as a certificate file (crt extension)

Stealing social and gaming accounts 

The dropped payload will not display icons and it does not operate directly on the screen of the user’s device. In the apps list of the system settings, it usually disguises the package name as something like “com.google.android.gsf” to make users think it is a system service of Google. It runs in the background in the way of Accessibility Service. Accessibility Service is an auxiliary function provided by the Android system to help people with physical disabilities use mobile apps. It will connect to other apps like a plug-in and can it access the Activity, View, and other resources of the connected app. 

The malware will first try to get root permissions and IMEI (International Mobile Equipment Identity) code that later access the system account database. Of course, even if it does not have root access, it still has other ways to steal account information. Finally, it also will try to activate the device-admin to difficult its removal. 

Methods to steal account information 

The first method to steal account credentials that this malware uses is to monitor the login window and account input box text of the stolen app through the AccessibilityService interface to steal account information. The target apps include Facebook (com.facebook.kakana), Twitter (com.twitter.android), Google (com.google.android.gms) and PUBG MOBILE game (com.tencent.ig) 

The second method is to steal account information (including account number, password, key, and token) by accessing the account database of the system, the user config file, and the database of the monitored app. This part of the malicious code is the same as the parent sample above: 

Figure 6. Malware accessing Facebook account information using root privileges 
Figure 6. Malware accessing Facebook account information using root privileges

Finally, the malware will report the stolen account information to the hacker’s server via HTTP.  

Gaming users infected worldwide 

PUBG games are popular all over the world, and users who use PUBG game assistant tools exist in all regions of the world. According to McAfee telemetry data, this malware and its variants affect a wide range of countries including the United States, India, and Saudi Arabia:  

Figure 7. Top affected countries include USA, India and Saudi Arabia
Figure 7. Top affected countries include USA, India , and Saudi Arabia

Conclusion 

The online game market is revitalizing as represented by e-sports. We can play games anywhere in various environments such as mobiles, tablets, and PCs (personal computers). Some users will be looking for cheat tools and hacking techniques to play the game in a slightly advantageous way. Cheat tools are inevitably hosted on suspicious websites by their nature, and users looking for cheat tools must step into the suspicious websites. Attackers are also aware of the desires of such users and use these cheat tools to attack them. 

This malware is still constantly producing variants that use several ways to counter the detection of anti-virus software including packing, code obfuscation, and strings encryption, allowing itself to infect more game users. 

McAfee Mobile Security detects this threat as Android/Stealer and protects you from this malware attack. Use security software on your device. Game users should think twice before downloading and installing cheat tools, especially when they request Superuser or accessibility service permissions. 

Indicators of Compromise 

Dropper samples 

36d9e580c02a196e017410a6763f342eea745463cefd6f4f82317aeff2b7e1a5

fac1048fc80e88ff576ee829c2b05ff3420d6435280e0d6839f4e957c3fa3679

d054364014188016cf1fa8d4680f5c531e229c11acac04613769aa4384e2174b

3378e2dbbf3346e547dce4c043ee53dc956a3c07e895452f7e757445968e12ef

7e0ee9fdcad23051f048c0d0b57b661d58b59313f62c568aa472e70f68801417

6b14f00f258487851580e18704b5036e9d773358e75d01932ea9f63eb3d93973

706e57fb4b1e65beeb8d5d6fddc730e97054d74a52f70f57da36eda015dc8548

ff186c0272202954def9989048e1956f6ade88eb76d0dc32a103f00ebfd8538e

706e57fb4b1e65beeb8d5d6fddc730e97054d74a52f70f57da36eda015dc8548

3726dc9b457233f195f6ec677d8bc83531e8bc4a7976c5f7bb9b2cfdf597e86c

e815b1da7052669a7a82f50fabdeaece2b73dd7043e78d9850c0c7e95cc0013d

Payload samples 

8ef54eb7e1e81b7c5d1844f9e4c1ba8baf697c9f17f50bfa5bcc608382d43778

4e08e407c69ee472e9733bf908c438dbdaebc22895b70d33d55c4062fc018e26

6e7c48909b49c872a990b9a3a1d5235d81da7894bd21bc18caf791c3cb571b1c

9099908a1a45640555e70d4088ea95e81d72184bdaf6508266d0a83914cc2f06

ca29a2236370ed9979dc325ea4567a8b97b0ff98f7f56ea2e82a346182dfa3b8

d2985d3e613984b9b1cba038c6852810524d11dddab646a52bf7a0f6444a9845

ef69d1b0a4065a7d2cc050020b349f4ca03d3d365a47be70646fd3b6f9452bf6

06984d4249e3e6b82bfbd7da260251d99e9b5e6d293ecdc32fe47dd1cd840654

Domain 

hosting-b5476[.]gq 

The post Social Network Account Stealers Hidden in Android Gaming Hacking Tool appeared first on McAfee Blogs.

Vulnerability Spotlight: Multiple vulnerabilities in ZTE MF971R LTE router

18 October 2021 at 19:04
Marcin “Icewall” Noga of Cisco Talos discovered this vulnerability. Blog by Jon Munshaw.  Cisco Talos recently discovered multiple vulnerabilities in the ZTE MF971R LTE portable router.  The MF971R is a portable router with Wi-Fi support and works as an LTE/GSM modem. An attacker could...

[[ This is only the beginning! Please visit the blog for the complete entry ]]

Karma Ransomware | An Emerging Threat With A Hint of Nemty Pedigree

18 October 2021 at 16:43

Karma is a relatively new ransomware threat actor, having first been observed in June of 2021. The group has targeted numerous organizations across different industries. Reports of a group with the same name from 2016 are not related to the actors currently using the name. An initial technical analysis of a single sample related to Karma was published by researchers from Cyble in August.

In this post, we take a deeper dive, focusing on the evolution of Karma through multiple versions of the malware appearing through June 2021. In addition, we explore the links between Karma and other well known malware families such as NEMTY and JSWorm and offer an expanded list of technical indicators for threat hunters and defenders.

Initial Sample Analysis

Karma’s development has been fairly rapid and regular with updated variants and improvements, oftentimes building multiple versions on the same day. The first few Karma samples our team observed were:

Sample 1: d9ede4f71e26f4ccd1cb96ae9e7a4f625f8b97c9
Sample 2: a9367f36c1d2d0eb179fd27814a7ab2deba70197
Sample 3: 9c733872f22c79b35c0e12fa93509d0326c3ec7f

Sample 1 was compiled on 18th, June 2021 and Samples 2 and 3 the following day on the 19th, a few minutes apart. Basic configuration between these samples is similar, though there are some slight differences such as PDB paths.

After Sample 1, we see more of the core features appear, including the writing of the ransom note. Upon execution, these payloads would enumerate all local drives (A to Z) , and encrypt files where possible.

Further hunting revealed a number of other related samples all compiled within a few days of each other. The following table illustrates compilation timestamps and payload size across versions of Karma compiled in a single week. Note how the payload size decreases as the authors’ iterate.

Ransom Note is not Created in Sample 1.

Also, the list of excluded extensions is somewhat larger in Sample 1 than in both Samples 2 and 3, and the list of extensions is further reduced from Sample 5 onwards to only exclude “.exe”, “.ini”, “.dll”, “.url” and “.lnk”.

The list of excluded extensions is reduced as the malware authors iterate

Encryption Details

From Sample 2 onwards, the malware calls CreateIoCompletionPort, which is used for communication between the main thread and a sub thread(s) handling the encryption process. This specific call is key in managing efficiency of the encryption process (parallelization in this case).

Individual files are encrypted by way of a random Chacha20 key. Once files are encrypted, the malware will encrypt the random Chacha20 key with the public ECC key and embed it in the encrypted file.

Chacha Encryption

Across Samples 2 to 5, the author removed the CreateIoCompletionPort call, instead opting to create a new thread to manage enumeration and encryption per drive. We also note the “KARMA” mutex created to prevent the malware from running more than once. Ransom note names have also been updated to “KARMA-ENCRYPTED.txt”.

Diving in deeper, some samples show that the ChaCha20 algorithm has been swapped out for Salsa20. The asymmetric algorithm (for ECC) has been swapped from Secp256k1 to Sect233r1. Some updates around execution began to appear during this time as well, such as support for command line parameters.

A few changes were noted in Samples 6 and 7. The main difference is the newly included background image. The file “background.jpg” is written to %TEMP% and set as the Desktop image/wallpaper for the logged in user.

Desktop image change and message

Malware Similarity Analysis

From our analysis, we see similarities between JSWorm and the associated permutations of that ransomware family such as NEMTY, Nefilim, and GangBang. Specifically, the Karma code analyzed bears close similarity to the GangBang or Milihpen variants that appeared around January 2021.

Some high-level similarities are visible in the configurations.

We can see deeper relationships when we conduct a bindiff on Karma and GangBang samples. The following image shows how similar the main() functions are:

The main() function & argument processing in Gangbang (left) and Karma

Victim Communication

The main body of the ransom note text hasn’t changed since the first sample and still contains mistakes. The ransom notes are base64-encoded in the binary and dropped on the victim machine with the filename “KARMA-AGREE.txt” or, in later samples, “KARMA-ENCRYPTED.txt”.

Your network has been breached by Karma ransomware group.
We have extracted valuable or sensitive data from your network and encrypted the data on your systems.
Decryption is only possible with a private key that only we posses.
Our group's only aim is to financially benefit from our brief acquaintance,this is a guarantee that we will do what we promise.
Scamming is just bad for business in this line of work.
Contact us to negotiate the terms of reversing the damage we have done and deleting the data we have downloaded.
We advise you not to use any data recovery tools without leaving copies of the initial encrypted file.
You are risking irreversibly damaging the file by doing this.
If we are not contacted or if we do not reach an agreement we will leak your data to journalists and publish it on our website.
http://3nvzqyo6l4wkrzumzu5aod7zbosq4ipgf7ifgj3hsvbcr5vcasordvqd.onion/

If a ransom is payed we will provide the decryption key and proof that we deleted you data.
When you contact us we will provide you proof that we can decrypt your files and that we have downloaded your data.

How to contact us:

{[email protected]}
{[email protected]}
{[email protected]}

Each sample observed offers three contact emails, one for each of the mail providers onionmail, tutanota, and protonmail. In each sample, the contact emails are unique, suggesting they are specific communication channels per victim. The notes contain no other unique ID or victim identifier as sometimes seen in notes used by other ransomware groups.

In common with other operators, however, the Karma ransom demand threatens to leak victim data if the victim does not pay. The address of a common leaks site where the data will be published is also given in the note. This website page appears to have been authored in May 2021 using WordPress.

The Karma Ransomware Group’s Onion Page

Conclusion

Karma is a young and hungry ransomware operation. They are aggressive in their targeting, and show no reluctance in following through with their threats. The apparent similarities to the JSWorm family are also highly notable as it could be an indicator of the group being more than they appear. The rapid iteration over recent months suggests the actor is investing in development and aims to be around for the foreseeable future. SentinelLabs continues to follow and analyze the development of Karma ransomware.

Indicators of Compromise

SHA1s
Karma Ransomware

Sample 1: d9ede4f71e26f4ccd1cb96ae9e7a4f625f8b97c9
Sample 2: a9367f36c1d2d0eb179fd27814a7ab2deba70197
Sample 3: 9c733872f22c79b35c0e12fa93509d0326c3ec7f
Sample 4: c4cd4da94a2a1130c0b9b1bf05552e06312fbd14
Sample 5: bb088c5bcd5001554d28442bbdb144b90b163cc5
Sample 6: 5ff1cd5b07e6c78ed7311b9c43ffaa589208c60b
Sample 7: 08f1ef785d59b4822811efbc06a94df16b72fea3
Sample 8: b396affd40f38c5be6ec2fc18550bbfc913fc7ea

Gangbang Sample 
ac091ce1281a16f9d7766a7853108c612f058c09

Karma Desktop image
%TEMP%/background.jpg
7b8c45769981344668ce09d48ace78fae50d71bc

Victim Blog (TOR)
http[:]//3nvzqyo6l4wkrzumzu5aod7zbosq4ipgf7ifgj3hsvbcr5vcasordvqd[.]onion/

Ransom Note Email Addresses
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]

MITRE ATT&CK
T1485 Data Destruction
T1486 Data Encrypted for Impact
T1012 Query Registry
T1082 System Information Discovery
T1120 Peripheral Device Discovery
T1204 User Execution
T1204.002 User Execution: Malicious File

Is Using Public WiFi Safe?

18 October 2021 at 13:30

Using public WiFi can leave you and your employees open to man-in-the-middle cyber attacks. With the increase of remote work, organizations must teach their employees how to stay safe on open WiFi networks. Get our tips on how to protect against hackers while on open WiFi networks.

The post Is Using Public WiFi Safe? appeared first on VerSprite.

Cracking Random Number Generators using Machine Learning – Part 2: Mersenne Twister

18 October 2021 at 13:00

Outline

1. Introduction

In part 1 of this post, we showed how to train a Neural Network to generate the exact sequence of a relatively simple pseudo-random number generator (PRNG), namely xorshift128. In that PRNG, each number is totally dependent on the last four generated numbers (which form the random number internal state). Although ML had learned the hidden internal structure of the xorshift128 PRNG, it is a very simple PRNG that we used as a proof of concept.

The same concept applies to a more complex structured PRNG, namely Mersenne Twister (MT). MT is by far the most widely used general-purpose (but still: not cryptographically secure) PRNG [1].  It can have a very long period of 32bit words with a statistically uniform distribution. There are many variants of the MT PRNG that follows the same algorithm but differ in the constants and configurations of the algorithm. The most commonly used variant of MT is MT19937. Hence, for the rest of this article, we will focus more on this variant of MT.

To crack the MT algorithm, we need first to examine how it works. In the next section, we will discuss how MT works.

** Editor’s Note: How does this relate to security? While both the research in this blog post (and that of Part 1 in this series) looks at a non-cryptographic PRNG, we are interested, generically, in understanding how machine learning-based approaches to finding latent patterns within functions presumed to be generating random output could work, as a prerequisite to attempting to use machine learning to find previously-unknown patterns in cryptographic (P)RNGs, as this could potentially serve as an interesting supplementary method for cryptanalysis of these functions. Here, we show our work in beginning to explore this space, extending the approach used for xorshift128 to Mersenne Twister, a non-cryptographic PRNG sometimes mistakenly used where a cryptographically-secure PRNG is required. **

2. How does MT19937 PRNG work?

MT can be considered as a twisted form of the generalized Linear-feedback shift register (LFSR). The idea of LFSR is to use a linear function, such as XOR, with the old register value to get the register’s new value. In MT, the internal state is saved in the form of a sequence of 32-bit words. The size of the internal state is different based on the MT variant, which in the case of  MT19937 is 624. The pseudocode of MT is listed below as shared in Wikipedia.

 // Create a length n array to store the state of the generator
 int[0..n-1] MT
 int index := n+1
 const int lower_mask = (1 << r) - 1 // That is, the binary number of r 1's
 const int upper_mask = lowest w bits of (not lower_mask)
 
 // Initialize the generator from a seed
 function seed_mt(int seed) {
     index := n
     MT[0] := seed
     for i from 1 to (n - 1) { // loop over each element
         MT[i] := lowest w bits of (f * (MT[i-1] xor (MT[i-1] >> (w-2))) + i)
     }
 }
 
 // Extract a tempered value based on MT[index]
 // calling twist() every n numbers
 function extract_number() {
     if index >= n {
         if index > n {
           error "Generator was never seeded"
           // Alternatively, seed with constant value; 5489 is used in reference C code[53]
         }
         twist()
     }
 
     int y := MT[index]
     y := y xor ((y >> u) and d)
     y := y xor ((y << s) and b)
     y := y xor ((y << t) and c)
     y := y xor (y >> l)
 
     index := index + 1
     return lowest w bits of (y)
 }
 
 // Generate the next n values from the series x_i 
 function twist() {
     for i from 0 to (n-1) {
         int x := (MT[i] and upper_mask)
                   + (MT[(i+1) mod n] and lower_mask)
         int xA := x >> 1
         if (x mod 2) != 0 { // lowest bit of x is 1
             xA := xA xor a
         }
         MT[i] := MT[(i + m) mod n] xor xA
     }
     index := 0
 } 

The first function is seed_mt, used to initialize the 624 state values using the initial seed and the linear XOR function. This function is straightforward, but we will not get in-depth of its implementation as it is irrelevant to our goal. The main two functions we have are extract_number and twist. extract_number function is called every time we want to generate a new random value.  It starts by checking if the state is not initialized; it either initializes it by calling seed_mt with a constant seed or returns an error. Then the first step is to call the twist function to twist the internal state, as we will describe later. This twist is also called every 624 calls to the extract_number function again. After that, the code uses the number at the current index (which starts with 0 up to 623) to temper one word from the state at that index using some linear XOR, SHIFT, and AND operations. At the end of the function, we increment the index and return the tempered state value. 

The twist function changes the internal states to a new state at the beginning and after every 624 numbers generated. As we can see from the code, the function uses the values at indices i, i+1, and i+m to generate the new value at index i using some XOR, SHIFT, and AND operations similar to the tempering function above.

Please note that w, n, m, r, a, u, d, s, b, t, c, l, and f are predefined constants for the algorithm that may change from one version to another. The values of these constants for MT19937 are as follows:

  • (wnmr) = (32, 624, 397, 31)
  • a = 9908B0DF16
  • (ud) = (11, FFFFFFFF16)
  • (sb) = (7, 9D2C568016)
  • (tc) = (15, EFC6000016)
  • l = 18
  • f = 1812433253

3. Using Neural Networks to model the MT19937 PRNG

As we saw in the previous section, MT can be split into two main steps, twisting and tempering steps. The twisting step only happens once every n calls to the PRNG (where n equals 624 in MT19937) to twist the old state into a new state. The tempering step happens with each number generated to convert one of the 624 states to a random number. Hence, we are planning to use two NN models to learn each step separately. Then we will use the two models to reverse the whole PRNG by using the reverse tempering model to get from a generated number to its internal state then use the twisting model to produce the new state. Then, we can temper the generated state to get to the expected new PRNG number.

3.1 Using NN for State Twisting

After checking the code listing for the twisting function line 46, we can see that the new state at the position, i, depends on the variable xA and the value of the old state value at position (i + m), where m equals 397 in MT19937. Also, the value of xA depends on the value of the variable x and some constant a. And the value of the variable x depends on the old state’s values at positions i and i + 1.

Hence, we can deduce that, and without getting into the details, each new twisted state depends on three old states as follows:

MT[i] = f(MT[i - 624], MT[i - 624 + 1], MT[i - 624 + m])

    = f(MT[i - 624], MT[i - 623], MT[i - 227])     ( using m = 397 as in MT19937 )

We want to train an NN model to learn the function f hence effectively replicating the twisting step. To accomplish this, we have created a different function that generates the internal state (without tempering) instead of the random number. This allows us to correlate the internal states with each other, as described in the above formula.

3.1.1 Data Preparation

We have generated a sequence of 5,000,000 32bits words of states. We then formatted these states into three 32bits inputs and one 32bits output. This would create around a five million records dataset (5 million – 624). Then we split the data into training and testing sets, with only 100,000 records for the test set, and the rest of the data is used for training.

3.1.2 Neural Network Model Design

Our proposed model would have 96 inputs in the input layer to represent the old three states at the specific locations described in the previous equation, 32-bit each, and 32 outputs in the output layer to represent the new 32-bit state. The main hyperparameters that we need to decide are the number of neurons/nodes and hidden layers. We have tested different models’ structures with different hidden layers and a different number of neurons in each layer. Then we used Keras’ BayesianOptimization to select the optimal number of hidden layers/neurons along with other optimization parameters (such as learning rate, … etc.). We used a small subset of the training set as the training set for this optimization. We have got multiple promising model structures. Then, we used the smallest model structure that had the most promising result.  

3.1.3 Optimizing the NN Inputs

After training multiple models, we noticed that the weights that connect the first 31-bit of the  96 input bits to the first hidden layer nodes are almost always close to zero. This implies that those bits have no effects on the output bits, and hence we can ignore them in our training. To check our observation with the MT implementation, we noticed that the state at position i is bitwise ANDed with a bitmask called upper_mask which, when we checked, has a value of 1 in the MSB and 0 for the rest when using r with the value of 31 as stated in MT19937. So, we only need the MSB of the state at position i. Hence, we can train our NN with only 65bits inputs instead of 96.

Hence, our neural network structure ended up with the following model structure (the input layer is ignored):

_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
dense (Dense)                (None, 96)                6336      
_________________________________________________________________
dense_1 (Dense)              (None, 32)                3104      
=================================================================
Total params: 9,440
Trainable params: 9,440
Non-trainable params: 0
_________________________________________________________________

As we can see, the number of the parameters (weights and biases) of the hidden layer is only 6,336 (65×96 weights + 96 biases), and the number of the parameters of the output layer is 3,104 (96×32 weights + 32 biases), which gets to a total of 9,440 parameters to train. The hidden layer activation function is relu, while the output layer activation function is sigmoid as we expect it to be a binary output. Here is the summary of the model parameters:

Twisting Model
Model Type Dense Network
#Layers 2
#Neurons 128 Dense
#Trainable Parameters 9,440
Activation Functions Hidden Layer: relu
Output Layer: sigmoid
Loss Function binary_crossentropy

3.1.4 Model Results

After a few hours and manual tweaking of the model architecture, we have trained a model that has achieved 100% bitwise accuracy for both the training and testing sets. To be more confident about what we have achieved, we have generated a new sample of 1000,000 states generated using a different seed than the one used to generate the previous data. We got the same result of 100% bitwise accuracy when we tested with the newly generated data.

This means that the model has learned the exact formula that relates each state to the three previous states. Hence if we got access to the three internal states at those specific locations, we could predict the next state. But, usually, we can get access only to the old random numbers, not the internal states. So, we need a different model that can reverse the generated tempered number to its equivalent internal state. Then we can use this model to create the new state. Then, we can use the normal tempering step to get to the new output. But before we get to that, let’s do model deep dive.

3.1.5 Model Deep Dive

This section will deep dive into the model to understand more what it has learned from the State relations data and if it matches our expectations. 

3.1.5.1 Model First Layer Connections

As discussed in Section 2, each state depends on three previous states, and we also showed that we only need the MSB (most significant bit) of the first state; hence we had 65bits as input. Now, we would like to examine whether all 65 input bits have the same effects on the 64 output bits? In other words, do all the input bits have more or less the same importance for the outputs? We can use the NN weights that connect the 65 inputs in the input layer to the hidden layer, the 96 hidden nodes, of our trained model to determine the importance of each bit. The following figure shows the weights connecting each input, in x-axes, to the 96 hidden nodes in the hidden layer:

We can notice that the second bit, which corresponds to the second state LSB, is connected to many hidden layer nodes, implying that it affects multiple bits in the output. Comparing this observation against the code listed in Section 2 corresponds to the if-condition that checks the LSB bit, and based on its value, it may XOR the new state with a constant value that dramatically affects the new state.

We can also notice that the 33rd input, marked on the figure, is almost not connected to any hidden layer nodes as all the weights are close to zero. This can also be mapped to the code in line 12, where we bit masked the second state to  0x7FFFFFFF in MT19937, which basically neglects the MSB altogether.

3.1.5.2 The Logic Closed-Form from the State Twisting Model Output

After creating a model that can replicate the twist operation of the states, we would like to see if we can possibly get to the logic closed form using the trained model. Of course, we can derive the closed-form by building all the 65bit input variations and testing each input combination’s outputs (2^65) and derive the closed-form. But this approach is tedious and requires lots of evaluations. Instead, we can test the effect of each input on each output separately. This can be done by using the training set and sticking each input bit individually, one time at 0 and one time at 1, and see which bits in the output are affected. This will show how which bits in the inputs affecting each bit in the output. For example, when we checked the first input, which corresponds to MSB of the first state, we found it only affects bit 30 in the output. The following table shows the effect of each input bit on each output bit:

Input Bits Output Bits
0   [30]
1   [0, 1, 2, 3, 4, 6, 7, 12, 13, 15, 19, 24, 27, 28, 31]
2   [0]
3   [1]
4   [2]
5   [3]
6   [4]
7   [5]
8   [6]
9   [7]
10   [8]
11   [9]
12   [10]
13   [11]
14   [12]
15   [13]
16   [14]
17   [15]
18   [16]
19   [17]
20   [18]
21   [19]
22   [20]
23   [21]
24   [22]
25   [23]
26   [24]
27   [25]
28   [26]
29   [27]
30   [28]
31   [29]
32   []
33   [0]
34   [1]
35   [2]
36   [3]
37   [4]
38   [5]
39   [6]
40   [7]
41   [8]
42   [9]
43   [10]
44   [11]
45   [12]
46   [13]
47   [14]
48   [15]
49   [16]
50   [17]
51   [18]
52   [19]
53   [20]
54   [21]
55   [22]
56   [23]
57   [24]
58   [25]
59   [26]
60   [27]
61   [28]
62   [29]
63   [30]
64   [31]

We can see that inputs 33 to 64, which corresponds to the last state, are affecting bits 0 to 31 in the output. We can also see that inputs 2 to 31, which corresponds to the second state from bit 1 to bit 30 (ignoring the LSB and MSB), affect bits 0 to 29 in the output. And as we know, we are using the XOR logic function in the state evaluation. We can formalize the closed-form as follows (ignoring bit 0 and bit 1 in the input for now):

          MT[i] = MT[i - 227] ^ ((MT[i - 623] & 0x7FFFFFFF) >> 1)

This formula is so close to the result. We need to add the effect of input bits 0 and 1. For input number 0, which corresponds to the first state MSB, it is affecting bit 30. Hence we can update the above closed-form to be:

          MT[i] = MT[i - 227] ^ ((MT[i - 623] & 0x7FFFFFFF) >> 1) ^ ((MT[i - 624] & 0x80000000) << 1)

Lastly, the first bit in the input, which corresponds to the second state LSB, affects 15 bits in the output. As we are using it in an XOR function, if this bit is 0, it does not affect the output, but if it has a value of one, it will affect all the listed 15bits (by XORing them). If we formulate these 15 bits into a hexadecimal value, we will get 0x9908B0DF (which corresponds to the constant a in MT19937). Then we can include this to the closed-form as follows:

          MT[i] = MT[i - 227] ^ ((MT[i - 623] & 0x7FFFFFFF) >> 1) ^ ((MT[i - 624] & 0x80000000) << 1) ^ ((MT[i - 623] & 1) * 0x9908B0DF)

So, if the LSB of the second state is 0, the result of the ANDing will be 0, and hence the multiplication will result in 0. But if the LSB of the second state is 1, the result of the ANDing will be 1, and hence the multiplication will result in 0x9908B0DF. This is equivalent to the if-condition in the code listing. We can rewrite the above equation to use the circular buffer as follows:

          MT[i] = MT[(i + 397) % 624] ^ ((MT[(i + 1) % 624] & 0x7FFFFFFF) >> 1) ^ ((MT[i] & 0x80000000) << 1) ^ ((MT[(i + 1) % 624] & 1) * 0x9908B0DF)

Now let’s check how to reverse the tempered numbers to the internal states using an ML model.

3.2 Using NN for State Tempering

We plan to train a neural network model for this phase to reverse the state tempering introduced in the code listing from line 10 to line 20. As we can see, a series of XORing and shifting is done to the state at the current index to generate the random number. So, we have updated the code to generate the state before and after the state tempering to be used as inputs and outputs for the Neural network.

3.2.1 Data Preparation

We have generated a sequence of 5,000,000 32 bit words of states and their tempered values. Then we used the tempered states as inputs and the original states as outputs. This would create a 5 million records dataset. Then we split the data into training and testing sets, with only 100,000 records for the test set, and the rest of the data is used for training.

3.2.2 Neural Network Model Design

Our proposed model would have 32 inputs in the input layer to represent the tempered state values and 32 outputs in the output layer to represent the original 32-bit state. The main hyperparameter that we need to decide is the number of neurons/nodes and hidden layers. We have tested different models’ structures with a different number of hidden layers and neurons in each layer, similar to what we did in the NN model before. We also used the smallest model structure that has the most promising result. Hence, our neural network structure ended up with the following model structure (the input layer is ignored):

_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
dense (Dense)                (None, 640)               21120     
_________________________________________________________________
dense_1 (Dense)              (None, 32)                20512     
=================================================================
Total params: 41,632
Trainable params: 41,632
Non-trainable params: 0
_________________________________________________________________

As we can see, the number of the parameters (weights and biases) of the hidden layer is only 21,120 (32×640 weights + 640 biases), and the number of the parameters of the output layer is 20,512 (640×32 weights + 32 biases), which gets to a total of 41,632 parameters to train. We can notice that this model is much bigger than the model for twisting as the operations done in tempering is much more complex than the operations done there. Similarly, the hidden layer activation function is relu, while the output layer activation function is sigmoid as we expect it to be a binary output. Here is the summary of the model parameters:

Tempering Model
Model Type Dense Network
#Layers 2
#Neurons 672 Dense
#Trainable Parameters 41,632
Activation Functions Hidden Layer: relu
Output Layer: sigmoid
Loss Function binary_crossentropy

3.2.3 Model Results

After manual tweaking of the model architecture, we have trained a model that has achieved 100% bitwise accuracy for both the training and testing sets. To be more confident about what we have achieved, we have generated a new sample of 1000,000 states and tempered states using a different seed than those used to generate the previous data. We got the same result of 100% bitwise accuracy when we tested with the newly generated data.

This means that the model has learned the exact inverse formula that relates each tempered state to its original state. Hence, we can use this model to reverse the generated numbers corresponding to the internal states we need for the first model. Then we can use the twisting model to get the new state. Then, we can use the normal tempering step to get to the new output. But before we get to that, let’s do a model deep dive.

3.2.4 Model Deep Dive

This section will deep dive into the model to understand more what it has learned from the State relations data and if it matches our expectations. 

3.2.4.1 Model Layers Connections

After training the model, we wanted to test if any bits are not used or are less connected. The following figure shows the weights connecting each input, in x-axes, to the 640 hidden nodes in the hidden layer (each point represents a hidden node weight):

As we can see, each input bit is connected to many hidden nodes, which implies that each bit in the input has more or less weight like others. We also plot in the following figure the connections between the hidden layer and the output layer:

The same observation can be taken from the previous figure, except that maybe bits 29 and 30 are less connected than others, but they are still well connected to many hidden nodes.

3.2.4.2 The Logic Closed-Form from the State Tempering Model Output

In the same way, we did with the first model, we can derive a closed-form logic expression that reverses the tempering using the model output. But, instead of doing so, we can derive a closed-form logic expression that uses the generated numbers at the positions mentioned in the first part to reverse them to the internal states, generate the new states and then generate the new number in one step. So, we plan to connect three models of the reverse tempering models to one model that generates the new state and then temper the output to get to the newly generated number as shown in the following figure:

Now we have 3 x 32 bits inputs and 32 bits output. Using the same approach described before, we can test the effect of each input on each output separately. This will show how each bit in the input bits affecting each bit in the output bits. The following table shows the input-output relations:

Input Index Input Bits Output Bits
0 0   []
0 1   []
0 2   [1, 8, 12, 19, 26, 30]
0 3   []
0 4   []
0 5   []
0 6   []
0 7   []
0 8   []
0 9   [1, 8, 12, 19, 26, 30]
0 10   []
0 11   []
0 12   []
0 13   []
0 14   []
0 15   []
0 16   [1, 8, 12, 19, 26, 30]
0 17   [1, 8, 12, 19, 26, 30]
0 18   []
0 19   []
0 20   [1, 8, 12, 19, 26, 30]
0 21   []
0 22   []
0 23   []
0 24   [1, 8, 12, 19, 26, 30]
0 25   []
0 26   []
0 27   [1, 8, 12, 19, 26, 30]
0 28   []
0 29   []
0 30   []
0 31   [1, 8, 12, 19, 26, 30]
1 0   [3, 5, 7, 9, 11, 14, 15, 16, 17, 18, 23, 25, 26, 27, 28, 29, 30, 31]
1 1   [0, 4, 7, 22]
1 2   [13, 16, 19, 26, 31]
1 3   [2, 6, 24]
1 4   [0, 3, 7, 10, 18, 25]
1 5   [4, 7, 8, 11, 25, 26]
1 6   [5, 9, 12, 27]
1 7   [5, 7, 9, 10, 11, 14, 15, 16, 17, 18, 21, 23, 25, 26, 27, 29, 30, 31]
1 8   [7, 11, 14, 29]
1 9   [1, 19, 26]
1 10   [9, 13, 31]
1 11   [2, 3, 5, 7, 9, 10, 11, 13, 14, 15, 16, 18, 20, 23, 24, 25, 26, 27, 28, 29, 30, 31]
1 12   [7, 11, 25]
1 13   [1, 9, 12, 19, 27]
1 14   [2, 10, 13, 20, 28]
1 15   [3, 14, 21]
1 16   [1, 8, 12, 15, 19, 26, 30]
1 17   [1, 5, 8, 13, 16, 19, 23, 26, 31]
1 18   [3, 5, 6, 7, 9, 11, 14, 15, 16, 18, 23, 24, 25, 26, 27, 28, 29, 30, 31]
1 19   [4, 18, 22, 25]
1 20   [1, 13, 16, 26, 31]
1 21   [6, 20, 24]
1 22   [0, 2, 3, 5, 6, 9, 11, 13, 14, 15, 16, 17, 20, 21, 23, 26, 27, 29, 30, 31]
1 23   [7, 8, 11, 22, 25, 26]
1 24   [1, 8, 9, 12, 19, 23, 26, 27]
1 25   [5, 6, 7, 9, 10, 11, 13, 14, 15, 16, 17, 18, 21, 23, 24, 25, 26, 27, 29, 30]
1 26   [11, 14, 25, 29]
1 27   [1, 8, 19]
1 28   [13, 27, 31]
1 29   [2, 3, 5, 7, 9, 11, 13, 14, 15, 16, 18, 20, 23, 24, 25, 26, 27, 29, 30, 31]
1 30   [7, 25, 29]
1 31   [8, 9, 12, 26, 27]
2 0   [0]
2 1   [1]
2 2   [2]
2 3   [3]
2 4   [4]
2 5   [5]
2 6   [6]
2 7   [7]
2 8   [8]
2 9   [9]
2 10   [10]
2 11   [11]
2 12   [12]
2 13   [13]
2 14   [14]
2 15   [15]
2 16   [16]
2 17   [17]
2 18   [18]
2 19   [19]
2 20   [20]
2 21   [21]
2 22   [22]
2 23   [23]
2 24   [24]
2 25   [25]
2 26   [26]
2 27   [27]
2 28   [28]
2 29   [29]
2 30   [30]
2 31   [31]

As we can see, each bit in the third input affects the same bit in order in the output. Also, we can notice that only 8 bits in the first input affect the same 6 bits in the output. But for the second input, each bit is affecting several bits. Although we can write a closed-form logic expression for this, we would write a code instead.

The following code implements a C++ function that can use three generated numbers (at specific locations) to generate the next number:

uint inp_out_xor_bitsY[32] = {
    0xfe87caa8,
    0x400091,
    0x84092000,
    0x1000044,
    0x2040489,
    0x6000990,
    0x8001220,
    0xeea7cea0,
    0x20004880,
    0x4080002,
    0x80002200,
    0xff95eeac,
    0x2000880,
    0x8081202,
    0x10102404,
    0x204008,
    0x44089102,
    0x84892122,
    0xff85cae8,
    0x2440010,
    0x84012002,
    0x1100040,
    0xecb3ea6d,
    0x6400980,
    0xc881302,
    0x6fa7eee0,
    0x22004800,
    0x80102,
    0x88002000,
    0xef95eaac,
    0x22000080,
    0xc001300
};

uint gen_new_number(uint MT_624, uint MT_623, uint MT_227) {
    uint r = 0;
    uint i = 0;
    do {
        r ^= (MT_623 & 1) * inp_out_xor_bitsY[i++];
    }
    while (MT_623 >>= 1);
    
    MT_624 &= 0x89130204;
    MT_624 ^= MT_624 >> 16;
    MT_624 ^= MT_624 >> 8;
    MT_624 ^= MT_624 >> 4;
    MT_624 ^= MT_624 >> 2;
    MT_624 ^= MT_624 >> 1;
    r ^= (MT_624 & 1) * 0x44081102;
    
    r ^= MT_227;
    return r;
}

We have tested this code by using the standard MT library in C to generate the sequence of the random numbers and match the generated numbers from the function with the next generated number from the library. The output was 100% matching for several millions of generated numbers with different seeds.

3.2.4.3 Further Improvement

We have seen in the previous section that we can use three generated numbers to generate the new number in the sequence. But we noticed that we only need 8 bits from the first number, which we XOR, and if the result of the XORing of them is one, we XOR the result with a constant, 0x44081102. Based on this observation, we can instead ignore the first number altogether and generate two possible output values, with or without XORing the result with 0x44081102. Here is the code segment for the new function.

#include<tuple>
  
using namespace std;

uint inp_out_xor_bitsY[32] = {
    0xfe87caa8,
    0x400091,
    0x84092000,
    0x1000044,
    0x2040489,
    0x6000990,
    0x8001220,
    0xeea7cea0,
    0x20004880,
    0x4080002,
    0x80002200,
    0xff95eeac,
    0x2000880,
    0x8081202,
    0x10102404,
    0x204008,
    0x44089102,
    0x84892122,
    0xff85cae8,
    0x2440010,
    0x84012002,
    0x1100040,
    0xecb3ea6d,
    0x6400980,
    0xc881302,
    0x6fa7eee0,
    0x22004800,
    0x80102,
    0x88002000,
    0xef95eaac,
    0x22000080,
    0xc001300
};

tuple <uint, uint> gen_new_number(uint MT_623, uint MT_227) {
    uint r = 0;
    uint i = 0;
    do {
        r ^= (MT_623 & 1) * inp_out_xor_bitsY[i++];
    }
    while (MT_623 >>= 1);
    
    r ^= MT_227;
    return make_tuple(r, r^0x44081102);
}

4. Improving MT algorithm

We have shown in the previous sections how to use ML to crack the MT algorithm and use only two or three numbers to predict the following number in the sequence. In this section, we will discuss two issues with the MT algorithm and how to improve them. Studying those issues can be particularly useful when MT is used as a part of cryptographically-secure PRNG, like in CryptMT. CryptMT uses Mersenne Twister internally, and it was developed by Matsumoto, Nishimura, Mariko Hagita, and Mutsuo Saito [2].

4.1 Normalizing the Runtime

As we described in Section 2, MT consist of two steps, twisting and tempering. Although the tempering step is applied on each step, the twisting step is only used when we exhaust all the states, and we need to twist the state array to create a new internal state. That is OK, algorithmically, but from the cybersecurity point of view, this is problematic. It makes the algorithm vulnerable to a different type of attack, namely a timing side-channel attack. In this attack, the attacker can determine when the state is being twisted by monitoring the runtime of each generated number, and when it is much longer, the start of the new state can be guessed. The following figure shows an example of the runtime for the first 10,000 generated numbers using MT:

We can clearly see that every 624 iterations, the runtime of the MT algorithm increases significantly due to the state-twisting evaluation. Hence, the attacker can easily determine the start of the state twisting by setting a threshold of 0.4 ms of the generated numbers. Fortunately, this issue can be resolved by updating the internal state every time we generate a new number using the progressive formula we have created earlier. Using this approach, we have updated the code for MT and measured the runtime again, and here is the result:

We can see that the runtime of the algorithm is now normalized across all iterations. Of course, we checked the output of both MT implementations, and they got the exact sequences. Also, when we evaluated the mean of the runtime of both MT implementations over millions of numbers generated, the new approach is a little more efficient but has a much smaller standard deviation.

4.2 Changing the Internal State as a Whole

We showed in the previous section that the MT twist step is equivalent to changing the internal state progressively. This is the main reason we were able to build a machine learning that can correlate a generated output from MT to three previous outputs. In this section, we suggest changing the algorithm slightly to overcome this weakness. The idea is to apply the twisting step on the old state at once and generate the new state from it. This can be achieved by doing the twist step in a new state array and swap the new state with the old one after twisting all the states. This will make the state prediction more complex as different states will use different distances for evaluating the new state. For example, all the new states from 0 to 226 will use the old states from 397 to 623, which are 397 apart. And all the new states from 227 to 623 will be using the value of the old states from 0 to 396, which are 227 apart. This slight change will generate a more resilient PRNG for the above-suggested attack, as any generated output can be either 227  or 397 away from the output it depends on as we don’t know the position of the generated number (whether it is in the first 227 numbers from the state or in the last 397 ones). Although this will generate a different sequence, the new state’s prediction from the old ones will be harder. Of course, this suggestion needs to be reviewed more deeply to check the other properties of the outcome PRNG, such as periodicity.

5. Conclusion

In this series of posts, we have shared the methodology of cracking two of the well-known (non-cryptographic) Pseudo-Random Number Generators, namely xorshift128 and Mersenne Twister. In the first post, we showed how we could use a simple machine learning model to learn the internal pattern in the sequence of the generated numbers. In this post, we extended this work by applying ML techniques to a more complex PRNG. We also showed how to split the problem into two simpler problems, and we trained two different NN models to tackle each problem separately. Then we used the two models together to predict the next sequence number in the sequence using three generated numbers. Also, we have shown how to deep dive into the models and understand how they work internally and how to use them to create a closed-form code that can break the Mersenne Twister (MT) PRNG. At last, we shared two possible techniques to improve the MT algorithm security-wise as a means of thinking through the algorithmic improvements that can be made based on patterns identified by deep learning (while still recognizing that these changes do not themselves constitute sufficient security improvements for MT to be itself used where cryptographic randomness is required).

The python code for training and testing the model outlined in this post is shared in this repo in a form of a Jupyter notebook. Please note that this code is based on top of an old version of the code shared in the post [3] and the changes that are done in part 1 of this post.

Acknowledgment

I would like to thank my colleagues in NCC Group for their help and support and for giving valuable feedback the especially in the crypto/cybersecurity parts. Here are a few names that I would like to thank explicitly: Ollie Whitehouse, Jennifer Fernick, Chris Anley, Thomas Pornin, Eric Schorn, and Eli Sohl.

References

[1] Mike Shema, Chapter 7 – Leveraging Platform Weaknesses, Hacking Web Apps, Syngress, 2012, Pages 209-238, ISBN 9781597499514, https://doi.org/10.1016/B978-1-59-749951-4.00007-2.

[2] Matsumoto, Makoto; Nishimura, Takuji; Hagita, Mariko; Saito, Mutsuo (2005). “Cryptographic Mersenne Twister and Fubuki Stream/Block Cipher” (PDF).

[3] Everyone Talks About Insecure Randomness, But Nobody Does Anything About It

AnyDesk Escalation of Privilege (CVE-2021-40854)

18 October 2021 at 09:51

Summary

Assigned CVE: CVE-2021-40854 has been assigned for the report of RedyOps Labs.

Known to Neurosoft’s RedyOps Labs since: 20/07/2021

Exploit Code: N/A

Vendor’s Advisory: https://anydesk.com/cve/2021-40854/

An Elevation of Privilege (EoP) exists in AnyDesk for Windows from versions 3.1.0 to 6.3.2 (excluding 6.2.6). The vulnerability described gives the ability to a low privileged user to gain access as NT AUTHORITY\SYSTEM.

The exploitation took place in an installed version of AnyDesk .

Description

When someone asks to perform a connection to your AnyDesk, the User Interface (UI) which is presented in order for you to accept the connection and specify the permissions, runs as NT AUTHORITY\SYSTEM.

In this same UI, you can open the chat log, by pressing the “Open Chat Log”. The notepad which opens, runs as NT AUTHORITY\SYSTEM .

The escalation from that point is trivial, as presented in the following video.

Exploitation

In order to Exploit the issue, no special program is needed .

Video PoC Step By Step


The video is pretty match easy to follow.

A low privileged user, opens the AnyDesk and performs a connection to his own ID.

In the popup, he opens the “Chat Log” and from inside the notepad the low privileged user, spawns a cmd.exe as NT AUTHORITY\SYSTEM.

Resources

RedyOps team

RedyOps team, uses the 0-day exploits produced by Research Labs, before vendor releases any patch. They use it in special engagements and only for specific customers.

You can find RedyOps team at https://redyops.com/

Angel

Discovered 0-days which affect marine sector, are being contacted with the Angel Team. ANGEL has been designed and developed to meet the unique and diverse requirements of the merchant marine sector. It secures the vessel’s business, IoT and crew networks by providing oversight, security threat alerting and control of the vessel’s entire network.

You can find Angel team at https://angelcyber.gr/

Illicium

Our 0-days cannot win Illicium. Today’s information technology landscape is threatened by modern adversary security attacks, including 0-day exploits, polymorphic malwares, APTs and targeted attacks. These threats cannot be identified and mitigated using classic detection and prevention technologies; they can mimic valid user activity, do not have a signature, and do not occur in patterns. In response to attackers’ evolution, defenders now have a new kind of weapon in their arsenal: Deception.

You can find Illicium team at https://deceivewithillicium.com/

Neutrify

Discovered 0-days are being contacted to the Neutrify team, in order to develop related detection rules. Neutrify is Neurosoft’s 24×7 Security Operations Center, completely dedicated to threats monitoring and attacks detection. Beyond just monitoring, Neutrify offers additional capabilities including advanced forensic analysis and malware reverse engineering to analyze incidents.

You can find Neutrify team at https://neurosoft.gr/contact/

The post AnyDesk Escalation of Privilege (CVE-2021-40854) appeared first on REDYOPS Labs.

Threat Roundup for October 8 to October 15

Today, Talos is publishing a glimpse into the most prevalent threats we've observed between Oct. 8 and Oct. 15. As with previous roundups, this post isn't meant to be an in-depth analysis. Instead, this post will summarize the threats we've observed by highlighting key behavioral characteristics,...

[[ This is only the beginning! Please visit the blog for the complete entry ]]

Cracking Random Number Generators using Machine Learning – Part 1: xorshift128

15 October 2021 at 17:29

Outline

1. Introduction

This blog post proposes an approach to crack Pseudo-Random Number Generators (PRNGs) using machine learning. By cracking here, we mean that we can predict the sequence of the random numbers using previously generated numbers without the knowledge of the seed. We started by breaking a simple PRNG, namely XORShift, following the lead of the post published in [1]. We simplified the structure of the neural network model from the one proposed in that post. Also, we have achieved a higher accuracy. This blog aims to show how to train a machine learning model that can reach 100% accuracy in generating random numbers without knowing the seed. And we also deep dive into the trained model to show how it worked and extract useful information from it.

In the mentioned blog post [1], the author replicated the xorshift128 PRNG sequence with high accuracy without having the PRNG seed using a deep learning model. After training, the model can use any consecutive four generated numbers to replicate the same sequence of the PRNG with bitwise accuracy greater than 95%. The details of this experiment’s implementation and the best-trained model can be found in [2]

At first glance, this seemed a bit counter-intuitive as the whole idea behind machine learning algorithms is to learn from the patterns in the data to perform a specific task, ranging from supervised, unsupervised to reinforcement learning. On the other hand, the pseudo-random number generators’ main idea is to generate random sequences and, hence, these sequences should not follow any pattern. So, it did not make any sense (at the beginning) to train an ML model, which learns from the data patterns, from PRNG that should not follow any pattern. Not only learn, but also get a 95% bitwise accuracy, which means that the model will generate the PRNG’s exact output and only gets, on average, two bits wrong. So, how is this possible? And why can machine learning crack the PRNG? Can we even get better than the 95% bitwise accuracy? That is what we are going to discuss in the rest of this article. Let’s start first by examining the xorshift128 algorithm.

** Editor’s Note: How does this relate to security? While this research looks at a non-cryptographic PRNG, we are interested, generically, in understanding how deep learning-based approaches to finding latent patterns within functions presumed to be generating random output could work, as a prerequisite to attempting to use deep learning to find previously-unknown patterns in cryptographic (P)RNGs, as this could potentially serve as an interesting supplementary method for cryptanalysis of these functions. Here, we show our work in beginning to explore this space. **

2. How does xorshift128 PRNG work?

To understand whether machine learning (ML) could crack the xorshift128 PRNG, we need to comprehend how it works and check whether it is following any patterns or not. Let’s start by digging into its implementation of the xorshift128 PRNG algorithm to learn how it works. The code implementation of this algorithm can be found on Wikipedia and shared in [2]. Here is the function that was used in the repo to generate the PRNG sequence (which is used to train the ML model):

def xorshift128():
    '''xorshift
    https://ja.wikipedia.org/wiki/Xorshift
    '''

    x = 123456789
    y = 362436069
    z = 521288629
    w = 88675123

    def _random():
        nonlocal x, y, z, w
        t = x ^ ((x << 11) & 0xFFFFFFFF)  # 32bit
        x, y, z = y, z, w
        w = (w ^ (w >> 19)) ^ (t ^ (t >> 8))
        return w

    return _random

As we can see from the code, the implementation is straightforward. It has four internal numbers, x, y, z, and w, representing the seed and the state of the PRNG simultaneously. Of course, we can update this generator implementation to provide the seed with a different function call instead of hardcoding it. Every time we call the generator, it will shift the internal variables as follows: y → x, z → y, and w → z. The new value of w is evaluated by applying some bit manipulations (shifts and XORs) to the old values of x and w. The new w is then returned as the new random number output of the random number generator. Let’s call the function output, o

Using this code, we can derive the logical expression of each bit of the output, which leads us to the following output bits representations of the output, o:

O0 = W0 ^ W19 ^ X0 ^ X8

O1 = W1 ^ W20 ^ X1 ^ X9

.

.

O31 = W31 ^ X20 ^ X31

where Oi is the ith bit of the output, and the sign ^ is a bitwise XOR. The schematic diagram of this algorithm is shown below:

We can notice from this algorithm that we only need w and x to generate the next random number output. Of course, we need y and z to generate the random numbers afterward, but the value of o depends only on x and w.

So after understanding the algorithm, we can see the simple relation between the last four generated numbers and the next one. Hence, if we know the last four generated numbers from this PRNG, we can use the algorithm to generate the whole sequence; this could be the main reason why this algorithm is not cryptographically secure. Although the algorithm-generated numbers seem to be random with no clear relations between them, an attacker with the knowledge of the algorithm can predict the whole sequence of xorshift128 using any four consecutive generated numbers. As the purpose of this post is to show how an ML model can learn the hidden relations in a sample PRNG, we will assume that we only know that there is some relation between the newly generated number and its last four ones without knowing the implementation of the algorithm.

Returning to the main question slightly: Can machine learning algorithms learn to generate the xorshift128 PRNG sequence without knowing its implementation using only the last four inputs?

To answer this question, let’s first introduce the Artificial Neural Networks (ANN or NN for short) and how they may model the XOR gates.

3. Neural Networks and XOR gates

Neural Networks (NN), aka Multi-Layer Perceptron (MLP), is one of the most commonly used machine learning algorithms. It consists of small computing units, called neurons or perceptrons, organized into sets of unconnected neurons, called layers. The layers are interconnected to form paths from the inputs of the NN to its outputs. The following figure shows a simple example of 2 layers NN (the input layer is not counted). The details of the mathematical formulation of the NN are out of the scope of this article.

While the NN is being trained from the input data, the connections between the neurons, aka the weights, either get stronger (high positive or low negative values) to represent a high positively/negatively strong connection between two neurons or get weaker (close to 0) to represent that there is no connection at all. At the end of the training, these connections/weights encode the knowledge stored in the NN model. 

One example used to describe how NNs handle nonlinear data is NN’s use to model a two-input XOR gate. The main reason for choosing the two-input XOR function is that although it is simple, its outputs are not linearly separable [3]. Hence, using an NN with two layers is the best way to represent the two input XOR gate. The following figure (from Abhranil blog [3]) shows how we can use a two-layer neural network to imitate a two-input XOR. The numbers on the lines are the weights, and the number inside the nodes are the thresholds (negative bias) and assuming using a step activation function. 

In case we have more than two inputs for XOR, we will need to increase the number of the nodes in the hidden layer (the layer in the middle) to make it possible to represents the other components of the XOR. 

4. Using Neural Networks to model the xorshift128 PRNG

Based on what we discussed in the previous section and our understanding of how the xorshift128 PRNG algorithm works, it makes sense now that ML can learn the patterns the xorshift128 PRNG algorithm follows. We can also now decide how to structure the neural network model to replicate the xorshift128 PRNG algorithm. More specifically, we will use a dense neural network with only one hidden layer as that is all we need to represent any set of XOR functions as implemented in the xorshift128 algorithm. Contrary to the model suggested in [1], we don’t see any reason to use a complex model like LSTM (“Long short-term memory,” a type of recurrent neural network), especially that all output bits are directly connected to sets of the input bits with XOR functions, as we have shown earlier, and there is no need for preserving internal states, as it is done in the LSTM.

4.1 Neural Network Model Design 

Our proposed model would have 128 inputs in the input layer to represent the last four generated integer numbers, 32-bit each, and 32 outputs in the output layer to express the next 32-bit random generated number. The main hyperparameter that we need to decide is the number of neurons/nodes in the hidden layer. We don’t want a few nodes that can not represent all the XOR functions terms, and at the same time, we don’t want to use a vast number of nodes that would not be used and would complex the model and increase the training time. In this case, and based on the number of inputs, outputs, and the XOR functions complexity, we made an educated guess and used 1024 hidden nodes for the hidden layer. Hence, our neural network structure is as follows (the input layer is ignored):

_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
dense (Dense)                (None, 1024)              132096    
_________________________________________________________________
dense_1 (Dense)              (None, 32)                32800     
=================================================================
Total params: 164,896
Trainable params: 164,896
Non-trainable params: 0
_________________________________________________________________

As we can see, the number of the parameters (weights and biases) of the hidden layer is 132,096 (128×1024 weights + 1024 biases), and the number of the parameters of the output layer is 32,800 (1024×32 weights + 32 biases), which gets to a total of 164,896 parameters to train. Comparing to the model used in [1]:

_________________________________________________________________
Layer (type) Output Shape Param # ================================================================= lstm (LSTM) (None, 1024) 4329472 _________________________________________________________________ dense (Dense) (None, 512) 524800 _________________________________________________________________ dense_1 (Dense) (None, 512) 262656 _________________________________________________________________ dense_2 (Dense) (None, 512) 262656 _________________________________________________________________ dense_3 (Dense) (None, 512) 262656 _________________________________________________________________ dense_4 (Dense) (None, 512) 262656 _________________________________________________________________ dense_5 (Dense) (None, 32) 16416 ================================================================= Total params: 5,921,312 Trainable params: 5,921,312 Non-trainable params: 0 _________________________________________________________________

This complex model has around 6 million parameters to train, which will make the model training harder and take a much longer time to get good results. It could also easily be stuck in one of the local minima in that huge 6 million dimension space. Additionally, storing this model and serving it later will use 36 times the space needed to store/serve our model. Furthermore, the number of the model parameters affects the amount of required data to train the model; the more parameters, the more input data is needed to train it.

Another issue with that model is the loss function used. Our target is to get as many correct bits as possible; hence, the most suitable loss function to be used in this case is “binary_crossentropy,” not “mse.” Without getting into the math of both of these loss functions, it is sufficient to say that we don’t care if the output is 0.8, 0.9, or even 100. All we care about is that the value crosses some threshold to be considered one or not to be considered zero. Another non-optimal choice of the parameter in that model is the activation function of the output layer.  As the output layer comprises 32 neurons, each representing a bit whose value should always be 0 or 1, the most appropriate activation function for these types of nodes is usually a sigmoid function. On the contrary, that model uses a linear activation function to output any value from -inf to inf. Of course, we can threshold those values to convert them to zeros and ones, but the training of that model will be more challenging and take more time. 

For the rest of the hyperparameters, we have used the same as in the model in [1]. Also, we used the same input sample as the other model, which consists of around 2 million random numbers sampled from the above PRNG function for training, and 10,000 random numbers for testing. Training and testing random number samples are formed into a quadrable of consequent random numbers to be used as inputs to the model, and the next random number is used as an output to the model. It is worth mentioning that we don’t think we need that amount of data to reach the performance we reached; we just tried to be consistent with the referenced article. Later, we can experiment to determine the minimum data size sufficient to produce a well-trained model. The following table summarises the model parameters/ hyperparameters for our model in comparison with the model proposed in [1]:

NN Model in [1] Our Model
Model Type LSTM and Dense Network Dense Network
#Layers 7 2
#Neurons 1024 LSTM
2592 Dense 
1056 Dense
#Trainable Parameters 5,921,312
(4,329,472 only from the LSTM layer)
164,896
Activation Functions Hidden Layers: relu
Output Layer: linear
Hidden Layer: relu
Output Layer: sigmoid
Loss Function mse binary_crossentropy
Comparison between our proposed model and the model from the post in [1]

4.2 Model Results

After a few hours of model training and optimizing the hyperparameters, the NN model has achieved 100% bitwise training accuracy! Of course, we ran the test set through the model, and we got the same result with 100% bitwise accuracy. To be more confident about what we have achieved, we have generated a new sample of 100,000 random numbers with a different seed than those used to generate the previous data, and we got the same result of 100% bitwise accuracy. It is worth mentioning that although the referenced article achieved 95%+ bitwise accuracy, which looks like a promising result. However, when we evaluated the model accuracy (versus bitwise accuracy), which is its ability to generate the exact next random numbers without any bits flipped, we found that the model’s accuracy dropped to around 26%. Compared to our model, we have 100% accuracy.  The 95%+ bitwise is not failing only on 2 bits as it may appear (95% of 32 bit is less than 2), but rather these 5% errors are scattered throughout 14 bits. In other words, although the referenced model has bitwise accuracy of 95%+, 18 bits only would be 100% correct, and on average, 2 bits out of the rest of the 14 bits would be altered by the model.

The following table summarizes the two model comparisons:

NN Model in [1] Our Model
Bitwise Accuracy 95% + 100%
Accuracy 26% 100%
Bits could be Flipped (out of 32) 14 0
Models accuracy comparison

In machine learning, getting 100% accuracy is usually not good news. It normally means either we don’t have a good representation of the whole dataset, we are overfitting the model for the sample data, or the problem is deterministic that it does not need machine learning. In our case, it is close to the third choice. The xorshift128 PRNG algorithm is deterministic; we need to know which input bits are used to generate output bits, but the function to connect them, XOR, is already known. So, in a way, this algorithm is easy for machine learning to crack. So, for this problem, getting a 100% accurate model means that the ML model has learned the bits connection mappings between the input and output bits. So, if we get any consequent four random numbers generated from this algorithm, the model will generate the random numbers’ exact sequence as the algorithm does, without knowing the seed.

4.3 Model Deep Dive

This section will deep dive into the model to understand more what it has learned from the PRNG data and if it matches our expectations. 

4.3.1 Model First Layer Connections

As discussed in section 2, the xorshift128 PRNG uses the last four generated numbers to generate the new random number. More specifically, the implementation of xorshift128 PRNG only uses the first and last numbers of the four, called w and x, to generate the new random number, o. So, we want to check the weights that connect the 128 inputs, the last four generated numbers merged, from the input layer to the hidden layer, the 1024 hidden nodes, of our trained model to see how strong they are connected and hence we can verify which bits are used to generate the outputs. The following figure shows the value of weights on the y-axes connecting each input, in x-axes, to the 1024 hidden nodes in the hidden layer. In other words, each dot on the graph represents the weight from any of the hidden nodes to the input in the x-axes:

As we can see, very few of the hidden nodes are connected to one of the inputs, where the weight is greater than ~2.5 or less than ~-2.5. The rest of the nodes with low weights between -2 and 2 can be considered not connected. We can also notice that inputs between bit 32 and 95 are not connected to any hidden layers. This is because what we stated earlier that the implementation of xorshift128 PRNG uses only x, bits from 0 to 31, and w, bits from 96 to 127, from the inputs, and the other two inputs, y, and z representing bits from 32 to 95, are not utilized to generate the output, and that is exactly what the model has learned. This implies that the model has learned that pattern accurately from the data given in the training phase.

4.3.2 Model Output Parsing

Similar to what we did in the previous section, the following figure shows the weights connecting each output, in x-axes, to the 1024 hidden nodes in the hidden layer:

Like our previous observation, each output bit is connected only to a very few nodes from the hidden layer, maybe except bit 11, which is less decisive and may need more training to be less error-prone. What we want to examine now is how these output bits are connected to the input bits. We will explore only the first bit here, but we can do the rest the same way. If we focus only on bit 0 on the figure, we can see that it is connected only to five nodes from the hidden layers, two with positive weights and three with negative weights, as shown below:

Those connected hidden nodes are 120, 344, 395, 553, and 788. Although these nodes look random, if we plot their connection to the inputs, we get the following figure:

As we can see, very few bits affect those hidden nodes, which we circled in the figure. They are bits 0, 8, 96, and 115, which if we modulo them by 32, we get bits 0 and 8 from the first number, x, and 0 and 19 from the fourth number, w. This matches the xorshift128 PRNG function we derived in section 2, the first bit of the output is the XOR of these bits, O0 = W0 ^ W19 ^ X0 ^ X8.  We expect the five hidden nodes with the one output node to resemble the functionality of an XOR  of these four input bits. Hence, the ML model can learn the XOR function of any set of arbitrary input bits if given enough training data.

5. Creating a machine-learning-resistant version of xorshift128

After analyzing how the xorshift128 PRNG works and how NN can easily crack it, we can suggest a simple update to the algorithm, making it much harder to break using ML. The idea is to introduce another variable in the seed that will decide:

  1. Which variables to use out of x, y, z, and w to generate o.
  2. How many bits to shift each of these variables.
  3. Which direction to shift these variables, either left or right.

This can be binary encoded in less than 32 bits. For example, only w and x variables generate the output in the sample code shown earlier, which can be binary encoded as 1001. The variable w is shifted 11 bits to the right, which can be binary encoded as 010110, where the least significant bit, 0, represents the right direction. The variable x is shifted 19 bits to the right, which can be binary encoded as 100111, where the least significant bit, 1, represents the left direction. This representation will take 4 bits + 4×6 bits = 28 bits. For sure, we need to make sure that the 4 bits that select the variables do not have these values: 0000, 0001, 0010, 0100, and 1000, as they would select one or no variable to XOR. 

Using this simple update will add small complexity to PRNG algorithm implementation. It will still make the ML model learn only one possible sequence of about 16M different sequences generated with the same algorithm with different seeds. As if the ML would have only learned the seed of the algorithm, not the algorithm itself. Please note that the purpose of this update is to make the algorithm more resilient to ML attacks, but the other properties of the outcome PRNG, such as periodicity, are yet to be studied.

6. Conclusion

This post discussed how the xorshift128 PRNG works and how to build a machine learning model that can learn from the PRNG’s “randomly” generated numbers to predict them with 100% accuracy. This means that if the ML model gets access to any four consequent numbers generated from this PRNG, it can generate the exact sequence without getting access to the seed. We also studied how we can design an NN model that would represent this PRNG the best. Here are some of the lessons learned from this article:

  1. Although xorshift128 PRNG may to humans appear to not follow any pattern, machine learning can be used to predict the algorithm’s outputs, as the PRNG still follows a hidden pattern.
  2.  Applying deep learning to predict PRNG output requires understanding of the specific underlying algorithm’s design.
  3. It is not necessarily the case that large and complicated machine learning models would attain higher accuracy that simple ones.
  4. Simple neural network (NN) models can easily learn the XOR function.

The python code for training and testing the model outlined in this post is shared in this repo in a form of a Jupyter notebook. Please note that this code is based on top of an old version of the code shared in the post in [1] with the changes explained in this post. This was intentionally done to keep the same data and other parameters unchanged for a fair comparison.

Acknowledgment

I would like to thank my colleagues in NCC Group for their help and support and for giving valuable feedback the especially in the crypto/cybersecurity parts. Here are a few names that I would like to thank explicitly: Ollie Whitehouse, Jennifer Fernick, Chris Anley, Thomas Pornin, Eric Schorn, and Marie-Sarah Lacharite.

References

[1] Everyone Talks About Insecure Randomness, But Nobody Does Anything About It

[2] The repo for code implementation  for [1]

[3] https://blog.abhranil.net/2015/03/03/training-neural-networks-with-genetic-algorithms/

Talos Takes Ep. #73 (NCSAM edition): Fight the phish from land, sea and air

15 October 2021 at 15:07
By Jon Munshaw. The latest episode of Talos Takes is available now. Download this episode and subscribe to Talos Takes using the buttons below, or visit the Talos Takes page. Most people may think of spam as being the classic email promising that you've won the lottery or some great prize,...

[[ This is only the beginning! Please visit the blog for the complete entry ]]

Adding a Beta NAS Device to Pwn2Own Austin

14 October 2021 at 20:26

Today, we are announcing the inclusion of the beta version of the Western Digital 3TB My Cloud Home Personal Cloud in our upcoming Pwn2Own Austin competition. Normally, devices under test are updated to the most recent publicly available patch level. This is still the case. However, our partners over at Western Digital wanted to include their upcoming beta software release in this year’s event. Consequently, we are adding the beta version as an available target in addition to the existing current version of the NAS device.

If a contestant can get code execution on the beta release of the Western Digital 3TB My Cloud Home Personal Cloud, they will earn $45,000 (USD) and 5 Master of Pwn points. There are some significant differences between the released software version and the beta version, so we suggest contestants upgrade their systems to test their exploits prior to the contest. To get the beta version installed on your NAS, you will need to enter your email address and the MAC address of your device in this form. Within a few hours, an automated process to update the NAS will begin. The updates will take you from 7.15.1-101 (current) to 7.16.0-216 and then the beta 8.0.0-301. Please note that not all features and applications included in the current version of the software release are available in the beta version.

Again, registration for the contest closes at 5:00 p.m. Eastern Daylight Time on October 29, 2021. A full copy of the rules – including this new change – is available here. They may be changed at any time without notice. We highly encourage potential entrants to read the rules thoroughly and completely should they choose to participate. If you have any questions, please forward them to [email protected].

We believe exploiting the beta version of this software will not be trivial, but we certainly hope some tries. We look forward to seeing all the attempts to learn about the latest exploits and attack techniques on these devices.

Good luck, and we’ll see you in Austin.

Adding a Beta NAS Device to Pwn2Own Austin

Threat Source newsletter (Oct. 14, 2021)

14 October 2021 at 18:00
Newsletter compiled by Jon Munshaw.Good afternoon, Talos readers.   It's still Cybersecurity Awareness Month, and what better way to celebrate by patching and then patching some more?  This week was Microsoft Patch Tuesday, which only included two critical vulnerabilities, but still...

[[ This is only the beginning! Please visit the blog for the complete entry ]]

Vulnerability Spotlight: Code execution vulnerabilities in Nitro Pro PDF

14 October 2021 at 17:17
A Cisco Talos team member discovered these vulnerabilities. Blog by Jon Munshaw.  Cisco Talos recently discovered multiple vulnerabilities in the Nitro Pro PDF reader that could allow an attacker to execute code in the context of the application.  Nitro Pro PDF is part of Nitro Software’s...

[[ This is only the beginning! Please visit the blog for the complete entry ]]

NCC Group placed first in global 5G Cyber Security Hack competition

14 October 2021 at 09:00

In June of this year, Traficom – the Finnish transport and communications agency – along with the Aalto University, Cisco, Ericsson, Nokia, and PwC, organized the 5G Cyber Security Hack competition. A similar event was organised in November 2019 in Oulu, Finland and this hackathon-style event was a follow-up to their successful 2019 event. Due to Covid restrictions, this year’s event was fully remote and as such made it easier for a larger pool of security experts to take part. Similar to 2019, there were four interesting hacking challenges relating to 5G technology and its use cases. The NCC Group team participated in the 2019 event and finished in second place in the Ericsson challenge, so we were eager to take on this year’s challenge.  A large number of participants took part in the event this year with around 130 security experts from around the world, and teams are invited to participate by application only.

This year, NCC Group consultants, Ross Bradley, Eva Esteban Molina, Phil Marsden and Mark Tedman took part in the Ericsson “Hack the Crown Jewels” challenge, for which they won first prize.

5G networks offer greater connectivity and faster wireless broadband and are now being rolled out over a large proportion of the world with it now being used extensively by 250+ million subscribers world-wide. With that comes the exponential increase in 5G speed and capacity, and there has been a decisive shift in technology development pushing new capability from home networks to critical national infrastructures all using 5G equipment. The security of 5G networks is now high on the agenda of many operators, vendors and governments and excellent events like the 5G Cyber Security hackathon highlight the continuing work ongoing to help secure these networks. 

This Hack challenge offered hackers four real-life use cases for 5G to work on – a Cisco CTF challenge on a staged 5G network, a 5G Ericsson Packet Core Gateway, Nokia’s edge data center system and PwC/Aalto University’s 5G testbed.

The Hack is admittedly fun, but it isn’t just for fun – these kinds of activities play a really crucial part in the iterative design and engineering process for network equipment manufacturers to ensure the security and safety of users. Each company putting forward a use case for hacking also offering up bug bounty prize money, which is shared among the best hacker(s) / team(s), as one mechanism for helping vendors to ideally find and remediate security risks in their systems before threat actors can exploit them.

The 5G Packet Core Gateway challenge

The details of the overall event, including the findings of individual challenges, are subject to non-disclosure agreements, but the exercise itself was a great demonstration of our Telecommunication Practice’s capability. The 5G Ericsson Packet Core Gateway is a key part of the mobile 5G core infrastructure and it is essential that all vendors understand how vulnerabilities and security risks within it could be exploited by an attacker. We dedicated our time to on the Ericsson challenge, which focussed on finding security weaknesses in the backbone of the mobile 5G core infrastructure – the Packet Core Gateway.

The hackathon started with an introduction on Friday evening, with challenges opening up to competitors at 8pm. Hacking continued all weekend till 11am on the Sunday morning. Our findings were shared throughout the competition with the Ericsson team, with final findings submitted before the 11am deadline on the Sunday morning. Prizes were award on the Sunday afternoon at which time it was revealed we had won the Ericsson challenge (plus come second in the team photo)! The event was well-run by the Ericsson team and the event organisers and we look forward to participating in any future events. 

About NCC Group’s Telecommunications Practice

This competition is just one of the many ways that NCC Group is actively improving the security of 5G infrastructure. In addition to our 5G security research (some of which is published on this blog), we regularly work closely with network equipment manufacturers and operators alike, including through paid consulting engagements. We conduct regular security reviews of 5G core and RAN equipment, with specialist researchers and consultants skilled in everything from reverse engineering and hardware reviews to detailed high level threat assessments.

All aboard the internship – whispering past defenses and sailing into kernel space

13 October 2021 at 12:25

Previously, we have already published Sander’s (@cerbersec) internship testimony. Since this post does not really contain any juicy technical details and Sander has done a terrific job putting together a walkthrough of his process, we thought it would be a waste not to highlight his previous posts again.

In Part 1, Sander explains how he started his journey and dove into process injection techniques, WIN32 API (hooking), userland vs kernel space, and Cobalt Strike’s Beacon Object Files (BOF).

Just being able to perform process injection using direct syscalls from a BOF did not signal the end of his journey yet, on the contrary. In Part 2, Sander extended our BOF arsenal with additional process injections techniques and persistence. With all this functionality bundled in an Agressor Script, CobaltWispers was born.

We are considering to open source this little framework, but some final tweaks would be required first, as explained in the part 2 blog post.

While this is the end (for now) of Sander’s BOF journey, we have another challenging topic lined up for him: The Kernel. Here’s a little sneak peek of the next blog series/walkthrough we will be releasing. Stay tuned!


KdPrint(“Hello, world!\n”);

When I finished my previous internship, which was focused on bypassing Endpoint Detection and Response (EDR) software and Anti-Virus (AV) software from a user land point of view, we joked around with the idea that the next topic would be defeating the same problem but from kernel land. At that point in time I had no experience at all with the Windows kernel and it all seemed very advanced and above my level of technical ability. As I write this blogpost, I have to admit it wasn’t as scary or difficult as I thought it to be. C/C++ is still C/C++ and assembly instructions are still headache-inducing, but comprehensible with the right resources and time dedication.

In this first post, I will lay out some of the technical concepts and ideas behind the goal of this internship, as well as reflect back on my first steps in successfully bypassing/disabling a reputable Anti-Virus product, but more on that later.


About the authors

Jonas is NVISO’s red team lead and thus involved in all red team exercises, either from a project management perspective (non-technical), for the execution of fieldwork (technical), or a combination of both. You can find Jonas on LinkedIn.
Sander is a cyber security student with a passion for red teaming and malware development. He’s a two-time intern at NVISO and a future NVISO bird.

Paradoxical Compression with Verifiable Delay Functions

13 October 2021 at 10:00

We present here a new construction which has no real immediate usefulness, but is a good illustration of a fundamental concept of cryptography, namely that there is a great difference between knowing that some mathematical object exists, and being able to build it in practice. Thus, this construction can be thought of as having some educational virtue, in the spirit of the mathematical recreations that have been a favourite pastime of mathematicians since at least the early 17th century.

We call paradoxical compression a lossless compression algorithm such that:

  • On any input x (a sequence of bits), it produces an output C(x) (another sequence of bits) such that the original x can be rebuilt from C(x) (the compression is lossless).
  • There is at least one input x0 such that C(x0) is strictly shorter than x0 (the algorithm really deserves to be called a compression algorithm).
  • The input size is never increased: for all x, C(x) is never strictly longer than x.

This is mathematically impossible. There is no compression algorithm that can achieve these three properties simultaneously. Despite this impossibility, we can achieve it in practice. A paper describing the algorithm, and a demo implementation, are available at:
https://github.com/pornin/paradox-compress

The paper is also available on eprint.

A Few Important Warnings

  • This is not “infinite compression”. We are not talking about a compression algorithm that could reduce the size of every possible input. That would be very useful indeed, but it is impossible, and, so to speak, a lot more impossible than paradoxical compression. We merely claim that the output is not larger than the input, not that it is always shorter.
  • Both paradoxical and infinite compression have been “achieved” by various people when working on files by moving some information into the file metadata (file name, date of creation or last modification…). We are not dealing here with such tricks; our inputs and outputs are generic sequences of bits, that do not have any metadata.

On the Impossibility of Paradoxical Compression

Let x0 be an input which is reduced by the compression algorithm. For instance, suppose that x0 has length 1000 bits, bit C(x0) has length 900 bits. For the compression algorithm to deserve that name, such an input must exist.

There are exactly 2901-1 possible bit sequences of up to 900 bits. Every one of them should be compressible into another bit sequence of up to 900 bits. Thus, all 2901-1 bit sequences are mapped to bit sequences in the same set. But the mapping must be injective: if two bit sequences are mapped to the same output, then that output cannot be reliably decompressed, since there would be two corresponding inputs. The hypothesis of losslessness forbids that situation. Therefore, all 2901-1 bit sequences of length up to 900 bits are mapped by the compression algorithm to 2901-1 distinct bit sequences of length up to 900 bits. This necessarily exhausts all of them, in particular C(x0). This implies that the compression of x0 produces an output which is also the result of compressing another input x1 distinct from x0 (since x1 has length at most 900 bits, while x0 has length 1000 bits). This is a contradiction: the compression algorithm cannot be lossless.

This simple demonstration is an application of what is now known as the pigeonhole principle, a well-known remark that can be expressed in set theory as follows: there cannot be an injective mapping from a finite set S1 to a finite set S2 if the cardinality of S2 is strictly lower than the cardinality of S1. In the early 17th century, the French Jesuit Jean Leucheron used it to explain that there necessarily are at least two men in the world with the same number of hair, since there are more men in the world than hairs on the head of any single man. The principle was restated several times along the centuries, with various metaphors, one of the most recent ones involving pigeons in a dovecote.

In the context of paradoxical compression, the pigeonhole principle basically means that there must exist a “problematic input” x that breaks one of the alleged properties: either C(x) is larger than x, or decompression of C(x) does not yield x back, or the compression or decompression algorithm fails to terminate.

Achieving Paradoxical Compression

Since we know that paradoxical compression is impossible, achieving it nonetheless is going to require some creativity. In other words, we will “cheat”. The conceptual trick here is the following: there exist some “problematic inputs”, but there are not necessarily many of them. Maybe we can arrange for them to be hard to find? In essence, we are moving the target: we won’t make an algorithm that can be proven to always work, but we might make one such that nobody can find a problematic input (even though everybody knows that these problematic inputs necessarily exist).

This is where the gist of the trick lies: there is an existence proof for problematic inputs, but this is not a constructive proof since it only demonstrates that such inputs must exist; it does not reveal them. In the world of mathematics, existence proofs are enough to conclude; but in the world of computers, which operate under finite resources, constructive proofs do not automatically follow. Most of cryptography strives in the gap between existence and constructive proofs. For instance, given a strong, cryptographically secure hash function such as SHA-256, we perfectly know that collisions must exist (the SHA-256 input space is vastly larger than the SHA-256 output space, so the SHA-256 function cannot be injective), but nobody ever managed to find one. We can even describe algorithms that will produce collisions if executed, but at a computational cost that far exceeds our available resources, so we cannot do that in practice.

In the case of paradoxical compression, the construction can be described as successive improvements. Let’s start with a “normal” compression algorithm such as DEFLATE (the core algorithm in the GZip and Zlib formats, also used in traditional Zip archives). This algorithm can reduce the size of some sequences of bits, in particular structured data commonly encountered in practical applications. However, for most possible inputs (e.g. output of a random generator), it will slightly increase the size. We want to fix that. A simple method is the following:

  • Compression: if DEFLATE(x) is shorter than x, then return DEFLATE(x). Otherwise, return x.
  • Decompression: if y can be inflated into x, then return x. Otherwise, return y.

This method assumes that a “compressed output” can be unambiguously distinguished from any other sequence of bits. This is, of course, not true, and it is easy to make this algorithm fail by applying the compressor on its own output: for a given x, C(x) is usually not itself compressible (after all, DEFLATE does a good job at removing the redundancies that DEFLATE itself leverages), so that C(C(x)) will return C(x) itself. Then, decompression of C(C(x)) will return x instead of C(x). C(x) is then a “problematic input” since it does not survive a compression-decompression cycle.

Now, we can handle that case by adjoining a counter in some sort of header. Compressed outputs will start with a counter of 0. If the input to the compression algorithm is already a compressed output, we’ll just increase the counter; the counter is really the number of nested invocations of the compression algorithm. This leads to the following:

  • Compression of x:
    • If DEFLATE(x) is shorter than x with some extra room for our counter (say, a 32-bit counter), then return DEFLATE(x) || 0.
    • Otherwise, if the input is itself x = DEFLATE(x’) || c for some input x’ and counter c, then return DEFLATE(x’) || c+1.
    • Otherwise, return x.
  • Decompression of y:
    • If y = DEFLATE(x) || 0 for some x, then return x.
    • Otherwise, if y = DEFLATE(x) || c from some input x and counter c > 0, then return DEFLATE(x) || c-1.
    • Otherwise, return y.

Does this work? Mostly. If you implement it and try it on some examples, including by trying to compress a compression output, then this seems to work. However, the problematic inputs are not unreachable. It can be shown that the above necessarily works except in a single place, which is the computation of c+1 in the second compression case: since the counter slot has a given fixed length, it may overflow. For instance, if the counter is a 32-bit integer, and c happens to be equal to 232-1, then c+1 does not fit. The compression algorithm would typically truncate the value to its low 32 bits, yielding 0, and decompression would not return the proper bit sequence. In other words, the “problematic inputs” are exactly the values DEFLATE(x) || 0xFFFFFFFF. You might take solace in the idea that it is unlikely that such an input occurs in practical, non-adversarial situations, but they are still easy enough to build. Thus, this is not good enough for us. We want a construction that cannot be broken even when actively trying to find a problematic input.

Note, though, that building a problematic input by compressing the output repeatedly is very inefficient; with a 32-bit counter, it takes more than 4 billions of recursive calls. Of course, if you want to make a problematic input on purpose, you don’t do that; you directly set the counter to the all-ones value. But what if you can’t?

Let’s now suppose that the compression and decompression algorithms are really compression and decompression systems that can be invoked, but which are opaque to attackers (this is called the “blackbox model”). In that case, we may imagine that the compressor somehow signs its outputs, so that attackers who want to make a problematic input cannot directly set the counter to the all-ones value; if the attacker must use the compression system to increase the counter, then reaching a counter overflow would require an inordinately large number of invocations. If the counter is large enough (e.g. 64 bits or more), then the compression system cannot do that in any practical time. This leads us to the following construction:

  • The compression and decompression system are assumed to share a secret key K. That key is used in a message authentication code (MAC). The MAC is supposed to be stateless, deterministic and unforgeable; in practice, consider it to be HMAC/SHA-256, possibly with an output truncated to 128 bits.
  • Compression of x:
    • If DEFLATE(x) is shorter than x by at least 64+128 = 192 bits, then return DEFLATE(x) || 0 || MAC(DEFLATE(x) || 0) (i.e. the concatenation of DEFLATE(x), the counter value 0 over 64 bits, and a 128-bit MAC computed over these two elements).
    • Otherwise, if the input is x = DEFLATE(x’) || c || MAC(DEFLATE(x’) || c) for some input x’ and counter c, then return DEFLATE(x’) || c+1 || MAC(DEFLATE(x’) || c+1).
    • Otherwise, return x.
  • Decompression of y:
    • If y = DEFLATE(x) || 0 || MAC(DEFLATE(x) || 0) for some input x, then return x.
    • Otherwise, if y = DEFLATE(x) || c || MAC(DEFLATE(x) || c) for some input x and counter c > 0, then return DEFLATE(x) || c-1 || MAC(DEFLATE(x) || c-1).
    • Otherwise, return y.

This construction achieves paradoxical construction because the problematic inputs (with counter c close to overflow) can be obtained only from the compression system itself (since we assume the MAC to be unforgeable), and the compression system produces new counter values only in minimal increments; getting a problematic input then requires too many (264 in this example) invocations of the compression system to be done in practice. If problematic inputs cannot be built, we win: nobody can make our compression/decompression system fail.

Removing the Shared Key Requirement

The construction above uses a MAC that relies on a shared secret value (the MAC key) between the compression and decompression systems. This is a restrictive model and we would like to remove this requirement. Can we build the same system, but in a keyless fashion?

We can, with a verifiable delay function (VDF). Such functions have been recently defined. In a nutshell, a VDF is a sort of hash function that takes a configurable time to compute (with no known shortcut) but also comes with a proof of correct computation, and the proof can be verified at a low computational cost. This is an extension of earlier time-lock puzzles. One can think of a VDF as a time-lock puzzle that can be verified to have been properly set.

In our paradoxical compression system described above, we used a MAC with a secret key in order to prevent outsiders from building problematic inputs. We can replace the MAC with a VDF, using the counter itself as work factor: an input with a very high counter value cannot be built in practice because that would mean a very high work factor: the secrecy of the key is replaced with a “computational wall” of the VDF becoming too expensive the compute for high counter values.

I implemented this method, using a VDF designed by Wesolowski. It is based on computing squarings modulo a big composite integer (i.e. an RSA modulus): raising an input x to the power 2c modulo n is postulated to require c successive squarings modulo n, with no known “shortcut” if the factorization of n is not known. Wesolowski’s VDF comes with a compact proof of correct construction, that can be efficiently verified. The implementation is in C# (mostly because C# has a DEFLATE implementation in its standard library, and I already had some C# implementation of the SHA3/SHAKE hash functions, and of big integers with primality tests). The 2048-bit modulus was generated as a random RSA key (I discarded the prime factors; now nobody knows them). The code can be obtained on GitHub.

The code is open-source and you may reuse it as you will (under the MIT license), but of course achieving paradoxical compression is not, in fact, a very useful property: it does not solve any real, practical problem. However, it is an enjoyable party trick.

Vulnerability Spotlight: Use-after-free vulnerability in Microsoft Excel could lead to code execution

12 October 2021 at 19:48
Marcin “Icewall” Noga of Cisco Talos discovered this vulnerability. Blog by Jon Munshaw.  Cisco Talos recently discovered a use-after-free vulnerability in the ConditionalFormatting functionality of Microsoft Office Excel 2019 that could allow an attacker to execute arbitrary code on the...

[[ This is only the beginning! Please visit the blog for the complete entry ]]

Techniques for String Decryption in macOS Malware with Radare2

12 October 2021 at 17:52

If you’ve been following this series so far, you’ll have a good idea how to use radare2 to quickly triage a Mach-O binary statically and how to move through it dynamically to beat anti-analysis attempts. But sometimes, no matter how much time you spend looking at disassembly or debugging, you’ll hit a roadblock trying to figure out your macOS malware sample’s most interesting behavior because much of the human-readable ‘strings’ have been rendered unintelligible by encryption and/or obfuscation.

That’s the bad news; the good news is that while encryption is most definitely hard, decryption is, at least in principle, somewhat easier. Whatever methods are used, at some point during execution the malware itself has to decrypt its code. This means that, although there are many different methods of encryption, most practical implementations are amenable to reverse engineering given the right conditions.

Sometimes, we can do our decryption statically, perhaps emulating the malware’s decryption method(s) by writing our own decryption logic(s). Other times, we may have to run the malware and extract the strings as they are decrypted in memory. We’ll take a practical look at using both of these techniques in today’s post through a series of short case studies of real macOS malware.

First, we’ll look at an example of AES 128 symmetric encryption used in the recent macOS.ZuRu malware and show you how to quickly decode it; then we’ll decrypt a Vigenère cipher used in the WizardUpdate/Silver Toucan malware; finally, we’ll see how to decode strings dynamically, in-memory while executing a sample of a notorious adware installer.

Although we cannot cover all the myriad possible encryption schemes or methods you might encounter in the wild, these case studies should give you a solid basis from which to tackle other encryption challenges. We’ll also point you to some further resources showcasing other macOS malware decryption strategies to help you expand your knowledge.

For our case studies, you can grab a copy of the malware samples we’ll be using from the following links:

  1. macOS.ZuRu pwd:infect3d
  2. WizardUpdate
  3. Adware Installer

Don’t forget to use an isolated VM for all this work: these are live malware samples and you do not want to infect your personal or work device!

Breaking AES Encryption in macOS.ZuRu

Let’s begin with a recent strain of new macOS malware dubbed ‘macOS.ZuRu’. This malware was distributed inside trojanized applications such as iTerm, MS Remote Desktop and others in September 2021. Inside the malware’s application bundle is a Frameworks folder containing the malicious libcrypto.2.dylib. The sample we’re going to look at has the following hash signatures:

md5 b5caf2728618441906a187fc6e90d6d5
sha1 9873cc929033a3f9a463bcbca3b65c3b031b3352
sha256 8db4f17abc49da9dae124f5bf583d0645510765a6f7256d264c82c2b25becf8b

Let’s load it into r2 in the usual way (if you haven’t read the earlier posts in this series, catch up here and here), and consider the simple sequence of reversing steps illustrated in the following images.

Getting started with our macOS.ZuRu sample

As shown in the image above, after loading the binary, we use ii to look at the imports, and see among them CCCrypt (note that I piped this to head for display purposes). We then do a case insensitive search on ‘crypt’ in the functions list with afll~+crypt.

If we add [0] to the end of that, it gives us just the first column of addresses. We can then do a for-each over those using backticks to pipe them into axt to grab the XREFS. The entire command is:

> axt @@=`afll~crypt[0]`

The result, as you can see in the lower section of the image above, shows us that the malware uses CCCrypt to call the AESDecrypt128 block cipher algorithm.

AES128 requires a 128-bit key, which is the equivalent of 16 bytes. Though there’s a number of ways that such a key could be encoded in malware, the first thing we should do is a simple check for any 16 byte strings in the binary.

To do that quickly, let’s pipe the binary’s strings through awk and filter on the len column for ‘16’: That’s the fourth column in r2’s iz output. We’ll also narrow down the output to just cstrings by grepping on ‘string’, so our command is:

> iz | awk ‘$4==16’ | grep string

We can see the output in the middle section of the following image.

Filtering the malware’s strings for possible AES 128 keys

We got lucky! There’s two occurrences of what is obviously not a plain text string. Of course, it could be anything, but if we check out the XREFS we can see that this string is provided as an argument to the AESDecrypt method, as illustrated in the lower section of the above image.

All that remains now is to find the strings that are being deciphered. If we get the function summary of AESDecrypt from the address shown in our last command, 0x348b, it reveals that the function is using base64 encoded strings.

> pds @ 0x348b
Grabbing a function summary in r2 with the pds command

A quick and dirty way to look for base64 encoded strings is to grep on the “=” sign. We’ll use r2’s own grep function, ~ and pipe the result of that through another filter for “str” to further refine the output.

> iz~=~str
A quick-and-dirty grep for possible base64 cipher strings

Our search returns three hits that look like good candidates, but the proof is in the pudding! What we have at this point is candidates for:

  1. the encryption algorithm – AES128
  2. the key – “quwi38ie87duy78u”
  3. three ciphers – “oPp2nG8br7oIB+5wLoA6Bg==, …”

All we need to do now is to run our suspects through the appropriate decryption routine for that algorithm. There are online tools such as Cyber Chef that can do that for you, or you can find code for most popular algorithms for your favorite language from an online search. Here, we implemented our own rough-and-ready AES128 decryption algorithm in Go to test out our candidates:

A simple AES128 ECB decryption algorithm implemented in Go

We can pipe all the candidate ciphers to file from within r2 and then use a shell one-liner in a separate Terminal window to run each line through our Go decryption script with the candidate key.

Revealing the strings in clear text with our Go decrypter

And voila! With a few short commands in r2 and a bash one-liner, we’ve decrypted the strings in macOS.ZuRu and found a valuable IoC for detection and further investigation.

Decoding a Vigenère Cipher in WizardUpdate Malware

In our second case study, we’re going to take a look at the string encryption used in a recent sample of WizardUpdate malware. The sample we’ll look at has the following hash signatures:

md5 0c91ddaf8173a4ddfabbd86f4e782baa
sha1 3c224d8ad6b977a1899bd3d19d034418d490f19f
sha256 73a465170feed88048dbc0519fbd880aca6809659e011a5a171afd31fa05dc0b

We’ll follow the same procedure as last time, beginning with a case insensitive search of functions with “crypt” in the name, filtering the results of that down to addresses, and getting the XREFS for each of the addresses. This is what it looks like on our new sample:

Finding our way to the string encryption code from the function analysis

We can see that there are several calls from main to a decrypt function, and that function itself calls sym.decrypt_vigenere.

Vigenère is a well-known cipher algorithm which we will say a bit more about shortly, but for now, let’s see if we can find any strings that might be either keys or ciphers.

Since a lot of the action is happening in main, let’s do a quick pds summary on the main function.

Using pds to get a quick summary of a function

There are at least two strings of interest. Let’s take a better look by leveraging r2’s afns command, which lists all strings associated with the current function.

r2’s afns can help you isolate strings in a function

That gives us a few more interesting looking candidates. Given its length and form, my suspicion at this point is that the “LBZEWWERBC” string is likely the key.

We can isolate just the strings we want by successive filtering. First, we get just the rows we want:

> afns~:1..5

And then grab just the last column (ignoring the addresses):

> afns~:1..5[2]

Then using sed to remove the “str.” prefix and grep to remove the “{MAID}” string, we end up with:

Access to the shell in r2 makes it easy to isolate the strings of interest

As before, we can now pipe these out to a “ciphers” file.

> afns~:1..5[2] | grep -v MAID | sed ‘s/str.//g’ > ciphers

Let’s next turn to the encryption algorithm. Vigenère has a fascinating history. Once thought to be unbreakable, it’s now considered highly insecure for cryptography. In fact, if you like puzzles, you can decrypt a Vigenère cipher with a manual table.

The Vigenère cipher was invented before computers and can be solved by hand

One of the Vigenère cipher’s weaknesses is that it’s possible to discern patterns in the ciphertext that can reveal the length of the key. That problem can be avoided by encrypting a base64 encoding of the plain text rather than the plain text itself.

Now, if we jump back into radare2, we’ll see that WizardUpdate does indeed decode the output of the Vigenère function with a base64 decoder.

WizardUpdate malware uses base64 encoding either side of encrypting/decrypting

There is one other thing we need to decipher a Vigenère cipher aside from the key and ciphertext. We also need the alphabet used in the table. Let’s use another r2 feature to see if it can help us find it. Radare2’s search function, /, has some crypto search functionality built in. Use /c? to view the help on this command.

Search for crypto materials with built-in r2 commands

The /ck search gives us a hit which looks like it could function as the Vigenère alphabet.

OK, it’s time to build our decoder. This time, I’m going to adapt a Python script from here, and then feed it our ciphers file just as before. The only differences are I’m going to hardcode the alphabet in the script and then run the output through base64. Let’s see how it looks.

Decoding the strings returns base64 as expected

So far so good. Let’s try running those through base64 -D (decode) and see if we get our plain text.

Our decoder returns gibberish after we try to decode the base64

Hmm. The script runs without error, but the final decoded base64 output is gibberish. That suggests that while our key and ciphers are correct, our alphabet might not be.

Returning to r2, let’s search more widely across the strings with iz~string

Finding cstrings in the TEXT section with r2’s ~ filter

The first hit actually looks similar to the one we tried, but with fewer characters and a different order, which will also affect the result in a Vigenère table. Let’s try again using this as the hardcoded alphabet.

Decoding the WizardUpdate’s encrypted strings back to plain text

Success! The first cipher turns out to be an encoding of the system_profiler command that returns the device’s serial number, while the second contains the attacker’s payload URL. The third downloads the payload and executes it on the victim’s device.

Reading Encrypted Strings In-Memory

Reverse engineering is a multi-faceted puzzle, and often the pieces drop into place in no particular order. When our triage of a malware sample suggests a known or readily identifiable encryption scheme has been used as we saw with macOS.ZuRu and WizardUpdate, decrypting those strings statically can be the first domino that makes the other pieces fall into place.

However, when faced with an incalcitrant sample on which the authors have clearly spent a great deal of time second-guessing possible reversing moves, a ‘cheaper’ option is to detonate the malware and observe the strings as they are decrypted in memory. Of course, to do that, you might need to defeat some anti-analysis and anti-debugging tricks first!

In our third case study, then, we’re going to take a look at a common adware installer. Adware is big business, employs lots of professional coders, and produces code that is every bit as crafty as any sophisticated malware you’re likely to come across. If you spend anytime dealing with infected Macs, coming across adware is inevitable, so knowing how to deal with it is essential.

md5 cfcba69503d5b5420b73e69acfec56b7
sha1 e978fbcb9002b7dace469f00da485a8885946371
sha256 43b9157a4ad42da1692cfb5b571598fcde775c7d1f9c7d56e6d6c13da5b35537

Let’s dump this into r2 and see what a quick triage can tell us.

This sample is keeping its secrets

Well, not much! If we print the disassembly for the main function with pdf @main, we see a mass of obfuscated code.

Lots of obfuscated code in this adware installer

However, the only calls here are to system and remove, as we saw from the function list. Let’s quit and reopen in r2’s debugger mode (remember: you may need to chmod the sample and remove any code signature and extended attributes as explained here).

sudo r2 -AA -d 43b9157a4ad42da1692cfb5b571598fcde775c7d1f9c7d56e6d6c13da5b35537

Let’s find the entrypoint with the ie command. We’ll set a breakpoint on that and then execute to that point.

Breaking on the entrypoint

Now that we’re at main, let’s break on the system call and take a look at the registers. To do that, first get the address of the system flag with

> f~system

Then set the breakpoint on the address returned with the db command. We can continue execution with dc.

Setting a breakpoint on the system call and continuing execution

Note that in the image above, our first attempt to continue execution results in a warning message and we actually hit our main breakpoint again. If this happens, repeating the dc command should get you past the warning. Now we can look at all the registers with drr.

Revealing the encoded strings in memory

At the rdi register, we can see the beginning of the decrypted string. Let’s see the rest of it.

The clear text is revealed in the rdi register

Ah, an encoded shell script, typical of Bundlore and Shlayer malware. One of my favorite things about r2 is how you can do a lot of otherwise complex things very easily thanks to the shell integration. Want to pretty-print that script? Just pipe the same command through sed from right within r2.

> ps 2048 @rdi | sed ‘s/;/\n/g’

We can easily format the output by piping it through the sed utility

More Examples of macOS String Decryption Techniques

WizardUpdate and macOS.ZuRu provided us with some real-world malware samples where we could use the same general technique: identify the encryption algorithm in the functions table, search for and isolate the key and ciphers in the strings, and then find or implement an appropriate decoding algorithm.

Some malware authors, however, will implement custom encryption and decryption schemes and you’ll have to look more closely at the code to see how the decryption routine works. Alternatively, where necessary, we can detonate the code, jump over any anti-analysis techniques and read the decrypted strings directly from memory.

If all this has piqued your interest in string encryption techniques used in macOS malware, then you might like to check out some or all of the following for further study.

EvilQuest, which we looked at in the previous post, is one example of malware that uses a custom encryption and decryption algorithm. SentinelLabs broke the encryption statically, and then created a tool based on the malware’s own decryption algorithm to decrypt any files locked by the malware. Fellow macOS researcher Scott Knight also published his Python decryption routine for EvilQuest, which is worth close study.

Adload is another malware that uses a custom encryption scheme, and for which researchers at Confiant also published decryption code.

Notorious adware dropper platforms Bundlore and Shlayer use a complex and varying set of shell obfuscation techniques which are simple enough to decode but interesting in their own right.

Likewise, XCodeSpy uses a simple but quite effective shell obfuscation trick to hide its strings from simple search tools and regex pattern matches.

Conclusion

In this post, we’ve looked at a variety of different encryption techniques used by macOS malware and how we can tackle these challenges both statically and dynamically. If you haven’t checked out the previous posts in this series, have a look Part 1 and Part 2. I hope you’ll join us for the next post in this series as we continue to look at common challenges facing macOS malware researchers.

Microsoft Patch Tuesday for Oct. 2021 — Snort rules and prominent vulnerabilities

12 October 2021 at 17:35
By Jon Munshaw, with contributions from Asheer Malhotra.  Microsoft released its monthly security update Tuesday, disclosing 78 vulnerabilities in the company’s various software, hardware and firmware offerings.   This month’s release is particularly notable because there are only...

[[ This is only the beginning! Please visit the blog for the complete entry ]]

The October 2021 Security Update Review

12 October 2021 at 17:28

The second Tuesday of the month is here, and that means the latest security updates from Adobe and Microsoft have arrived. Take a break from your regularly scheduled activities and join us as we review the details for their latest security offerings.

Adobe Patches for October 2021

For October, Adobe released six patches covering 10 CVEs in Adobe Reader, Acrobat Reader for Android, Adobe Campaign Standard, Commerce, Ops-CLI, and Adobe Connect. The update for Adobe Acrobat fixes four bugs in total – two rated Critical and two rated Moderate in severity. Two of these bugs were submitted through the ZDI program. The Critical-rated bugs could allow remote code execution while the Moderate-rated bugs could allow a privilege escalation. The update for Reader for Android fixes a single path traversal bug that could lead to code execution. All require some form of user interaction, such as browsing to a web page or opening a PDF.

Several cross-site scripting (XSS) bugs receive patches this month. The patch for Campaign Standard fixes a DOM-based XSS. The fix for Adobe Commerce addresses a stored XSS. The patch for Adobe Connect fixes two bugs, one of which is a reflective XSS. The other bug is more a more severe Critical-rated deserialization vulnerability that could allow remote code execution. The final Adobe patch for October fixes a Critical-rated deserialization bug in Ops-CLI, which is a python wrapper for Terraform, Ansible, and SSH for cloud automation.

None of the bugs fixed this month by Adobe are listed as publicly known or under active attack at the time of release.

Microsoft Patches for October 2021

For October, Microsoft released patches today for 71 new CVEs in Microsoft Windows and Windows Components, Microsoft Edge (Chromium-based), Exchange Server, .NET Core and Visual Studio, Microsoft Office Services and Web Apps, SharePoint Server, Microsoft Dynamics, InTune, and System Center Operations Manager. This is in addition to the eight CVEs patched by Microsoft Edge (Chromium-based) earlier this month and three previously released OpenSSL patches, which brings the October total to 82 CVEs – slightly down from last month. A total of 11 of these bugs were submitted through the ZDI program.

Of the 71 CVEs patched today, two are rated Critical, 68 are rated Important, and one is rated Low in severity. Three of today’s patches are listed as publicly known, while one is listed as being under active attack at the time of release. This is in addition to two of the Chromium bugs that were listed as under active attack when Chrome patched on September 30. For those wondering, this month does include patches for the recently released Windows 11 operating system.

Let’s take a closer look at some of the more interesting updates for this month, starting with the kernel bug that’s listed as under active attack:

-       CVE-2021-40449 - Win32k Elevation of Privilege Vulnerability
This patch corrected a kernel bug that could be used to escalate privileges on an affected system. Attackers typically use these types of bugs in conjunction with code execution bugs to take over a system. Considering the source of this report, this bug is likely being used in a targeted malware attack. We will also likely see more information about this bug and the associated attack within the next few days.

-       CVE-2021-26427 - Microsoft Exchange Server Remote Code Execution Vulnerability
The bug will certainly receive its fair share of attention, if nothing else, due to it being reported by the National Security Agency (NSA). Due to the similar CVE numbers, this bug was likely reported when they reported the more severe Exchange issues back in April. This bug is not as severe since this exploit is limited at the protocol level to a logically adjacent topology and not reachable from the Internet. This flaw, combined with the other Exchange bugs patched this month, should keep Exchange admins busy for a while.

-       CVE-2021-40486 - Microsoft Word Remote Code Execution Vulnerability
This patch corrects a bug that would allow code execution when a specially crafted Word document is viewed on an affected system. Although Microsoft lists user interaction required, the Preview Pane is also listed as an attack vector. This creates a much larger attack surface. When combined with a privilege escalation – like the one currently under active attack – this could be used to take over a target system. This bug came through the ZDI program and results from the lack of validating the existence of an object before performing operations on the object.

-       CVE-2021-40454 - Rich Text Edit Control Information Disclosure Vulnerability
We don’t often highlight information disclosure bugs, but this vulnerability goes beyond just dumping random memory locations. This bug could allow an attacker to recover cleartext passwords from memory, even on Windows 11. It’s not clear how an attacker would abuse this bug, but if you are using the rich text edit control in Power Apps, definitely test and deploy this bug quickly.

Here’s the full list of CVEs released by Microsoft for October 2021:

CVE Title Severity CVSS Public Exploited Type
CVE-2021-40449 Win32k Elevation of Privilege Vulnerability Important 7.8 No Yes EoP
CVE-2021-41338 Windows AppContainer Firewall Rules Security Feature Bypass Vulnerability Important 5.5 Yes No SFB
CVE-2021-40469 Windows DNS Server Remote Code Execution Vulnerability Important 7.2 Yes No RCE
CVE-2021-41335 Windows Kernel Elevation of Privilege Vulnerability Important 7.8 Yes No EoP
CVE-2021-40486 Microsoft Word Remote Code Execution Vulnerability Critical 7.8 No No RCE
CVE-2021-38672 Windows Hyper-V Remote Code Execution Vulnerability Critical 8 No No RCE
CVE-2021-40461 Windows Hyper-V Remote Code Execution Vulnerability Critical 8 No No RCE
CVE-2021-41355 .NET Core and Visual Studio Information Disclosure Vulnerability Important 5.7 No No Info
CVE-2021-41361 Active Directory Federation Server Spoofing Vulnerability Important 5.4 No No Spoofing
CVE-2021-41337 Active Directory Security Feature Bypass Vulnerability Important 4.9 No No SFB
CVE-2021-41346 Console Window Host Security Feature Bypass Vulnerability Important 5.3 No No SFB
CVE-2021-40470 DirectX Graphics Kernel Elevation of Privilege Vulnerability Important 7.8 No No EoP
CVE-2021-41363 Intune Management Extension Security Feature Bypass Vulnerability Important 4.2 No No SFB
CVE-2021-41339 Microsoft DWM Core Library Elevation of Privilege Vulnerability Important 4.7 No No EoP
CVE-2021-41354 Microsoft Dynamics 365 (on-premises) Cross-site Scripting Vulnerability Important 4.1 No No XSS
CVE-2021-40457 Microsoft Dynamics 365 Customer Engagement Cross-Site Scripting Vulnerability Important 7.4 No No XSS
CVE-2021-41353 Microsoft Dynamics 365 Sales Spoofing Vulnerability Important 5.4 No No Spoofing
CVE-2021-40472 Microsoft Excel Information Disclosure Vulnerability Important 5.5 No No Info
CVE-2021-40471 Microsoft Excel Remote Code Execution Vulnerability Important 7.8 No No RCE
CVE-2021-40473 Microsoft Excel Remote Code Execution Vulnerability Important 7.8 No No RCE
CVE-2021-40474 Microsoft Excel Remote Code Execution Vulnerability Important 7.8 No No RCE
CVE-2021-40479 Microsoft Excel Remote Code Execution Vulnerability Important 7.8 No No RCE
CVE-2021-40485 Microsoft Excel Remote Code Execution Vulnerability Important 7.8 No No RCE
CVE-2021-34453 Microsoft Exchange Server Denial of Service Vulnerability Important 7.5 No No DoS
CVE-2021-41348 Microsoft Exchange Server Elevation of Privilege Vulnerability Important 8 No No EoP
CVE-2021-26427 Microsoft Exchange Server Remote Code Execution Vulnerability Important 9 No No RCE
CVE-2021-41350 Microsoft Exchange Server Spoofing Vulnerability Important 6.5 No No Spoofing
CVE-2021-40480 Microsoft Office Visio Remote Code Execution Vulnerability Important 7.8 No No RCE
CVE-2021-40481 Microsoft Office Visio Remote Code Execution Vulnerability Important 7.1 No No RCE
CVE-2021-40482 Microsoft SharePoint Server Information Disclosure Vulnerability Important 5.3 No No Info
CVE-2021-41344 Microsoft SharePoint Server Remote Code Execution Vulnerability Important 8.1 No No RCE
CVE-2021-40487 Microsoft SharePoint Server Remote Code Execution Vulnerability Important 8.1 No No RCE
CVE-2021-40484 Microsoft SharePoint Server Spoofing Vulnerability Important 7.6 No No Spoofing
CVE-2021-41330 Microsoft Windows Media Foundation Remote Code Execution Vulnerability Important 7.8 No No RCE
CVE-2021-40454 Rich Text Edit Control Information Disclosure Vulnerability Important 5.5 No No Info
CVE-2021-41352 SCOM Information Disclosure Vulnerability Important 7.5 No No Info
CVE-2021-40478 Storage Spaces Controller Elevation of Privilege Vulnerability Important 7.8 No No EoP
CVE-2021-40488 Storage Spaces Controller Elevation of Privilege Vulnerability Important 7.8 No No EoP
CVE-2021-40489 Storage Spaces Controller Elevation of Privilege Vulnerability Important 7.8 No No EoP
CVE-2021-26441 Storage Spaces Controller Elevation of Privilege Vulnerability Important 7.8 No No EoP
CVE-2021-41345 Storage Spaces Controller Elevation of Privilege Vulnerability Important 7.8 No No EoP
CVE-2021-40450 Win32k Elevation of Privilege Vulnerability Important 7.8 No No EoP
CVE-2021-41357 Win32k Elevation of Privilege Vulnerability Important 7.8 No No EoP
CVE-2021-40456 Windows AD FS Security Feature Bypass Vulnerability Important 5.3 No No SFB
CVE-2021-40476 Windows AppContainer Elevation Of Privilege Vulnerability Important 7.5 No No EoP
CVE-2021-41347 Windows AppX Deployment Service Elevation of Privilege Vulnerability Important 7.8 No No EoP
CVE-2021-40468 Windows Bind Filter Driver Information Disclosure Vulnerability Important 5.5 No No Info
CVE-2021-40475 Windows Cloud Files Mini Filter Driver Information Disclosure Vulnerability Important 5.5 No No Info
CVE-2021-40443 Windows Common Log File System Driver Elevation of Privilege Vulnerability Important 7.8 No No EoP
CVE-2021-40466 Windows Common Log File System Driver Elevation of Privilege Vulnerability Important 7.8 No No EoP
CVE-2021-40467 Windows Common Log File System Driver Elevation of Privilege Vulnerability Important 7.8 No No EoP
CVE-2021-41334 Windows Desktop Bridge Elevation of Privilege Vulnerability Important 7 No No EoP
CVE-2021-40477 Windows Event Tracing Elevation of Privilege Vulnerability Important 7.8 No No EoP
CVE-2021-38663 Windows exFAT File System Information Disclosure Vulnerability Important 5.5 No No Info
CVE-2021-38662 Windows Fast FAT File System Driver Information Disclosure Vulnerability Important 5.5 No No Info
CVE-2021-41343 Windows Fast FAT File System Driver Information Disclosure Vulnerability Important 5.5 No No Info
CVE-2021-41340 Windows Graphics Component Remote Code Execution Vulnerability Important 7.8 No No RCE
CVE-2021-26442 Windows HTTP.sys Elevation of Privilege Vulnerability Important 7 No No EoP
CVE-2021-40455 Windows Installer Spoofing Vulnerability Important 5.5 No No Info
CVE-2021-41336 Windows Kernel Information Disclosure Vulnerability Important 5.5 No No Info
CVE-2021-41331 Windows Media Audio Decoder Remote Code Execution Vulnerability Important 7.8 No No RCE
CVE-2021-40462 Windows Media Foundation Dolby Digital Atmos Decoders Remote Code Execution Vulnerability Important 7.8 No No RCE
CVE-2021-41342 Windows MSHTML Platform Remote Code Execution Vulnerability Important 6.8 No No RCE
CVE-2021-40463 Windows NAT Denial of Service Vulnerability Important 7.7 No No DoS
CVE-2021-40464 Windows Nearby Sharing Elevation of Privilege Vulnerability Important 8 No No EoP
CVE-2021-41332 Windows Print Spooler Information Disclosure Vulnerability Important 6.5 No No Info
CVE-2021-36970 Windows Print Spooler Spoofing Vulnerability Important 8.8 No No Spoofing
CVE-2021-40460 Windows Remote Procedure Call Runtime Security Feature Bypass Vulnerability Important 6.5 No No SFB
CVE-2021-36953 Windows TCP/IP Denial of Service Vulnerability Important 7.5 No No DoS
CVE-2021-40465 Windows Text Shaping Remote Code Execution Vulnerability Important 7.8 No No RCE
CVE-2021-40483 Microsoft SharePoint Server Spoofing Vulnerability Low 7.6 No No Spoofing
* CVE-2021-37973 Chromium: CVE-2021-37973 Use after free in Portals High N/A No No RCE
* CVE-2021-37974 Chromium: CVE-2021-37974 Use after free in Safe Browsing High N/A No Yes RCE
* CVE-2021-37975 Chromium: CVE-2021-37975 Use after free in V8 High N/A No Yes RCE
* CVE-2021-37977 Chromium: CVE-2021-37977 Use after free in Garbage Collection High N/A No No RCE
* CVE-2021-37978 Chromium: CVE-2021-37978 Heap buffer overflow in Blink High N/A No No RCE
* CVE-2021-37979 Chromium: CVE-2021-37979 Heap buffer overflow in WebRTC High N/A No No RCE
* CVE-2021-37980 Chromium: CVE-2021-37980 Inappropriate implementation in Sandbox High N/A No No RCE
* CVE-2021-37976 Chromium: CVE-2021-37976 Information leak in core Medium N/A No No Info
* CVE-2020-1971 OpenSSL: CVE-2020-1971 EDIPARTYNAME NULL pointer de-reference Important N/A No No DoS
* CVE-2021-3449 OpenSSL: CVE-2021-3449 NULL pointer deref in signature_algorithms processing Important N/A No No DoS
* CVE-2021-3450 OpenSSL: CVE-2021-3450 CA certificate check bypass with X509_V_FLAG_X509_STRICT Important N/A No No Info

* Indicates this CVE had previously been released by a 3rd-party and is now being incorporated into Microsoft products.

The remaining Critical-rated bugs fix remote code execution vulnerabilities in Hyper-V server. One of these bugs could allow a guest OS to execute code on the host OS if the guest can cause a memory allocation error within the guest VM. Microsoft provides no details on the other bug, but it could also be used for a guest-to-host escape.

Looking at the remaining 18 code execution bugs, most are within the Office family and require a user to open a specially crafted file. One notable exception is a remote code execution bug in the DNS server. No user interaction is required to exploit this bug, but it does require high privileges, knocking this from Critical rated to Important. Microsoft lists this as publicly known but doesn’t state where which is frustrating. Knowing how widespread the knowledge of this vulnerability could benefit network defenders in creating a true risk assessment for their enterprise. There are also a couple of SharePoint code execution bugs receiving patches, but both require local privileges to exploit. These bugs came through the ZDI program, and we’ll have more details about them in the future. Another interesting RCE bug impacts the MSHTML platform. Although Internet Explorer is now “retired”, it lives on as the underlying MSHTML, EdgeHTML, and scripting platforms are still supported. There are even patches here for Windows 11. The legacy of IE hasn’t quite left us after all.

Moving on to the privilege escalation bugs, most require an attacker to log on to a system and run their own code to take advantage of an affected component. There’s another kernel bug here, and it is listed as publicly known – again with no additional information or details on the public disclosure. There’s also a privilege escalation in Exchange that also requires the attacker to be on an adjacent network. No user interaction is listed, so the likely scenario would be an insider threat.

There are five security feature bypass (SFB) bugs patched in this month’s release. The first is a vulnerability in RPC Runtime that could allow an attacker to bypass Extended Protection for Authentication provided by Service Principal Name (SPN) target name validation. A different bug in the Windows active directory could allow an attacker to bypass the Active Directory Federation Services (AD FS) BannedIPList entries for WS-Trust workflows. A different Active Directory bug could allow an attacker to bypass Active Directory domain permissions for Key Admins groups. The bypass in Intune requires the Intune Management Extension to be installed, but Microsoft provides no further details on what is being bypassed. Microsoft provides no details on what security feature is being bypassed on either the console Windows host or the Windows AppContainer Firewall. The lack of details around the container firewall vulnerability is especially frustrating since Microsoft lists this bug as publicly known.

The October release contains fixes for three new Denial-of-Service (DoS) bugs, each of which is significant. The first patch fixes a DoS vulnerability in TCP/IP that impacts all supported versions of Windows – including Windows 11. It’s not clear if this would allow an attacker to completely shut down a system, but without further details from Microsoft, network defenders should assume this worst-case scenario is likely. There’s a DoS bug in Exchange Server, and again, details are scarce. Since the CVSS score lists Availability=High, assume an attacker can abuse this bug to shut down an Exchange server. The final DoS bug getting fixed this month impacts Windows Network Address Translation (NAT) and was discovered by the same researchers that found the TCP/IP bug. Again, the CVSS score indicates this vulnerability could be used to take down a system, so test and deploy these patches quickly.

In addition to the one previously mentioned, there are 13 information disclosure bugs receiving fixes in this month’s release. Most of these simply result in leaks consisting of unspecified memory contents. However, if you’re running the web console of the System Center Operations Manager (SCOM), you definitely want to pay attention to the bug that could disclose file content on an affected system.

The October release is rounded out by fixes for six spoofing bugs and two cross-site scripting (XSS) bugs. Microsoft provides no details on what may be spoofed for any of these vulnerabilities, but the ones for Print Spooler and Exchange stand out. There are only a couple of print spooler bugs in this month’s release, so perhaps the days of PrintNightmare are finally behind us. The only clue we have for the impact of the Exchange spoofing bug is the CVSS rating of Confidentiality=High. This implies a total loss of confidentiality, which is not something you want to be associated with your Exchange server. The remaining spoofing bugs read very close to XSS bugs, including the rare Low severity fix for SharePoint Server.

No new advisories were released this month. The latest servicing stack updates can be found in the revised ADV90001.

Looking Ahead

The next Patch Tuesday falls on November 9, and we’ll return with details and patch analysis then. Until then, stay safe, happy patching, and may all your reboots be smooth and clean!

The October 2021 Security Update Review

❌