Lnk icon indicating copy to clipboard operation
Lnk copied to clipboard

Parsing confusion as Windows actually limits CountCharacters when reading LNK files

Open securechicken opened this issue 6 months ago • 4 comments

Hi (again, this is somehow related to #21 ),

The result of StringData (such as NAME_STRING, RELATIVE_PATH, WORKING_DIR, COMMAND_LINE_ARGUMENTS and ICON_LOCATION; see MS-SHLLINK as of 9.0 and before, title 2.4) parsing in LNK files from this Lnk library (and the associated LECmd project) does not match the actual parsing that is implemented in Windows.

As a result, a specifically crafted Lnk file can trick this library (and LECmd) into displaying different command line arguments (COMMAND_LINE_ARGUMENTS) than those that will actually be executed by Windows. In the attached example file for instance, the LNK file arguments are parsed by the library (through LECmd as of version 1.5.1.0) as being file.pdf, whereas /c "calc.exe" will actually be executed by Windows (note that this Lnk library and LECmd are actually displaying the actual arguments just before, where the Lnk description, NAME_STRING, should fit. This is partially correct as in this example case, the arguments that Windows will use are kind of "embedded" in the NAME_STRING from the MS-SHLLINK specification perspective).

This issue is due to Windows actually limiting the number of characters (CountCharacters) for some StringData (NAME_STRING, RELATIVE_PATH, WORKING_DIR and ICON_LOCATION) to 260 (259 "useful" characters, and the last character will be used to store a NUL character), despite the indications in MS-SHLLINK (as of 9.0 and before, title 2.4). This discrepancy is further detailed in a public cybersecurity blogpost here, as it has been leveraged by malicious actors in the wild.

The choice to either implement the Lnk parsing as Windows does it, or as the current MS-SHLLINK specification (version 9.0) tells is left to the maintainers. To implement as Windows does, the parsing code in LnkFile constructor for NAME_STRING, RELATIVE_PATH, WORKING_DIR and ICON_LOCATION should be changed so that at most 260 characters are allowed for those strings, ignoring the value for CountCharacters where it is greater than 260. As an example for NAME_STRING, starting line 352:

var nameLen = BitConverter.ToInt16(rawBytes, index);

This nameLen value should be capped to 260. Then, the corresponding string (Name in this case) should be NUL-terminated. If the corresponding string is exactly 260-characters long, then the 260th character should be replaced by a NUL character.

An attached file is provided so the bug can be reproduced using LECmd as of version 1.5.1.0: LECmd.exe -f ./lnk_fixed_blah.lnk .

Image

lnk_fixed_blah.zip

securechicken avatar Jun 17 '25 11:06 securechicken

Does lecmd show both the pdf and calc? Or just off in it's arguments output? If both, this seems correct to me and the analyst should see there is a PDF and calc.exe in there

EricZimmerman avatar Jun 17 '25 11:06 EricZimmerman

In this case yes, but likely not where expected, as described (and shown in screenshot):

(note that this Lnk library and LECmd are actually displaying the actual arguments just before, where the Lnk description, NAME_STRING, should fit. This is partially correct as in this example case, the arguments that Windows will use are kind of "embedded" in the NAME_STRING from the MS-SHLLINK specification perspective).

securechicken avatar Jun 17 '25 11:06 securechicken

ill see what i can do, but the f act its showing both strings should be enough for someone to know something is wrong.

if i truncate to 260, do we lose seeing both? ill do some tests

EricZimmerman avatar Jun 17 '25 13:06 EricZimmerman

forcing at 260. this is lnk project dumping data, not lecmd.

`Source file: C:\Temp\lnk_fixed_blah.lnk Source created: 6/17/2025 1:19:02 PM +00:00 Source modified: 6/17/2025 12:34:08 PM +00:00 Source accessed: 6/17/2025 1:20:19 PM +00:00

--- Header --- File size: 1,672,246,141 Flags: HasTargetIdList, HasName, HasArguments File attributes: FileAttributeNormal Icon index: 0 Show window: SwShowminnoactive (Display the window as minimized without activating it.) Target created: 2/24/2210 6:28:07 PM +00:00 Target modified: 1/20/2286 10:16:46 PM +00:00 Target accessed: 6/19/2129 10:48:18 PM +00:00

--- Target ID information ---

Type: Root folder: GUID, Value: This PC

Type: Drive letter, Value: C:

Type: Directory, Value: Windows

Short name: Windows

Type: Directory, Value: System32

Short name: System32

Short name: c|m|d|.|e|x|e

File size: 1,048,576

Type: File, Value: c|m|d|.|e|x|e

Name: Blah\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000 Arguments: /c "calc.exe"`

EricZimmerman avatar Jun 17 '25 13:06 EricZimmerman