Skip to content

Commit 0d32864

Browse files
[3.12] gh-157190: Fix tarfile data/tar filter bypass via hard link to a symlink (GH-157191) (GH-157192)
(cherry picked from commit 480ea4a) The backport to 3.13 and below includes a NEWS entry. (cherry picked from commit b8f23e3) Co-authored-by: Stan Ulbrych <stan@python.org>
1 parent c016c25 commit 0d32864

3 files changed

Lines changed: 28 additions & 1 deletion

File tree

‎Lib/tarfile.py‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2665,7 +2665,11 @@ def makelink_with_filter(self, tarinfo, targetpath,
26652665
return
26662666
else:
26672667
if os.path.exists(tarinfo._link_target):
2668-
os.link(tarinfo._link_target, targetpath)
2668+
# Resolve the target so the hard link points to the file
2669+
# itself. Otherwise os.link() may duplicate a symlink to a
2670+
# shallower location, where it's relative target escapes the
2671+
# destination directory. (CVE-2026-82049)
2672+
os.link(os.path.realpath(tarinfo._link_target), targetpath)
26692673
return
26702674
except symlink_exception:
26712675
keyerror_to_extracterror = True

‎Lib/test/test_tarfile.py‎

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4220,6 +4220,24 @@ def test_sneaky_hardlink_fallback_deep(self):
42204220
self.expect_file("a/b/s", symlink_to=os.path.join('..', 'escape'))
42214221
self.expect_file("s", symlink_to=os.path.join('..', 'escape'))
42224222

4223+
@symlink_test
4224+
@os_helper.skip_unless_hardlink
4225+
def test_sneaky_hardlink_relocation(self):
4226+
with ArchiveMaker() as arc:
4227+
arc.add("a/escape", content="decoy")
4228+
arc.add("a/b/s", symlink_to=os.path.join("..", "escape"))
4229+
arc.add("s", hardlink_to=os.path.join("a", "b", "s"))
4230+
4231+
for filter in 'data', 'tar':
4232+
with self.subTest(filter), self.check_context(arc.open(), filter):
4233+
self.expect_file("a/escape", content="decoy")
4234+
if os_helper.can_symlink():
4235+
self.expect_file("a/b/s", symlink_to=os.path.join('..', 'escape'))
4236+
else:
4237+
self.expect_file("a/b/s", content="decoy")
4238+
self.expect_file("s", content="decoy")
4239+
self.assertFalse((self.destdir / "s").is_symlink())
4240+
42234241
@symlink_test
42244242
def test_exfiltration_via_symlink(self):
42254243
# (CVE-2025-4138)
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
Fixed a vulnerability in the :mod:`tarfile` ``data`` and ``tar`` extraction
2+
filters where a crafted archive using a hard link to a symbolic link could
3+
change the permissions and modification time of a file outside the
4+
destination directory, and expose its contents inside the extracted tree.
5+
This addresses :cve:`2026-82049`.

0 commit comments

Comments
 (0)