LINUX.ORG.RU

Kiddy — модуль ядра Linux для защиты от скрипт-кидди

 , , ,


0

2

Kiddy – модуль для ядра Linux, разработанный с целью снижения рисков эксплуатации (некоторых) уязвимостей ядра.

В основе механизма защиты, реализованного в данном модуле, лежит простая идея, которая заключается в том, что в процессе атаки так или иначе происходит идентификация объекта атаки. Поэтому, если затруднить такую идентификацию, можно кратно повысить сложность эксплуатации, т.к. во множестве случаев готовые эксплойты содержат в себе таблицы различного рода смещений (оффсетов), соответствующих целевым версиям ядра.

Например, вот как это сделано для CVE-2017-1000112. Там же можно видеть, что идентификация версии ядра осуществляется с использованием uname.

Разработанный модуль является простым в реализации и позволяет:

  • менять идентификацию ядра;
  • ограничивать доступ к журналу ядра (dmesg);
  • ограничивать доступ к некоторым файлам в /proc, также содержащим идентифицирующую информацию;
  • ограничивать доступ к файлам и папкам, потенциально содержащим идентифицирующую информацию;
  • менять идентификацию версии ядра, доступную через vDSO.

В процессе сборки модуль позволяет использовать т.н. «пресеты», реализующие различную логику изменения идентификации. Например, используя пресет «windows» можно получить следующее поведение:

До загрузки модуля

$ ./misc/id.sh
** UNAME identidty leaks
 - uname -r
   2.6.32-754.35.1.el6.x86_64
 - uname -v
   #1 SMP Sat Nov 7 12:42:14 UTC 2020
 - uname -a
   Linux localhost.localdomain 2.6.32-754.35.1.el6.x86_64 #1 SMP Sat Nov 7 12:42:14 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux
** PROCFS identidty leaks
 - /proc/cmdline
   ro root=/dev/mapper/VolGroup00-LogVol00 rd_NO_LUKS no_timer_check console=tty0 console=ttyS0,115200n8 net.ifnames=0 biosdevname=0 LANG=en_US.UTF-8 rd_NO_MD SYSFONT=latarcyrheb-sun16  rd_LVM_LV=VolGroup00/LogVol01 rd_LVM_LV=VolGroup00/LogVol00  KEYBOARDTYPE=pc KEYTABLE=us rd_NO_DM rhgb quiet
 - /proc/version
   Linux version 2.6.32-754.35.1.el6.x86_64 (mockbuild@x86-02.bsys.centos.org) (gcc version 4.4.7 20120313 (Red Hat 4.4.7-23) (GCC) ) #1 SMP Sat Nov 7 12:42:14 UTC 2020
 - /proc/sys/kernel/version
   #1 SMP Sat Nov 7 12:42:14 UTC 2020
 - /proc/sys/kernel/osrelease
   2.6.32-754.35.1.el6.x86_64
which: no hostnamectl in (/usr/local/bin:/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/sbin:/home/vagrant/bin)

После загрузки модуля

$ ./misc/id.sh
** UNAME identidty leaks
 - uname -r
   Windows
 - uname -v
   NT 4.0
 - uname -a
   Linux localhost.localdomain Windows NT 4.0 x86_64 x86_64 x86_64 GNU/Linux
** PROCFS identidty leaks
 - /proc/cmdline
   \EFI\Microsoft\Boot\bootmgfw.efi
 - /proc/version
   Windows NT 4.0
 - /proc/sys/kernel/version
   NT 4.0
 - /proc/sys/kernel/osrelease
   Windows
which: no hostnamectl in (/usr/local/bin:/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/sbin:/home/vagrant/bin)

Скрипт-кидди не пройдут!

>>> Подробности

★★

Проверено: hobbit ()
Последнее исправление: CYB3R (всего исправлений: 5)

Ответ на: комментарий от peregrine

Да тот «скрипт» на сях uname(2) использует, то есть при загруженном модуле выдаст тебе то, что ты захочешь и что определишь при компиляции модуля. В чём вопрос-то?

i82 ★★
() автор топика
Ответ на: комментарий от i82

Значит всё же вызовы перехватываешь. Это хорошо. А что по

#include <stdio.h>
#include <linux/version.h>
#include <sys/utsname.h>

/*
 * from rhel7's linux/version.h:
 *   #define LINUX_VERSION_CODE 199168
 *   #define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))
 */

int main(void)
{
  int maj = LINUX_VERSION_CODE >> 16;
  int min = ( LINUX_VERSION_CODE - ( maj << 16 ) ) >> 8;
  int pat = LINUX_VERSION_CODE - ( maj << 16 ) - ( min << 8 );
  struct utsname unamedata;

  printf("linux/version.h : %d = %d.%d.%d\n", LINUX_VERSION_CODE, maj, min, pat);

  uname(&unamedata);
  printf("uname : utsname.release = %s\n", unamedata.release);

  return(0);
}

PS

код честно взят тут https://gist.github.com/ryanwoodsmall/be00c6b191f6459782dea1516020e91c

peregrine ★★★★★
()
Последнее исправление: peregrine (всего исправлений: 1)
Ответ на: комментарий от beastie

Опыт показывает, что запуск публичных корпоративных сервисов на «нестандартных» портах уже серьезно снижает риск проникновения ботов. Вектор атаки обычно настроен на конкретные порты.

gns ★★★★★
()
Ответ на: комментарий от peregrine

