Издательский дом ООО "Гейм Лэнд"ЖУРНАЛ ХАКЕР #79, ИЮЛЬ 2005 г.

Захват нулевого кольца

Крис Касперски ака мыщъх

Хакер, номер #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'а этого более чем достаточно.

Назад на стр. 079-104-2  Содержание  Вперед на стр. 079-104-4