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)
(http://www.tcpipguide.com/free/diagrams/tcpopen3way.png (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:
(//http://img250.imageshack.us/img250/5602/synscanco2.png)
Informações sobre outros métodos:
http://nmap.org/nmap_doc.html (http://nmap.org/nmap_doc.html)
Bye
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
Isso sim é um verdadeiro topic.
Parabens!!!