Analyzing Lu0Bot: A Node.js Malware with Near-Unlimited Capabilities. Part 1

In this article, we’ll examine a Lu0Bot malware sample we stumbled upon while tracking malicious activity in ANY.RUN’s public tasks

What caught our interest is that the sample is written in Node.js. While initially, it appeared to be a regular bot for DDOS attacks, things turned out to be a lot more complex. 

Node.js malware is intriguing because it targets a runtime environment commonly used in modern web applications. The runtime’s platform-agnostic nature depends on the specific code and libraries used, but it often allows for greater versatility. Typically, this kind of malware employs multi-layer obfuscation techniques using JavaScript. It combines traditional malware characteristics with web technologies, making it a unique challenge for detection and analysis. 

Due to the extensive scope of the article, we’ve decided to split it into two parts: 

  • Part 1: core analysis: In the first part, we’ll explore the malware’s architecture and what’s stored inside of it. 
  • Part 2: traffic analysis: In the second part, we’ll dive into a real-world instance where the sample communicates with a C2 server. 

What is Lu0Bot Malware?

Before diving into the analysis, let’s do a quick overview of Lu0Bot and talk about what makes it particularly interesting. 

Lu0bot initially appeared in February 2021 as a second-stage payload in GCleaner attacks. Now, it serves as a bot, waiting for commands from a C2 server and sending encrypted basic system information back to that server.  

It is worth noting that the bot’s activity level remains relatively low, averaging 5-8 new samples on Bazaar each month. As of this writing, only one new sample was uploaded in August. However, it is possible that the real popularity of this malware is higher than the activity level shows, with many samples lying dormant and awaiting C2 commands — though, this is just a speculation on our part. 

Regardless, even with this limited activity, Lu0bot is interesting as it demonstrates a creative approach to malware design — written in Node.js its capabilities are restricted only by what’s possible in this programming language. 

While we couldn’t locate a live sample receiving commands — likely due to the bot’s inability to find an IP address — a public sample did successfully connect. In this instance, the server responded with JavaScript code, initiated a new domain, and proceeded with encrypted code exchange. The decryption process is hard-coded within the bot — but we’ll dive deeper into the decryption algorithm in part 2 of our analysis.

Static analysis of the source file 

Let’s begin our breakdown of Lu0Bot by analyzing it statically. 

Link to the task: https://app.any.run/tasks/4696b947-92f0-4413-95dc-644c45ca99a6

                <thead>        <tr>
                            <th>
                                    SHA256                    </th>
                                            <th>
                                    FB808BE98B583A2004B0AF7B6F4BF5E3419D8B6A385C5CE4E8FAB4DDC0B48428                    </th>
                                    </tr>
                </thead></table>

The first thing we noticed is that the file uses an SFX packer (see Fig 1) — this is a self-extracting archive that can be opened with any archive utility.  

SFX-packer

Inside the archive, there was a BAT file and several other contents (see the screenshot below). Let’s break down what they do one by one: 

Archive contents

1. BAT-file  

The content of the BAT file 

The first line contains a comment, but its meaning remains unclear—it wasn’t referenced later in our analysis. 

Next, multiple files are bundled into an EXE file, specifically a Node interpreter named fjlpexyjauf.exe.

In the third line, this interpreter receives a file containing bytes and a distinct number, which in our case is 3991425476. This number likely acts as an encryption key for the byte file. 

On the third line, this interpreter receives a file containing bytes and a number (in place-holder terms, %1%, as seen in figure 3), but the real number in our case is 3991425476. This number probably acts as an encryption key for the byte file. 

2. Files eqnyiodbs.dat 

This one file is split into different byte blocks. These blocks are later combined to form the Node interpreter. 

Contents of eqnyiodbs files

3. lknidtnqmg.dat file 

This file contains bytes encrypted in Base64. It is likely decrypted using the number provided as input. 

Contents of the lknidtnqmg.dat file

4. gyvdcniwvlu.dat file 

This is a driver designed to let 32-bit programs on x64 systems convert key scan codes into Unicode characters. The main process relies on it, most likely to enable keylogging functionality. 

Dynamic malware analysis of Lu0Bot in ANY.RUN 

Static analysis points to the EXE file and lknidtnqmg.dat as noteworthy. The next step is to examine dynamic behavior and attempt to either decrypt the bytes or find them decrypted in the process memory.

We’ll use ANY.RUN interactive malware sandbox to perform the dynamic analysis. 

