Windows ZIP extraction bug (CVE-2022-41049) lets attackers craft ZIP files, which evade warnings on attempts to execute packaged files, even if ZIP file was downloaded from the Internet.
In October 2022, I’ve come across a tweet from 5th July, from @wdormann, who reported a discovery of a new method for bypassing MOTW, using a flaw in how Windows handles file extraction from ZIP files.
Will Dormann
@wdormann
The ISO in question here takes advantage of several default behaviors: 1) MotW doesn’t get applied to ISO contents 2) Hidden files aren’t displayed 3) .LNK file extensions are always hidden, regardless of the Explorer preference to hide known file extensions.

So if it were a ZIP instead of ISO, would MotW be fine? Not really. Even though Windows tries to apply MotW to extracted ZIP contents, it’s really quite bad at it. Without trying too hard, here I’ve got a ZIP file where the contents retain NO protection from Mark of the Web.
https://twitter.com/wdormann/status/1544416883419619333
This sounded to me like a nice challenge to freshen up my rusty RE skills. The bug was also a 0-day, at the time. It has already been reported to Microsoft, without a fix deployed for more than 90 days.
What I always find the most interesting about vulnerability research write-ups is the process on how one found the bug, what tools were used and what approach was taken. I wanted this post to be like this.
Now that the vulnerability has been fixed, I can freely publish the details.
Background
What I found out, based on public information about the bug and demo videos, was that Windows, somehow, does not append MOTW to files extracted from ZIP files.
Mark-of-the-web is really another file attached as an Alternate Data Stream (ADS), named
[ZoneTransfer] ZoneId=3
For example, when you download a ZIP file
This is what happens when you try to execute an executable like a JScript file, through double-clicking, stored in a ZIP file, with MOTW attached.

Clearly the user would think twice before opening it when such popup shows up. This is not the case, though, for specially crafted ZIP files bypassing that feature.
Let’s find the cause of the bug.
Identifying the culprit
What I knew already from my observation is that the bug was triggered when

ProcessHacker revealed
WScript.Echo("YOU GOT HACKED!!1");
I then added a MOTW ADS file with Notepad and filled it with MOTW contents, mentioned above:
notepad file.zip:Zone.Identifier
I loaded up
CheckUnZippedFile
One of the function calls I noticed nearby, revealed an interesting pattern in IDA. Right after the file is extracted and specific conditions are meet,

Having a hunch that
It was clear to me that if I managed to manipulate the execution flow in such a way that the branch, executing this function is skipped, I will be able to achieve the desired effect of bypassing the creation of MOTW on extracted files. I looked into the function to investigate further.
I noticed that

Considering that I controlled the filename of the extracted ZIP file, I could possibly manipulate its content to trigger
I opened 010 Editor, which I highly recommend for any kind of hex editing work, and opened my sample ZIP file with a built-in ZIP template.

I spent few hours testing with different filename lengths, with different special characters, just to see if the extraction function would behave in erratic way. Unfortunately I found out that there was another path length check, called prior to the one I’ve been investigating. It triggered much earlier and prevented me from exploiting this one specific check. I had to start over and consider this path a dead end.
I looked if there are any controllable branching conditions, that would result in not triggering the call to
CAttachmentServices::SetReferrer(unsigned short const * __ptr64) CAttachmentServices::SetSource(unsigned short const * __ptr64) CAttachmentServices::SaveWithUI(struct HWND__ * __ptr64)
realized I am about to go deep down a rabbit hole and I may spend there much longer than a hobby project like that should require. I had to get a public exploit sample to speed things up.
Huge thanks you @bohops & @bufalloveflow for all the help in getting the sample!
Detonating the live sample
I managed to copy over all relevant ZIP file parameters from the obtained exploit sample into my test sample and I confirmed that MOTW was gone, when I extracted the sample JScript file.
I decided to dig deeper into

This is the Windows API function for handling the creation of INI configuration files. Considering that
The search led me to the final call of

