Analysis of packed variation of Agent Tesla

28.05.2021
by VirLab team

For reverse engineering analysis, the following file was provided:

  • OUTSTANDING STATEMENT.xlz.exe

A24BF02CE43A846463532E46211962EC7E883E682ABBDE61F922FCD46FC301BC

General Description

In December 2020, one of our tLab system clients, the Center for the Development of Human Resources JSC, received malicious software on their corporate email. The malware was not detected at the static, signature level by antivirus software, but at the behavioral and heuristic levels by the tLab system.

Based on the results of the initial assessment, as part of the technical support of the tLab system, the sample was sent out to our malware reverse engineering laboratory for further deep manual analysis.

The examination results revealed it was a zero-day threat. The date of creation of the sample matches the day of its discovery.

The findings were published later on Center for the Development of Human Resources JSC

The tLab system dynamic and static analysis showed that the sample is spyware and is responsible for collecting confidential user (victim) data, such as authentication data stored in browsers, VPN, FTP and email clients. Based on the nature of the attack (not massive), one can classify it as a targeted attack.

Additionally, during the analysis, we found lots of similarities with the Agent Tesla malware.

Agent Tesla is a malware used for espionage. It has been known since 2014, and the number of infections has increased over the past two or three years, usually using phishing emails.

Workflow overview of the sample (OUTSTANDING STATEMENT.xlz.exe)

At stage 1, the sample unpacks a resource named dx, which is an URL-encoded base64 string that results in a PE file.

At stage 2, the library uses a second resource called hLuPu.png to convert it to a byte array. Next, an XOR operation is performed over the array with a previously known key, resulting in a gzip archive that contains a malicious payload.

At stage 3, the malicious payload is injected into the target process.

Analysis

The file was a program written in C# that contained a malicious payload. Using static and dynamic analysis, we were able to extract the payload.

The sample appeared to contain several visual forms with the help of which attackers disguised the presence of malicious functionality.

On the program startup, the StudentScores form is created within the Main() method:

The control is passed to the form initialization function:

Next, the wx() function is called containing main malicious functionality:

The wx() function decodes a resource file named dx and executes the “Buta” method:

In total, there are three files in the malware resources:

dx is a URL-encoded string in Base 64 format:

The rest two files have the PNG extension and contain encoded information. The file named q.png:

The file named hLuPu.png:

This method of encoding information in graphic images is called steganography. Using an algorithm in the malware code, we decoded the dx file that resulted in a PE file.
One can quickly determine the file format by the first few bytes. In our case, 4D 5A bytes signals that the file was in PE format.

This PE file appeared to be a dynamic library written in C #. The library was obfuscated with Deep Sea 4.1 and contained the method called Buta.

The Buta method takes an object as an argument:

SoapDate is a class with two lines, one of which contains the name of the png file from the resources of the first file:

The Buta method code:

The algorithm of the Buta method:

  • At the beginning, the execution sleeps for 41.5 seconds.
  • The hLuPu resource becomes a Bitmap object

  • Bitmap is decoded into a byte array. This method of decoding the payload is described in the article Commodity .NET Packers use Embedded Images to Hide Payloads , following which we can uniquely identify that this sample uses the “Cyax” packer. The essence of the algorithm is to use the png image’s RGB model and generate the corresponding byte array:

  • The resulting byte array is decoded by the XOR function using the “wNspdaa” key.

  • The resulting archive is then unpacked using the gzip algorithm and the result is the final payload:

  • The payload runs a function that is the entry point:

To automate the payload extraction, we wrote the following script:

using System;
using System.Drawing;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Reflection;
using System.Text;

namespace Outstanding
{
   
class Program
   {
       
static void Main(string[] args)
       {
           Bitmap bitmap = (Bitmap) Bitmap.FromFile(@
"hLuPu.png");
           byte[] bitmap_arr = smethod_0(bitmap);
           byte[] bitmap_decoded = decode(bitmap_arr,
"wNspdaa");
           byte[] payload = getPayload(bitmap_decoded);
           File.WriteAllBytes(
"payload", bitmap_decoded);
       }

       
public static byte[] getPayload(byte[] bitmap_decoded)
       {
           byte[] result;
           
using (MemoryStream memoryStream = new MemoryStream(bitmap_decoded))
           {
               byte[]
array = new byte[4];
               memoryStream.Read(
array, 0, 4);
               
int num = BitConverter.ToInt32(array, 0);
               
using (GZipStream gzipStream = new GZipStream(memoryStream, CompressionMode.Decompress))
               {
                   byte[] array2 =
new byte[num];
                   gzipStream.Read(array2,
0, num);
                   result = array2;
               }
           }
           
return result;
       }

       
private static byte[] smethod_0(Bitmap bitmap_0)
       {
           List<byte>
list = new List<byte>();
           checked
           {
               
int num = bitmap_0.Width - 1;
               
for (int i = 0; i <= num; i++)
               {
                   
int num2 = bitmap_0.Height - 1;
                   
for (int j = 0; j <= num2; j++)
                   {
                       Color pixel = bitmap_0.GetPixel(i, j);
                       
if (pixel != Color.FromArgb(0, 0, 0, 0))
                       {
                           
list.InsertRange(list.Count, new byte[]
                           {
                               pixel.R,
                               pixel.G,
                               pixel.B
                           });
                       }
                   }
               }
               
return (byte[])list.ToArray();
           }
       }

       
public static byte[] decode(byte[] bitmap_arr, string key)
       {
           byte[] bytes = Encoding.ASCII.GetBytes(key);
           
int num = (int)(bitmap_arr[bitmap_arr.Length - 1] ^ 112);
           byte[]
array = new byte[bitmap_arr.Length + 1];
           
int num2 = 0;
           
for (int i = 0; i <= bitmap_arr.Length - 1; i++)
           {
               
array[i] = (byte)((int)bitmap_arr[i] ^ num ^ (int)bytes[num2]);
               
if (num2 == key.Length - 1)
               {
                   num2 =
0;
               }
               
else
               {
                   num2++;
               }
           }
           Array.Resize<byte>(ref
array, bitmap_arr.Length - 1);
           
return array;
       }
   }
}


