Vous pouvez vérifier s'ils sont en lecture seule en recherchant les symboles du noyau. Le "R" signifie en lecture seule.
$ grep sys_call_table /proc/kallsyms
0000000000000000 R sys_call_table
0000000000000000 R ia32_sys_call_table
0000000000000000 R x32_sys_call_table
Alors ils sont en lecture seule, et ce depuis le noyau 2.6.16. Cependant, un rootkit du noyau a la capacité de les rendre à nouveau accessibles en écriture. Tout ce qu'il a à faire est d'exécuter une fonction comme celle-ci en mode noyau (directement ou via des gadgets ROP suffisamment flexibles, dont il y en a beaucoup) avec chaque adresse comme argument :
static void set_addr_rw(const unsigned long addr)
{
unsigned int level;
pte_t *pte = lookup_address(addr, &level);
if (pte->pte &~ _PAGE_RW)
pte->pte |= _PAGE_RW;
local_flush_tlb();
}
Cela modifie les permissions de la table syscall et permet de la modifier. Si cela ne fonctionne pas pour une raison quelconque, la protection en écriture dans le noyau peut être globalement désactivée avec l'ASM suivant :
cli
mov %cr0, %eax
and $~0x10000, %eax
mov %eax, %cr0
sti
Cela désactive les interruptions, désactive le bit WP (Write-Protect) dans CR0 et réactive les interruptions. L'utilisation de l'assemblage permet que cela fonctionne malgré write_cr0(read_cr0() & ~0x10000)
échec en raison de la fonction prédéfinie d'écriture sur CR0 épinglant maintenant les bits sensibles. Assurez-vous cependant de réactiver WP après !
Alors pourquoi est-il marqué en lecture seule s'il est si facile à désactiver ? L'une des raisons est qu'il existe des vulnérabilités qui permettent de modifier la mémoire du noyau mais pas nécessairement d'exécuter directement du code. En marquant les zones critiques du noyau en lecture seule, il devient plus difficile de les exploiter sans trouver un supplémentaire vulnérabilité pour marquer les pages comme inscriptibles (ou désactiver complètement la protection en écriture). Maintenant, cela ne fournit pas une sécurité très forte, donc la principale raison pour laquelle il est marqué en lecture seule est de faciliter l'arrêt accidentel écrase de provoquer un plantage catastrophique et irrécupérable du système.
* L'exemple particulier donné concerne un processeur x86_64. Le premier tableau concerne les appels système en mode natif 64 bits (x64). Le second est pour les appels système en mode 32 bits (IA32). Le troisième est pour l'ABI d'appel système x32 rarement utilisé qui permet aux programmes d'utiliser toutes les fonctionnalités du mode 64 bits (par exemple, SSE au lieu de x87 pour les opérations en virgule flottante) tout en utilisant des pointeurs et des valeurs 32 bits.
† L'API interne du noyau change tout le temps, donc cette fonction exacte peut ne pas fonctionner sur les noyaux plus anciens ou plus récents. Désactivation globale de CR0.WP
dans ASM, cependant, il est garanti de fonctionner sur tous les systèmes x86, quelle que soit la version du noyau.
Comme indiqué par forest, Linux moderne ne le permet pas, mais il est facile de passer outre.
Cependant, historiquement, il était utile (et l'est peut-être encore) à des fins de sécurité :correctifs à chaud contre les vulnérabilités. Dans les années 1990 et au début des années 2000, chaque fois qu'une nouvelle vulnérabilité était annoncée pour un appel système dont je n'avais absolument pas besoin (ptrace
était très courant à l'époque), j'écrirais un module noyau pour écraser l'adresse de la fonction dans la table syscall avec l'adresse d'une fonction qui vient d'exécuter return -ENOSYS;
. Cela a éliminé la surface d'attaque jusqu'à ce qu'un noyau mis à niveau soit disponible. Pour certains appels système douteux dont je n'avais pas besoin et qui avaient des vulnérabilités à plusieurs reprises, je l'ai juste fait de manière préventive pour eux et j'ai laissé le module activé tout le temps.