Вот что выдаёт данный код без модуля Kiddy и с ним (сборка с опцией PRESET=default make)

linux/version.h : 132640 = 2.6.32
uname : utsname.release = 2.6.32-754.35.1.el6.x86_64

vs

linux/version.h : 132640 = 2.6.32
uname : utsname.release = 2.6-kiddy

Поведение вполне логичное, однако можно придраться к тому, что при компиляции версия ядра была взята из файла /usr/include/linux/version.h.

Чтобы это исправить, достаточно добавить в пресет запрет на доступ к /usr/include, что конечно выглядит как стрельба из пушки по воробьям, но определённо решает проблему.

$ gcc test.c -o test
test.c:1:19: error: /usr/include/stdio.h: Operation not permitted
test.c:2:27: error: /usr/include/linux/version.h: Operation not permitted
test.c:3:25: error: /usr/include/sys/utsname.h: Operation not permitted
test.c: In function ‘main’:
test.c:13: error: ‘LINUX_VERSION_CODE’ undeclared (first use in this function)
test.c:13: error: (Each undeclared identifier is reported only once
test.c:13: error: for each function it appears in.)
test.c:16: error: storage size of ‘unamedata’ isn’t known
test.c:18: warning: incompatible implicit declaration of built-in function ‘printf’

Возможно, в модуле можно предосмотреть возможность ограничения пользователей по UID, а не всех подряд (кроме рута)…

i82 ★★
() автор топика
Ответ на: комментарий от greedyskoof

Он по-моему ругается только если /boot/efi не закрыт. А на /boot повсеместно 755 права:

drwxr-xr-x   5 root root 4.0K Jan  1  1970 boot

Вроде установщик даже такие права выставляет. Не вижу причин права менять, хотя оно может и правильно сменить на 700, но я на 100% уверен, что никто этим заморачиваться не станет.

Если он world-readable, то это дыра в безопасности конкретно вашей системы

К безопасности это не имеет отношения. Это идиотия лишних людей, которых обязали нанимать в штатам, потому что какие-то 70-летние деды думают, что если везде по безопаснику посадить ломать все подряд перестанут…

Какой-нить продвинутый безопасник еще бы вспомнил про права на выполнение для /tmp и /dev/shm… А вот то поделие исправляет убирает права на выполнение, чтобы запретить эксплуатацию dirty pipe??? Ну ладно я там не доглядел, что он запретил в /boot лазать, но как оно запретит эксплуатировать эту уязвимость…

rtxtxtrx
()
Ответ на: комментарий от rtxtxtrx

Сейчас догадались делать ограничение на доступ к /boot

Infra_HDC ★★★★★
()
Ответ на: комментарий от gns

Из этого, в ядре Linux сейчас только KASLR. Я не считаю его бесполезным (многие считают), но есть способы его обхода - всякие утечки адресов и атаки по сторонним каналам. Кстати, обсуждаемые в этом контексте возможные дополнения в LKRG - это также устранение некоторых непосредственных утечек адресов, а не только скрытие другой информации о сборке ядра. Кое-что уже блокируется существующими sysctl dmesg_restrict, kptr_restrict и lkrg.hide.

solardiz
()

Kiddy — модуль ядра Linux от скрипт-кидди

Camel ★★★★★
()

@i82, кстати модуль по сути дырявый в текущем состоянии. Из проверок доступа только uid == 0, но существование неймспейсов никак не учтено. То есть любой контейнер или неймспейс с псевдорутом так же будет иметь uid == 0 и успешно пройдет проверку.

Хорошего не добавляет и факт наличия по дефолту включенного в ядре unpriviledged user namespaces (печально известный unprivileged_userns_clone), который позволяет абсолютно любому пользователю создать неймспейс с псевдорутом. С ним много уязвимостей было найдено чисто в виде прозеваных проверок в ядре, которые так же полагали, что проверять uid == 0 достаточно.

greedyskoof
()
Ответ на: комментарий от greedyskoof

Хороший комментарий.

Проверил на 6.1.19 с Kiddy собранным на default-пресете, получил вот что:

[nix-shell:~/projects/github/kiddy]$ unshare --map-root-user bash

[root@nixos:~/projects/github/kiddy]# id
uid=0(root) gid=0(root) groups=0(root),65534(nogroup)

[root@nixos:~/projects/github/kiddy]# ls -al /boot
ls: cannot open directory '/boot': Operation not permitted

[root@nixos:~/projects/github/kiddy]# ls -al /boot
ls: cannot open directory '/boot': Operation not permitted

[root@nixos:~/projects/github/kiddy]# uname -r
6.1-kiddy

Вроде не дыряво?

i82 ★★
() автор топика
Ответ на: комментарий от i82

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

Тогда еще есть смысл с контейнерами проверить. Которые не через unshare, а через что-то типа докера (у которого основной сервис с реальными рут правами крутится). Или хотя бы chroot банально. По идее проверка должна пройтись.

greedyskoof
()
Ответ на: комментарий от greedyskoof

Обнаружил такой момент, что если смонтировать ФС (например, proc) в другое место, то реализованный механизм блокировки доступа к файлам по пути не будет отрабатывать при обращении по альтернативному имени. В общем-то логично (с точки зрения того, как реализовано), но не логично с точки зрения пользователя. Вот это нужно будет поправить. А что касается chroot и контейнеров вообще, не вижу там проблем, если честно.

i82 ★★
() автор топика
Для того чтобы оставить комментарий войдите или зарегистрируйтесь.