[Source] TCP SYN Scanner - Linux - C

Started by Dark_Side, 19 de January , 2008, 06:55:33 AM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

Dark_Side

Hi,


Existem diferentes métodos de implementação para um scanner de portas. Um dos exemplos mais comuns é o TCP Connect Scan, que consiste em tentar estabelecer uma conexão TCP em uma determinada porta do host alvo. Se a conexão for feita com êxito, a porta utilizada é marcada como aberta. O trecho de código abaixo ilustra a técnica:



// ...

int sock;
struct sockaddr_in addr;

// ...

addr.sin_family  =  AF_INET;
addr.sin_addr.s_addr = inet_addr("xxx.xxxx.xxx.xx");

while(PortaInicial < PortaFinal)
 {

  sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
 
  addr.sin_port = htons(PortaInicial);
 
  if(connect(sock, (struct sockaddr*)&addr, sizeof(addr)) == 0)
    {
      printf("Porta %d aberta.",PortaInicial);     
      PortaInicial++;
      closesocket(sock);
    }

 
 }

//...



Neste caso, cada chamada à função connect() irá gerar o processo "Three Way Handshake", isto é, as etapas necessárias para se estabelecer uma conexão completa entre dois hosts. Como não sou nada bom em desenhos, achei o seguinte esquema para exemplificar:




(http://www.tcpipguide.com/free/diagrams/tcpopen3way.png)


Como se nota, utilizando este metódo, o cliente sempre envia o último pacote ACK para completar a conexão. E mais, ao chamar a função closesocket(), outros pacatoes com a flags RST/FIN serão enviados para o alvo, para finalizar a conexão. Isso irá se repetir enquanto o programa estiver executando as instruções dentro do loop.

Entretanto, é possivel construir um scanner que ignore algumas etapas desse processo, tornando a busca mais rápida. O objetivo deste tópico é justamente abordar como fazê-lo usando o TCP Syn Scan como método. A diferença principal entre ele e o TCP Connect, é que não é necessário enviar o último pacote ACK para completar a conexão, já que, se o alvo responder com um pacote contendo as flags SYN e ACK, é porque a porta na qual tentamos nos conectar está aberta. Então, tudo o que precisamos fazer é criar pacotes TCP com a flag SYN ligada, o enviarmos para o alvo e, finalmente, aguardar por um pacote com as flags SYN e ACK ligadas.


Utilizando Raw Sockets, podemos tanto criar e enviar quanto receber pacotes TCP para posteriormente filtrá-los.
Diferentemente do Windows, no Linux, podemos utilizar tais sockets sem algumas restrições...

Pois bem, abaixo segue o código em C, de um TCP Syn Scanner:

protohdr.

#pragma pack(push,1)

// Cabeçalho ETHERNET
struct ethhdr
{
unsigned char  eth_dst[6];
unsigned char  eth_src[6];
unsigned short eth_type;
};


// Cabeçalho IP
struct iphdr
{
    unsigned char  ip_hl:4, ip_v:4;
    unsigned char  ip_tos;
    unsigned short ip_len;
    unsigned short ip_id;
    unsigned short ip_off;
    unsigned char  ip_ttl;
    unsigned char  ip_p;
    unsigned short ip_sum;
    unsigned long  ip_src;
    unsigned long  ip_dst;
};


// Cabeçalho PSEUDO
struct psdhdr
{
    unsigned long  saddr, daddr;
    unsigned char  zero, protocol;
    unsigned short len;
};


// Cabeçalho TCP
struct tcphdr
 {
    unsigned short th_sport;
    unsigned short th_dport;
    unsigned int   th_seq;
    unsigned int   th_ack;
    unsigned char  th_x2:4, th_off:4;
    unsigned char  th_flags;
    unsigned short th_win;
    unsigned short th_sum;
    unsigned short th_urp;
};


// Cabeçalho UDP
struct udphdr
{
    unsigned short uh_sport;
    unsigned short uh_dport;
    unsigned short uh_len;
    unsigned short uh_sum;
};


// Cabeçalho ICMP
struct icmphdr
{
    unsigned char  ih_code;
    unsigned char  ih_type;
    unsigned short ih_sum;
    unsigned short ih_id;
    unsigned short ih_seq;
};


#pragma pack(pop)

#define IPTYPE htons(0x0800);

#define ETHTAM  sizeof(struct ethhdr)   // Tamanho da estrutura ETHERNET
#define IPTAM   sizeof(struct iphdr)    // Tamanho da estrutura IP
#define PSTAM   sizeof(struct psdhdr)   // Tamanho da estrutura PSEUDO
#define ICMPTAM sizeof(struct icmphdr)  // Tamanho da estrutura ICMP
#define TCPTAM  sizeof(struct tcphdr)   // Tamanho da estrutura TCP
#define UDPTAM  sizeof(struct udphdr)   // Tamanho da estrutura UDP
 

#define MAC_ORIGEM_PADRAO   "\x00\x00\x00\x00\x00\x00"
#define MAC_DESTINO_PADRAO  "\xFF\xFF\xFF\xFF\xFF\xFF"

// Função para cálculo de checksum
unsigned short cksum(unsigned short * buffer,int len)
{
 unsigned long cksum = 0;
 
    while(len > 1)
    {
        cksum += *buffer++;
        len   -= sizeof(unsigned short);
    }
 
    if(len)
      cksum += *(unsigned char*)buffer;

    cksum = (cksum >> 16) + (cksum & 0xffff);
    cksum += (cksum >>16);

 return (unsigned short)(~cksum);

}

scan.c
#include <stdio.h>       
#include <stdlib.h>     
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <net/if.h>
#include <ifaddrs.h>
#include <netdb.h>
#include <fcntl.h>
#include <errno.h>


#include "protohdr.h"   

#define MAX_INTERFACES 16

////////////////////////////////////////////////////////////////////////////////

void MostrarErro(const char * msg)
{
 
   printf("\033[31;1m[-] %s\n    ID: %d\n\033[0m",msg,errno);
 
}

////////////////////////////////////////////////////////////////////////////////

void MostrarStatus(const char * msg)
{

   printf("\033[32;1m[+] %s\n\033[0m",msg);

}

////////////////////////////////////////////////////////////////////////////////

int ObterInterface(unsigned long * RetIP)
{
     
 struct ifaddrs  * addrs, * ifa;
 struct sockaddr_in * addr;
 unsigned long Interfaces[MAX_INTERFACES];

 unsigned char op;
 int NumInterfaces = 0;

   if(getifaddrs(&addrs) != 0)
    return 0;

  puts("[*] Interfaces disponiveis:\n");
 
  for (ifa = addrs; ifa != NULL && NumInterfaces < MAX_INTERFACES; ifa = ifa->ifa_next)
     {
       if (ifa->ifa_addr == NULL) continue;
       if ((ifa->ifa_flags & IFF_UP) == 0) continue;

       if (ifa->ifa_addr->sa_family == AF_INET)
        {
          addr = (struct sockaddr_in *)(ifa->ifa_addr);
          Interfaces[NumInterfaces] = addr->sin_addr.s_addr;
          printf("[%d] %s\n",NumInterfaces++,inet_ntoa(addr->sin_addr));
       
        }
    }
 
   freeifaddrs(addrs);
 
   if(!NumInterfaces)
     return 0;

   do
    {
      printf("\n[*] Escolha uma interface (0-%d): ",NumInterfaces-1);
      op = getchar();
    }
   while(op < '0' || op >= NumInterfaces + '0');
 

  *RetIP = Interfaces[op - '0'];
 
  puts("\n");

return 1;
}

////////////////////////////////////////////////////////////////////////////////


int ConfigurarSniffer(unsigned long IP)
{
     
 int sock;
 struct sockaddr_in InAddr;
 unsigned long on = 1;
   
   if((sock = socket(AF_INET,SOCK_RAW,IPPROTO_TCP)) < 0)
     return 0;

   InAddr.sin_family       = AF_INET;
   InAddr.sin_port         = htons(0);
   InAddr.sin_addr.s_addr  = IP;
   
   
   if(bind(sock,(struct sockaddr*)&InAddr,sizeof(InAddr)) < 0)
     return 0;
   
   if(fcntl(sock,F_SETFL,O_NONBLOCK) == -1)
     return 0;

  return sock;

}

////////////////////////////////////////////////////////////////////////////////

int CriarSocket()
{
       
  int sock;
  int opt = 1;

     if((sock = socket(AF_INET,SOCK_RAW,IPPROTO_RAW)) < 0)
        return 0;
     
     if(setsockopt(sock,IPPROTO_IP,IP_HDRINCL,(const char*)&opt,sizeof(opt)) < 0)
        return 0;

     
  return sock; 
}

////////////////////////////////////////////////////////////////////////////////

void CriarPacote(unsigned char * buffer,  unsigned long  IPLocal,
                 unsigned long IPDestino, unsigned short PortaDestino)
{
 
 struct iphdr  * ip;
 struct psdhdr * psd;
 struct tcphdr * tcp;
 
 ip  = (struct iphdr *)(buffer);
 psd = (struct psdhdr*)(buffer + IPTAM);
 tcp = (struct tcphdr*)(buffer + IPTAM + PSTAM);

      ip->ip_hl   =  5;
      ip->ip_v    =  4;
      ip->ip_tos  =  0;
      ip->ip_len  =  htons(IPTAM + TCPTAM);
      ip->ip_id   =  htons(rand());
      ip->ip_off  =  0;
      ip->ip_ttl  =  255;
      ip->ip_p    =  IPPROTO_TCP;
      ip->ip_sum  =  0;
      ip->ip_src  =  IPLocal;
      ip->ip_dst  =  IPDestino;
 
      ip->ip_sum    =  cksum((unsigned short *)ip, IPTAM);
     

      psd->saddr    = ip->ip_src;
      psd->daddr    = ip->ip_dst;
      psd->protocol = ip->ip_p;
      psd->zero     = 0;
      psd->len      = htons(TCPTAM); 
   
   
      tcp->th_sport = htons (rand() % 65536);
      tcp->th_dport = htons(PortaDestino);
      tcp->th_seq   = htonl(rand());
      tcp->th_ack   = 0;
      tcp->th_x2    = 0;
      tcp->th_off   = 5;
      tcp->th_flags = 0x2;
      tcp->th_win   = htons(rand() % 65536);
      tcp->th_sum   = 0;
      tcp->th_urp   = 0;
 
      tcp->th_sum   = cksum((unsigned short *)psd,PSTAM + TCPTAM);
   
      memcpy(psd, tcp, TCPTAM);
 
}

////////////////////////////////////////////////////////////////////////////////

int main(int argn, char ** arg)
{
   int InSock, OutSock;
 
   struct hostent *   host;
   struct sockaddr_in destino;
   
   unsigned char * pacote = NULL;
   unsigned char   buffer[8192];
   
   unsigned short PortaInicial, PortaFinal;
   unsigned long  IPLocal;
   
   unsigned long sec = 0, msec = 0;

   struct iphdr  * ip;
   struct tcphdr * tcp;
   
   fd_set con;
   struct timeval tm;
   
   char * p;
   int i;
   
     
     system("clear");

     if(argn != 5)
      {     
        printf ("\033[355555;1mUso: scan <alvo> <porta-inicial> <porta-final> <tempo> \n\n\033[0m");
        return 1; 
      }

     MostrarStatus("Obtendo interfaces...");

     if(!ObterInterface(&IPLocal))
      {
       MostrarErro("Erro ao obter a lista de interfaces.");
       return 1;
      }
     

     InSock  = ConfigurarSniffer(IPLocal);
     OutSock = CriarSocket();

 
     MostrarStatus("Criando sockets...");
     
     if(!InSock || !OutSock)
      {
        MostrarErro("Erro ao criar sockets.");
        return 1;
      }
     
     host = gethostbyname(arg[1]);

     MostrarStatus("Resolvendo hostname...");
     
     if(!host)
     {
       MostrarErro("Ocorreu um erro ao resolver o host.");
       return 1;
     }
   
     PortaInicial = (unsigned short)atol(arg[2]);
     PortaFinal   = (unsigned short)atol(arg[3]);
     
     sec  = atol(arg[4]);
     
     p = strchr(arg[4],'.');
     
     if(p++ && *p >= '0' && *p <= '9')
       msec = (long)(*p - '0') * 100000;

       
     if(PortaInicial == 0 || PortaFinal == 0 || PortaInicial > PortaFinal)
      {
       PortaInicial = 1;
       PortaFinal   = 1024;
      }
 
     destino.sin_family = AF_INET;
     destino.sin_port   = htons(PortaInicial);
     destino.sin_addr   = *(struct in_addr*)host->h_addr;
     
     MostrarStatus("Alocando pacote...");

     if(!(pacote = (unsigned char*)malloc(IPTAM + PSTAM + TCPTAM)))
      {
        MostrarErro("Erro ao alocar pacote.");
        return 1;
      }   

     printf("\033[36;1m[+] Escaneando portas de %d a %d...\n\n\033[0m",PortaInicial, PortaFinal);
   
     while(PortaInicial <= PortaFinal && PortaInicial > 0)
      {
     
        CriarPacote(pacote, IPLocal, destino.sin_addr.s_addr, PortaInicial);
 
        if(sendto(OutSock,pacote,IPTAM + TCPTAM,0,(struct sockaddr*)&destino,sizeof(destino)) < 0)
         {
           MostrarErro("Erro ao enviar pacote.");
           break;
         }
 
        FD_ZERO(&con);
        FD_SET(InSock,&con);
       
        tm.tv_sec  = sec;
        tm.tv_usec = msec;   
     
i = select(OutSock+1,&con,0,0,&tm);

        if(i < 0)
         {
           MostrarErro("Erro ao executar select().");
           break;
         }
        else if (i > 0 && FD_ISSET(InSock,&con))
         {
     
          recv(InSock,buffer,sizeof(buffer),0);
       
          ip = (struct iphdr*)buffer;
       
          if(ip->ip_src != destino.sin_addr.s_addr)
            continue;
       
          tcp = (struct tcphdr*)(buffer + IPTAM);
       
          if(tcp->th_sport == htons(PortaInicial) && tcp->th_flags == 0x12)
            printf("    [*] Porta %d aberta\n",PortaInicial);

}     
     
     PortaInicial++;
   
   }
 
 
   putchar('\n');
   
   MostrarStatus("Fechando sockets e liberando pacote...");
 
   close(InSock);
   close(OutSock);   
   free(pacote);     
   MostrarStatus("Terminado.");
 return 0;

}


Obs:: É necessário ter privilégios root para utilizar raw sockets.

Ah, embora as estruturas de cabeçalho (IP, TCP, etc) já estejam definidas em headers nativos (netinet/ip.h, netinet/tcp.h,...), optei por utilizar um header próprio pois o mesmo pode ser aplicado tanto em Linux quanto em Windows, ficando mais fácil portar aplicativos que utilizem tais estruturas ;P

Imagem de exemplo:




Informações sobre outros métodos:

http://nmap.org/nmap_doc.html

Bye ;)

whit3_sh4rk

Muito foda velho.. entendi apenas antes do source do TCP Syn Scanner, porque aí já tem que ter um conhecimento bom em C que eu não tenho.
Interessante para quem ainda não tem conhecimento sobre a comunicação TCP(S/S+A/A).
Quem estuda está estudando sockets em C, isso é uma maravilha.

Ponto positivo.

[]s

keyhell

Isso sim é um verdadeiro topic.

Parabens!!!
Prefiro morrer de pé a viver sempre ajoelhado.
(Ernesto "Che" Guevara)