Contents

Aurora Stealer Builder

Introduction

in the previous article, I discussed what’s inside Aurora Stealer. After the release, @Gi7w0rm provided me samples of some versions of Aurora Stealer builder, a new version that was created recently and another one that was created in 2022. The newer version has some improvements in the builder and new features we will discuss in this article. Before we start this article, it is important to note that the Builder also contains and creates the Web panel to control the bots. This means the binaries we are looking at are actually a hybrid between a builder and a panel.

Startup info

In main_main the first display page is prepared to accept the credentials of the user and start checking them. It first displays an ASCII art of the word Aurora and provides communication channels for contacting the Aurora developers.

/aurora-stealer-builder/Aurora%20Stealer%20Builder%2071154b54173740119405bcc7b0c65504/Untitled.png

After the initial screen, it saves the UUID of the user, with the same function discussed before to make sure that only one user is using the builder.

Then it asks for the login and password of the user

/aurora-stealer-builder/Aurora%20Stealer%20Builder%2071154b54173740119405bcc7b0c65504/Untitled%201.png

Authentication method

After the credentials where provided, it calls main_createAccess. it saves the string 123 It passes the directory ./cache/Auth.aurora to a function called main_exists that checks if the file exists or not. If it existed it will ask for hand deleting it, if not it will create it.

/aurora-stealer-builder/Aurora%20Stealer%20Builder%2071154b54173740119405bcc7b0c65504/Untitled%202.png

It appends the UUID and the string AURORA_TECHNOLOGY and calculates the MD5 hash to it using the form

<UUID>AURORA_TECNOLOGY

after which it takes this hash to make a string in the following form:

123_aurora_<MD5_OF(<UUID>AURORA_TECNOLOGY)>_technology_123

/aurora-stealer-builder/Aurora%20Stealer%20Builder%2071154b54173740119405bcc7b0c65504/Untitled%203.png

Then the SHA1 hash is calculated for this string:

/aurora-stealer-builder/Aurora%20Stealer%20Builder%2071154b54173740119405bcc7b0c65504/Untitled%204.png

It generates the first string again and its MD5 hash. It uses the MD5 hash as a key for the AES GCM encryption routine. The generated bytes are then written to ./cache/Auth.aurora

To know what was written to the file, we can use this script:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
from Crypto.Cipher import AES
import binascii

# key is MD5 hash of <UUID>AURORA_TECHNOLOGY
key = b"<KEY>" 
# Auth.aurora content
cipher = "<CIPHER>"

data = binascii.unhexlify(cipher)
nonce, tag = data[:12], data[-16:]
cipher = AES.new(key, AES.MODE_GCM, nonce)
cleartext = cipher.decrypt_and_verify(data[12:-16], tag)
print(cleartext)
# cleartext is SHA1 hash of the string "123_aurora_<MD5_OF(<UUID>AURORA_TECNOLOGY)>_technology_123 "

which shows us the SHA-1 Hash of the string: 123_aurora_<MD5_OF(<UUID>AURORA_TECNOLOGY)>_technology_123

Server Authentication check

Going back to main_main , where it creates yet another hash:

/aurora-stealer-builder/Aurora%20Stealer%20Builder%2071154b54173740119405bcc7b0c65504/Untitled%205.png

This time, the password and login is used to create a string using the following form <LOGIN>_*Aurora_2023_Technology_<PASS>. then it calculates the SHA1 hash of it.*

Then, it calls main_server . This could be where the authentication of the user happens, just a hypothesis.

/aurora-stealer-builder/Aurora%20Stealer%20Builder%2071154b54173740119405bcc7b0c65504/Untitled%206.png

it sleeps 1000000000 nanoseconds. Then it makes a TCP connection with 185.106.93.237:56763 which seems to be the server where user authentication is done.

Dynamic Key calculation

If the connection is established, it calls main_DynamicKey which generates a key based on the current minutes in the current time, In America/Los_Angeles time format.

/aurora-stealer-builder/Aurora%20Stealer%20Builder%2071154b54173740119405bcc7b0c65504/Untitled%207.png

and calculate the SHA1 hash of it.

Back in the main_Server function the builder then puts all the hashes in JSON format to be sent to the server.

/aurora-stealer-builder/Aurora%20Stealer%20Builder%2071154b54173740119405bcc7b0c65504/Untitled%208.png

Server Response Info

the remote server then verifies the given data and response with one of the few response strings below:

/aurora-stealer-builder/Aurora%20Stealer%20Builder%2071154b54173740119405bcc7b0c65504/Untitled%209.png

