Opened 46 minutes ago

Last modified 39 minutes ago

#37390 assigned Bug

archive.extract() crashes with AttributeError on special tar members (and applies setuid/setgid bits)

Reported by: Dean Ruina Owned by: Dean Ruina
Component: Core (Management commands) Version: dev
Severity: Normal Keywords: startproject startapp archive hardening
Cc: Triage Stage: Unreviewed
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

django.utils.archive.extract() (used by startproject/startapp --template for tar/zip templates) crashes on a tar member that is a FIFO or a character/block device: TarFile.extractfile() returns None, and TarArchive.extract() then opens the target path for writing and calls shutil.copyfileobj(None, ...), which raises AttributeError - after an empty file has already been created at the destination. So a template archive containing a device/FIFO member aborts extraction uncleanly and leaves a partial tree behind, instead of failing with a clear error.

Steps to reproduce: build a tar whose members include a FIFO (e.g. a tarfile.TarInfo with type tarfile.FIFOTYPE), then run django-admin startproject proj --template that.tar. Expected: a clear error. Actual: AttributeError: 'NoneType' object has no attribute 'read' from shutil.copyfileobj in archive.py.

While fixing that, a related hardening gap in the same function: member modes are applied verbatim with os.chmod() (BaseArchive._copy_permissions), so a member with mode 0o4755 becomes a setuid file, and TemplateCommand.apply_umask() then copies stat.S_IMODE() -- including the setuid/setgid/sticky bits -- onto the generated project file.

Proposed fix, mirroring Python's own tarfile "data" filter (PEP 706, the default in Python 3.14):

  • raise SuspiciousOperation for tar members that are not regular files, directories or links, before anything is written (consistent with the function's existing handling of invalid paths);
  • mask the applied mode to 0o777 (the existing behavior of preserving 0o775 etc. is unchanged).

This hardening is in the spirit of CVE-2021-3281 and CVE-2025-59682, which both treated this function as a boundary against hostile templates. The documented "used as is" warning on --template is unchanged.
I have a patch ready with tests and a 6.2 release note, and will open a PR once the ticket is accepted.

Change History (1)

comment:1 by Dean Ruina, 39 minutes ago

Owner: set to Dean Ruina
Status: new → assigned
Note: See TracTickets for help on using tickets.
Back to Top