We break down the full infection chain of the Brazilian-targeted threat BBTok and demonstrate how to deobfuscate the loader DLL using PowerShell, Python, and dnlib.
In a complex infection chain that starts with an email containing an ISO image, this malware stands out by its way of compiling C# code directly on the infected machine. It also uses a technique known as AppDomain Manager Injection to advance execution. Articles from
and
describe a similar infection chain and attribute it to BBTok banker, but to our knowledge, no one has yet published an analysis on the obfuscated .NET based loader named Trammy.dll.
The loader writes a log file with obscure words onto infected machines, for which we provide a translation table so that incident responders can decode the logs.
The obfuscation of Trammy.dll, which uses a ConfuserEx variant, prevents current automatic tools from retrieving the strings. We provide the necessary scripts and commands to deobfuscate them.
In a complex infection chain that starts with an email containing an ISO image, this malware stands out by its way of compiling C# code directly on the infected machine. It also uses a technique known as AppDomain Manager Injection to advance execution. Articles from
Bu bağlantı ziyaretçiler için gizlenmiştir. Görmek için lütfen giriş yapın veya üye olun.
Bu bağlantı ziyaretçiler için gizlenmiştir. Görmek için lütfen giriş yapın veya üye olun.
The loader writes a log file with obscure words onto infected machines, for which we provide a translation table so that incident responders can decode the logs.
The obfuscation of Trammy.dll, which uses a ConfuserEx variant, prevents current automatic tools from retrieving the strings. We provide the necessary scripts and commands to deobfuscate them.
Intrusion utilizing the Microsoft Build Engine
Recently, we discovered several malicious ISO images[F1-4] in our telemetry, apparently targeting Brazilian entities. A
mentions that they are delivered via email. All these ISOs[F1-4] contain one Windows shortcut file (LNK)[F5] and one folder (see figure 1). Inside the folder we found an executable[F6], an XML file[F7], a PDF[F8] and a ZIP archive[F9].
The LNK file[F5], DANFE10103128566164.pdf.lnk, links to the executable[F6] in the folder and passes the XML file[F7] as input along with the –nologo option. All files inside the ISO[F1] are named "DANFE10103128566164" which includes the Portuguese acronym "DANFE". The acronym means "Documento Auxiliar da Nota Fiscal Electronica" and
usually distributed in PDF format between Brazilian companies.
The attackers take advantage of this by disguising the LNK file[F5] with the PDF icon that is embedded within the standard Microsoft Edge executable (msedge.exe) of the system to lure targets into executing it.
The executable, DANFE10103128566164.exe[F6], is a validly signed Microsoft Build Engine version 4.7.3190.0 (MSBuild.exe). The malware uses it to compile malicious C# code embedded within the XML file[F7] on the infected machine (see figure 2). The result of the compilation process is a .NET DLL[F13] which is dropped and executed in the local TEMP folder with a randomized name. Since the plain C# code is included in the XML file[F7], the analysis is straightforward.
First, the freshly compiled .NET DLL[F13] opens the decoy PDF[F8] which displays a DANFE invoice to the target user. Afterwards, it extracts the ZIP archive[F9] and copies the Microsoft Build Engine[F6] to C:\ProgramData\regid.5498-06.com.microsoft[P1] via PowerShell. Finally, it leverages a
together with the auto-elevated system binary computerdefaults.exe to run the Microsoft Build Engine[F6] once again.
This time, however, MSBuild.exe[F6] does not compile C# code again but runs another DLL, Trammy.dll[F10], based on project configuration file[F11]. Both are extracted from the previously mentioned ZIP archive[F9]. To prevent consecutive execution of the UAC bypass, it creates a local mutex called TiiSbtvhvbCMW.
Figure 3 shows a comprehensive overview of this part of the infection chain.
Bu bağlantı ziyaretçiler için gizlenmiştir. Görmek için lütfen giriş yapın veya üye olun.
The LNK file[F5], DANFE10103128566164.pdf.lnk, links to the executable[F6] in the folder and passes the XML file[F7] as input along with the –nologo option. All files inside the ISO[F1] are named "DANFE10103128566164" which includes the Portuguese acronym "DANFE". The acronym means "Documento Auxiliar da Nota Fiscal Electronica" and
Bu bağlantı ziyaretçiler için gizlenmiştir. Görmek için lütfen giriş yapın veya üye olun.
The attackers take advantage of this by disguising the LNK file[F5] with the PDF icon that is embedded within the standard Microsoft Edge executable (msedge.exe) of the system to lure targets into executing it.
Bu bağlantı ziyaretçiler için gizlenmiştir. Görmek için lütfen giriş yapın veya üye olun.
The executable, DANFE10103128566164.exe[F6], is a validly signed Microsoft Build Engine version 4.7.3190.0 (MSBuild.exe). The malware uses it to compile malicious C# code embedded within the XML file[F7] on the infected machine (see figure 2). The result of the compilation process is a .NET DLL[F13] which is dropped and executed in the local TEMP folder with a randomized name. Since the plain C# code is included in the XML file[F7], the analysis is straightforward.
Bu bağlantı ziyaretçiler için gizlenmiştir. Görmek için lütfen giriş yapın veya üye olun.
Bu bağlantı ziyaretçiler için gizlenmiştir. Görmek için lütfen giriş yapın veya üye olun.
First, the freshly compiled .NET DLL[F13] opens the decoy PDF[F8] which displays a DANFE invoice to the target user. Afterwards, it extracts the ZIP archive[F9] and copies the Microsoft Build Engine[F6] to C:\ProgramData\regid.5498-06.com.microsoft[P1] via PowerShell. Finally, it leverages a
Bu bağlantı ziyaretçiler için gizlenmiştir. Görmek için lütfen giriş yapın veya üye olun.
This time, however, MSBuild.exe[F6] does not compile C# code again but runs another DLL, Trammy.dll[F10], based on project configuration file[F11]. Both are extracted from the previously mentioned ZIP archive[F9]. To prevent consecutive execution of the UAC bypass, it creates a local mutex called TiiSbtvhvbCMW.
Figure 3 shows a comprehensive overview of this part of the infection chain.
Bu bağlantı ziyaretçiler için gizlenmiştir. Görmek için lütfen giriş yapın veya üye olun.
AppDomain Manager Injection
The configuration file[F11] declares the class SacApp.SacApp of Trammy.dll[F10] as AppDomainManager (see figure 4). An AppDomainManager is responsible for customizing the AppDomain of an application, which is an isolated environment for the managed code.
That means declaring SacApp.SacApp as AppDomainManager leads to the execution of malicious code in InitializeNewDomain() - a standard method that has been overridden by SacApp.SacApp[F10]. This technique is known as
.
That means declaring SacApp.SacApp as AppDomainManager leads to the execution of malicious code in InitializeNewDomain() - a standard method that has been overridden by SacApp.SacApp[F10]. This technique is known as
Bu bağlantı ziyaretçiler için gizlenmiştir. Görmek için lütfen giriş yapın veya üye olun.
Bu bağlantı ziyaretçiler için gizlenmiştir. Görmek için lütfen giriş yapın veya üye olun.
Deobfuscation of Trammy.dll
DANFE10103128566164.dll[F10], is not packed but obfuscated with
. It has the module name Trammy.dll. Specific deobfuscation tools like NoFuserEx and
remove the control flow flattening, but do not automatically retrieve the strings.
There are five string decode methods and each one takes an integer as key that it uses to compute the deobfuscated string. After applying de4dot-cex to the DLL, we retrieve all these keys using
and Python. This code searches for pairs of ldc_i4 and call instructions, and returns the ldc_i4 operands. This may return more than the string decoding keys, but it does not matter for the steps that follow.
We use DnSpy's IL editing feature to remove the anti-reversing checks from the five string decoding methods. Every methods starts with an if statement that checks if the caller is the current assembly. If it is not the current assembly, an empty string will be returned. Replacing the if statement with NOP instructions allows us to execute the code from PowerShell.
We recover the strings dynamically for each method using the following PowerShell commands. The variable $nums is an array of all the keys that we extracted with the previous script. The string decoding methods have two characteristics that must be taking into account.
Bu bağlantı ziyaretçiler için gizlenmiştir. Görmek için lütfen giriş yapın veya üye olun.
Bu bağlantı ziyaretçiler için gizlenmiştir. Görmek için lütfen giriş yapın veya üye olun.
There are five string decode methods and each one takes an integer as key that it uses to compute the deobfuscated string. After applying de4dot-cex to the DLL, we retrieve all these keys using
Bu bağlantı ziyaretçiler için gizlenmiştir. Görmek için lütfen giriş yapın veya üye olun.
Code:
import clr
clr.AddReference(r'dnlib.dll')
import dnlib
from dnlib.DotNet import *
from dnlib.DotNet.Emit import OpCodes
def extract_values_from_method(method):
if not method.HasBody: return []
values = []
instr = [x for x in method.Body.Instructions]
while len(instr) >= 2:
ldc_i4 = instr[0]
call_instr = instr[1]
if ldc_i4.OpCode.Code == OpCodes.Ldc_I4.Code and call_instr.OpCode.Code == OpCodes.Call.Code:
print('found pattern in', method)
i4_val = ldc_i4.GetLdcI4Value()
print('value', i4_val)
values.append(i4_val)
instr = instr[1:]
return values
def extract_values_from_module(module):
values = []
for t in module.GetTypes():
for m in t.Methods:
values.extend(extract_values_from_method(m))
return values
afile = r"DANFE10103128566164.dll"
module = ModuleDefMD.Load(afile) # type: ignore
values = extract_values_from_module(module)
print(values)
print('done')
We use DnSpy's IL editing feature to remove the anti-reversing checks from the five string decoding methods. Every methods starts with an if statement that checks if the caller is the current assembly. If it is not the current assembly, an empty string will be returned. Replacing the if statement with NOP instructions allows us to execute the code from PowerShell.
We recover the strings dynamically for each method using the following PowerShell commands. The variable $nums is an array of all the keys that we extracted with the previous script. The string decoding methods have two characteristics that must be taking into account.
- They are located in the global type <Module> and cannot be accessed via [namespace.ClassName]::methodname(). So we resolve the string decoding methods via their token instead (here 0x6000005).
- The string decoding methods have a generic return type, so we must provide the return type via MakeGenericMethod([string]).
-
Code:
$nums = @(<extracted keys>) [CENTER]$assembly = [Reflection.Assembly].LoadFile("DANFE10103128566164_antidebug_fixed.dll") $method = $assembly.ManifestModule.ResolveMethod(0x6000005) $gen = $method.MakeGenericMethod([string]) $nums | ForEach-Object { try { "$($_):" + $gen.Invoke($null, @( $_.PSObject.BaseObject )) } catch {} } > result.txt
[/CENTER] - The last command creates a mapping of the keys for the string decoding methods to the deobfuscated strings. The try-catch swallows any error messages that would be printed due to wrong keys.
We repeat these commands for all string decoding methods until we have a merged result.txt that contains all keys and decoded strings. This file will also have empty mappings that we remove with by replacing the regex ^.*:\r\n$ with nothing. We transform the result into a Python dictionary by replacing ^(-?\d+)
.*)$ with \1:r'\2', (Notepad++ syntax). Then we slightly modify the previous Python script so that it replaces call instructions to the string decoding functions with ldstr instructions and the appropriate deobfuscated string as operand.
-
[/CENTER]Code:
import clr [CENTER]clr.AddReference(r'dnlib.dll') import dnlib from dnlib.DotNet import * from dnlib.DotNet.Emit import OpCodes strings_dict = { <key to deobfuscated string from previous step> } def decrypt_strings_from_method(method): if not method.HasBody: return [] instr = [x for x in method.Body.Instructions] while len(instr) >= 2: ldc_i4 = instr[0] call_instr = instr[1] if ldc_i4.OpCode.Code == OpCodes.Ldc_I4.Code and call_instr.OpCode.Code == OpCodes.Call.Code: i4_val = ldc_i4.GetLdcI4Value() if i4_val in strings_dict: dec_str = strings_dict.get(i4_val) call_instr.OpCode = OpCodes.Ldstr call_instr.Operand = dec_str ldc_i4.OpCode = OpCodes.Nop print('decoded', dec_str) instr = instr[1:] def decrypt_strings_from_module(module): for t in module.GetTypes(): for m in t.Methods: decrypt_strings_from_method(m) afile = r"DANFE10103128566164_fixed_antidebug.dll" module = ModuleDefMD.Load(afile) decrypt_strings_from_module(module) out_file = afile + '.deobfus' module.Write(out_file) print('done')
- We execute the script on the Trammy.dll and successfully deobfuscate the strings. As a final step, we useto remove indirect calls by selecting the 'Nothing' profile in the deobfuscator and enabling the 'Direct Call' option.Bu bağlantı ziyaretçiler için gizlenmiştir. Görmek için lütfen giriş yapın veya üye olun.
Figures 5 and 6 show the SacApp.SacApp.InitializeNewDomain method before and after applying string deobfuscation and indirect call removal
malware,malware analysis,rat,ddos,hacks