Response Action
HWID_BAD [Aurora] HWID has a different value on the license server, write support
NOT_FOUND_ACCOUNT [Aurora] Account has been not found, wrong login or password.
LOST_LICENSE [Aurora] License expired.
DYNAMIC_KEY [Aurora] Dynamic key wrong, check time your OS or write support.

Network emulation

I tried to emulate the C2 communication with fakenet. After a very long time trying to do that. it works to respond to it with the format of data it waits for, but there is something still missing.

I edited the configs of the TCPListener of fakenet as can be seen below:

  1. In default.ini edit the default configs to the following:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
[RawTCPListener]
Enabled:     True
Port:        56763 # port it comm over
Protocol:    TCP
Listener:    RawListener
UseSSL:      No
Timeout:     100
Hidden:      False
# To read about customizing responses, see docs/CustomResponse.md
Custom:    sample_custom_response.ini
  1. Create or use the sample_custom_response.ini provided to contain the following, this is already set by default:
1
2
3
[ExampleTCP]
InstanceName:     RawTCPListener
TcpDynamic:       CustomProviderExample.py
  1. The builder waits for a JSON string delimited by the character 0x0A if this is not in the response it will wait forever.

/aurora-stealer-builder/Aurora%20Stealer%20Builder%2071154b54173740119405bcc7b0c65504/Untitled%2010.png

As a result CustomProviderExample.py should contain a JSON string ending with 0x0A , I was testing with the following code:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
def HandleTcp(sock):
    """Handle a TCP buffer.

    Parameters
    ----------
    sock : socket
        The connected socket with which to recv and send data
    """
    while True:
        try:
            data = None
            data = sock.recv(1024)
        except socket.timeout:
            pass

        if not data:
            break

        resp = b'{"Test":"test","Test2":"Test2"}\x0A' 
        sock.sendall(resp)

A value of the JSON string accepted must be the Dynamic key which is generated based on the local time of the user.

Anti-Debugging check

This Dynamic key is calculated again and the two values are compared in order to check if the sample is being debugged. Nice!

/aurora-stealer-builder/Aurora%20Stealer%20Builder%2071154b54173740119405bcc7b0c65504/Untitled%2011.png

License info and IP used

The JSON strings also contain some other information about the User and the license

/aurora-stealer-builder/Aurora%20Stealer%20Builder%2071154b54173740119405bcc7b0c65504/Untitled%2012.png

Also, it contains an IP that is used later in some other interesting functions. the author expects only one IP to be used by the builder.

/aurora-stealer-builder/Aurora%20Stealer%20Builder%2071154b54173740119405bcc7b0c65504/Untitled%2013.png

It calls convTstring which takes a generic value -any type- and converts it to a string. I don’t really know why it calls convTstring as it is an IP it would be passed as a string in the JSON. maybe later we realize what’s going on here.

/aurora-stealer-builder/Aurora%20Stealer%20Builder%2071154b54173740119405bcc7b0c65504/Untitled%2014.png

We see some calls to runtime.newProc . This function generates a new go running function and put it in a running Queue of other go functions waiting to run. This is generated by the compiler when using go keyword. Interested topic hah? Read more about it here. Sadly it makes debugging more difficult.

Why network emulation doesn’t work well

Back to the JSON data, it’s decoded with json.Unmashal function which takes a structure as an input and with the second parameter being the data in bytes. How is the data mapped to the structure? Well, according to Go documentation

How does Unmarshal identify the fields in which to store the decoded data? For a given JSON key "Foo"Unmarshal will look through the destination struct’s fields to find (in order of preference):

  • An exported field with a tag of "Foo" (see the Go spec for more on struct tags),
  • An exported field named "Foo", or
  • An exported field named "FOO" or "FoO" or some other case-insensitive match of "Foo".

What happens when the structure of the JSON data doesn’t exactly match the Go type?

Unmarshal will decode only the fields that it can find in the destination type

So, we should guess the names of the JSON data. One of them is Dynamic key but we should figure out how it’s decoded.

We can use the pattern of the previously sent data, It was called DK . Sadly, this and other attempts didn’t work. So, I will continue the other things only static in IDA.

Main Functionality

The main functionality of the builder is invoked with a series of goroutine calls. Each called function is preparing some data to be used later or to start the server itself. This serves as the main function of the builder.

IP Geolocation database

The first function of the series of newProc calls is main_LoadToDB which loads a very huge file called geo.aurora that contains a list of IP ranges all over the world.

/aurora-stealer-builder/Aurora%20Stealer%20Builder%2071154b54173740119405bcc7b0c65504/Untitled%2015.png

Viewing the cross-reference we can deduce that it is used to identify the geo-location of a victim.

/aurora-stealer-builder/Aurora%20Stealer%20Builder%2071154b54173740119405bcc7b0c65504/Untitled%2016.png

