Захват нулевого кольца Крис Касперски ака мыщъх Хакер, номер #079, стр. 079-104-3 ... } Остается сущая мелочь. Обломать функцию strnlen_user(), расположив обе переменные в секции ELF-файла с закрытым доступом (PROT_NONE), при обращении к которой произойдет исключение. Для сброса коры программы ядро вызовет core_dump(), которая в свою очередь вызовет elf_core_dump(), и тут-то и произойдет переполнение! Перезапись области ядра открывает практически неограниченные возможности, ведь shell-код выполняется на нулевом кольце! Демонстрационной эксплоит лежит здесь: www.isec.pl/vulnerabilities/isec-0023-coredump.txt [Проблемы многопоточности] В классической UNIX никаких потоков вообще не было, а потому не существовало проблемы их синхронизации. С функцией fork() и развитыми средствами межпроцессорного взаимодействия потоки не очень-то и нужны. Но все-таки они появились, продырявив систему до самого дна. Ядро превратилось в настоящее скопище багов. Вот только один из них, обнаруженный в начале января 2005 года и поражающий все ядра версии 2.2, а ядра с версиями от 2.4 до 2.4.29-pre3 и от 2.6 до 2.6.10 включительно. Рассмотрим фрагмент функции load_elf_library(), автоматически вызываемой функцией sys_uselib() при загрузке новой библиотеки: Ключевой фрагмент функции load_elf_library(), содержащей ошибку синхронизации потоков static int load_elf_library(struct file *file) { down_write(¤t->mm->mmap_sem); /* warn */ error = do_mmap(file, ELF_PAGESTART(elf_phdata->p_vaddr), (elf_phdata->p_filesz + ELF_PAGEOFFSET(elf_phdata->p_vaddr)), PROT_READ | PROT_WRITE | PROT_EXEC, MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE, (elf_phdata->p_offset - ELF_PAGEOFFSET(elf_phdata->p_vaddr))); up_write(¤t->mm->mmap_sem); if (error != ELF_PAGESTART(elf_phdata->p_vaddr)) goto out_free_ph; elf_bss = elf_phdata->p_vaddr + elf_phdata->p_filesz; padzero(elf_bss); len = ELF_PAGESTART(elf_phdata->p_filesz + elf_phdata->p_vaddr + ELF_MIN_ALIGN - 1); bss = elf_phdata->p_memsz + elf_phdata->p_vaddr; if (bss > len) do_brk(len, bss - len); /* warn */ ... } Как мы видим, семафор mmap_sem освобождается до вызова функции do_brk(), порождая тем самым проблему синхронизации потоков. В тоже время, анализ функции sys_brk() убеждает нас в том, что функция do_brk() должна вызываться с взведенным семафором. Рассмотрим фрагмент исходного кода, позаимствованный из файла mm/mmap.c: Ключевой фрагмент функции sys_brk(), страдающей нарушением когерентности служебных структур данных vma = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL); /* warn */ if (!vma) return -ENOMEM; vma->vm_mm = mm; vma->vm_start = addr; vma->vm_end = addr + len; vma->vm_flags = flags; vma->vm_page_prot = protection_map[flags & 0x0f]; vma->vm_ops = NULL; vma->vm_pgoff = 0; vma->vm_file = NULL; vma->vm_private_data = NULL; vma_link(mm, vma, prev, rb_link, rb_parent); В отсутствии семафора состояние виртуальной памяти может быть изменено между вызовами функций kmem_cache_alloc() и vma_link(), и тогда вновь созданный VMA-дескриптор будет размещен совсем не в том месте, на которое рассчитывали разработчики! Для захвата root'а этого более чем достаточно. |