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.
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
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.
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
Then the SHA1 hash is calculated for this string:
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:
|
|
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:
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.
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.
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.
Server Response Info
the remote server then verifies the given data and response with one of the few response strings below:
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:
- In
default.ini
edit the default configs to the following:
|
|
- Create or use the
sample_custom_response.ini
provided to contain the following, this is already set by default:
|
|
- The builder waits for a JSON string delimited by the character
0x0A
if this is not in the response it will wait forever.
As a result CustomProviderExample.py
should contain a JSON string ending with 0x0A
, I was testing with the following code:
|
|
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!
License info and IP used
The JSON strings also contain some other information about the User and the license
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.
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.
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.
Viewing the cross-reference we can deduce that it is used to identify the geo-location of a victim.
A sample of the content of geo.Aurora
can be seen below. The file contains ~380MB of data like this.
|
|
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.
Clear old screenshots
The third function deletes all the screenshots stored in the bot
directory!
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!
It then proceeds to finally delete the file.
Command Receiver
The next function is main_CommandReceiver
. It queues the commands received by the builder.
The function map.Range
has the definition:
|
|
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.
For all other commands, it goes to another function main_CommandReceiver_func2_1
. It’s expecting a 3-character long command MIX
.
It packs data about the victims with GZip and base64 encode it then, stores it back using map.store
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.
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
Then this function calls aurora_core_server__Server_Start
, this long value is passed with the port number passed to its driver function
This function starts the main server that displays the dashboard. I tried to adjust the execution to continue, but the program crashed.
Note: SixSixSix
is the author of the Stealer and not my username.
TCP listener
Back to function main_Server_0
(main_Server
).
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.
This function only calls the main_Client
function.
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.
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.
This image is used to update the screenshot that contains _ACTUAL.png
. The old one is then deleted.
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.
The victim’s Location identification
main_GetGeo
is then called. If we remember, the loaded JSON string was referenced in this function.
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
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:
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 |
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.
|
|
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.
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:
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.
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.
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.
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.
|
|
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 |
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:
|
|
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
|
|
old builder version
|
|
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/”
|
|
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”
|
|
Yara Seeds
To create the Yara rules, the following strings were used. Those are all present in the builder:
|
|
Acknowledgments:
@gi7w0rm for providing me with the samples and helping me formatting the article to make it better.