A sample of the content of geo.Aurora can be seen below. The file contains ~380MB of data like this.

  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
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
[
  {
    "Country_short": "AU",
    "City": "Queensland",
    "Region": "",
    "Zipcode": "",
    "Timezone": "",
    "In": "1.0.0.0",
    "Out": "1.0.0.255"
  },
  {
    "Country_short": "CN",
    "City": "Fujian",
    "Region": "",
    "Zipcode": "",
    "Timezone": "",
    "In": "1.0.1.0",
    "Out": "1.0.3.255"
  },
  {
    "Country_short": "AU",
    "City": "Victoria",
    "Region": "",
    "Zipcode": "",
    "Timezone": "",
    "In": "1.0.4.0",
    "Out": "1.0.7.255"
  },
  {
    "Country_short": "CN",
    "City": "Guangdong",
    "Region": "",
    "Zipcode": "",
    "Timezone": "",
    "In": "1.0.8.0",
    "Out": "1.0.15.255"
  },
  {
    "Country_short": "JP",
    "City": "Tokyo",
    "Region": "",
    "Zipcode": "",
    "Timezone": "",
    "In": "1.0.16.0",
    "Out": "1.0.16.255"
  },
  {
    "Country_short": "JP",
    "City": "Tokyo",
    "Region": "",
    "Zipcode": "",
    "Timezone": "",
    "In": "1.0.17.0",
    "Out": "1.0.31.255"
  },
  {
    "Country_short": "CN",
    "City": "Guangdong",
    "Region": "",
    "Zipcode": "",
    "Timezone": "",
    "In": "1.0.32.0",
    "Out": "1.0.63.255"
  },
  {
    "Country_short": "JP",
    "City": "Hiroshima",
    "Region": "",
    "Zipcode": "",
    "Timezone": "",
    "In": "1.0.64.0",
    "Out": "1.0.64.255"
  },
  {
    "Country_short": "JP",
    "City": "Hiroshima",
    "Region": "",
    "Zipcode": "",
    "Timezone": "",
    "In": "1.0.65.0",
    "Out": "1.0.66.255"
  },
  {
    "Country_short": "JP",
    "City": "Hiroshima",
    "Region": "",
    "Zipcode": "",
    "Timezone": "",
    "In": "1.0.67.0",
    "Out": "1.0.67.255"
  },
  {
    "Country_short": "JP",
    "City": "Hiroshima",
    "Region": "",
    "Zipcode": "",
    "Timezone": "",
    "In": "1.0.68.0",
    "Out": "1.0.68.127"
  },
  {
    "Country_short": "JP",
    "City": "Miyagi",
    "Region": "",
    "Zipcode": "",
    "Timezone": "",
    "In": "1.0.68.128",
    "Out": "1.0.69.255"
  },
  {
    "Country_short": "JP",
    "City": "Hiroshima",
    "Region": "",
    "Zipcode": "",
    "Timezone": "",
    "In": "1.0.70.0",
    "Out": "1.0.71.255"
  },
....
]

Bot state

The second function is to get the status of the infected systems. This includes a check if the bot is active, the last connection time of the bot, and the current time.

/aurora-stealer-builder/Aurora%20Stealer%20Builder%2071154b54173740119405bcc7b0c65504/Untitled%2017.png

Clear old screenshots

The third function deletes all the screenshots stored in the bot directory!

/aurora-stealer-builder/Aurora%20Stealer%20Builder%2071154b54173740119405bcc7b0c65504/Untitled%2018.png

It sorts the pictures to be deleted by _ in it, then it gets what has ACTUAL word in it, lastly, it deletes the file extension .png from the string using strings.Trim and the new string should be a number as it calls strconv.atoi and then gets the current time. What a mess!

/aurora-stealer-builder/Aurora%20Stealer%20Builder%2071154b54173740119405bcc7b0c65504/Untitled%2019.png

It then proceeds to finally delete the file.

/aurora-stealer-builder/Aurora%20Stealer%20Builder%2071154b54173740119405bcc7b0c65504/Untitled%2020.png

Command Receiver

The next function is main_CommandReceiver. It queues the commands received by the builder.

/aurora-stealer-builder/Aurora%20Stealer%20Builder%2071154b54173740119405bcc7b0c65504/Untitled%2021.png

The function map.Range has the definition:

1
func (m *Map) Range(f func(key, value any) bool)

where f is a function called for each <key,value> pair. So the variable CMD_QUEUE would contain the received commands.

Going through the function main_CommandReceiver_func2 we see that the software first checks if the received command is STOP. If the STOP command is received, the builder exits.

