Aurora Stealer


Aurora Stealer is an information stealer Written in GO. It is a commercial stealer that costs around 250$ per month. The malware can steal Browser password and saved cookies, crypto information (Desktop and Web), Telegram, Steam and Specific files from the victim machine and can take a screenshot from it.


Basic information

The icon of the executable gives us a hit about how this is spreading. It has Photoshop icon, most probably it was spreading using Malvertising.


Binary Identification

First we want to know some basic information about the file so I will use DiE to do so.


It was identified as GO binary. .symtab is a legacy section in GO binaries. In GO binaries prior to Version 1.3 .symtab section hold the symbol table but it is no longer filed with anything useful. Without Symbols, the reversing will be so hard as a simple Hello world program in GO has about 2000 function this is a result of that GO compiler statically linking all the needed libraries. Later, I will try to tackle this problem using existed Tools.

An important aspect of the basic Triaging of a Malware is to check the readable Strings of the file. But GO is different in everything. The strings has a part of that too.

In GO, the strings are stored in Unicode format without null terminating character so many tools will handle that wrong. Also, the existence of this large number of library functions will make it worse. The resulting number of lines using strings utility in Die is 7371 line. We can reduce this number by matching for the library functions like the following Regex


this matches the lines that contains runtime, usr and root. this filters around 2500 line but still around 5000 line. these lines contains the function imported in program, you can check them but it will be so exhausting to get information from it. Let’s Continue our analysis using the disassembler.

Code Analysis

I will upload the sample to IDA to explore it. In the old versions of IDA, Library functions will not be recognized and renamed. Also the types will be mostly wrong.

To handle this there is some tools you can use to fix the types and names. I’ve used GoReSym.

This is a standalone executable you can run with following parameters

GoReSym_win.exe -t -d -p <PATH_TO_FILE> > fix.json

for more info about the available parameters, Check the repo of the tool.

content of the output is in JSON format so I saved it to use it in this IDA Script to rename the functions and correct the types in IDA database.

NOTE: -t parameter fix the types information but if you the decompiler will fail to decompile it.

If you want to know how this tool is working, Check this article. Basically it search for pclntab structure by searching for a magic header and follow the pointer to symbols table.

