Locky Ransomware Dropper - XOR Encoding, Regex, Scheduled Tasks
Javascript-based Locky Ransomware Dropper that utilises Iterative XOR Encoding, Regex, Scheduled Tasks and registry keys.
Locky Ransomware Dropper - Iterative XOR encoding, Regex and Scheduled Tasks
Summary
The sample is a piece of obfuscated javascript code, utilising some interesting encoding/obfuscation techniques to hide it’s final purpose of dropping a malicious binary. Once the Iterative XOR encryption and regex has been successfully “reversed”, the payload is revealed to be a dropper for a malicious binary file. Likely, this file is Locky Ransomware (see note below).
Although I was unable to connect to the domain to retrieve the binary payload, I was able to find references to the c2 domain in the below writeup by blackberry. Which indicates that it has been used to host and deploy Locky Ransomware.
https://blogs.blackberry.com/en/2017/11/threat-spotlight-locky-ransomware
Interesting Notes on the sample:
- Uses obfuscated variable/function names
- Customised encoding routine, no base64 or similar
- Brute forces it’s own function definitions
- Utilises relatively clean domains for hosting payload
- Uses registry keys to check for victim current windows version
- Attempts to use scheduled tasks for execution.
Indicators of Compromise
- http(s)://dboosajqn[.]top/1/
- dboosajqn[.]top
Source File
https://github.com/HynekPetrak/javascript-malware-collection/blob/master/2017/20170321/20170321_d2bdd39119af20dbfc0c1822224b59ba.js
Initial Overview
Initial overview of the code shows a small function that takes an input, and converts it to base16. And another function that contains and returns a large obfuscated string, likely the main payload.
Below the main obfuscated string, the following functions can be seen.
The Third function looks the most interesting.
Since all three “sections” will need to be analysed. I’ll break them up and analyse one by one.
Code Piece 1 - Random Character Generator
The code from lines 31-35 look like this.
- Line 33 converts the string into an array, with each array value containing a single letter/char
- This is done by using regex to extract non-whitespace values (of length 1), using \S{1]}
- line 34 grabs a random char value from this array. Using a random number generator.
TLDR: This is a random character generator Take special note of the possible return values, which contain a “+” and “eval”
Since we know that kleoonfkcw() is a random character generator, we can ctrl+f and replace all references with “randomChar” or anything similar.
The code will now look like
Code Piece 2 - base16 converter
On lines 36-39, we have the following code, which simply uses parseInt to convert an input number to base16 (HEX)
Fixing this up, the code will now look like the following.
Section 3 - De-obfuscation Routine
Based on below, this function serves as the primary de-obfuscation routine for the giant string declared previously in the code.
T
Looking closely, we can see where the obfuscated string ends up going.
In order to make more sense of the function, I took it out of the string and put it into a new doc.
Which looks like this. Note the references to randomChar(). Which will populate the function logic with a value from “yvla+e_”
It’s not very obvious at first glance, but this will eventually populate the values with “+” and “eval”
Thus creating a “+=” on line 6, and “eval” on line 9.
Since the function is being defined dynamically inside of an infinite loop, eventually the values will line up below, allowing the code to execute.
Cleaning up a bit more, the de-obfuscation code looks like this.
With functions and variables renamed, looks more like this.
The logic above is fairly simple, and can be recreated as a python script below. Alternatively, the code could easily be executed with a print/echo within javascript, but I like to use python.
De-obfuscation Script - Payload Extracted
The output is a bit messy, so I used cyberchef to add some newlines/spacing for readability.
I then moved it into vscode, since I like the interface and highlighting better.
See below for a quick snippet.
Analysis of Final Payload
Looking at the final payload, it can be seen that the malware uses XMLHTTP objects for sending http requests and retrieving the final binary payload.
Below we can see the URL/Domain where the malware is retrieved from.
Below we can see that the malware gets the location of the current users temp folder, and generates a 7 digit filename for the dropped binary.
Below it can be seen that the malware does a few interesting things.
- Checks the registry, to make sure that the victim PC is running windows 6.0 (Vista) or above
- Attempts to create a scheduled task to execute the malware
- Interestingly, only sets the malware to execute once (based on the /sc once) parameter
- Names the task a 7 digit value, eg 3482888. Doesn’t try to masquerade as something legitimate.
- Uses the previously grabbed time value to execute the code at current time + 2 minutes.
- If the scheduled task creation fails, launches the malware directly using cmd.exe
Analysis of Malware Domain/IOC
Below we can see
Also interesting is that the domain itself is relatively clean.
The domain is also mentioned in this writeup of Locky Ransomware. Indicating that Locky Ransomware could be contained in the dropped binary payload.
https://blogs.blackberry.com/en/2017/11/threat-spotlight-locky-ransomware
##
Python Script
#Python Script used for decoding, slightly modified from the previous screenshot
#Mostly just changes for readability
bad_code = "9228379b1840edabf<<SNIPPED>>adfaa82a4bfbdf104f2c30badf7d7bd8435af6e5f95d41352d938877a573744c1a46c08caaedf37f18b8d1325b3670b897303dd60951e7e5f41e4a662b879457ec3c314e014bc494b1a1b37516b39e144d3078b2912f2b971e00a2bea600173c649b9b0af7"
#array of integers used for XOR operations
Arr1 = [244,93,89,248,108,41,130,197,219,125,106,21,95,230,230,35,204,30,17,101,33,59,165,224,217,136,136,8,107,208,255,96,46,88,88,154]
#extracts hex values from the main string, stores them as a list
#The \S{2} will extract all non-whitespace values of length two
Hex_Values = re.findall("\S{2}", bad_code)
result = "", counter1 = 0, counter2 = 0
#while loop that recreates the de-obfuscation logic
while (counter1 < len(Hex_values)):
#resets the XOR array if loop has reached the end
if (counter2 >= len(Arr1)):
counter2 = 0
#XORS the extracted hex value, with a value from the XOR array
num = int(Hex_Values[counter1], 16)^Arr1[counter2]
#returns the ascii value of the XOR result
result += chr(num)
counter1 += 1
counter2 += 1
print(result)