/aurora-stealer-builder/Aurora%20Stealer%20Builder%2071154b54173740119405bcc7b0c65504/Untitled%2022.png

For all other commands, it goes to another function main_CommandReceiver_func2_1 . It’s expecting a 3-character long command MIX .

/aurora-stealer-builder/Aurora%20Stealer%20Builder%2071154b54173740119405bcc7b0c65504/Untitled%2023.png

It packs data about the victims with GZip and base64 encode it then, stores it back using map.store

/aurora-stealer-builder/Aurora%20Stealer%20Builder%2071154b54173740119405bcc7b0c65504/Untitled%2024.png

There were some log messages related to other commands here. However, I couldn’t figure out how the commands are treated. Based on the sample I discussed in a previous article, I guess this is connected to the messages sent from the victim machine.

/aurora-stealer-builder/Aurora%20Stealer%20Builder%2071154b54173740119405bcc7b0c65504/Untitled%2025.png

Main server functionality

The server is now ready to work and build the graphical interface of the builder to view the victim’s data and state and further use the victims as Bots and Stealer hosting servers using SFTP.

server start!

Next function is main_SERVER_func1 it calls main_ForwardPort with argument :7367

/aurora-stealer-builder/Aurora%20Stealer%20Builder%2071154b54173740119405bcc7b0c65504/Untitled%2026.png

Then this function calls aurora_core_server__Server_Start , this long value is passed with the port number passed to its driver function

/aurora-stealer-builder/Aurora%20Stealer%20Builder%2071154b54173740119405bcc7b0c65504/Untitled%2027.png

This function starts the main server that displays the dashboard. I tried to adjust the execution to continue, but the program crashed.

/aurora-stealer-builder/Aurora%20Stealer%20Builder%2071154b54173740119405bcc7b0c65504/Untitled%2028.png

Note: SixSixSix is the author of the Stealer and not my username.

TCP listener

Back to function main_Server_0 (main_Server).

/aurora-stealer-builder/Aurora%20Stealer%20Builder%2071154b54173740119405bcc7b0c65504/Untitled%2029.png

It logs the start of the server in the main display.

The server is started using net.Listen function that takes the protocol = tcp and port = 456 .

Main Client

After setting up the Server, the function main_server_func2 is called.

/aurora-stealer-builder/Aurora%20Stealer%20Builder%2071154b54173740119405bcc7b0c65504/Untitled%2030.png

This function only calls the main_Client function.

/aurora-stealer-builder/Aurora%20Stealer%20Builder%2071154b54173740119405bcc7b0c65504/Untitled%2031.png

Handling incoming data

To handle incoming data from the victim, the panel/builder reads the data on the listening port using bufio__Reader_ReadString. This data must be delimited by 0x0A as discussed previously. It comes in a compressed format, so the function main_uncompress is used to decompress it.

/aurora-stealer-builder/Aurora%20Stealer%20Builder%2071154b54173740119405bcc7b0c65504/Untitled%2032.png

To do so, the function takes the base64 encoded data and decodes it, then it is decompressed using GZip. You might remember from my last article, that this is the way the data was sent from the victim’s device.

The data is in form of JSON so it’s extracted with a call to json.Unmarshal . The resulting data is then stored in a victim database file. The last message is additionally stored in the map function.

Update victims DB

One of the first packets received from the victim is a large base64 blob. After decoding it using the above-mentioned method, it can be seen that this blob is a screenshot from the victim’s machine.

/aurora-stealer-builder/Aurora%20Stealer%20Builder%2071154b54173740119405bcc7b0c65504/Untitled%2033.png

This image is used to update the screenshot that contains _ACTUAL.png . The old one is then deleted.

/aurora-stealer-builder/Aurora%20Stealer%20Builder%2071154b54173740119405bcc7b0c65504/Untitled%2034.png

The other screenshots are stored in a similar way but the name is different.

It updates the stolen victim data as well, and the last response from each infected host is stored in the previously created map.

/aurora-stealer-builder/Aurora%20Stealer%20Builder%2071154b54173740119405bcc7b0c65504/Untitled%2035.png

The victim’s Location identification

main_GetGeo is then called. If we remember, the loaded JSON string was referenced in this function.

/aurora-stealer-builder/Aurora%20Stealer%20Builder%2071154b54173740119405bcc7b0c65504/Untitled%2036.png

It parses the string IP to convert to IP to a Go IP type which is a decimal dotted IP address.

Then it goes through a very large loaded JSON string that contains every IP range associated to each region all over the world.

The new victims will have an identifier is the string MIX that is checked to handle the new victims

/aurora-stealer-builder/Aurora%20Stealer%20Builder%2071154b54173740119405bcc7b0c65504/Untitled%2037.png