// pcHeader holds data used by the pclntab lookups.
type pcHeader struct {
	magic          uint32
													go12magic  = 0xfffffffb
													go116magic = 0xfffffffa
													go118magic = 0xfffffff0
													go120magic = 0xfffffff1
	pad1, pad2     uint8   // 0,0
	minLC          uint8   // min instruction size
	ptrSize        uint8   // size of a ptr in bytes
	nfunc          int     // number of functions in the module
	nfiles         uint    // number of entries in the file tab
	textStart      uintptr // base for function entry PC offsets in this module, equal to moduledata.text
	funcnameOffset uintptr // offset to the funcnametab variable from pcHeader
	cuOffset       uintptr // offset to the cutab variable from pcHeader
	filetabOffset  uintptr // offset to the filetab variable from pcHeader
	pctabOffset    uintptr // offset to the pctab variable from pcHeader
	pclnOffset     uintptr // offset to the pclntab variable from pcHeader

This is also used by the go parser itself in order to locate the function, For more info here

Another set of scripts available we can use it doing the same thing is Alphagolang

I will use Alphagolang here but both will provide similar result.

First I used script to recreate pclntab structure.

Second, I used script to rename the functions.

Third, I used to categorize the functions and pack them in folders, This will be very helpful to focus on user-code.

Fourth, I used to fix string references.

Fifth, I used to correct the types information by applying C like types to the used structures.

The result


Now, We have a better environment so we can start exploring the code efficiently.

Calling Conventions in GO

In function calls, GO has a different calling convention.

All the argument are passed using the stack from the left to right. The following assembly code is in Go assembler format

func testConv(x,y int) int {return x+y}
	MOVQ 0x8(SP), AX  ; get arg x
	MOVQ 0x10(SP), CX ; get arg y
	ADDQ CX, AX       ; %ax <- x + y
	MOVQ AX, 0x20(SP) ; return x+y-z

the compiler have to make sure that there is enough space on the stack to accommodate all the arguments and return values.

Strings in GO

Go stores strings in a Unicode -UTF-8- format without null terminating characters in a section contain all the strings but.

Strings in go stored in structure of value and length pair called StringHeader. So, in all the function where a string argument is passed, you will see an extra argument contain the length of the string.

type StringHeader struct {
	Data uintptr
	Len  int

First we start with main_init function (sub_595590). In GO, init() is a predefined function that takes no argument, Return no values. And Runs before any code in the package.


The block number 1 shows that it loads some DLLs and functions.

DLL Function
user32.dll GetDesktopWindow
user32.dll EnumDisplayMonitors
user32.dll GetMonitorInfoW
user32.dll EnumDisplaySettingsW
kernel32.dll LocalFree
Crypt32.dll CryptUnprotectData

In Block number 2, It Reads the the environment Variable USERPROFILE and concatenate \\APPDATA\\LOCAL\ and \\APPDATA\\ROAMING\ and save the new string to the memory.

In block 3, It did the same thing to get the Paths C:\\Users\\{user}\\APPDATA\\ROAMING,<Local>\\ but it replaces the string C:\\Users with C:\\windows.old\Users with Replace function from strings package

func Replace(original string, old string, new string, n int) string
//where n is the number of times replacing occures. -1 for replace all

this location is created when the user update from one version to another and it contains all the old information from the previous installation.

moving to main_main (sub_595470). It creates a new procedure by making a call to newproc function from runtime package.


Connect To server

following the code to main_ConnectToServer (sub_58ABE0). This function has some interesting functionality we will explore next.


In block 1, the malware sleeps for 1000000000 nanoseconds -I tried a simple program with the same call to sleep and it was equivalent to time.Nanosecond -

Then it establishes a TCP connection to IP address using function Dial from net package. Then it Reads the Received packet. the Dial function in GO returns 2 values, Conn interface and Error, which IDA cannot recognize so, I will follow my intuition. If the connection returned error, it will try to reconnect again.

In Block 2, The connection was established but it first checks the response from the remote IP. If it was blocked due to the geo location, as the IP is Russian, it will try to reconnect.

If the response was WORK string, the connection is established successfully and the malware can continue with its functionality as shown in block 3 and 4

Collect victim information

Moving to main_GetInfoUser() (sub_58B880). The first Lines in this subroutine takes us to another function, main_MachineID (sub_5897A0)


The malware Runs the command cmd.exe /c wmic csproduct get uuid to get UUID of the device. Returning to main_GetInfoUser .


It retrieves the screen width and height using win32 API GetSystemMetrics , GO allow using third-party packages directly from GitHub and the the cause of the function naming. The screen resolution is represented in the format <width>x<height> .

The next call is to main_GetOS (sub_58A530).


This function retrieves the OS version using wmic command wmic os get Caption . and filter the output based on the form it is printed to format is in a space separated string.

Returning back to main_GetInfoUser a call to main_getGPU (sub_58A200) is made.


The GPU information retrieved by executing the command cmd /C wmic path win32_VideoController get name

Using the same method in main_getCPU (sub_589F10). It gets CPU information with command cmd /c wmic cpu get name

in main_sysTotalMemory (sub_58B550)It gets the memory status by executing GlobalMemoryStatusEx function.


main_CMD_SHELL is called to execute cmd /c systeminfo that gets all the specs of the device.


That was the last thing the function main_GetInfoUser do.

Back in main_main , the function main_grab (sub_593E80) is called. This function responsible for doing the main goal of the malware, Stealing.


  • panic function is used to check for unexpected errors. common use of panic is to abort if a function returns an error value that we don’t want to handle.

File grabber

Going to the first function main_file_grabber (sub_594110)

this function search for a specific file taken from the C2 server and it is base64 encoded and in JSON format.


Then, It search for the file in some predefined directories and location.


the function io_ioutil_ReadDir reads the content of the directory and stores the output in a fs.fileinfo structure , sorted by the filename

type FileInfo interface {
	Name() string       // base name of the file
	Size() int64        // length in bytes for regular files; system-dependent for others
	Mode() FileMode     // file mode bits
	ModTime() time.Time // modification time
	IsDir() bool        // abbreviation for Mode().IsDir()
	Sys() any           // underlying data source (can return nil)

Then it walks through the returned structure and reads the file of interest


then it encode the file content in Base64 and adds the tags used in the JSON formatted packet content to be sent to the remote system


Browser data

We will visit SendToServer latter. Now, lets go back to the caller function and explore the next function, main_Grab_func3 (sub_58F0B0).


This function goes through the %APPDATA%Roaming directory and calls another function. the function path_filepath_Walk walks the directory from the Root passed in the second parameter calling a function fn.WinDirFunc at each file and directory in it including the Root.

func Walk(root string, fn WalkFunc) error
type WalkFunc func(path string, info fs.FileInfo, err error) error

So, Next one to visit is WalkFunc used main_Grab_func3_2 (sub_58DED0).

This function steals the Browser information stored


For Chromium based browsers it gets the Local State file and calls main_getMasterKey that as the name suggest, Gets the master key and decode it .then, decrypts it by calling CryptUnprotectData which is called from main_xDecrypt


It handles the case of using Opera and Firefox browsers


Back to the caller function, The malware steals the password and cookies from the browser data and adds the tags of the JSON file to be sent to the C2 server.





Then, It goes through the %USERPROFILE% searching for any Crypto wallets information


It Looks for PC applications and Web based wallets and add its associated type and name to the JSON data to be sent

Screenshot Capture

function main_Grab_func_7 (sub_591D50) is used to take a screenshot from the victim system


The PNG file is then base64 encoded and add the value to the tag screenshot to be sent.

Telegram Data

The next targeted information is Telegram, It did the same procedure discussed before with telegram data folder at main_Grab_func_6 (sub_591980)


WalkFuncmain_Grab_func_6_2 (sub_591120)


Steam data

function main_Grab_func9 (sub_593B30) steals steam data in the same way


send To server

main_SendToServer_NEW (sub_594DD0) is used to send the collected data to the server.


The collected information stored in JSON format. the Data then compressed using gzip compression algorithm and encoded with Base64 encoding to be sent to the server using the previously established TCP connection.

Network Analysis

we can look at the network communication using PCAP file provided by Any Run sandbox.

By opening the file in Wireshark and filter using the IP


Following the TCP stream


The first packet received is WORK indicates that the connection is successful and the malware then begin to collect the required data and compress it and send it to the server. At the last packet received from the the C2 server is Thanks.

we can use Cyberchef to decode and decompress the data.




the Error list include the files that the malware cannot read or access. On of the packets has a very large size, as the screenshot field has a very large Base64 encoded data


the screenshot:


Sample JSON file can be found here


Aurora stealer is a new commercial infostealer. Most of it’s capabilities are typical things that can be found in most of the stealers. it can grab Browser saved password/cookies and Cryptocurrency wallets information from Desktop applications and Web based wallets. Also, it can grab a files from the victim machine and take a screenshot. The communication with C2 server is done over TCP protocol. Most of these things can be found in most of the stealer But being written in GO makes it special, even it has a plaintext strings, The reversing process is quite annoying as most of the tools cannot handle GO binaries in a right way.


  • 29339458f4a33ee922f25d36b83f19797a15a279634e9c44ebd3816866a541cb
  • 82.115.223[.]249:8081

Yara Rule

rule aurora_stealer{
    malware = "Aurora stealer"
    hash = "29339458f4a33ee922f25d36b83f19797a15a279634e9c44ebd3816866a541cb"
    reference = ""
    Author = "d01a"
    description = "detect Aurora stealer"

    $is_go = "Go build" ascii

    $a1 = "C:\\Windows.old\\Users\\" ascii
    $a2 = "\\AppData\\Roaming\\" ascii
    $a3 = "wmic csproduct get uuid" ascii
    $a4 = "wmic cpu get name" ascii
    $a5 = "systeminfo" ascii
    $a6 = "coNNNECTIONGWQFGQW"  ascii

    $fun1 = "main.Grab"  ascii
    $fun2 = "main.getMasterKey"  ascii
    $fun3 = "main.SendToServer_NEW"  ascii
    $fun4 = "main.ConnectToServer"  ascii
    $fun5 = "main.xDecrypt" ascii
    $fun6 = "main.GetDisplayBounds" ascii

    uint16(0) == 0x5a4d and ( $is_go and (4 of ($a*)) and (4 of ($fun*)) )