Vlad Stolyarov, Google Threat Analysis Group

The Basics

Disclosure or Patch Date: July 20, 2023

Product: RARLAB WinRAR

Advisory: https://www.win-rar.com/singlenewsview.html?&L=0&tx_ttnews%5Btt_news%5D=230&cHash=d5b004cf8e13ffaf713f4ec6b604694e

Affected Versions: 6.22 and other versions prior

First Patched Version: 6.23 Beta 1

Issue/Bug Report: N/A

Patch CL: N/A

Bug-Introducing CL: N/A

Reporter(s):

  • Andrey Polovinkin from Group-IB Threat Intelligence unit

The Code

Proof-of-concept: https://github.com/b1tg/CVE-2023-38831-winrar-exploit

Exploit sample: See PoC

Did you have access to the exploit sample when doing the analysis? yes

The Vulnerability

Bug class: Logical flaw

Vulnerability details:

CVE-2023-38831 is a logical vulnerability within WinRAR causing extraneous temporary file expansion when processing crafted archives, combined with a quirk in the implementation of Windows’ ShellExecute when attempting to open a file with an extension containing spaces. The vulnerability allows attackers to execute arbitrary code when a user attempts to view a benign file (such as an ordinary PNG file) within a ZIP archive.

Consider the following archive structure:

# CVE-2023-38831.rar
├── "poc.png_"   # benign file that triggers the bug after double-click
└── "poc.png_/"  # directory that causes the .cmd file to be expanded
    └──── poc.png_.cmd" # malicious payload

When a user double-clicks on a benign poc.png_ (underscore is used to indicate a space) from WinRAR’s user interface, WinRAR prior to 6.23 will instead execute poc.png_/poc.png_.cmd.

After a user double-clicks on a file, WinRAR attempts to determine which files need to be temporarily expanded by iterating through all archive entries. However, due to the way the matching is made, if a directory is found with the same name as the selected entry, both the selected file and the files inside a matched directory are extracted to the root of a random temporary directory.

The pseudocode below shows WinRAR’s extraction logic and whether an archive entry should to be extracted:

def ShouldExtractArchiveEntry(selected_filename, entry_filename, flags):
  ..
  if flags & EXPAND_DIRS:
    if entry_filename.startswith(selected_filename):
      last_char = entry_filename[len(selected_filename)]
      if last_char == '\\' or last_char == '/' or len(entry_filename) == len(selected_filename):
      return True # entry should be temporarily extracted
  ..

ShouldExtractArchiveEntry('poc.png ', 'poc.png ', EXPAND_DIRS | OTHER_FLAGS) # True
ShouldExtractArchiveEntry('poc.png ', 'poc.png /poc.png .cmd', EXPAND_DIRS | OTHER_FLAGS) # True, insecure temporary file creation

When writing contents of the files, WinRAR performs path normalization that removes appended spaces, because Windows doesn’t allow files with trailing spaces. Note that while most samples exploiting CVE-2023-3883 use an archive entry with a trailing space, it is not a requirement, and a space in any position in the file extension is sufficient to trigger the bug (e.g. entry with poc.invalid_ext will also result in shell32!ApplyDefaultExts code path to be taken). However, if WinRAR were to also normalize the path in ShellExecute arguments - the bug could've only been triggered with spaces not in last position.

Finally, WinRAR calls ShellExecuteExW, passing the non-normalized path with a trailing space %TEMP%\{random_directory}\poc.png_ to run the user-selected file. Internally, ShellExecute attempts to identify file extensions by calling shell32!PathFindExtension which fails because extensions with spaces are considered invalid. Instead of bailing out, ShellExecute proceeds to call shell32!ApplyDefaultExts which iterates through all files in a directory, finding and executing the first file with an extension matching any of the hardcoded ones: “.pif, .com, .exe, .bat, .lnk, .cmd”.

Patch analysis:

The patch introduces a new flag with a value of 0x080000000 (PERFORM_FULL_MATCH), which is set by default for temporary expansion initiated from a users' double-click in WinRAR user-interface. This flag performs full string comparison between entry_filename and selected_filename (instead of the first len(selected_filename)) when choosing candidates for extraction in ShouldExtractArchiveEntry routine. This results in only one, selected file, to be temporarily expanded.

Thoughts on how this vuln might have been found (fuzzing, code auditing, variant analysis, etc.):

CVE-2023-38831 is an unusual vulnerability that requires user interaction - user has to double-click on a specific file in WinRAR's "preview" user interface. It is unlikely that someone was looking for a bug of this type intentionally, so CVE-2023-38831 was probably an accidental discovery while doing a code audit of WinRAR while looking for other bugs.

(Historical/present/future) context of bug: N/A

The Exploit

(The terms exploit primitive, exploit strategy, exploit technique, and exploit flow are defined here.)

Exploit strategy (or strategies): N/A

Exploit flow: N/A

Known cases of the same exploit flow: N/A

Part of an exploit chain? N/A

The Next Steps

Variant analysis

Areas/approach for variant analysis (and why):

Possible approaches to find variants would be to look for other similar software where attacker controlled files are executed using ShellExecute.

Found variants:

Only poked around manually in 7-Zip and WinZIP, no results so far.

Structural improvements

What are structural improvements such as ways to kill the bug class, prevent the introduction of this vulnerability, mitigate the exploit flow, make this type of vulnerability harder to exploit, etc.?

Ideas to kill the bug class:

Hide ShellExecute's "default extension search" behavior under a flag that is not set by default. Set the flag in Windows code that currently relies on this functionality (explorer, cmd, etc.).

Ideas to mitigate the exploit flow:

Introduce a UAC-style dialog when "default extension search" was applied and the executed filename is different from the passed one.

Other potential improvements:

0-day detection methods

What are potential detection methods for similar 0-days? Meaning are there any ideas of how this exploit or similar exploits could be detected as a 0-day? N/A

Other References