Decoding a New JavaScript Malware Campaign
Recently researchers from HuntressLabs shared data about a case where a fake DocuSign document resulted in a network compromise involving AvosLocker & data collection with RClone. HuntressLabs identified the initial infection vector as a file with the name: Invoice-DocuSign-Mar03-2023.js
Part 1: Gathering Samples on VirusTotal
Leveraging VirusTotal Intelligence I was able to identify ~70 recent samples with the same file naming schema.
I’ve uploaded the IOCs for these files to github. While reviewing over these results I noticed that there were a lot of .html files. I found one example where the html file was delivered as a email attachment.
Out of all the possible related samples I decided to further review the file Invoice-DocuSign-Mar03-2023.js
Part 2: Decoding the Malware
This javascript malware has painfully annoying obfuscation so lets use dynamic analysis to speed things up! When you run this malware the first thing you see is a message about file corruption. You might think the malware author made a typo however this popup is actually just to deceive users and security analyst.
After reviewing the javascript malware source code I identified two functions that returned data. One of the functions executed String.fromCharCode to decode data before returning the variable. To capture this output I added a WScript.Echo statement to the malware:
To effectively utilize this decoding trick with javascript malware it’s better to run the malware with cscript instead of wscript and pipe the output to another file like such:
1
cscript sample.js > data.txt
Next I used CyberChef to help investigated this partially decoded data. The recipe I used was:
1
2
Find_/_Replace({'option':'Regex','string':'\\n'},'',true,false,true,false)
Find_/_Replace({'option':'Simple string','string':';'},';\\n',true,false,true,false)
Although this data now has some functional code such as the popup mentioned earlier there are still multiple variables with encoded data and more analysis needed.
After assembling the multiple base64 encoded varables and throwing it into CyberChef we get this ugly bit of powershell code that uses io.compression to deflate base64 encoded data.
Using CyberChef again I decoded the inner layer to find more encoded powershell!
Finally to investigate this last bit of obfuscation lets open the code in the built-in PowerShell editor. I used the Write-Output function to add output logging for different varables before running the malicious powershell. This revealed the encoded C2 value (159.223.101.65) and also showed that a scheduled task was created!
Further investigating Windows Scheduled Tasks confirmed this finding.
Part 3: Network Traffic Analysis
While performing dynamic analysis earlier I captured network traffic using MITMProxy.
This malware performs two types of HTTP requests:
- GET requests to C2 urls that start with /r/
- This will returned powershell to execute
- POST requests to c2 urls that start with /c/
- This contains gathered victim information
Here’s what the C2 response provided to execute:
This powershell contains the logic for automated reconnaissance as well as data encoding using a randomly generated XOR key. Taking a look the POST data request that followed we can confirmed that there is encoded data:
Fortunately the randomly generated XOR key is included in the post request in the variable magic.
Using python3 (and a little bit of help from ChatGPT) I wrote a small program that decodes this data:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import sys
def decode(encoded_str, xor_key):
decoded_str = ""
key_len = len(xor_key)
for i in range(0, len(encoded_str), 2):
encoded_byte = int(encoded_str[i:i+2], 16) # Convert the hexadecimal byte to an integer
xor_key_byte = ord(xor_key[i//2 % key_len]) # Get the corresponding byte from the XOR key
decoded_byte = encoded_byte ^ xor_key_byte # Perform the XOR operation
decoded_str += chr(decoded_byte) # Convert the result back to a character and append to the decoded string
return decoded_str
if __name__ == "__main__":
if len(sys.argv) != 3:
print("Usage: python script.py <encoded_str> <xor_key>")
else:
encoded_str = sys.argv[1]
xor_key = sys.argv[2]
decoded_str = decode(encoded_str, xor_key)
print(decoded_str)
This code has been uploaded to a Github gist.
Using the data captured I confirmed the decoding works!
Summary
Based on what was shared publicly by HuntressLabs and what was found on VirusTotal we can summarize this threat campaign as:
Email Attachment -> JavaScript Malware -> Scheduled Task & C2 Communication > Domain Recon > Ransomware