Process Tree

Processes and activity 

The screenshot of the process tree above displays the process tree during sample execution. The main process initiates a familiar BAT file, which in turn launches the EXE file. Post-analysis verifies that this is a Node.js interpreter, accepting encrypted JS code as input. 

Alongside attempting connections, the JS code fetches system data using WMIC. It specifically gathers information about processes and the execution location, aligning with Tactic T1047

WMIC

Dynamic analysis revealed that the interpreter gets copied to the startup folder. After a system restart, the connection to the domain continues (this is seen in the screenshot of the process tree above) — reference the process number 5252 (Tactic T1053.005). This ensures the bot remains operational post-restart.  

Startup directory

Network and traffic

A unique characteristic of this malware is its approach to domain connection. The domain is constructed from various parts, assembled into a single entity within the JS code:

                <thead>        <tr>
                            <th>
                                    59c58bb                     </th>
                                            <th>
                                    3                     </th>
                                            <th>
                                    170                     </th>
                                            <th>
                                    1693221099                     </th>
                                            <th>
                                    118                     </th>
                                            <th>
                                    0308a04a642894b53635018356690221232f                     </th>
                                            <th>
                                    .hsh.juz09.cfd                     </th>
                                    </tr>
                </thead></table>

DNS-requests 

	<div>
		<div>
			<p>
				Free malware research with ANY.RUN
			</p>
			<div>
				<a href="https://app.any.run/#register" rel="noreferrer" target="_blank">
					Start Now!
				</a>
			</div>
		</div>
	</div>

Above is a small preview into Lu0Bot’s traffic — we will break it down in more detail in Part 2 of our analysis.

Analyze Lu0Bot in ANY.RUN

Get started 

Technical analysis of Lu0Bot malware using a disassembler and debugger 

In our case, the dump — or script — is both packed and encrypted. To access the main JS code, we’ll need to: 

  • Unpack the SFX archive 
  • Run a command to collect the Node.js file 
  • Launch fjlpexyjauf.exe in x32dbg?, entering the incoming data into the command line 
  • Get to the point where JS code execution starts 
  • Locate the code in memory and save a dump 

Steps for unpacking and byte collection 

To unpack the archive, we can use any standard archiver tool. For byte collection, we will focus on the second line of the BAT script — let’s execute it. 


Byte collection 

Extracting the dump 

Let’s run the file in the debugger and write the input data to the command line.


Command Line 

We’re looking for the spot where JS code execution kicks off, marked by the call to the uv_run function. After this call, the program starts domain connection attempts and hangs indefinitely. Let’s navigate to this function and search for the code. To make it easier, we can use syntax cues and variable attributes — like the word ini(), which is unique to JS syntax. 

uv_run function
JS code

Once we spot the decrypted code, let’s head to Memory Map and save that section. This is what our dump should look like: 

Dump

Analyzing the JS code

The JavaScript code we are presented with is heavily obfuscated and unreadable:  

This code is unreadable, but we can fix it

We can transform the code into a readable form by removing extra bytes and using a JavaScript deobfuscator (Here’s a GitHub link to handy script you can use). After the transformation, this is what the result should look like: 

Result of code transformation

Note the following: 

  1. The code starts with an array containing encrypted strings. 
  1. Right after, the array undergoes manipulation, moving specific elements to the end. 
  1. Next, there’s a function dedicated to decrypting the array strings. It first uses an alternative form of BASE64 (T1132.002), followed by URL encode-decode, and then applies RC4. 

This function is called with two variables: the first is an element from the array, and the second is the RC4 key. 

To simplify the task of parsing this code, we wrote a small script that decrypts these lines automatically. You can download it from our GitHub.  

Running the script gives us the following before-and-after:

 Before code deobfuscation 

After code deobfuscation

The decrypted lines reveal that portions of the domains are hard-coded into the sample (see Fig. 16). Following that, you’ll find the section of the code responsible for assembling the domain: 

Domain construction

Debugging the JavaScript code

For debugging, we’ll use Node.js along with its inspect-brk parameter (node.exe –inspect-brk *obfuscate dump without garbage bytes*). Let’s place a breakpoint on the “var” keyword and observe the output generated by each line: 

  • The first function, ginf, handles information gathering. It outputs an array with 15 elements, all of which are details about the system. 

ginf function 

An array containing the output of the ginf function 
  • The hwco function takes the 15-element array from the ginf function as input. The output is the tail-end portion of the domain, up to the dot. Analysis shows that this output is actually a hash of the collected system data. 
hwco function 
hwco function 
String output from the hwco function 
  • Next, elements like the port, number, and the domain segment following the dot are extracted from the acc array and assigned to variables. 

 
Extracting elements from the acc array 

  • The variable acc is added with 3, rns, and bt. Rns is generated randomly, and bt represents the current time. 

Part of domain: addition acc, 3, rns, bt 

More about rns and bt 
  • After that, a variable containing a random number is appended to the domain segment before the dot. The next line handles domain selection after the dot: if certain conditions are met, an alternative domain is chosen, if available. 

Inserting image...Choise domain after the point 

  • The full domain gets assembled and all required elements are packed into a JSON object: 

{“gttk”,”59c58bb5327116933080087040012a04a641e14b536350088dba00221232f.hsh.juz09.cfd”,18223,”59c58bb5″,”331c90″,1693308008704,null,[“win32″,”ia32″,32,”10.0.19044″,6386.265,3220688896,1396203520,4,”Intel(R) Core(TM) i5-6400 CPU @ 2.70GHz”,3094,”PC”,”admin”,”C:\\Users\\admin\\Desktop\\node-v20.5.0-win-x86″,”C:\\Users\\admin\\AppData\\Local\\Temp”,”20.5.0″],”0012a04a641e14b536350088dba00221232f”} 

Let’s summarize, then — what does our domain consist of?

                <thead>        <tr>
                            <th>
                                    Beginning                     </th>
                                            <th>
                                    A number                     </th>
                                            <th>
                                    Random num                     </th>
                                            <th>
                                    Time                     </th>
                                            <th>
                                    Hashes                     </th>
                                            <th>
                                    Domain ending                     </th>
                                    </tr>
                </thead><tbody>        <tr>
                            <td>
                                    59c58bb5                     </td>
                                            <td>
                                    3                     </td>
                                            <td>
                                    271                     </td>
                                            <td>
                                    1693308008704                     </td>
                                            <td>
                                    0012a04a641e14b536350088dba00221232f                     </td>
                                            <td>
                                    hsh.juz09.cfd                     </td>
                                    </tr>
                </tbody></table>

The final function on the screen employs aes-128-cbc encryption. The output is a 435-element array, consisting of 1 byte, followed by a 16-byte IV, then 2 bytes, and finally the encrypted data (Tactic T1573). 

 
Encrypted JSON object 

We also discovered a key: becfe83392d83ef8c743ea00711a25c8, which aligned with all live tasks identified by our team. 

Post-execution, the malware continuously attempts to locate an address for data transmission. When traffic successfully reaches the server, data exchange occurs, involving the C2 server sending JS code. More on this in Part 2 of our analysis. 

How to Identify Lu0Bot

SIGMA RULE: 

status: experimental 

detection: 

    parent_process: 

        ParentImage|endswith: '\cmd.exe' 

        CommandLine|re: '\/d \/c [A-z0-9]+\.bat \d+$' 

    child_process: 

        OriginalFileName: 'node.exe' 

        CommandLine|re: '\.dat \d+$' 

    condition: parent_process and child_process

YARA RULE: 

strings: 

        $start_code = /var \_0x[a-f0-9]{4,6}/ 

        $altBase64 = "'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/='" ascii 

        $domain = "var acc=" ascii 

        $end_code = "}ini();" ascii 

        $func = “gifn” ascii 

  

    condition: 

       all of them and #start_code >= 50 

Writing Suricata rules for Lu0Bot

For effective Suricata network rules, content is key. DNS queries are a big part of all network protocol requests. Lu0bot, as mentioned earlier, doesn’t offer much stable content in its DNS queries—mostly random bytes or hashes. But there’s a small part of the domain name that includes a Unix-format timestamp. We’ll use that for network detection. 

To capture three bytes of this timestamp in the rule, we limited the rule’s active timeframe. We pinpointed five periods tied to the initial bytes 169, 170, 171, 172, and 173 in the timestamp. This gave us five rules targeting the malware’s activity within specific windows.

                <thead>        <tr>
                            <th>
                                    GMT activity end date                     </th>
                                            <th>
                                    Timestamp                     </th>
                                            <th>
                                    Rule Message                     </th>
                                    </tr>
                </thead><tbody>        <tr>
                            <td>
                                    Nov 14 2023 22:13:20                     </td>
                                            <td>
                                    &lt;1700000000                     </td>
                                            <td>
                                    BOTNET [ANY.RUN] Lu0bot<br />

DNS Query M1






















Mar 09 2024 15:59:59

<1709999999

BOTNET [ANY.RUN] Lu0bot

DNS Query M2

Jul 03 2024 09:46:39

<1719999999

BOTNET [ANY.RUN] Lu0bot

DNS Query M3

Oct 27 2024 03:33:19

<1729999999

BOTNET [ANY.RUN] Lu0bot

DNS Query M4

Feb 19 2025 21:19:59

<1739999999

BOTNET [ANY.RUN] Lu0bot

DNS Query M5

In real-world scenarios, some Lu0bot DNS requests lack hashes altogether, ending just at the timestamp. Because of this, the regular expression should account for both hashed and non-hashed query versions. 

The regular expression below is part of the BOTNET [ANY.RUN] Lu0bot DNS Query M1 network rule. It reflects the variables obtained from our analysis and is tailored for timestamps starting with the number 169. Note that this rule will expire in November 2023, when the timestamp transitions to starting with 170.

A regular expression targetting the DNS request. 

 
Schematic representation of the regular expression. 

                <thead>        <tr>
                            <th>
                                    Network rule text                     </th>
                                            <th>
                                    Description                     </th>
                                    </tr>
                </thead><tbody>        <tr>
                            <td>
                                    alert dns any any -&gt; any any<br />

