<- back to blogs

Reversing EsqueleStealer

Have you heard of this stealer? Probably not. Me neither. I am clinically insane.

Initial dropper analysis

This ZIP file was found on some random channel on Discord as some "cheat loader" software - at least that's what I assume, since I wasn't the person that found it.
Extracting the zip file, there's an Immortal.exe file that weighs 0 bytes, but inside of mapper... we have an actual exe file!

Putting this into DetectItEasy, we can see that it is a.. Rust compiled executable.

Since Rust is nearly impossible to statically analyze (or at least I have zero experience), that's going to be a no-go for me.

After putting this on any.run, we can see that it does a whole load of.. nothing?

It compiles a C# DLL, which seems to just hide the console window. After that, it extracts and attempts to run a file... that doesn't exist. It seems to at least try to download it, but it ultimately fails at some point.

Is the sample broken? Maybe. At the time I thought that this was some sort of anti-VM measure. Maybe when it detects it running in a VM it intentionally doesn't download the file. We have to dig deeper.

After failing to do anything with Ghidra, I decided to just run strings on the .exe file. What I found actually made me jump from joy:

We have some sort of obfuscated JavaScript! Turns out that this isn't even written in Rust. It's a Deno compiled executable.
This is (likely) how they manage to keep the dropper on 0 detections on VirusTotal, a fact that they really praise themselves about - of course it isn't so sunshine and rainbows after you get to the executables downloaded later :)

After putting it through the first deobfuscator I found on Google, we have something:

There's a bunch of code in here that I'm probably never going to understand. But we have a bunch of URLs:

So, here's a general flow of what the initial dropper does (judging by the any.run result, as well as the deobfuscated code):

I also noticed that the C2 servers have normal browser user-agents blocked:

However, downloading with an up-to-date version of cURL has worked fine for me :)

The two .exe's

Putting both .exe's into DetectItEasy, we can see that they are... packed with something called Nuitka?!

It turns out that Nuitka is some magic utility that transpiles Python code to C and then compiles that as the final .exe. It also somehow still uses normal Python libraries...

Pretty quickly, I found an utility called nuitka-extractor. This gives us... another exe file. That's also packed with Nuitka. Sigh.

Let's go over both of these .exe files. They seem to be packed with Nuitka's OneFile function, which extracts all of the Python libraries to some directory prior to running the real .exe file. Once again, those libraries can be extracted with nuitka-extractor. But static analysis is gonna lead you nowhere (at least it did for me because I have no clue what I was doing).

launcher.exe - the boring one

The general flow seems to be:

services.exe does the following:

This executable does the following:

Then it ends itself, and services.exe keeps running to monitor if someone opens the Temp folder in Explorer, if so it kills the process (probably to prevent anyone from grabbing the extracted exe).

...that's it. Booooriiiiiinggg. Let's get to the cooler one.

system.exe - initial failure

The executable name it extracts is different - it's Lopped.exe. However, during testing, after the anti-VM commands and the Update file executed (this time it's also another Nuitka-packed executable), it proceeded to.. do nothing. Weird.

At this point I was running everything on my own VM to minimize detection of the any.run sandbox. I also had mitmproxy running, however I wasn't seeing any requests. Looking at the console logs however, I noticed what was happening:

Even though I had added the mitmproxy cert file to the Windows certificate store, it still wasn't trusting it. That means it uses its own certificate store. Sigh.

The magic trick

But then I had a brilliant idea. Since it uses its own store, that means it has to be located somewhere.. right?

And since we know that it extracts its libraries and where they are, I just searched that entire folder for .pem files. And would you look at that:

Changing the file on the fly didn't work, so we need to form a plan to somehow change out that file before it reads it. I came up with this idea:

...profit indeed. :D

Funnily enough, "Preventing HTTP scrapping" is a feature that's listed on their website. Did I just beat the malware at its own game? 🤭

system.exe - pt. 2

On startup, it does basically the same thing as launcher.exe - check if it's a VM, if it is then blue-screen it, if not - extract the Update exe, run that.

The Update exe does some new stuff, here's a general flow of my first attempt of running it:

However, after this the executable looked like it entered a loop here - mitmproxy explained what happened: as I was researching all of this, the youarewatched[.]fun domain was actually suspended :)

This executable isn't configured to contact the other C2 domain for general stuff if this one fails - only for error logging. This means that this stealer was fully non-functional and has literally no backups 😽

"Fixing" their work

I was honestly flabbergasted and wanted to keep going. That's when I got another brilliant idea: Since we know what the "backup" C2 domain is, and we have this fancy proxy software, what if we just intercept the request... and change out the domain?

This all sounds so stupid.. but it actually worked. I have fixed their stealer for myself.. and it's a shame that I'm their only victim. :)

Continued flow

Notice that half of these have .exe.exe. Yes, that makes them not work entirely. Great engineering.

The rest is just the usual info grabbing stuff, which is shown further in the network requests.

Network requests

As soon as I changed the domain, as you could see in the video from before, requests started flooding out like crazy. All outgoing requests are formatted in URLEncoded Forms.


    auth:       esq123 // no idea what this could be, hardcoded in executable
    username:   c2Fub3duc3lvdQ== // username from netsuser.dll file
    ip:         xxx.xxx.xxx.xxx
    pcusername: username
    hwid:       xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxxxxxxxx
            

response:


    {
        "Username": "c2Fub3duc3lvdQ==", // username from netsuser.dll
        "exe": "1",
        "purchased": true,
        "Config": {
            "Token": "=5ESQ|ESQ)7ESQ [...] |ESQ#6ESQ$26ESQ", // this is some custom encryption system and frankly im just too stupid to figure out, its base64 and custom character mapping
            "GuildID": "=2ESQ [...] =4ESQ"
        },
        "Utils": {
            "AsarUrl": ")19ESQ$22ESQ$2ESQ$0ESQ)19ESQ)22ESQ#-1ESQ#14ESQ#7ESQ)22ESQ#6ESQ#2ESQ#19ESQ)22ESQ#7ESQ#11ESQ#9ESQ",
            "PsEnc": "#1 [...] Q",
            "Silent": "False", // "Silent Mode" in builder
            "CE": "", // "Custom Message"(?) in builder
            "FP": "False", // "Fake Presence" in builder
            "DWF": "False", // ???
            "AU": "", // ???
            "ZP": "", // "ZIP Password(?)" in builder
            "Status": "=2ESQ=1ESQ [...] =SQ" // ???
        }
    }                
            

    username:         c2Fub3duc3lvdQ== // contents of netsuser.dll
    ip:               xxx.xxx.xxx.xxx
    pcusername:       username
    infected_date:    x/x/2025 - xx:xx
    infected_current: x/x/2025 - xx:xx
    exe:              1
    current:          sanownsyou // the same thing as username but not base64??
            

response: 1


    auth:     8uhjk3f4rx548hytx // also hardcoded in executable
    ip:       xxx.xxx.xxx.xxx
    infector: sanownsyou    
            

response:


    {
        "all": {
            "minute": "N",
            "c1": "N",
            "w1": "N",
            "id": "N"
        },
        "ip": {
            "minute": "N",
            "c1": "N",
            "w1": "N",
            "id": "N"
        },
        "center": {
            "minute": "N",
            "id": "N",
            "cmd": "N"
        }
    }
    // ??????????????? what is this for
            

    2000: 20 // this is probably to identify what kind of request this is
    ip:   username-xxxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxxx // nice ip bro
            

response: 1


    000:      0
    build:    1
    username: sanownsyou
    ip:       username-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx // nice ip bro (again)
    pcname:   username // nice pc name bro
    system:   Windows 10.0.19044 64bit
            

(response is just the website with <a>Are you okay?</a>)


    1000:     10
    username: sanownsyou
    ip:       username-xxxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx
    pcname:   username // im tired this is the third fucking time
    system:   Windows 10.0.19044 64bit
    data:     {
    "fistInfect":"xx/xx/2025", // fist??
    "latestInfect":"xx/xx/2025",
    "information":{
        "username":"username",
        "system":"Windows 10.0.19044 64bit",
        "name":"DESKTOP-XXXXX",
        "cpu":"Dacia SuperCPU 1MHz",
        "cores":"4",
        "threads":"4",
        "socket":"CPU 0",
        "speed":"3600",
        "ram":"4.0", // 4 ram sir
        "motherboard":"ASUS Pile-Of-Shit ROG Extreme",
        "bios":"IBM BIOS 1.00",
        "osVersion":"10.0.19044",
        "osArch":"64-bit",
        "users":"3", // blatantly false LMFAOO
        "gpu":"NVIDIA GeForce RTX 5090",
        "disks":[
            {
                "disk":"C:\\",
                "total":"69.39",
                "busy":"27.34",
                "perBusy":"18.3",
                "available":"15.75",
                "perAvailable":"65.5",
                "type":"NTFS"
            }
        ],
        "connection":{
            "ip":"xxx.xxx.xxx.xxx",
            "location":"no",
            "coords":"no",
            "isp":"no",
            "domain":"no",
            "history":[
                "ip | location"
            ]
        },
        "wifi":[
            "Without WIFI adapter"
        ],
        "antimalware":"No information available",
        "screenshot":"P/[...]2Q==",
        "moreinfo":{
            "files":"35",
            "download":"https://gofile.io/d/xxxxx"
        },
        "clipboard":{
            "type":"1",
            "data":"-"
        }
    },
    "data":{
        "files":{
            "MicrosoftEdge":{
                "passwords":"0",
                "history":"0",
                "downloads":"0",
                "predicts":"1",
                "netPersist":"0",
                "webdata":"252"
            }
        },
        "download":"https://gofile.io/d/xxxxx"
    },
    "sysinfo":{
        "files":[
            "Ipconfig_Displaydns.txt",
            "ipconfig_Allcompartments_All.txt",
            "schtasks.txt",
            "wmic_context.txt",
            "wmic_qfe_list.txt",
            "wmic_product_get_name.txt",
            "Ipconfig_All.txt",
            "DriverQuery.txt"
        ],
        "download":"-"
    },
    "social":{
        "discord":{
            "accounts":
            "backupCodes":
        },
        "telegram":{
            "accounts":"0",
            "download":"-"
        }
    },
    "games":{
        "roblox":{
            "cookies":
            "software":
        },
        "steam":
        "minecraft":
        "rdp":
        "pnp":{
            "found":"0",
            "download":"-"
        }
    },
    "keylogger":{
        "size":"0",
        "download":"-",
        "history":
    "sessions":{
        "crunchyroll":
        "pinterest":
        "instagram":
        "tiktok":
        "netflix":
    },
    "webcam":{
        "size":"0",
        "download":"-",
        "history":
    },
    "microphone":{
        "size":"0",
        "download":"-",
        "history":
    },
    "screenshots":{
        "size":"0",
        "download":"-",
        "history":
    },
    "other":{
        "anydesk":
        "python":
        "discord":
        "windows":{
            "key":"" // epic fail
        },
        "net":{
            "ipv4":"0.0.0.0",
            "ipv6":"0:0:0:0:0",
            "mask":"255.255.255.0",
            "door":"192.168.0.1" // DOOR??????
        }, // i didn't edit this btw. this is how it is LMFAOO
        "esquelestealer":{
            "runner":"username",
            "mode":"Normal",
            "rights":"Admin (SYSTEM)",
            "version":"2.0.14.95 (PLUS)"
        }
    },
    "wallets":
    }
    ver:      2.0.14.95
            

... I have to pause it right there. You call the gateway a DOOR, you call an username and a UUID an "ip", you call a username a "pcname", you fail to grab a Windows product key... I have to look at the actual files you send.

..oh my fucking god.

The rest of the requests are repeats and it uploading data to Gofile.... and the dreaded Discord integration.

The Discord "functionality"

The builder has an option to communicate via a Discord bot instead of the web UI. The bot token is fetched and decrypted and uses the normal discord.py library.

Free Discord Bot Token Tutorial (100% no scam)

The token is fetched from the /api/info/1 request from before in their fancy encrypted form. But because the bot is client-sided, we don't even need to figure out how to decrypt the token. Since we have this fancy proxy, we can just see the bot's token in the requests 🥰

Getting the token statically is impossible since the token is server-side and encrypted. However, if one breaks this encryption somehow, it would make getting the token one API request away, all you need is the username from the initial dropper .exe :)

..or you can run the final system .exe sample in a controlled environment where the only request it's allowed to make is the /api/info/1 one and the initial Discord gateway one. Just some ideas 😗

Bonus: Insane Social Media Larp

These people claim that their stealer has "great power" and "violates other "well-known" stealers". I personally don't think so ;)

Sometime during the making of this post I got sent two of their "showcase" videos. Honestly I could not believe what I was watching.

..yes, this is them picking an "icon" for their dropper executable.

They also claim that it's "impossible to disinfect" claiming that they "infect the BIOS". This is straight up false - their "proof" that's shown later in the video is the Windows "let's finish setting up your PC" screen - something that literally runs as a standard user LMFAO

The "PC frying" payload literally does nothing

They also claim to have functionality that can "fry" a computer.. by overwriting the MBR. Yes, we still have MBR overwriters in the year 2025... remember when MEMZ was new? That was almost 10 years ago now. Damn.

I wanted to fact check the claim of "100% use" - I found the part where it downloads the MBR data. It attempts a download from two places:

I put it on a floppy disk image on a VM and started it. Unsurprisingly, it does not use 100% of anything.

Live Developer Reaction

Not long after I released this, the developer saw this and said that it's "fake" LMFAO

They put me in their "Clowns" channel on their Discord and tried to downplay the whole thing, saying that:

The "World's Largest DDoS Attack"

What I was very much expecting however, came at the very end:

Guess how long this lasted. 10 seconds. It all came from one IP and Cloudflare mitigated 90% of it. This might be the world's largest DDoS attack, I don't know...

Live Developer Reaction (2)

Right before their Great DDoS Attack, they posted a bunch of stuff on their Telegram channel trying to defame me as a "malware researcher".. even though I did say that I'm terrible at doing shit. Oh well.

Let's break down their.. inability to read and terrible Google Translate.

I should correct a few things you said here - I was not "unable to do anything to EsqueleStealer", I was unable to do anything to the Deno runtime since it is pretty large.
No, Rust does not compile JavaScript - the Deno runtime, which is written in Rust, runs your JavaScript.

Sure, maybe my screenshot was terribly postitioned. However this does not mean that the deobfuscation didn't work, it was enough to resolve your C2 URL's.
Quite a chunk of the code was unreadable to me - but it doesn't matter since I got your main payload anyway :) - and if you're talking about the function names, you should go learn about how obfuscation works.

I ackshually🤓🤓 got quite a lot of interesting things. Take your rose-tinted glasses off for a second - it may be useless to you but I've provided TONS of IOCs here which could make it very easily detectable, whether that's by knowing where the malware gets copied or other stuff.

Sure, that's a public thing. How about the other three links in that screenshot? :)

Once again, learn about what an IOC is. Please. And learn how to parse escaped characters in Python. Just saying.

You've probably never heard of having two virtual machines. And oh no, my VPN's IP... what will I ever do... 😭💔

First off, this was in your own executable's strings.

Second of all, I will remind you: you literally turned off your C2 once I said in a public Discord I started working on your malware :)

I think 30 people is a bit of an exaggeration. "You're a nobody"... says the person with a botted Discord server :)

To put that into perspective, a similarly sized Discord server has ~5,000 members online. These screenshots were taken at the same time.

Also, I like how you've completely ignored:

I wonder why. Is that because that would lose you customers? 🤔