If the victim is new, it will store the screenshot with _ACTUAL tag as discussed before but there is no old one to delete.

At the very end of the function, a call to main_Registration is made. This function just adds a new entry to the victims’ list and gets the geolocation of the victim.

Main web server

At the beginning of the function main_Server there was a goroutine that I missed initially. It calls main_web before the call to net.Listen .

main_web initializes the web interface of the builder and the dashboard with all of its functionality. the server starts at port 8181 .

The function follows the same pattern to set the methods of the handler for APIs:

/aurora-stealer-builder/Aurora%20Stealer%20Builder%2071154b54173740119405bcc7b0c65504/Untitled%2038.png

The following table contains all available APIs with their associated handlers:

API APIHandler name APIHandler address Description
getbots main_web_func1 0x7635A0 List all the victims by walking through main_BOT_CONN map
callback main_web_func2 0x763800 get the callback message of each victim through the main_BOT_LASTMESSAGE or Queriyng the raw query of the connection address and get the message associated with victim IP
callback_STR main_web_func3 0x763A00 get the callback message string for each victim stored at main_BOT_LASTMESSAGE_STRING
callback_ps main_web_func4 0x763C00 get the PowerShell response of each victim through main_BOT_POWERSHELL_MESSAGE or Queriyng the raw query of the connection address and get the PowerShell message.
Statistic main_web_func5 0x763E00 shows statistics about the victims stored in .Aurora file in ./bots/ folder and redirects to web/statistic.html html template. The statistics show all the users with their IP addresses and geolocation
send_pw main_web_func6 0x764428 sends a base64 encoded PowerShell command to the victim using the json format. The associated key in the query is argument string
GiveMeBuild main_web_func7 0x7648E0 checks\builds the executable file of the stealer .The build file is stored in .\build it first checks if it exists on the system. if exists, tries to read it. If read is not successfully done, it exits. If not, the author prepared the file to be sent as an attachment for another remote system. it’s sent in the Content-deposition as follows: Content-Desposition: attachment = .exe
send main_web_func8 0x764E60 sends cmd \ PowerShell commands to the victims. They are sent through the argument key in the URL raw query
sftp_stop_reverse main_web_func9 0x7655A0 closes the SFTP connection with the victims and closes the associated port forwarding functionality. Also, it deletes the entry associated with the deleted victim’s SFTP connection in main_BOT_CLIENT_SFTP map
sftp_reverse main_web_func10 0x765820 start a SFTP server with the victim. the connection is done through port 7273 . The successful connection is indicated by WORK string. the configuration and data about the connection in the associated maps main_BOT_CLIENT_SFTP , main_BOT_LASTMESSAGE . This reverse shell is then used to host the stealer. The infected Bots can be used in DoS attacks too.
screenshot main_web_func11 0x766540 Takes a screenshot of the victim, it first checks if it’s active. SHA1 hash is calculated to the png file to see if the screenshot is the same as the stored or not before updating the database of the victims. the process is identified by Bad or Good statement.
bot main_web_func12 0x766C00 displays the status of the bots and all information , online boots its geo location, SFTP connected bots in the web/bot.html html template page. it also reads the content of ./core/scr_n_f.png but I don’t see any use of it. It encodes the data in it and then redirect to bot.html
logout main_web_func13 0x767680 Logs out!
auth main_web_func14 0x767780 Authenticate the access of the client. It uses the file ./cache/Auth.Aurora to compare its content with the newly calculated hashes as discussed before.
dashboard main_web_func15 0x767BA0 The dashboard of the stealer, which shows some data about the active and offline Bots.
del_cmd main_web_func16 0x768220 deletes a registered command from the main_CMD_QUEUE assigned to the victim
commands main_web_func17 0x768380 display the command selection interface in the web/commands.html html template
AddCommand main_web_func18 0x768840 add a new command to the victim commands list, it reads the assigned commands JSON data and adds a new command to it buy calling main_AddCommand that updates main_CMD_QUEUE map assigned to the victim.
AddLoaderCommand main_web_func19 0x768B60 add loader command. reads the response of the Client.Get() method and then the associated JSON data and base64 encode it. There are some strings used in the identification like EXTERNAL_RUN_PE_X64 . the data then stored in the associated map (main_CMD_QUEUE) and the victims DB
  • net.Query in Go parses the raw query and returns the values.
1
2
3
4
5
u, err := url.Parse("https://example.com/?a=1&b=2")
q := u.Query()
// q will have the values associated to a & b
fmt.Println(q.Get("a")) // print 1
fmt.Println(q.Get("b")) // print 2

Older version of the builder

There’s another sample provided to me, executable hash33fc61e81efa609df51277aef261623bb291e2dd5359362d50070f7a441df0ad

