Reversing Synasploit.exe


Posted 2021/06/28

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.

Basic Analysis

Looking through strings of the file I found many references to Python so I thought this executable may have been created by PyInstaller.

image-20210626104239710

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.

image-20210626105400636

Reversing the Bytecode

I have decompiled a few .pyc files in the past using the uncompyle6 decompiler but running file on CloudBot.pyc revealed that it was compiled using Python 3.9 which uncompyle6 does not support currently.

image-20210626105318342

I tried using dis to disassemble the bytecode but was met with an IndexError from an invalid opcode.

image-20210628111320107

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
import os
if os != 'nt':
    exit()

from re import findall
from json import loads, dumps
from base64 import b64decode
from subprocess import Popen, PIPE
from urllib.request import Request, urlopen
from datetime import datetime
from threading import Thread
from time import sleep
from sys import argv

LOCAL = os.getenv('LOCALAPPDATA')
ROAMING = os.getenv('APPDATA')
PATHS = {
    'Discord':ROAMING+'\\Discord',
    'Discord Canary':ROAMING+'\\discordptb',
    'Discord PTB':ROAMING+'\\discordptb',
    'Google Chrome':LOCAL+'\\Google\\Chrome\\User Data\\Default',
    'Opera':ROAMING+'\\Opera Software\\Opera Stable',
    'Brave':LOCAL+'\\BraveSoftware\\Brave-Browser\\User Data\\Default',
    'Yandex':LOCAL+'\\Yandex\\YandexBrowser\\User Data\\Default'
}

def getheaders(token, content_type=(None, 'application/json')):

def getuserdata(token):

def gettokens(path):

def getdeveloper():

def getip():

def getavatar(uid, aid):

def gethwid():

def getchat(token, uid):

def has_payment_methods(token):

def main():

try:
    main()
except Exception as e:
    print(e)
    exit()

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

getheaders()

1
2
3
4
5
6
7
8
def getheaders(token, content_type=(None, 'application/json')):
    headers = {
        'Content-Type': content_type,
        'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.64 Safari/537.11'
    }
    if token:
        headers.update({'Authorization': token})
    return headers

Simple method that generates headers for the malware’s API requests

getuserdata()

Retrieves the username, user ID, avatar, email, and phone number

gettokens()

Finds Discord authentication tokens from the local storage of browsers and Discord client

getdeveloper()

Gets the author of the program who is wodx. Tries to retrieve more information about the author from a pastebin that was taken down

getip()

Gets the IP address of the device

getavatar()

Finds the avatar URL

gethwid()

Calls wmic csproduct get uuid to get the HWID

getfriends()

Gets a list of all of the infected user’s friends

getchat()

Gets a list of all channels the infected user is in

has_payment_methods()

Finds if there are payment methods associated with the infected account

send_message()

Function to send messages using the Discord API

spread()

Sends a copy of the malware to all of the infected user’s Discord friends

main()

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 getuserdata(), getavatar(), and 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 spread() method.

Conclusions

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.