Skip to content
KitploitKITPLOIT
ToolsExploitsBlog
Log in
Submit
ToolsExploitsBlog
Submit

Hacking, PenTest, and Cybersecurity Tools for Your Security Arsenal!

Kitploit is a directory of hacking, cybersecurity, and pentesting tools. Discover the latest project updates to find vulnerabilities, analyze systems, automate testing, and strengthen your security.

FeedsContactPrivacy© 2026 Kitploit

Tool Directory

Categories

View all categories
Loading categories
CVE-2026-65320-fastcore — Documents CVE-2026-65320, a tar-slip path traversal in fastcore's untar_dir(), with a harmless proof-of-concept demonstrating arbitrary file write and code execution. | Kitploit
Tools/GitHubGitHub/rahulreddykarne/cve-2026-65320-fastcore
Vulnerability AnalysisCode AnalysisExploitationWeb Application ExploitationPapers & ResearchLearning & Education
GitHubrahulreddykarne/cve-2026-65320-fastcore

CVE-2026-65320-fastcore

Documents CVE-2026-65320, a tar-slip path traversal in fastcore's untar_dir(), with a harmless proof-of-concept demonstrating arbitrary file write and code execution.

View Repository
1324 days agoNot yet reviewed

Most Popular

View all →

Discover the most used tools by our community.

Explore all tools

Browse our collection of tools

View all tools →
Share

CVE-2026-65320: Path Traversal (Tar Slip) in fastcore via untar_dir()

Severity: Critical, CVSS 3.1 9.8

Vector (v3.1): CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H

Affected: fastcore <= 2.2.30

Fixed in: Not fixed at time of writing

CWE: CWE-22 (Improper Limitation of a Pathname to a Restricted Directory, 'Path Traversal')

Component: fastcore.xtras.untar_dir() → fastcore.xtras._unpack() → shutil.unpack_archive()

High-risk runtime: Python < 3.14 (Python 3.14 makes the safe data tar filter the default)

Reported by: Rahul Karne

CNA: VulnCheck


Summary

fastcore.xtras.untar_dir(fname, dest) is a documented helper that untars an archive "into dest".

Internally, it hands the archive to the standard library's shutil.unpack_archive() without an extraction filter, without validating archive member paths, and without any post-extraction containment check.

As a result, a TAR archive whose members contain ../ traversal sequences or absolute paths can write files outside the directory the caller asked fastcore to extract into.

A caller who reasonably expects untar_dir(fname, dest) to confine everything to dest instead gets arbitrary file write anywhere the process can write.


Impact

Arbitrary file write under the authority of the process calling untar_dir().

Depending on what is overwritten, this can result in:

  • Code execution — overwrite a Python file that the application later imports, such as a plugin, startup hook, or importable module, or drop a file into an auto-loaded location.
  • Data / configuration tampering — overwrite configuration files, credentials, or application data.
  • Denial of service — corrupt files the service depends on.

The proof of concept demonstrates the full write → import → code-execution chain end to end, entirely inside a harmless demo directory.


Reach

fastcore is a foundational dependency across the fast.ai ecosystem, including:

  • fastai
  • nbdev
  • ghapi
  • Other related packages

It receives tens of millions of downloads per month.

untar_dir() is the extraction primitive behind the common "download an archive, then unpack it" workflow, which follows the same general pattern as fastai's untar_data.

Any code path that feeds untar_dir() an archive whose contents are not fully trusted is exposed.


Technical Detail

The vulnerable code exists in fastcore/xtras.py:

def _unpack(fname, out):
    import shutil
    shutil.unpack_archive(str(fname), str(out))   # <-- no filter, no validation
    ls = out.ls()
    return ls[0] if len(ls) == 1 else out


def untar_dir(fname, dest, rename=False, overwrite=False, uid=-1, gid=-1):
    "untar `file` into `dest` ..."
    import tempfile, shutil
    dest = Path(dest)

    with tempfile.TemporaryDirectory() as d:
        out = Path(d) / remove_suffix(Path(fname).stem, '.tar')
        out.mkdir()

        ...

        src = _unpack(fname, out)      # extraction escapes `out` here

        ...

        shutil.move(str(src), dest)

        ...

        return dest