This sample looks like it was one of the first trials of the author to create a stealer in Go. It depends on so many additional legitimate packages from GitHub to create the server and handle the database manipulation and some other things. In the newer builder, it seems like he got more familiar with the Go Language and didn’t rely on the packages from GitHub.

/aurora-stealer-builder/Aurora%20Stealer%20Builder%2071154b54173740119405bcc7b0c65504/Untitled%2039.png

The package used to grab the favicon (from the first GitHub account), create the GUI web application (the second account), provide sqlite3 interface and provide a library like ReadLine in C.

The repositories are in the following table:

Old sample New sample
http://github.com/adampresley/gofavigrab http://github.com/vmihailenco/tagparser
http://github.com/asticode/go-astikit http://github.com/vmihailenco/msgpack
http://github.com/chzyer/readline
http://github.com/go-telegram-bot-api/telegram-bot-api
http://github.com/gorilla/mux
http://github.com/jroimartin/gocui
http://github.com/manifoldco/promptui
http://github.com/mattn/go-runewidth
http://github.com/nsf/termbox-go

The old sample has some functions that were described before, which were extended in the 2023 version. The hash calculation method and dynamic key but instead of Aurora_Stealer_2023 it is Aurora_Stealer_2022. Then it connects to the remote server to authenticate the user data, to the IP 185.106.93.237:6969 using TCP protocol.

/aurora-stealer-builder/Aurora%20Stealer%20Builder%2071154b54173740119405bcc7b0c65504/Untitled%2040.png

Another dynamic key is used to authenticate with the server, based on the current time too however in the old sample the string Aurora_Stealer_SERVER is used.

/aurora-stealer-builder/Aurora%20Stealer%20Builder%2071154b54173740119405bcc7b0c65504/Untitled%2041.png

This key is sent to the remote server and calculated later in the following code to verify the user access and the dynamic key to make sure there is no debugging session started.

/aurora-stealer-builder/Aurora%20Stealer%20Builder%2071154b54173740119405bcc7b0c65504/Untitled%2042.png

If the keys do not match, the function breaks and the program is terminated.

Another dynamic key is calculated but this time for the client, it uses the string Aurora_Stealer_2033 with the same timing method of calculation discussed.

The hashes are stored then in ATX.Aurora in ./cache folder.

It then checks the existence of some files: ./cache/ATX.Aurora , ./cache/telegram.Aurora , ./cache/Config.Aurora and ./cache/Trash .

./cache/Trash contains older Aurora executables, the older executables are auto-moved to this folder using PowerShell command, and the new version, which is expected to be in .zip format with the name Update.zip, is then unzipped and replaces the older version. The program is then restarted using PowerShell. This is all done in main_AutoUpdate function.

The function main_ReadTGData reads telegram data from the file ./cache/telegram.Aurora which is AES encrypted. The authentication is done using a telegram bot through the telegram API. This authentication method is removed from the new version, where everything is done through communicating with the remote server.

The old builder additionally contains an important function called main_LoadStealer . This function calls two other goroutines. both two functions execute PowerShell commands that configure the firewall to allow it to receive incoming TCP connections through Port 80 and 8081.

/aurora-stealer-builder/Aurora%20Stealer%20Builder%2071154b54173740119405bcc7b0c65504/Untitled%2043.png

1
2
3
4
#function main_LoadStealer_func2 allow it on local port 80
netsh advfirewall firewall add rule name=Port 80 dir=in action=allow protocol=TCP localport=80
#function main_LoadStealer_func2 allow it on local port 80
netsh advfirewall firewall add rule name=Port 8081 dir=in action=allow protocol=TCP localport=8081

At the end of the main function, it creates a new hidden instance of CMD and starts the Web service of the stealer. using the function main_StartWeb

This function starts the web service on localhost http://127.0.0.1/dashboard . It has a different set of APIs and different associated handlers then the newer version.

  • The command strings are highlighted.
