A few months ago an unknown user joined our school’s cubing club’s Discord server and dropped a piece of malware in one of the text channels. The user was quickly banned but I downloaded a copy of the malware for future analysis (VirusTotal). This was my first dive into malware analysis and reverse engineering as I have always focused on forensics and some cryptography in CTFs.
Looking through strings of the file I found many references to Python so I thought this executable may have been created by PyInstaller.
Running pyinstxtractor extracted
.pyc files from the executable and
.pyd files containing the Python modules the program used. With many of the other
.pyc files being standard modules it became clear that
CloudBot.pyc contained the compiled Python bytecode for the malware.
Reversing the Bytecode
I have decompiled a few
.pyc files in the past using the uncompyle6 decompiler but running
CloudBot.pyc revealed that it was compiled using Python 3.9 which uncompyle6 does not support currently.
I tried using dis to disassemble the bytecode but was met with an IndexError from an invalid opcode.
I switched to using Decompyle++‘s disassembler (I have since switched to xdis which has better formatting) which gave me the full bytecode for the program and functions. With no available decompilers I translated the bytecode to equivalent Python source code.
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
The bytecode for the program was pretty simple to reverse but the bulk of the program lies in the
main() function. Things to note from this code is that the malware only runs on Windows and it targets the storage locations of Discord, Chrome, Opera, Brave, and Yandex browsers. Not all the translated code will be included in this post, I do not wish to share functional malware code on this site, just a breakdown on how scrapers like these work
1 2 3 4 5 6 7 8
Simple method that generates headers for the malware’s API requests
Retrieves the username, user ID, avatar, email, and phone number
Finds Discord authentication tokens from the local storage of browsers and Discord client
Gets the author of the program who is
wodx. Tries to retrieve more information about the author from a pastebin that was taken down
Gets the IP address of the device
Finds the avatar URL
wmic csproduct get uuid to get the HWID
Gets a list of all of the infected user’s friends
Gets a list of all channels the infected user is in
Finds if there are payment methods associated with the infected account
Function to send messages using the Discord API
Sends a copy of the malware to all of the infected user’s Discord friends
This function contains the bulk of the program. The function starts by getting the target IP, Windows username, and hostname. It then proceeds to look through the local storage of all the browsers mentioned above looking for discord tokens using the
gettokens() function. If a valid token is found then it finds information about that user using the
has_payment_methods() functions. This data is then sent in an embed to a hardcoded Discord webhook address. If the constant
self_spread is set to
True then the malware also runs the
From this analysis I learnt how to reverse Python bytecode and executables. The spread of Discord token scrapers like this program is concerning especially since the source code of many of these pieces of malware can be found on GitHub for script kiddies to use. Steven also managed to find a repo with a variant using the same webhook address as the executable that I analysed. I think this goes without saying but don’t run suspicious executables that get sent over messages.