Windows: creating ~/.config and ~/.cache with mode=0o700 while elevated locks the user out of both directories (Python >= 3.12.4)
Nobody has claimed this yet.
Assessment
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Newbie friendliness
- 78/100
- Issue type
- Bug
- Clarity
- Clearly specified
- Activity status
- Active
- Tech stack
- python
- Domain
- operating-systems
Research direction
Start in scapy/main.py at _probe_xdg_folder and reproduce with repro_scapy_xdg.py using elevated and non-elevated Windows prompts. Check how path.mkdir(mode=0o700, exist_ok=True) behaves with Python 3.12.4 or newer. Done means Scapy can initialize its XDG directories while the interactive user and other applications retain access.
Written by the indexing model from the issue text.
Description
Brief description
On Windows with Python ≥ 3.12.4, running Scapy elevated (as it usually is, for Npcap) creates ~/.config and ~/.cache with an ACL that excludes the interactive user. From then on every application on the machine that uses those XDG base directories fails with PermissionError / EPERM, and the user cannot even read the ACL (icacls → Access is denied) until an elevated takeown + icacls /reset.
Found while debugging an unrelated app's startup crash on a user's machine: ~/.cache/scapy was the only thing in ~/.cache, created in the same minute as ~/.cache and ~/.config themselves.
flowchart LR
A["elevated: import scapy"] --> B["_probe_xdg_folder:<br/>~/.config missing → mkdir(mode=0o700)"]
B --> C["CPython ≥ 3.12.4 (CVE-2024-4030 fix):<br/>mode==0o700 → protected DACL SY, BA, OW"]
C --> D["elevated token ⇒ owner = Administrators<br/>⇒ OW ≠ user"]
D --> E["~/.config, ~/.cache:<br/>no ACE for the user, inheritance off"]
E --> F["every other app:<br/>mkdir ~/.config/<app> → WinError 5 / EPERM"]
Environment
- Scapy version: 2.7.0 (pip)
- Python version: 3.13.7
- Operating System: Windows 11 Pro 10.0.26200, x64, NTFS
How to reproduce
repro_scapy_xdg.py (calls Scapy's own probe functions; XDG_* pointed at fresh dirs so it doesn't touch your real ~/.config):
"""Scapy creates the XDG base dirs (~/.config, ~/.cache) with mode=0o700; on Windows + Python >= 3.12.4,
run elevated, this locks the interactive user out of both directories for every application.
Usage (Windows, Python >= 3.12.4, scapy installed):
python repro_scapy_xdg.py create # run from an ELEVATED prompt (as Scapy usually is, for Npcap)
python repro_scapy_xdg.py check # run from a NON-elevated prompt, same user
"""
import ctypes, os, pathlib, subprocess, sys
home = pathlib.Path(os.environ["USERPROFILE"])
os.environ["XDG_CONFIG_HOME"] = str(home / ".repro-scapy-config")
os.environ["XDG_CACHE_HOME"] = str(home / ".repro-scapy-cache")
elevated = bool(ctypes.windll.shell32.IsUserAnAdmin())
print(f"python {sys.version.split()[0]} user={os.getlogin()} elevated={elevated}")
if sys.argv[1] == "create":
import scapy, scapy.main as m
print(f"scapy {scapy.__version__}")
print("created:", m._probe_config_folder("scapy"), m._probe_cache_folder("scapy"))
for d in ("XDG_CONFIG_HOME", "XDG_CACHE_HOME"):
print(subprocess.run(["icacls", os.environ[d]], capture_output=True, text=True).stdout)
if sys.argv[1] == "check":
for d in ("XDG_CONFIG_HOME", "XDG_CACHE_HOME"):
print(subprocess.run(["icacls", os.environ[d]], capture_output=True, text=True).stdout.strip())
try:
(pathlib.Path(os.environ[d]) / "someotherapp").mkdir(parents=True, exist_ok=True)
print(f"mkdir {d}/someotherapp: ok")
except OSError as e:
print(f"mkdir {d}/someotherapp: {type(e).__name__}: {e}")
Actual result
=== create (elevated)
python 3.13.7 user=Lukem elevated=True
scapy 2.7.0
created: C:\Users\Lukem\.repro-scapy-config\scapy C:\Users\Lukem\.repro-scapy-cache\scapy
C:\Users\Lukem\.repro-scapy-config NT AUTHORITY\SYSTEM:(OI)(CI)(F)
BUILTIN\Administrators:(OI)(CI)(F)
OWNER RIGHTS:(OI)(CI)(F)
C:\Users\Lukem\.repro-scapy-cache NT AUTHORITY\SYSTEM:(OI)(CI)(F)
BUILTIN\Administrators:(OI)(CI)(F)
OWNER RIGHTS:(OI)(CI)(F)
=== check (non-elevated)
python 3.13.7 user=Lukem elevated=False
Successfully processed 0 files; Failed processing 1 files
mkdir XDG_CONFIG_HOME/someotherapp: PermissionError: [WinError 5] Access is denied: 'C:\\Users\\Lukem\\.repro-scapy-config\\someotherapp'
Successfully processed 0 files; Failed processing 1 files
mkdir XDG_CACHE_HOME/someotherapp: PermissionError: [WinError 5] Access is denied: 'C:\\Users\\Lukem\\.repro-scapy-cache\\someotherapp'
Owner of both directories is BUILTIN\Administrators; AreAccessRulesProtected = True. Non‑elevated Scapy produces the same DACL but with owner = user, so it is harmless — the damage needs elevation, which Scapy on Windows effectively requires.
Expected result
Other applications can still use ~/.config and ~/.cache after Scapy has run.
Where
scapy/main.py _probe_xdg_folder @ 5166573:
if not path.exists():
# ~ folder doesn't exist. Create according to spec
# https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
# "If, when attempting to write a file, the destination directory is
# non-existent an attempt should be made to create it with permission 0700."
path.mkdir(mode=0o700, exist_ok=True)
path here is the XDG base directory (~/.config, ~/.cache, ~/.local/share), not …/scapy.
The interaction is with CPython's CVE‑2024‑4030 change (posixmodule.c os_mkdir_impl): on Windows, exactly mode == 0o700 is now special‑cased into D:P(A;OICI;FA;;;SY)(A;OICI;FA;;;BA)(A;OICI;FA;;;OW). OW is the object owner, which for an elevated token is BUILTIN\Administrators. Reported to CPython separately; Scapy is the concrete trigger and can avoid it independently:
- Don't pass
mode=0o700on Windows (modeis otherwise ignored there; only this exact value has an effect). E.g.path.mkdir(mode=0o700 if os.name != "nt" else 0o777, exist_ok=True). - Or create only the
scapyleaf (path.joinpath(*cf).mkdir(parents=True, ...)) and leave the base directory's permissions to the OS defaults — the XDG 0700 recommendation is about POSIX modes and has no sane mapping to a shared Windows profile folder.
Related resources
- CPython change: https://github.com/python/cpython/issues/118486 (CVE‑2024‑4030)
- Windows default owner for elevated tokens:
BUILTIN\Administrators(observable viaGet-Aclon any object created elevated)
- Dominant language
- Python
- Stars
- 12.6k
- Forks
- 2.2k
- Avg merge
- 1d 9h
- Merged PRs (30d)
- 60
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from secdev/scapy
-
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 65/100
-
Difficulty 5/5 Over a week Newbie friendliness 25/100
-
2.8.0 release Opendiscussion major
Difficulty 4/5 3-5 days Newbie friendliness 35/100
Similar issues
-
Difficulty 2/5 1-3 hours Newbie friendliness 88/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
-
enhancement
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 74/100