API APIHandler name APIHandler address Description
receive main_StartWeb_func1 0x140421B00 It receives the incoming commands and connects to the remote server 185.106.93.237:6969 to get match the stored hashes with the calculated one in form of Aurora<PASSWORD .this function has a lot of other functionality. it reads the command from the response of the server. It allows the user to delete a directory Delete, remove file grabber RemoveG, or remove the loader RemoveL.GEO_URL to get the geolocation of all victims. AddDmen Add a new domain name received from the server.BuildGen builds a new version of the stealer and the ability to increase the file size PumbMB.DeleteTG , AddTelegram delete\add telegram configuration.DeleteAll Delete all the configs.ChangePassword , change password and download all logs files Download_AllLogs. Download_OnlyCrypto downloads the crypto wallet information only.
api.exe main_StartWeb_func2 0x140421B60 adds a new telegram API key to the stealer and adds an icon using resource hacker cmd command ./resource/ResourceHacker.exe -open ./builds/<STEALER_NAME>.exe -save ./builds/<STEALER_NAME>.exe -action addskip -res ./resource/main.ico -mask ICONGROUP,MAIN .
dashboard/{id:[0-9]+} main_productsHandler 0x14041D080 display the main window of the web service displays information about a specific victim ID: Cookies, passwords, the Geolocation, and crypto wallet information. Logs are stored in ./logs/ folder contain passwords in passwords.txt , cookies in folder Cookies . All the information is shown through the HTML template ./gui/Dashboard.html
download_geo main_StartWeb_func3 0x140422100 retrieves the geolocation information, the same as the new one.
download_l main_StartWeb_func4 0x1404222A0 gets the logs in a .zip archive, uncompresses it and deletes the archive. the logs contain all the stolen data
api/get-log-build main_StartWeb_func5 0x140422620 get the build logs from ./logs associated with a specific API key used
build.exe main_StartWeb_func6 0x140422B60 gets a build executable of the stealer stored at ./builds
dashboard main_StartWeb_func7 0x140422EA0 display the dashboard of the stealer, and shows some statistics about the infected system. IPs, geo-location and the stolen information
loader main_StartWeb_func8 0x140422FE0 display information about the Loader and file grabber. the threat actor can use this section to configure the loader and specify the target file to grab. file ./config/telegram.txt is used to extract the telegram connection configuration. The information is viewed by executing gui/Loader.html HTML template.
setting main_StartWeb_func9 0x1404234A0 builder settings, display information about the subscribed plan and change the password and telegram configuration and API. and shows the used domains
auth main_StartWeb_func10 0000000140423A40 the AUTH page that the user signs in to where the used credentials and AUTH cache file in ./cache/AuthHash.Aurora are checked. Whenever the user navigates, the credentials and hashes are checked. if not valid, will be redirected to this page
builder main_StartWeb_func11 0x140423CC0 creates a new build through it. the build target architecture victims group is chosen.
checker main_StartWeb_func12 0x140424380 checks the wanted information from the victim DB. check the build used and get the geolocation of the victim specified.

then the server is started on port 80

In function main_AddNewClient , the victim entries on the data based are created by calling main_CreateDB data stored about the user in UserInformation.txt:

  • HWID
  • Build ID
  • Log date
  • IP
  • Country
  • Region
  • City
  • PC INFORMATION
    • CPU
    • Screen Size
    • Screen Size
    • RAM
    • Display Device (GPU)

in addition to the stolen information the following credentials are received:

  • Steam
  • Passwords
  • cookies
  • crypto wallets -stored in subdirectory /wallets
  • Telegram info
  • screenshots
  • grabbed files -stored in subdirectory ./FileGrabber
  • Cards information

Browser cookies are stored in .db files in ./cache to be decrypted and the extracted data is stored in .txt file.

The end of the packet is checked by END_PACKET_ALL_SEND sentence. And the last packet sent to the victim is Thanks , then, the data are zipped and sent to the telegram account configured.

The function main_DecryptLog_Card is used to decrypt the credit card information collected. It uses the following sqlite3 query to achieve that:

1
select name_on_card, expiration_month, expiration_year, card_number_encrypted, date_modified, use_date, use_count, nickname from credit_cards

Web service HTML templates

You can find screenshots of the HTML templates in this tweet.

Yara Rules

all the rules can be found here.

new builder version

 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
rule aurora_stealer_builder_new{
    meta:
    malware = "Aurora stealer Builder new version 2023"
    hash = "ebd1368979b5adb9586ce512b63876985a497e1727ffbd54732cd42eef992b81"
    reference = "https://d01a.github.io/"
    Author = "d01a"
    description = "detect Aurora stealer Builder new version 2023"

    strings:
    $is_go = "Go build" ascii

    $s1 = "_Aurora_2023_Technology_"    ascii
    $s2 = "AURORA_TECHNOLOGY"  ascii
    $s3 = "scr_n_f.png" ascii
    $s4 = "EXTERNAL_RUN_PE_X64" ascii
    $s5 = "[Aurora]" ascii //log messages begin with [Aurora] __LOGMSG__

    $fun1 = "main.Server" ascii
    $fun2 = "main.GetAcess" ascii
    $fun3 = "main.AddCommand" ascii
    $fun4 = "main.GetGeoList" ascii
    $fun5 = "main.GiveMeBuild" ascii

    condition:
    uint16(0) == 0x5a4d and ( $is_go and (2 of ($s*)) and (2 of ($fun*)) )
}

