Windows shortcut creation wizard will not allow you to place a relative path as a target. Furthermore, much of the functionality has been abstracted away when creating .lnk with alternatives like powershell and this can cause the creation of shortcuts that work inconsistently accross different machines. Other existing third-party solutions and tricks typically use a link target like explorer.exe, msiexec.exe, or cmd.exe with arguments to run the desired relative path. While these may work in some cases they are not OPSEC friendly and rely on additional tools when the functionality is in fact already included in the .lnk file structure itself. lnksphinx is a workaround which uses a valid .lnk file skeleton as a template and modifies it dynamically based on the user supplied input. The resultant shortcut points directly to the chosen relative path. The skeleton has a ShellLinkHeader which specifies the HasRelativePath structure is present. It also has HasLinkTargetIDList value. The LinkTargetID structures contains ItemIDs that will never resolve, which will trigger usage of the defined relativeLink structure. The relative Link value is modified dynamically by the tool to contain the value of the payload specified by the user. This results in a portable .lnk file which will always launch the specified payload when it is placed adjacent to its target. This is particularly useful for red team engagements where a shortcut file is required for initial detonation of a payload, for example, triggering binaries that contain DLL hijacking vulnerabilities without having to rename the binary (which can trigger an EDR based on Masquerading behaviour detection).
python3 lnksphinx.py <targetFile> <outFile>
For example, the below command will generate a .lnk called example that is a shortcut to .\test.txt. Clicking the shortcut will open test.txt when they are located in the same directory. This can be used for any filetype, and packaged together with its target in order to launch it on any system the package is sent to:
python3 lnksphinx.py test.txt example
The tool uses a skeleton hex value for a .lnk file that contains unresolvable elements and modifies the hex value of the relative link target which is used when these are not found. The skeleton value defined in the bigBlob variable is shown below (in HxD) :
The user supplied data is appended to the end along with the correct spacing nullbytes and a count value that precedes it defining the length of the unicode string. The parsed .lnk file generated by the tool will look like this :
Information about the .lnk file structure : https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-shllink/16cb4ca1-9339-4d0c-a68d-bf1d6cc0f943