untar_dir() creates an internal temporary extraction directory:

<tempdir>/<random>/<archive-stem>/

It then calls _unpack(), which forwards the archive directly to:

shutil.unpack_archive()

For TAR archives, this ultimately calls tarfile.extractall().

On Python < 3.14, the default extraction behavior is fully_trusted, meaning traversal members can be honored.

For example, an archive member such as:

../../../some/other/dir/file

is written relative to the extraction directory and can therefore escape it.

Nothing in fastcore:

  • Passes filter="data" where supported.
  • Rejects absolute paths.
  • Rejects drive-letter paths.
  • Rejects UNC paths.
  • Rejects .. path components.
  • Rejects symlink or hardlink members.
  • Verifies that every extracted path remained under the intended extraction root before returning success.

Root Cause

untar_dir() treats shutil.unpack_archive() as though it were a safe, sandboxed archive extractor.

It is not.

Python's standard library documentation warns that extracting untrusted archives may create files outside the requested destination and recommends safe extraction filtering for TAR archives.

fastcore neither passes an appropriate extraction filter nor implements an equivalent containment guard for the Python versions it supports.

It also does not warn callers of untar_dir() about this security-sensitive behavior.


Why the TAR Path Specifically

TAR members can contain arbitrary path strings.

On affected Python versions, tarfile.extractall() does not automatically neutralize .. traversal sequences by default.

Writing a traversal member relative to fastcore's temporary extraction directory is therefore sufficient to escape that directory and write anywhere the process has permission to write.

A reliable primitive is relative traversal such as:

../../../target/file

This is the technique used by the proof of concept.


Delivery Over the Network

The dangerous input is the archive contents, not the destination path chosen by the developer.

Archives are frequently obtained from external or semi-trusted locations, including:

  • Model bundles
  • Dataset archives
  • Plugin bundles
  • CI/CD artifacts
  • User uploads
  • Partner uploads
  • Remote HTTP downloads

Any service or tool that downloads an archive from an untrusted or semi-trusted source and subsequently processes it using untar_dir() may therefore be reachable.

The PoC also includes a variant that serves the crafted archive from an attacker-controlled HTTP endpoint to demonstrate this delivery path.


Exploitation Preconditions

Successful exploitation requires:

  1. The application calls fastcore.xtras.untar_dir() on an archive whose contents are attacker-influenced, such as an uploaded, downloaded, or otherwise untrusted archive.

  2. The runtime uses Python < 3.14, or another configuration where a safe TAR extraction filter is not enforced.

  3. For escalation from arbitrary file write to code execution, the process later imports or loads a file from a location reachable by the traversal, such as:

    • A plugin directory
    • An importable Python module
    • A startup hook
    • Another auto-loaded file location

Arbitrary file write and configuration/data modification require only conditions 1 and 2.


Proof of Concept

poc_fastcore_cna_demo.py invokes the real fastcore.xtras.untar_dir() API.

The demonstration is intentionally harmless.

Every file created by the PoC remains inside a single demo directory specified using:

--demo-root

The PoC does not modify real system files and does not access cloud services or credentials.

What the PoC Does

  1. Prints the environment details and vulnerable source code for _unpack() and untar_dir().

  2. Creates a demonstration layout containing:

    • An intended extraction root
    • A separate application directory:
      service_app/plugins/
      
  3. Creates:

attacker_supplied_upload.tar

containing archive members designed to point outside the intended extraction root, including traversal members and:

service_app/plugins/startup_hook.py
  1. Calls a simulated upload handler:
def auto_extract_uploaded_archive(received_archive):
    return untar_dir(
        received_archive,
        intended_root,
        overwrite=True
    )
  1. Verifies that files were written outside the intended extraction directory.

  2. Simulates a service reload by importing the archive-written:

startup_hook.py

The harmless startup hook creates:

SERVICE_RELOAD_MARKER

This demonstrates the complete chain:

Download Tool