old builder version

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
rule aurora_stealer_builder_old{
    meta:
    malware = "Aurora stealer Builder old version 2022"
    hash1 = "33fc61e81efa609df51277aef261623bb291e2dd5359362d50070f7a441df0ad"
    reference = "https://d01a.github.io/"
    Author = "d01a"
    description = "detect Aurora stealer Builder old version 2022"

    strings:
    $is_go = "Go build" ascii

    $s1 = "ATX.Aurora"    ascii
    $s2 = "Aurora_Stealer_2033"  ascii
    $s3 = "Aurora_Stealer_SERVER" ascii
    $s4 = "[Aurora Stealer]" //log messages

    $fun1 = "main.DecryptLog" ascii
    $fun2 = "main.CreateDB" ascii
    $fun3 = "main.GenerateKey" ascii
    $fun4 = "main.TGParce" ascii

    condition:
    uint16(0) == 0x5a4d and ( $is_go and (2 of ($s*)) and (2 of ($fun*)) )
}

IOCs:

ebd1368979b5adb9586ce512b63876985a497e1727ffbd54732cd42eef992b81 aurora.exe (2023 version)
e7aa0529d4412a8cee5c20c4b7c817337fabb1598b44efbf639f4a7dac4292ad builder archive (2023 version)
33fc61e81efa609df51277aef261623bb291e2dd5359362d50070f7a441df0ad aurora.exe (2022 version)
33b61eb5f84cb65f1744bd08d09ac2535fe5f9b087eef37826612b5016e21990 geo.Aurora
1def6bdec3073990955e917f1da2339f1c18095d31cc12452b40da0bd8afd431 ds.html
f1ba92ae32fcaeea8148298f4869aef9bcd4e85781586b69c83a830b213d3d3c statistic.html
8b1abbb51594b6f1d4e4681204ed97371bd3d60f093e38b80b8035058116ef1d bot.html
e9cf3e7d2826fa488e7803d0d19240a23f93a7f007d66377beb1849c5d51c0af commands.html
d7829f17583b91fb1e8326e1c80c07fc29e0608f1ba836738d2c86df336ea771 rergister.html
1b88624936d149ecdea6af9147ff8b2d8423125db511bdf1296401033c08b532 settings.html
185.106.93.237:56763 Aurora server -version 2023- used in user account verification
185.106.93.237:6969 Aurora server -version 2022- used in user account verification
Auth.aurora locally created for each Aurora panel user and used in account verification
scr_n_f.png contains config information
screenshot/ a local folder that contains victims’ screenshots
<*>_ACTUAL.png screenshot of current state of online bots
<>_<>.png custom screenshots format

The following go files were identified in the binary, all starting with the path: “C:/Users/SixSixSix/Desktop/Botnet 2023/26.01.2023/new/”

 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
auth.go
crypt.go
command.go
compressor.go
core.go
geo.go
main.go
pfor.go
port.go
web.go

core/statistics/window.go
core/statistics/winfuns.go
core/statistics/queue.go
core/monitor/monitor.go
core/common/copy.go
core/common/udpconn.go
core/common/util.go
core/logger/logger.go
core/schema/monitor.go
core/schema/util.go
core/server/client.go
core/server/client_handlers.go
core/server/server.go
core/server/server_handlers.go

There are similar files identified in the old version of the builder/panel.

The common path for this older sample is: “C:/Users/SixSixSix/Desktop/Aurora 2022/server”

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
auth.go
compressor.go
config.go
cryptography.go
favicon.go
geo.go
gui.go
main.go
notify.go
other.go
server.go
telegram.go
zip.go

Yara Seeds

To create the Yara rules, the following strings were used. Those are all present in the builder:

 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
127.0.0.1:7273

POWR

WORK

PORT_FORWARD

FTP_RUN - REVESRE START

_*Aurora_2023_Technology_*

AURORA_TECHNOLOGY

./cache/Auth.aurora

_ACTUAL

./bots/screenshot/

./core/scr_n_f.png

EXTERNAL_RUN_PE_X64

[Aurora] Botnet - SERVER - RUN

- old sample.
    
    ./cache/Config.Aurora
    
    ./cache/Aurora.Aurora
    
    ./cache/telegram.Aurora
    
    ./cache/ATX.Aurora
    
    Aurora_Stealer_2033
    
    Aurora_Stealer_SERVER
    
    Aurora_Stealer_2022
    
    https://api.telegram.org/bot%s/%s
    
    ./cache/AuthHash.Aurora
    
    [Aurora Stealer]: Yes i am work!

Acknowledgments:

@gi7w0rm for providing me with the samples and helping me formatting the article to make it better.