
Анализ и смягчение уязвимости Copy Fail (CVE-2026-31431) ядра Linux, эксплуатирующей мутацию кэша страниц через AF_ALG/splice, с проверкой PoC, правилами обнаружения auditd и проверкой обновлений ядра.
Данный проект представляет собой мини-проект по анализу принципа действия уязвимости Copy Fail (CVE-2026-31431) ядра Linux, а также сравнению состояния до и после применения патча с помощью неразрушающего проверщика из публичного PoC-репозитория.
Фактическая эксплуатация setuid-бинарников или подмена /etc/passwd не производилась; основное внимание уделено безопасной проверке уязвимости на основе временного testfile, а также анализу с точки зрения обнаружения и смягчения последствий.
Цель этого проекта — не просто выполнение эксплойта, а понимание того, какая комбинация внутренних структур ядра Linux приводит к возникновению уязвимости, и как её можно выявить и нейтрализовать с эксплуатационной точки зрения.
Область работ следующая:
Анализ принципа уязвимости
↓
Анализ структуры PoC-кода
↓
Практика с неразрушающим проверщиком
↓
Сравнение до и после патча
↓
Обобщение мер обнаружения/смягчения
В ходе данной практики выполнялся только файл vulnerable.c из репозитория copy-fail-c.
| Раздел | До патча | После патча |
|---|---|---|
| ОС | Ubuntu 24.04.2 LTS | Ubuntu 24.04.4 LTS |
| Ядро | 6.8.0-53-generic | 6.8.0-134-generic |
| Учётная запись | Обычный пользователь client | Обычный пользователь client |
| Проверщик | vulnerable | vulnerable |
| Метод тестирования | Неразрушающая проверка на основе временного testfile | Повторный запуск того же проверщика |
Информация о ядре до патча:
Linux ubuntu-server 6.8.0-53-generic #55-Ubuntu SMP PREEMPT_DYNAMIC Fri Jan 17 15:37:52 UTC 2025 x86_64 x86_64 x86_64 GNU/Linux
Информация о ядре после патча:
Linux ubuntu-server 6.8.0-134-generic #134-Ubuntu SMP PREEMPT_DYNAMIC Fri Jun 26 18:43:11 UTC 2026 x86_64 x86_64 x86_64 GNU/Linux
Краткое описание изменений пакетов ядра:
- linux-image-6.8.0-53-generic 6.8.0-53.55
- linux-image-generic 6.8.0-53.55+1
+ linux-image-6.8.0-134-generic 6.8.0-134.134
+ linux-image-generic 6.8.0-134.134
+ linux-generic 6.8.0-134.134
+ linux-headers-generic 6.8.0-134.134
Copy Fail — это уязвимость, возникающая при сочетании пути обработки AEAD через AF_ALG в ядре Linux с zero-copy операцией splice(), в результате чего страничный кеш (page cache) файла, доступного только для чтения, может быть ошибочно использован в качестве цели для записи.
Ключевые компоненты:
| Компонент | Роль |
|---|---|
| Страничный кеш Linux | Механизм ядра, кэширующий содержимое дисковых файлов в ОЗУ |
splice() | Zero-copy системный вызов, связывающий данные внутри ядра через ссылки без копирования в пользовательское пространство |
AF_ALG | Интерфейс, позволяющий использовать криптографический API ядра Linux из пользовательского пространства наподобие сокета |
| In-place обработка AEAD | Оптимизация, при которой входной и выходной буферы не разделяются, а обработка ведётся в одном буфере |
authencesn | Криптографический шаблон, в котором при обработке AEAD возникает запись 4 байт scratch |
Ключевой поток уязвимости:
Файл, доступный для чтения
↓
Попадает в страничный кеш Linux
↓
Через splice() ссылка на страничный кеш передаётся в криптографический путь AF_ALG
↓
In-place обработка AEAD связывает входной и выходной scatterlist
↓
При обработке authencesn возникает запись 4 байт scratch
↓
Запись происходит не в отдельный выходной буфер, а в страничный кеш
↓
Происходит мутация страничного кеша
То есть splice() передаёт ссылку на страничный кеш, in-place обработка AEAD связывает вход и выход по одному пути, а authencesn фактически осуществляет запись 4 байт.
Структура репозитория, использованного в практике, следующая:
copy-fail-c/
├── exploit.c
├── exploit-passwd.c
├── vulnerable.c
├── payload.c
├── utils.c
├── utils.h
├── Makefile
└── nolibc/
| Файл | Роль | Использование в данной практике |
|---|---|---|
utils.c, utils.h | Реализация примитива мутации страничного кеша на основе AF_ALG/splice | Использованы для анализа и запуска vulnerable |
vulnerable.c | Инструмент неразрушающей проверки уязвимости на основе временного testfile | Выполнен |
exploit.c | Вариант подмены страничного кеша setuid root-бинарника | Не выполнялся |
exploit-passwd.c | Вариант подмены страничного кеша /etc/passwd | Не выполнялся |
payload.c | Полезная нагрузка, выполняемая с правами root | Не выполнялся |
Makefile | Автоматизация сборки | Использована только цель vulnerable |
nolibc/ | Лёгкая замена libc для сборки небольших статических ELF payload | Только анализ |
utils.cЯдро utils.c — это примитив мутации страничного кеша из серии patch_chunk(). Эта функция подключает страничный кеш целевого файла к пути криптографической обработки с помощью AF_ALG и splice(), и в уязвимом ядре проверяет, перезаписывается ли часть страничного кеша во время обработки AEAD.
vulnerable.cvulnerable.c не затрагивает реальные системные файлы. Он создаёт временный testfile в текущем каталоге и проверяет, происходит ли мутация его страничного кеша.
В данном проекте выполнялся только этот файл.
Перед обновлением ядра были зафиксированы состояние ОС, ядра и пакетов.
mkdir -p ~/copyfail-mini/{before,after,logs}
cd ~/copyfail-mini
uname -a | tee before/uname.txt
cat /etc/os-release | tee before/os-release.txt
dpkg -l | grep -E 'linux-image|linux-headers|linux-generic|linux-virtual' | tee before/kernel-package.txt
git clone https://github.com/jihwan77/copy-fail-c.git
cd copy-fail-c
make clean
make vulnerable
В данной практике не использовалась сборка всех эксплойтов через make по умолчанию; использовалась только цель vulnerable.
./vulnerable > ../before/vulnerable-output.txt 2>&1
echo $? >> ../before/vulnerable-output.txt
cat ../before/vulnerable-output.txt
Результат выполнения до патча:

Оценка:
exit code 100
→ подтверждена мутация страничного кеша
→ на ядре до патча примитив Copy Fail работает
После сохранения результатов до патча было выполнено обновление пакетов Ubuntu.
sudo apt update
sudo apt full-upgrade -y
sudo reboot
После перезагрузки ядро изменилось следующим образом:
До: 6.8.0-53-generic
После: 6.8.0-134-generic
cd ~/copyfail-mini/copy-fail-c
make clean
make vulnerable
./vulnerable > ../after/vulnerable-output.txt 2>&1
echo "exit_code=$?" >> ../after/vulnerable-output.txt
cat ../after/vulnerable-output.txt
Результат выполнения после патча:

Сравнение результатов:
| Пункт | До патча | После патча |
|---|---|---|
| Ядро | 6.8.0-53-generic | 6.8.0-134-generic |
| Результат проверщика | VULNERABLE | authencesn template not registered |
| Код завершения | 100 | 2 |
| Мутация страничного кеша | Подтверждена | Проверщик не дошёл до стадии мутации |
| Интерпретация | Примитив Copy Fail работает | Не удалось войти в путь AEAD/authencesn, требуемый PoC |
Код завершения exit_code=2 после патча не означает просто "не уязвимо". Точнее это: