Wyłączenie ładowania niepodpisanych modułów
Napisał: Patryk Krawaczyński
11/06/2025 (2 tygodnie temu) w Bezpieczeństwo Brak komentarzy. (artykuł nr 927, ilość słów: 669)
M
oduły dostarczane przez dystrybucyjne jądra pochodzą z ich kodu źródłowego. Powstają one podczas procesu kompilacji jądra, a narzędzia towarzyszące przy tej operacji tworzą parę autowygenerowanych kluczy (prywatny / publiczny) i podpisują każdy moduł powstający z drzewa kodu źródłowego jądra (używając klucza prywatnego). Klucz publiczny jest zapisywany w samym jądrze systemu. Gdy moduł dostarczony z jądrem jest ładowany w systemie, klucz publiczny może zostać użyty do sprawdzenia, czy moduł pozostał niezmieniony i pochodzi z pierwotnego drzewa kodu źródłowego. Podczas uruchamiania systemu możemy poinstruować jądro, aby zawsze weryfikowało moduły i raportowało wszelkie niepowodzenia ładowania modułów „spoza drzewa” do logów, ponieważ gdy „wymusimy” obsługę tylko podpisanych modułów jądra – system Linux będzie ładować tylko moduły podpisane cyfrowo wcześniej autowygenerowanym kluczem.
Umożliwia to szybkie i proste wzmocnienie systemu (oraz uzupełnienie wyłączenia ładowania dodatkowych modułów) poprzez uniemożliwienie ładowania niepodpisanych modułów lub modułów podpisanych niewłaściwym kluczem. W samym na eliminację złośliwych modułów, które są powszechną metodą do instalacji rootkitów w systemie Linux. Weryfikacja podpisanych modułów jest funkcją jądra, więc musi być włączona poprzez jego konfigurację. Niezbędne opcje możemy znaleźć w menu przy kompilacji:
[*] Enable loadable module support ---> [*] Module signature verification [ ] Require modules to be validly signed [*] Automatically sign all modules Which hash algorithm should modules be signed with? (Sign modules with SHA-512) --->
co również ma swoje odzwierciedlenie w zapisanej konfiguracji jądra:
root@darkstar:~# grep CONFIG_MODULE_SIG /boot/config-`uname -r` CONFIG_MODULE_SIG_FORMAT=y CONFIG_MODULE_SIG=y # CONFIG_MODULE_SIG_FORCE is not set CONFIG_MODULE_SIG_ALL=y CONFIG_MODULE_SIG_SHA512=y CONFIG_MODULE_SIG_HASH="sha512" CONFIG_MODULE_SIG_KEY="certs/signing_key.pem" CONFIG_MODULE_SIG_KEY_TYPE_RSA=y # CONFIG_MODULE_SIG_KEY_TYPE_ECDSA is not set
Opcja „Module signature verification” (CONFIG_MODULE_SIG
) włącza weryfikację podpisu modułów w jądrze Linux. Obsługuje ona dwa podejścia do obsługi podpisanych modułów: tryb zezwalający i rygorystyczny. Domyślnie używane jest podejście dopuszczające, co oznacza, że moduły jądra Linux mogą mieć lub nie mieć podpisu odpowiednim kluczem. W przypadku wybrania podejścia rygorystycznego, poprawny podpis musi być obecny dla modułu. W powyższym przykładzie tryb rygorystyczny nie jest wybrany: „Require modules to be validly signed” (CONFIG_MODULE_SIG_FORCE
). Ale nic straconego, ponieważ innym sposobem jego włączenia jest ustawienie opcji rozruchowej jądra: module.sig_enforce=1
. W systemie Ubuntu wystarczy edytować plik: /etc/default/grub i w sekcji GRUB_CMDLINE_LINUX
dodać wspomnianą opcję rozruchową:
root@darkstar:~# grep GRUB_CMDLINE_LINUX /etc/default/grub GRUB_CMDLINE_LINUX_DEFAULT="quiet splash" GRUB_CMDLINE_LINUX="module.sig_enforce=1"
Następnie aktualizujemy ustawienia GRand Unified Bootloader:
root@darkstar:~# update-grub Sourcing file `/etc/default/grub' Generating grub configuration file ... Found linux image: /boot/vmlinuz-6.8.0-60 Found initrd image: /boot/initrd.img-6.8.0-60 Adding boot menu entry for UEFI Firmware Settings ... done
Możemy sprawdzić, czy przy następnym restarcie systemu opacja rozruchowa zostanie przekazana do jądra:
root@darkstar:~# grep module.sig_enforce /boot/grub/grub.cfg linux /boot/vmlinuz-6.8.0-60 root=UUID=1bd02967 ro module.sig_enforce=1 console=ttyS0
Po restarcie systemu możemy spróbować załadować „obcy” moduł do systemu:
root@darkstar:~# make all make -C /lib/modules/6.8.0-60/build M=/root modules make[1]: Entering directory '/usr/src/linux-headers-6.8.0-60' warning: the compiler differs from the one used to build the kernel The kernel was built by: x86_64-linux-gnu-gcc-13 (Ubuntu 13.3.0-6ubuntu2~24.04) 13.3.0 You are using: gcc-13 (Ubuntu 13.3.0-6ubuntu2~24.04) 13.3.0 CC [M] /root/nfsec_rootkit.o MODPOST /root/Module.symvers CC [M] /root/nfsec_rootkit.mod.o LD [M] /root/nfsec_rootkit.ko BTF [M] /root/nfsec_rootkit.ko Skipping BTF generation for /root/nfsec_rootkit.ko due to unavailability of vmlinux make[1]: Leaving directory '/usr/src/linux-headers-6.8.0-60' root@darkstar:~# insmod nfsec_rootkit.ko insmod: ERROR: could not insert module nfsec_rootkit.ko: Key was rejected by service root@darkstar:~# dmesg -T | grep 'unsigned module' [Wed Jun 11 20:53:44 2025] Loading of unsigned module is rejected
Gdzie inne moduły są załadowane i podpisane:
root@darkstar:~# modprobe x_tables root@darkstar:~# modinfo x_tables | grep ^sig sig_id: PKCS#7 signer: Build time autogenerated kernel key sig_key: 6D:97:F7:E0:E3:5E:DD:23:6A:0F:B7:E7:57:F1:51:88:FD:C7:1A:3A sig_hashalgo: sha512 signature: 8A:6F:6F:31:72:E5:22:7D:75:46:B9:DD:C4:0A:E9:B2:A9:C4:8D:71:
Należy mieć na uwadze, że tego typu „szybkie” rozwiązanie powinno być stosowane na systemach, które nie przewidują ładowania dodatkowych modułów pochodzących spoza oficjalnego jądra systemu dostarczonego przez dystrybucję. Wymuszenie rygorystycznego trybu powoduje użycie autowygenerowanych kluczy, do których nie mamy już dostępu. Dlatego nie mamy możliwości wykorzystania ich do podpisania zewnętrznych, zaufanych modułów. W tym celu należy stworzyć i wykorzystać własne klucze kryptograficzne.
Więcej informacji: Signed kernel module support, Signed kernel modules, Kernel module signing facility, How to Use Signed Kernel Modules
Poprzedni wpis Brak nowszych postów