It looked like the majority of parameters were constant, as you can see on the screenshot above. The only place where I’d expect anything dynamic was in the structure of
This led me to realize that something had to be happening prior to the creation of the ADS file, which I did not account for. There was no better way to figure that out than to use Process Monitor, which honestly I should’ve used long before I even opened IDA 😛.
Backtracking
I set up my filters to only list file operations related to files extracted to TEMP directory, starting with

The test sample clearly succeeded in creating the

While the exploit sample failed:

Through comparison of these two listings, I could not clearly see any drastic differences. I exported the results as text files and compared them in a text editor. That’s when I could finally spot it.
Prior to creating

I looked up what was that

The
It doesn’t even seem to be a bug, since everything is working as expected 😛. The file attributes in a ZIP file are set in
4.4.15 external file attributes: (4 bytes) The mapping of the external attributes is host-system dependent (see 'version made by'). For MS-DOS, the low order byte is the MS-DOS directory attribute byte. If input came from standard input, this field is set to zero. 4.4.2 version made by (2 bytes) 4.4.2.1 The upper byte indicates the compatibility of the file attribute information. If the external file attributes are compatible with MS-DOS and can be read by PKZIP for DOS version 2.04g then this value will be zero. If these attributes are not compatible, then this value will identify the host system on which the attributes are compatible. Software can use this information to determine the line record format for text files etc. 4.4.2.2 The current mappings are: 0 - MS-DOS and OS/2 (FAT / VFAT / FAT32 file systems) 1 - Amiga 2 - OpenVMS 3 - UNIX 4 - VM/CMS 5 - Atari ST 6 - OS/2 H.P.F.S. 7 - Macintosh 8 - Z-System 9 - CP/M 10 - Windows NTFS 11 - MVS (OS/390 - Z/OS) 12 - VSE 13 - Acorn Risc 14 - VFAT 15 - alternate MVS 16 - BeOS 17 - Tandem 18 - OS/400 19 - OS X (Darwin) 20 thru 255 - unused 4.4.2.3 The lower byte indicates the ZIP specification version (the version of this document) supported by the software used to encode the file. The value/10 indicates the major version number, and the value mod 10 is the minor version number.
Changing the value of external attributes to anything with the lowest bit set e.g.
Conclusion
I honestly expected the bug to be much more complicated and I definitely shot myself in the foot, getting too excited to start up IDA, instead of running Process Monitor first. I started with IDA first as I didn’t have an exploit sample in the beginning and I was hoping to find the bug, through code analysis. Bottom line, I managed to learn something new about Windows internals and how extraction of ZIP files is handled.
As a bonus, Mitja Kolsek from 0patch asked me to confirm if their patch worked and I was happy to confirm that it did!


The patch was clean and reliable as seen in the screenshot from a debugger:

I’ve been also able to have a nice chat with Will Dormann, who initially discovered this bug, and his story on how he found it is hilarious:
I merely wanted to demonstrate how an exploit in a ZIP was safer (by way of prompting the user) than that *same* exploit in an ISO. So how did I make the ZIP? I: 1) Dragged the files out of the mounted ISO 2) Zipped them. That's it. The ZIP contents behaved the same as the ISO.
Every mounted ISO image is listing all files in read-only mode. Drag & dropping files from read-only partition, to a different one, preserves the read-only attribute set for created files. This is how Will managed to unknowingly trigger the bug.
Will also made me realize that 7zip extractor, even though having announced they began to add MOTW to every file extracted from MOTW marked archive, does not add MOTW by default and this feature has to be enabled manually.


I mentioned it as it may explain why MOTW is not always considered a valid security boundary. Vulnerabilities related to it may be given low priority and be even ignored by Microsoft for 90 days.
When 7zip announced support for MOTW in June, I honestly took for granted that it would be enabled by default, but apparently the developer doesn’t know exactly what he is doing.
I haven’t yet analyzed how the patch made by Microsoft works, but do let me know if you did and I will gladly update this post with additional information.
Hope you enjoyed the write-up!