SHA256 payload:

DE87F049D9DC99A60D457FDAC61C332BB88F5BCFF042AA395F55BCEE4A3C17E8

The payload was also written in C# and contained a set of functions for injecting code into a process also known as the “Process Hollowing” technique:

The first step is to create the RegSvcs.exe process, with the CREATE_SUSPENDED parameter (constant 0x00000004).

Next, the PEB structure containing the information of the target process is obtained by taking the 41 bytes from GetThreadContext function’s output.

After getting the PEB structure, the base address (ImageBaseAddress) of the RegSvcs.exe process is read.

Before allocating memory in the process, it is prepared by the NtUnmapViewOfSection function.

VirtualAllocEx function allocates memory for malicious code inside the RegSvcs.exe process.

The malicious code is then written to the process using the WriteProcessMemory function.

Finally, the RegSvcs.exe process is started by the ResumeThread function.

By storing the byte array used as the argument to WriteProcessMemory function, we received another PE. The functionality of this sample was similar to the functionality of the file described in the New Agent Tesla Variant Spreading by Phishing article, that lead us to assume it was another variant of AgentTesla. The sample was a stealer written in C #.

SHA256 sample:

E701BED0853C2F782B0710F7895D7DCE22A58DBB0042DB56A7D9509929C8F05E

The execution of the AgentTesla sample starts with the D() function, which checks if the stealer is already running to prevent restarting.

Then, there goes a garbage code which executes Sleep for several times. The number of Sleeps depends on the predefined parameter passed to the A.b.a() function, which appears ubiquitous in the code.

Getting the username (in this case, the username is john):

The AgentTesla sample does not use any persistence methods on the operating system. All collected data is sent to the attackers' server. The malware also monitors clipboard for changes.

Getting the current ip address:

The main functionality is built with the help of the following functions:

All names are encoded, and after decoding one can see the paths to the data of different browsers

The string encoding algorithm lays inside the DF7A7911_002D5159_002D4174_002D9C8D_002D344C41F0E4CF class. It defines a byte array of 12005 elements:

Inside the class constructor, the array is decoded through the XOR operation using its index number and 0xAA key

The main function of the class returns a string, which is constructed by the passed arguments.

The real string values are obtained by calling lots of alike functions.

Having the decompiled code, one can write the code that will decode the strings by calling all the functions of the class.

As a result, one can get a file with all decoded lines.

Another piece of strings decoding code is related to FTP clients:

Using the tLab system, one can also observe the established Internet connection:

T&T security provides its clients with Yara rules for each malicious file. Yara rule for sample A24BF02CE43A846463532E46211962EC7E883E682ABBDE61F922FCD46FC301BC:

Identification of the sample by Yara rule on the tLab system:

Yara rule for sample C88A66CBF00B12C88E2B970B8BC220E970E8465E56098CED24E97D42BE901B94::

Identification of the sample by Yara rule on the tLab system:

Yara rule for sample DE87F049D9DC99A60D457FDAC61C332BB88F5BCFF042AA395F55BCEE4A3C17E8:

Identification of the sample by Yara rule on the tLab system:

Yara rule for sample E701BED0853C2F782B0710F7895D7DCE22A58DBB0042DB56A7D9509929C8F05E:

Identification of the sample by Yara rule on the tLab system:

There was also another sample found, similar to the previously analyzed one. The sample used a built-in resource called Wids, which was also an image with encoded data.

The resource is decoded pixel by pixel and turned into a byte array:

The result is a PE file in which a specific method is called, where keys are passed for further decoding:

The “aI” method is intended to decode the following malicious payload:

The algorithm of the "aI" method:

  • The main stream falls asleep for 65-78 seconds

  • The string "436F6D70617469626C654672616D65776F726B734D65746164617461456E7472794669656C644964" is decoded by the following algorithm:

    The result is the string “CompatibleFrameworksMetadataEntryFieldId”.


  • The resource under that name turns into a Bitmap object


  • Then the resource decoding takes turn

  • The result is another PE file