(msg: “BOTNET [ANY.RUN] Lu0bot DNS Query M1”;

flow: to_server;




















Indicates the protocol, the direction of data transfer and the message if the rule is triggered.

dns.query;

Targets an inspected buffer containing a DNS query

content: “169”; offset:12; depth:3;

Content check for the M1 rule range

pcre:“/^(?:[a-f0-9]{8}\d\d{3}169\d{10})(?:[0-9a-z]{36})?./”;

Regular expression describing the structure of a DNS request

threshold:

classtype: trojan-activity;

reference:

metadata: malware_family Lu0bot

sid: 8000603; rev: 2;

The rule’s service fields provide essential information: they describe the malware family, set the trigger threshold, and offer a list of links for further reading on this threat.

Detecting Lu0bot in ANY RUN 

We’ve already implemented Lu0bot detection in ANY.RUN — our service can automatically decrypt strings and C2 domains are now visible in our service. 

These tasks show Lu0bot detection in ANY.RUN: 

Wrapping up

In this article, we delved into Lu0bot, a malware incorporating NODE JS and executable JS code. Based on our analysis, we arrive at these key conclusions: 

  1. All data is obfuscated. The code primarily focuses on gathering basic info and awaiting C2 commands. 
  1. The malware’s functionality is constrained only by what its JS code can do. 
  1. The domain structure of the malware is unique. 
  1. Custom encryption methods are used for strings. 

Given these factors, Lu0bot could pose significant risk if its campaign scales and the server start actively responding. Its unique implementation using NODE JS makes it a highly interesting subject for analysis. 

Should the server become operational, the malware could potentially have capabilities like: 

  • Recording keystrokes 
  • Identity theft 
  • Near-total control of the victim’s computer 
  • Functioning as a DDOS bot 
  • Conducting illegal activities using the compromised system 

If you found this article informative, make sure to also read our technical breakdown of XWorm, as well as an in-depth analysis of a new LaplasClipper sample. And, of course, we will break down the traffic structure of Lu0bot in much greater detail in an upcoming Part 2 of this analysis — stay tuned. 

Appendix 1

MITRE

                <thead>        <tr>
                            <th>
                                    Tactics                     </th>
                                            <th>
                                    Techniques                     </th>
                                            <th>
                                    Description                     </th>
                                    </tr>
                </thead><tbody>        <tr>
                            <td>
                                    TA0011:&nbsp;Command and Control&nbsp;                    </td>
                                            <td>
                                    T1071.001:&nbsp;Application Layer &nbsp;Protocol&nbsp;                    </td>
                                            <td>
                                    Sending collected data &nbsp;to the control server&nbsp;                    </td>
                                    </tr>
                        <tr>
                            <td>
                                                        </td>
                                            <td>
                                    T1132.002 - Data Encoding: Standard Encoding&nbsp;                    </td>
                                            <td>
                                    encode data with alternative BASE64&nbsp;                    </td>
                                    </tr>
                        <tr>
                            <td>
                                                        </td>
                                            <td>
                                    T1573 - Encrypted Channel&nbsp;                    </td>
                                            <td>
                                    Use Symmetric and Asymmetric Cryptography in traffic&nbsp;                    </td>
                                    </tr>
                        <tr>
                            <td>
                                    TA0005:&nbsp;Defense Evasion&nbsp;                    </td>
                                            <td>
                                    T1027 - Obfuscated Files or Information&nbsp;                    </td>
                                            <td>
                                    attempt to make an executable or file difficult to discover or analyze&nbsp;                    </td>
                                    </tr>
                        <tr>
                            <td>
                                    TA0002:&nbsp;Execution&nbsp;                    </td>
                                            <td>
                                    T1053.005 - Scheduled Task/Job: Scheduled Task&nbsp;                    </td>
                                            <td>
                                    abuse the Windows Task Scheduler to create file in statup&nbsp;                    </td>
                                    </tr>
                        <tr>
                            <td>
                                                        </td>
                                            <td>
                                    T1047 - Windows Management Instrumentation&nbsp;                    </td>
                                            <td>
                                    use wmic to gather information from a system&nbsp;                    </td>
                                    </tr>
                </tbody></table>

Appendix 2: IOCs

                <thead>        <tr>
                            <th>
                                    Title&nbsp;                    </th>
                                            <th>
                                    Description&nbsp;                    </th>
                                    </tr>
                </thead><tbody>        <tr>
                            <td>
                                    Name&nbsp;                    </td>
                                            <td>
                                    Fb808be98b583a2004b0af7b6f4bf5e3419d8b6a385c5ce4e8fab4ddc0b48428.exe&nbsp;                    </td>
                                    </tr>
                        <tr>
                            <td>
                                    MD5&nbsp;                    </td>
                                            <td>
                                    6181206d06ce28c1bcdb887e547193fe&nbsp;                    </td>
                                    </tr>
                        <tr>
                            <td>
                                    SHA1&nbsp;                    </td>
                                            <td>
                                    8eb65b4895a90d343f23f9228e0d53af62de3dab&nbsp;                    </td>
                                    </tr>
                        <tr>
                            <td>
                                    SHA256&nbsp;                    </td>
                                            <td>
                                    fb808be98b583a2004b0af7b6f4bf5e3419d8b6a385c5ce4e8fab4ddc0b48428&nbsp;                    </td>
                                    </tr>
                </tbody></table>

Dropped executable file

                <thead>        <tr>
                            <th>
                                    Name&nbsp;                    </th>
                                            <th>
                                    SHA256&nbsp;                    </th>
                                    </tr>
                </thead><tbody>        <tr>
                            <td>
                                    C:\Users\admin\AppData\Local\Temp\IXP000.TMP\fjlpexyjauf.exe&nbsp;                    </td>
                                            <td>
                                    9c5898b1b354b139794f10594e84e94e991971a54d179b2e9f746319ffac56aa&nbsp;                    </td>
                                    </tr>
                        <tr>
                            <td>
                                    C:\Users\admin\AppData\Local\Temp\IXP000.TMP\gyvdcniwvlu.dat&nbsp;                    </td>
                                            <td>
                                    7c37b8dd32365d41856692584f4c8e943610cda04c16fe06b47ed2d1e5c6415e&nbsp;                    </td>
                                    </tr>
                </tbody></table>

DNS requests 

  • 59c58bb5317016932210991180008a04a642894b53635018356690221232f.hsh.juz09.cfd 
  • 59c58bb5317016932210991180108a04a642894b53635018356690221232f.hsh.juz09.cfd 
  • 59c58bb5317016932210991180208a04a642894b53635018356690221232f.hsh.juz09.cfd 
  • 59c58bb5317016932210991180209a04a642894b53635018356690221232f.hsh.juz09.cfd 

More submissions: 

https://app.any.run/tasks/4888f835-d2c3-4d89-9dc8-ac6cecf96409/
https://app.any.run/tasks/c068028b-ce61-46a7-b12d-aef39a033bdd/
https://app.any.run/tasks/e13d4388-8f32-4182-aff2-a85c89aeaa35

The post Analyzing Lu0Bot: A Node.js Malware with Near-Unlimited Capabilities. Part 1 appeared first on ANY.RUN's Cybersecurity Blog.

Article Link: Analyzing Lu0Bot: A Node.js Malware with Vast Capabilities. Part 1