Listen to this Post

Introduction:
Windows shell shortcuts (.lnk files) are ubiquitous – but few understand the COM interface that powers them. Attackers have long abused .lnk files as a persistence mechanism, dropping them into startup folders or masquerading as legitimate documents. By learning to read and write .lnk files programmatically via the IShellLink interface, defenders can reverse malicious shortcuts and blue teams can detect covert execution chains.
Learning Objectives:
- Programmatically read and write Windows .lnk files using C++ and the IShellLink COM interface.
- Identify .lnk‑based persistence techniques and implement detection scripts in PowerShell.
- Use OleView to explore COM interfaces and understand limitations of classic COM enumeration.
- Reading .lnk Files with IShellLink – A Step‑by‑Step Guide
The IShellLink COM interface (defined in shobjidl_core.h) allows you to load an existing .lnk file and extract its target path, arguments, working directory, icon location, and description. This is invaluable for forensics: when you find a suspicious shortcut, you can automatically resolve where it points.
Step 1 – Initialize COM
Call `CoInitialize(NULL)` to start the COM library.
Step 2 – Create the ShellLink object
Use `CoCreateInstance` with `CLSID_ShellLink` to get an `IShellLink` pointer.
Step 3 – Load the .lnk file
`pShellLink->Load(pszPath, STGM_READ)`
Step 4 – Retrieve properties
Call `GetPath`, `GetArguments`, `GetWorkingDirectory`, `GetIconLocation`, `GetDescription`.
Step 5 – Release COM
`pShellLink->Release()` and `CoUninitialize()`.
include <windows.h>
include <shobjidl_core.h>
include <iostream>
int main() {
CoInitialize(NULL);
IShellLink psl = NULL;
HRESULT hr = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (void)&psl);
if (SUCCEEDED(hr)) {
IPersistFile ppf = NULL;
hr = psl->QueryInterface(IID_IPersistFile, (void)&ppf);
if (SUCCEEDED(hr)) {
hr = ppf->Load(L"C:\malicious.lnk", STGM_READ);
if (SUCCEEDED(hr)) {
wchar_t target[bash], args[bash], icon[bash], desc[bash];
psl->GetPath(target, MAX_PATH, NULL, SLGP_RAWPATH);
psl->GetArguments(args, MAX_PATH);
psl->GetDescription(desc, INFOTIPSIZE);
wprintf(L"Target: %s\nArgs: %s\nDesc: %s\n", target, args, desc);
}
ppf->Release();
}
psl->Release();
}
CoUninitialize();
return 0;
}
Security note: Many droppers use .lnk files with arguments like `-w hidden` or encoded PowerShell. Always inspect the arguments field.
- Creating a Programmatic .lnk File (for Testing or Persistence)
Creating .lnk files is identical but uses `STGM_CREATE` and sets properties with SetPath, SetArguments, etc. Adversaries often register a shortcut in `%APPDATA%\Microsoft\Windows\Start Menu\Programs\Startup` to gain persistence after reboot.
Step‑by‑step creation (C++):
– `CoCreateInstance` as before.
– `SetPath(L”C:\\Windows\\System32\\cmd.exe”)`
– `SetArguments(L”/c calc.exe”)`
– `SetWorkingDirectory(L”C:\\”)`
– `SetIconLocation(L”shell32.dll, 3″)`
– `SetDescription(L”System Maintenance”)` – social engineering.
– `QueryInterface` for IPersistFile, then Save(L"C:\\Users\\Public\\Updates.lnk", TRUE).
PowerShell one‑liner (often used by scripts):
$WshShell = New-Object -comObject WScript.Shell
$Shortcut = $WshShell.CreateShortcut("$env:APPDATA\Microsoft\Windows\Start Menu\Programs\Startup\OneDrive.lnk")
$Shortcut.TargetPath = "C:\Windows\System32\rundll32.exe"
$Shortcut.Arguments = "javascript:\"..\mshtml,RunHTMLApplication \"calc.exe\"\""
$Shortcut.Save()
This hidden COM approach bypasses many static detection tools.
- Detecting Suspicious .lnk Files – Windows Commands and Scripts
Because .lnk files are binary, simple text searches fail. Use PowerShell to read shortcut properties without C++:
Function Read-LnkFile {
param([bash]$lnkPath)
$shell = New-Object -ComObject WScript.Shell
$shortcut = $shell.CreateShortcut($lnkPath)
Write-Host "Target: $($shortcut.TargetPath)"
Write-Host "Args: $($shortcut.Arguments)"
Write-Host "Dir: $($shortcut.WorkingDirectory)"
Write-Host "Icon: $($shortcut.IconLocation)"
}
Read-LnkFile "C:\Users\Public\fake_update.lnk"
Bulk scan for persistence locations:
$paths = @("$env:APPDATA\Microsoft\Windows\Start Menu\Programs\Startup",
"$env:PROGRAMDATA\Microsoft\Windows\Start Menu\Programs\Startup")
foreach ($p in $paths) {
Get-ChildItem $p -Filter .lnk | ForEach-Object {
Write-Host "Checking $<em>" -ForegroundColor Cyan
Read-LnkFile $</em>.FullName
}
}
Sysinternals Sigcheck also reveals .lnk targets:
`sigcheck -l C:\path\to\shortcut.lnk`
- Using OleView to Discover COM Interfaces – Why Classic COM Hides Interfaces
The post mentions OleView (part of Windows SDK). Run `oleview.exe` and navigate to “All Objects” → “Shell Link Object”. OleView shows the CLSID, supported interfaces, and type library. However, classic COM does not provide a built‑in way to enumerate all interfaces a COM object supports at runtime (unlike WinRT’s IInspectable). Attackers exploit this ambiguity: they can implement a malicious object that silently supports `IPersistFile` but not IOleObject, confusing static analysis. To inspect a live COM object, you must query for known IIDs – a brute‑force approach used by some security tools.
Practical exercise:
1. Launch OleView as Administrator.
2. Find “Shell Link Object” (CLSID `{00021401-0000-0000-C000-000000000046}`).
- Right‑click → “View Type Information” to see method signatures.
- Use `CoCreateInstance` and repeatedly call `QueryInterface` for
IID_IUnknown,IPersistFile,IShellLinkA/W, `IExtractIconA/W` to confirm support. -
Hardening Against .lnk Persistence – Group Policy and Attack Surface Reduction
Windows provides several defenses:
Disable .lnk file execution from untrusted locations (ASR Rule):
In Microsoft Defender for Endpoint:
`Add-MpPreference -AttackSurfaceReductionRules_Ids “d4f940ab-401b-4efc-aadc-ad5f3c50688a” -AttackSurfaceReductionRules_Actions Enabled`
This rule blocks .lnk files downloaded from the web (Zone.Identifier Alternative Data Stream).
Restrict startup folder access:
Use AppLocker or WDAC to prevent execution from `%APPDATA%\Microsoft\Windows\Start Menu\Programs\Startup` unless signed.
Monitor .lnk creation events with Sysmon:
Sysmon event ID 11 (FileCreate) includes .lnk writes. Configure rule:
`.lnk `
Then forward to SIEM.
Linux forensics (when analyzing Windows drives):
Install `liblnk` (part of `libbde` tools):
`sudo apt install liblnk-utils`
`lnkinfo /mnt/windows/Users/Attacker/Desktop/evil.lnk` – extracts target, arguments, MAC times.
- Real‑World Exploit Scenario: Sticky Key Backdoor via .lnk
A classic persistence trick: replace `sethc.exe` (Sticky Keys) with a .lnk pointing to cmd.exe.
1. Create .lnk with `SetPath(“C:\\Windows\\System32\\cmd.exe”)` and no arguments.
- Save as `sethc.lnk` in `C:\Windows\System32\` (requires trusted installer bypass).
- When attacker presses Shift 5 times at login, `cmd.exe` launches as SYSTEM.
Mitigation: Enable `sethc.exe` file integrity monitoring. Audit `C:\Windows\System32\.lnk` – legitimate OS shortcuts rarely exist there.
What Undercode Say:
- The IShellLink COM interface is a critical forensic artifact parser and a common attacker persistence primitive. Understanding its methods demystifies .lnk file internals.
- PowerShell’s `WScript.Shell` provides a high‑level malicious shortcut creator – detection must focus on startup folder writes and unusual argument patterns like
rundll32 javascript. - Classic COM’s lack of interface enumeration forces defenders to use OleView and behavioral analysis; WinRT’s design is inherently more secure in this regard.
- Group Policy ASR rules effectively block internet‑sourced .lnk files but do not prevent locally created ones – defense in depth requires Sysmon logging and user education.
Prediction:
As Microsoft pushes WinRT and modern app containers, classic COM will gradually be legacy — but .lnk files remain too deeply integrated into Windows to disappear. We will see a rise in “Living off the Land” attacks that chain .lnk with LNK‑based LOLBins (e.g., mshta.exe, wmic.exe). Future EDR solutions will add specific IShellLink API hooks to detect process creation originating from shortcut resolution, forcing attackers to revert to low‑level NTFS parsing or direct shell item ID lists (PIDLs). Meanwhile, defenders should prioritize monitoring startup folder – the most abused .lnk persistence vector – using the PowerShell scripts above.
▶️ Related Video (76% Match):
🎯Let’s Practice For Free:
IT/Security Reporter URL:
Reported By: Pavely Windowsinternals – Hackers Feeds
Extra Hub: Undercode MoN
Basic Verification: Pass ✅