The PE file has a line with parameters

Depending on the first parameter, it is injected into one of the legitimate processes. In this case, the process RegSvcs.exe

The creation process:

Another resource is being decoded and injected into the process:

The final malicious payload is exactly the same as the previously detected sample.

The analyzed two samples are similar in the way the malicious load is stored as encoded information in an image. Further unpacking differs in the algorithms, but the essence remains the same - XOR bytes.

Yara rule for the new sample:

import "pe"

rule win_spy_misterhook_755E_cyax_agent_tesla_tlab {

        meta:
              author =
"T&T security"
              date =
"2021-02-23"
              description =
"C# Agent Tesla packed by Cyax packer"
              hash =
"755EA48BC49FAD06DA579A292D2A917A8D31699FBD7E70E19D8BB58BC3F92C60"

        strings:
       
              $unpack_func = {
6f [4] 13 04 12 04 28 [4] 17 daa 0d 16 0c 38 [4] 03 6f [4] 13 04 12 0428 [4] 17} /* decode pixels */

              $encoded_png_1 = {
89 50 4E 47 0D 0A 1A 0A 00 00 00 0D 49 48 44 52 00 00 00 55 00 00 00 55 08 06 00 00 00 38 F5 A8 5A 00 00 00 04 67 41 4D 41 00 00 B1 8F 0B FC 61 05 00 00 00 09 70 48 59 73 00 00 0E C3 00 00 0E} /* part of the sequence of bytes that represents a resource with an encoded image */

        condition:
              uint16(
0) == 0x5a4d
             
             
and pe.imports("mscoree.dll")

             
and $unpack_func

             
and $encoded_png_1
}

rule win_spy_ai_D357_cyax_agent_tesla_tlab {

        meta:
              author =
"T&T security"
              date =
"2021-02-23"
              description =
"C# encoded payload inside Agent Tesla"
              hash =
"D357B9EFB6E7729B3AD31599DA973860A740B18270F10CBF4D798DE14D160F0C"

        strings:
       
              $decode_func = {
28 [4] 28 [4] 00 02 28 [4] 04 28 [4] 0b 07 28 [4] 03 28 [4] 28 [4] 0c}

        condition:
              uint16(
0) == 0x5a4d
             
             
and pe.imports("mscoree.dll")

             
and $decode_func
}

rule win_spy_positivesign_8AF7_cyax_agent_tesla_tlab {

        meta:
              author =
"T&T security"
              date =
"2021-02-23"
              description =
"C# injector Agent Tesla"
              hash =
"8AF7CECBB5576942BF2EAF4B3F2D6BA3B19684065C0651082425DFAEC107750D"

        strings:
       
              $f1 = {
28 [4] 20 [4] 28 [4] 28 [4] 0a 38 [4] 00}

              $res1 = {
40 A8 A5 48 4B 26 38 32 85 03 28 18 65 D4 F8 F2 7B B5 72 27 1C 50 38 32 81 38 28 6E DD 6C 97 A6 F8 EF E2 27 24 50 4E 75 82 57 7C 18 22 93 97 9D 40 99 A5 60 4B 04 38 75 C2 57 47 18 54 D4 D0 F2}

              $res2 = {
78 6D 6C 20 76 65 72 73 69 6F 6E 3D 22 31 2E 30 22 20 65 6E 63 6F 64 69 6E 67 3D 22 55 54 46 2D 31 36 22 3F 3E 0D 0A 3C 54 61 73 6B 20 76 65 72 73 69 6F 6E 3D 22 31 2E 32 22 20}

        condition:
              uint16(
0) == 0x5a4d
             
             
and pe.imports("mscoree.dll")

             
and all of them
}

rule win_spy_agent_E701_cyax_agent_tesla_tlab {

        meta:
              author =
"T&T security"
              date =
"2021-01-05"
              description =
"C# Agent Tesla"
              hash =
"E701BED0853C2F782B0710F7895D7DCE22A58DBB0042DB56A7D9509929C8F05E"

        strings:
       
              $encoded_str_array = {
9F 9B 99 99 D7 D6 D5 D4 8F EE ED 8C C2 C3 84 ED F2 81 D5 D4 84 CC CF C4 CB CA C9 EE FB FA EB D1 EE D4 C0 C1 D1 E2 E1 D2 F1 F0 BC E3 F4 B9 B8 ED E8 A5 D7 FB F4 FA}

              $xor_algo = {
7E [4] 06 7E [4] 06 91 06 61 20 [4] 61 D2 9C 06 17 58 0A}

        condition:
              uint16(
0) == 0x5a4d
             
             
and pe.imports("mscoree.dll")

             
and $encoded_str_array

             
and $xor_algo
}

Identification of the sample by Yara rule on the tLab system:

Conclusion

The AgentTesla sample is pretty well packaged in a variety of ways and uses several unpacking steps. String coding and steganography intentionally increase the difficulty of conducting manual analysis. Besides, the sample features with a large list of software extracted from within.