J'ai appris récemment sur les rootkits et j'ai remarqué ces techniques hooking
que les rootkits kernel-land utilisent pour effectuer des actions malveillantes.
Une opération de raccordement typique consisterait à se connecter à un appel système légitime, puis à remplacer l'action légitime par l'action malveillante avant de réellement appeler l'action légitime.
Mais si tel est le cas, pourquoi ne pas rendre la table d'appels système non modifiable dès le départ?
La table syscall est en lecture seule, et a été depuis le noyau 2.6.16 . Cependant, un rootkit du noyau a la capacité de le rendre à nouveau accessible en écriture. Il suffit d'exécuter une fonction comme celle-ci* avec la table comme argument:
static void set_addr_rw(const unsigned long addr)
{
unsigned int level;
pte_t *pte;
pte = lookup_address(addr, &level);
if (pte->pte &~ _PAGE_RW)
pte->pte |= _PAGE_RW;
local_flush_tlb();
}
Cela modifie les autorisations 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 CR , et réactive les interruptions.
Alors pourquoi est-il marqué en lecture seule s'il est si facile à désactiver? 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 une vulnérabilité supplémentaire pour marquer les pages comme inscriptibles (ou désactiver complètement la protection en écriture). 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 la détection de écrasements accidentels causés par des bogues provoquant un crash système catastrophique et irrécupérable.
* 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 CR0.WP
dans ASM est cependant garanti de fonctionner sur tous les systèmes x86 quelle que soit la version du noyau.
Comme indiqué par la forêt, Linux moderne ne le permet pas, mais il est facile de le remplacer.
Cependant, historiquement, cela était utile (et peut-être l'est toujours) à des fins de sécurité: correction à 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 pas absolument besoin (ptrace
était vraiment courante à l'époque), j'écrivais un module de noyau pour écraser la fonction adresse dans la table syscall avec l'adresse d'une fonction qui vient d'effectuer return -ENOSYS;
. Cela a éliminé la surface d'attaque jusqu'à ce qu'un noyau amélioré soit disponible. Pour certains appels système douteux, je n'avais pas besoin de vulnérabilités répétées, je l'ai simplement fait à titre préventif pour eux et laissé le module activé tout le temps.