=-|================================================-{ www.enye-sec.org }-====| =-[ Bricando com sockets (port scanning) ]-==================================| =-|==========================================================================| =-[ por Pepelux ]-=====================-[13/11/2007]-=| =-|==========================================================================| =-[ Traduzido por: Th1nk3r ]=======-[19/09/2008]-=| =-|==========================================================================| O Texto na sua versão original pode ser encontrado em: http://www.milw0rm.com/papers/224 NOTA DO TRADUTOR (Note of Translator): ============================================================================== Todo o conteúdo apresentado neste documento é de responsabilidade do escritor, sendo que eu apenas me dei o trabalho de traduzi-lo. Perdoem-me pelos poucos erros ortográficos e gramaticais, pois não tive tempo de fazer uma revisão completa. [:p] Dedico a tradução à todos meus amigos. Th1nk3r ============================================================================== ------[ 0.- Índice ] 0.- Índice 1.- Breve introdução 1.1.- Scan aberto (Open Scan) 1.2.- Scan semi-aberto (Half Open Scan) 1.3.- Stealth Scan 2.- Brincando com sockets 2.1.- TCP Sockets 2.2.- UDP / ICMP Sockets 3.- Detecção de sistemas operacionais 3.1.- Alguns testes 4.- Arquivos 5.- Referências 6.- Finalizando ------[ 1.- Breve Introdução ] Por muitos tempo nossos métodos de escanear portas foi usando o telnet, fazendo manualmente. Hoje as pessoas usam os mais sofisticados programas que possuem varios métodos para escanear um faixa de IP procurando por uma grande quantidade de portas. Eu estarei escrevendo de forma rapida os diferentes métodos de scanning, pois um conteúdo que vá mais fundo pode ser encontrado na Internet. Além disso, ao fim desse arquivo você pode encontrar dicas de como aprender sockets de maneira divertida. ------[ 1.1.- Scan aberto (Open Scan) ] Conhecida como TCP Scan e normalmente usada para programar sockets, essa técnica é antiga e trabalha fazendo uma conexão completa com o servidor. É como se conectar a cada porta de um servidor através do telnet, mas automaticamente. Com isso se faz uma conexão com 3 pacotes (conhecida como three-way-handshake [aperto-de-mao em tres passos]): Para portas abertas temos: Cliente ----> SYN ----> <---- SYN/ACK <---- Servidor Cliente ----> ACK ----> Para portas fechadas temos: Cliente ----> SYN ----> <---- RST <---- Servidor Vantagens : Muito fácil de programar. Desvantagens : Muito fácil de detectar e gera logs a cada conexão. ------[ 1.2.- Scan semi-aberto (Half Open Scan)] Ésta tecnica trabalha fazendo com que a conexão seja iniciada, mas não completada. Envia-se um SYN, mas não envia-se um ACK se a porta estiver aberta. Usando RAW sockets nós podemos programar manualmente os cabeçalhos do socket e enviar somente o inicio da conexão. Logo veremos isso melhor com alguns exemplos. ------[ 1.3.- Stealth Scan ] É similar aos métodos anteriores, mas envia-se outra flag, ou uma combinação de flags. O mais conhecido e usado pelo nmap é: SYN SYN+ACK FIN ACK NULL (all flags with 0) XMAS (FIN=URG=PSH=1) Nos exemplos que virão nós veremos isso claramente. ------[ 2.- Brincando com sockets ] É a hora de se divertir. Nós enviaremos diferentes pacotes alterando as flags e estudando os resultados. Nos testes eu usei dois computadores: Cliente : 192.168.2.5 (Linux Debian - kernel 2.6.18.1) Servidor : 192.168.2.7 (Linux Debian - kernel 2.6.21.2) Portas abertas no servidor: 22(TCP), 80(TCP), 111(UDP) ------[ 2.1.- TCP Sockets ] Para examinar as portas TCP, nós usaremos um programa para enviar e receber RAW Sockets, o que nos permitirá escolher os valores dos flags. - sendsock.c -> Usage: ./sendsocket [s|a|r|p|u|f] [-x host_source] -d host_destination -c port -s SYN flag enabled -a ACK flag enabled -r RST flag enabled -p PSH flag enabled -u URG flag enabled -f FIN flag enabled Enviar SYN (half open connection) --------------------------------- Consiste em fazer uma semi conexão, enviando um SYN, e quando obtermos qualquer resposta (ACK+SYN ou RST) enviaramos um RST para finalizar a conexão (usando RAW Sockets, o pacote RST é enviado automaticamente pelo kernel). A idéia original consiste em enviar um SYN e se o servidor responder com um ACK+SYN, nós enviamos um pacote RST dizendo que nós não queremos fazer uma conexão. Como a conexão não foi completada o servidor nao salva nenhum log. :-) Atualmente muitos computadores podem detectar essa tecnica e salvar um log ou bloquear esse tipo de ataque com um firewall. Vantangens : Funciona em todos os sistemas operacionais e portas, se não existir um firewall. Desvantagens : É muito fácil de detectar e gerar logs. flashgordon# ./sendsocket -x 192.168.2.5 -d 192.168.2.7 -c 80 -s DADOS ENVIADOS --------- | SYN | ACK | RST | PSH | URG | FIN | TTL | Window | 1 | 0 | 0 | 0 | 0 | 0 | 64 | 65535 seq=846930886 - ack_seq=0 - doff=5 - check=39553 - ID=27032 DADOS RECEBIDOS ------------- | SYN | ACK | RST | PSH | URG | FIN | TTL | Window | 1 | 1 | 0 | 0 | 0 | 0 | 64 | 53270 seq=-1378930670 - ack_seq=863708102 - doff=6 - check=8049 - ID=0 Porta aberta. Nós obtemos um SYN+ACK e Window<>0 flashgordon# ./sendsocket -x 192.168.2.5 -d 192.168.2.7 -c 23 -s DADOS ENVIADOS --------- | SYN | ACK | RST | PSH | URG | FIN | TTL | Window | 1 | 0 | 0 | 0 | 0 | 0 | 64 | 65535 seq=846930886 - ack_seq=0 - doff=5 - check=54145 - ID=27032 DADOS RECEBIDOS ------------- | SYN | ACK | RST | PSH | URG | FIN | TTL | Window | 0 | 1 | 1 | 0 | 0 | 0 | 64 | 0 seq=0 - ack_seq=863708102 - doff=5 - check=49281 - ID=0 Porta fechada. Nós obtemos um ACK+RST e Window=0. Enviar ACK ---------- Este método apenas tem efeito em alguns DBS antigos e Sistemas Operacionais Unix. Consiste em checar o campo Window. Se obter um Window=0 a porta está aberta e se obter um Window<>0 a porta está fechada. Vantangens : É dificil de ser detectado. Desvantangens : Não funciona em todos os SOs. flashgordon# ./sendsocket -x 192.168.2.5 -d 192.168.2.7 -c 80 -a DADOS ENVIADOS --------- | SYN | ACK | RST | PSH | URG | FIN | TTL | Window | 0 | 1 | 0 | 0 | 0 | 0 | 64 | 65535 seq=846930886 - ack_seq=0 - doff=5 - check=35969 - ID=27032 DADOS RECEBIDOS ------------- | SYN | ACK | RST | PSH | URG | FIN | TTL | Window | 0 | 0 | 1 | 0 | 0 | 0 | 64 | 0 seq=0 - ack_seq=0 - doff=5 - check=61122 - ID=0 flashgordon# ./sendsocket -x 192.168.2.5 -d 192.168.2.7 -c 23 -a DADOS ENVIADOS --------- | SYN | ACK | RST | PSH | URG | FIN | TTL | Window | 0 | 1 | 0 | 0 | 0 | 0 | 64 | 65535 seq=846930886 - ack_seq=0 - doff=5 - check=50561 - ID=27032 DADOS RECEBIDOS ------------- | SYN | ACK | RST | PSH | URG | FIN | TTL | Window | 0 | 0 | 1 | 0 | 0 | 0 | 64 | 0 seq=0 - ack_seq=0 - doff=5 - check=10179 - ID=0 Nesse caso meu linux não é vulnerável e nós obtemos o mesmo resultado resultados com a porta aberta ou fechada. Enviar RST ---------- Este ataque apenas tem efeito em alguns sistemas e nem sempre é eficaz. Nesse caso nós obtemos apenas um ACK ou se nós nao obtemos resposta, a porta está aberta e se nós obtermos um RST, a porta está fechada. Vantangens : É dificil de ser detectado. Desvantangens : Não funciona em todos os SOs e nem em todas as portas. flashgordon# ./sendsocket -x 192.168.2.5 -d 192.168.2.7 -c 80 -r DADOS ENVIADOS --------- | SYN | ACK | RST | PSH | URG | FIN | TTL | Window | 0 | 0 | 1 | 0 | 0 | 0 | 64 | 65535 seq=846930886 - ack_seq=0 - doff=5 - check=39041 - ID=27032 DADOS RECEBIDOS ------------- | SYN | ACK | RST | PSH | URG | FIN | TTL | Window | 0 | 1 | 0 | 1 | 0 | 0 | 53 | 37920 seq=1193401395 - ack_seq=908914171 - doff=5 - check=43532 - ID=4979 Porta aberta. Nós obtemos ACK+PSH, TTL<64 e Window<>0. flashgordon# ./sendsocket -x 192.168.2.5 -d 192.168.2.7 -c 23 -r DADOS ENVIADOS --------- | SYN | ACK | RST | PSH | URG | FIN | TTL | Window | 0 | 0 | 1 | 0 | 0 | 0 | 64 | 65535 seq=846930886 - ack_seq=0 - doff=5 - check=53633 - ID=27032 Sem resposta ... Porta fechada. flashgordon# ./sendsocket -x 192.168.2.5 -d 192.168.2.7 -c 22 -r DADOS ENVIADOS | SYN | ACK | RST | PSH | URG | FIN | TTL | Window | 0 | 0 | 1 | 0 | 0 | 0 | 64 | 65535 seq=846930886 - ack_seq=0 - doff=5 - check=53889 - ID=27032 Sem resposta ... A resposta não é confiável pois a porta está aberta. Como nós podemos ver, se obtermos qualquer resposta a porta está aberta e se nós nao obtermos resposta, nós nao podemos saber se a porta está aberta ou fechada. Enviar PSH ---------- Este ataque apenas tem efeito em alguns sistemas e nem sempre é eficaz. Nesse caso se nós obtemos um ACK+PSH e um Windows<>0 ou não obtermos resposta, a porta está aberta e se obtermos um RST, a porta está fechada. Vantagens : É dificil de ser detectado. Desvantagens : Não funciona em todos os SOs. flashgordon# ./sendsocket -x 192.168.2.5 -d 192.168.2.7 -c 80 -p DADOS ENVIADOS --------- | SYN | ACK | RST | PSH | URG | FIN | TTL | Window | 0 | 0 | 0 | 1 | 0 | 0 | 64 | 65535 seq=846930886 - ack_seq=0 - doff=5 - check=38017 - ID=27032 DADOS RECEBIDOS ------------- | SYN | ACK | RST | PSH | URG | FIN | TTL | Window | 0 | 1 | 0 | 1 | 0 | 0 | 53 | 17447 seq=1710733151 - ack_seq=1317887646 - doff=5 - check=32634 - ID=4439 Porta aberta. Nós obtemos um ACK+PSH, TTL<64 e Window<>0. flashgordon# ./sendsocket -x 192.168.2.5 -d 192.168.2.7 -c 23 -p DADOS ENVIADOS --------- | SYN | ACK | RST | PSH | URG | FIN | TTL | Window | 0 | 0 | 0 | 1 | 0 | 0 | 64 | 65535 seq=846930886 - ack_seq=0 - doff=5 - check=52609 - ID=27032 DADOS RECEBIDOS ------------- | SYN | ACK | RST | PSH | URG | FIN | TTL | Window | 0 | 1 | 1 | 0 | 0 | 0 | 64 | 0 seq=0 - ack_seq=846930886 - doff=5 - check=49537 - ID=0 Porta fechada. Nós obtemos RST, TTL=64 e Window=0. flashgordon# ./sendsocket -x 192.168.2.5 -d 192.168.2.7 -c 22 -p DADOS ENVIADOS --------- | SYN | ACK | RST | PSH | URG | FIN | TTL | Window | 0 | 0 | 0 | 1 | 0 | 0 | 64 | 65535 seq=846930886 - ack_seq=0 - doff=5 - check=52865 - ID=27032 Sem resposta ... porta fechada. Enviar URG ---------- Este ataque apenas tem efeito em alguns sistemas e nem sempre é eficaz. Nesse caso se obtermos um ACK sem um RST ou se nao obtermos resposta, a porta está aberta. Se obtermos um RST, a porta está fechada. Vantagens : É dificil de ser detectado. Desvantagens: Funciona no meu Linux mas pode nao funcionar em alguns sistemas. flashgordon# ./sendsocket -x 192.168.2.5 -d 192.168.2.7 -c 80 -u DADOS ENVIADOS --------- | SYN | ACK | RST | PSH | URG | FIN | TTL | Window | 0 | 0 | 0 | 0 | 1 | 0 | 64 | 65535 seq=846930886 - ack_seq=0 - doff=5 - check=31873 - ID=27032 DADOS RECEBIDOS ------------- | SYN | ACK | RST | PSH | URG | FIN | TTL | Window | 0 | 1 | 0 | 1 | 0 | 0 | 53 | 37920 seq=-716127216 - ack_seq=1741636632 - doff=5 - check=41524 - ID=59663 Porta aberta. Obtemos ACK+PSH, TTL<64 e Window<>0. flashgordon# ./sendsocket -x 192.168.2.5 -d 192.168.2.7 -c 23 -u DADOS ENVIADOS --------- | SYN | ACK | RST | PSH | URG | FIN | TTL | Window | 0 | 0 | 0 | 0 | 1 | 0 | 64 | 65535 seq=846930886 - ack_seq=0 - doff=5 - check=46465 - ID=27032 DADOS RECEBIDOS ------------- | SYN | ACK | RST | PSH | URG | FIN | TTL | Window | 0 | 1 | 1 | 0 | 0 | 0 | 64 | 0 seq=0 - ack_seq=846930886 - doff=5 - check=49537 - ID=0 Porta fechada. Obtemos ACK+RST, TTL=64 e Window=0. flashgordon# ./sendsocket -x 192.168.2.5 -d 192.168.2.7 -c 22 -u DADOS ENVIADOS --------- | SYN | ACK | RST | PSH | URG | FIN | TTL | Window | 0 | 0 | 0 | 0 | 1 | 0 | 64 | 65535 seq=846930886 - ack_seq=0 - doff=5 - check=3968 - ID=27032 Sem resposta: Porta aberta. Enviar FIN ---------- Este ataque apenas tem efeito em alguns sistemas e nem sempre é eficaz. No caso se obtermos um ACK ou se nao obtermos resposta, a porta está aberta. Se obtermos um RST a porta está fechada. Em outros sistemas voce poderá notar a diferença na flag RST. Se está ativada a porta está fechada e se o valor de RST é zero a porta está aberta. Vantagens : É dificil de detectar. Desvantagens: Nao funciona em todos os sistemas operacionais porque é baseada em um bug. flashgordon# ./sendsocket -x 192.168.2.5 -d 192.168.2.7 -c 80 -f DADOS ENVIADOS --------- | SYN | ACK | RST | PSH | URG | FIN | TTL | Window | 0 | 0 | 0 | 0 | 0 | 1 | 64 | 65535 seq=846930886 - ack_seq=0 - doff=5 - check=39809 - ID=27032 DADOS RECEBIDOS ------------- | SYN | ACK | RST | PSH | URG | FIN | TTL | Window | 0 | 1 | 0 | 1 | 0 | 1 | 53 | 37920 seq=-1722694640 - ack_seq=1741636632 - doff=5 - check=39751 - ID=56001 Porta aberta. Nós obtemos ACK+PSH+FIN,TTL<64 e Window<>0. flashgordon# ./sendsocket -x 192.168.2.5 -d 192.168.2.7 -c 23 -f DADOS ENVIADOS --------- | SYN | ACK | RST | PSH | URG | FIN | TTL | Window | 0 | 0 | 0 | 0 | 0 | 1 | 64 | 65535 seq=846930886 - ack_seq=0 - doff=5 - check=54401 - ID=27032 DADOS RECEBIDOS ------------- | SYN | ACK | RST | PSH | URG | FIN | TTL | Window | 0 | 1 | 1 | 0 | 0 | 0 | 64 | 0 seq=0 - ack_seq=863708102 - doff=5 - check=49281 - ID=0 Porta fechada. Obtemos ACK+RST, TTL=64 e Window=0. flashgordon# ./sendsocket -x 192.168.2.5 -d 192.168.2.7 -c 22 -f DADOS ENVIADOS --------- | SYN | ACK | RST | PSH | URG | FIN | TTL | Window | 0 | 0 | 0 | 0 | 0 | 1 | 64 | 65535 seq=846930886 - ack_seq=0 - doff=5 - check=54657 - ID=27032 Sem resposta... Porta aberta. Enviar SYN+ACK -------------- Nós usaremos algumas combinações de flags. Este ataque só funciona em alguns sistemas. Quando funciona, se voce obter qualquer resposta, a porta está aberta e se receber um RST, a porta está fechada. Vantagens : É dificil de detectar. Desvantagens: Não funciona em todos os sistemas. flashgordon# ./sendsocket -x 192.168.2.5 -d 192.168.2.7 -c 80 -s -a DADOS ENVIADOS --------- | SYN | ACK | RST | PSH | URG | FIN | TTL | Window | 1 | 1 | 0 | 0 | 0 | 0 | 64 | 65535 seq=846930886 - ack_seq=0 - doff=5 - check=35457 - ID=27032 DADOS RECEBIDOS ------------- | SYN | ACK | RST | PSH | URG | FIN | TTL | Window | 0 | 0 | 1 | 0 | 0 | 0 | 64 | 0 seq=0 - ack_seq=0 - doff=5 - check=61122 - ID=0 flashgordon# ./sendsocket -x 192.168.2.5 -d 192.168.2.7 -c 23 -s -a DADOS ENVIADOS --------- | SYN | ACK | RST | PSH | URG | FIN | TTL | Window | 1 | 1 | 0 | 0 | 0 | 0 | 64 | 65535 seq=846930886 - ack_seq=0 - doff=5 - check=50049 - ID=27032 DADOS RECEBIDOS ------------- | SYN | ACK | RST | PSH | URG | FIN | TTL | Window | 0 | 0 | 1 | 0 | 0 | 0 | 64 | 0 seq=0 - ack_seq=0 - doff=5 - check=10179 - ID=0 No caso nós obtemos o mesmo resultado e nós nao sabemos se porta está aberta ou fechada. Como eu disse, isso tem efeito em apenas alguns sistemas. :) Enviar SYN+PSH -------------- Quando nós nao obtemos um RST e Windows<>0, a porta está aberta e com um RST e Window=0, a porta está fechada. Vantagens : É dificil detectar. Desvantagens: Funciona no meu Linux mas nao sei se funciona em outros sistemas. flashgordon# ./sendsocket -x 192.168.2.5 -d 192.168.2.7 -c 80 -s -p DADOS ENVIADOS --------- | SYN | ACK | RST | PSH | URG | FIN | TTL | Window | 1 | 0 | 0 | 1 | 0 | 0 | 64 | 65535 seq=846930886 - ack_seq=0 - doff=5 - check=37505 - ID=27032 DADOS RECEBIDOS ------------- | SYN | ACK | RST | PSH | URG | FIN | TTL | Window | 1 | 1 | 0 | 0 | 0 | 0 | 64 | 53270 seq=1874969709 - ack_seq=863708102 - doff=6 - check=51491 - ID=0 Porta aberta. Nós obtemos um SYN+ACK e Window<>0. flashgordon# ./sendsocket -x 192.168.2.5 -d 192.168.2.7 -c 22 -s -p DADOS ENVIADOS --------- | SYN | ACK | RST | PSH | URG | FIN | TTL | Window | 1 | 0 | 0 | 1 | 0 | 0 | 64 | 65535 seq=846930886 - ack_seq=0 - doff=5 - check=52353 - ID=27032 DADOS RECEBIDOS ------------- | SYN | ACK | RST | PSH | URG | FIN | TTL | Window | 1 | 1 | 0 | 0 | 0 | 0 | 64 | 53270 seq=1445991790 - ack_seq=863708102 - doff=6 - check=52148 - ID=0 Porta aberta. Obtemos SYN+ACK e Window<>0. flashgordon# ./sendsocket -x 192.168.2.5 -d 192.168.2.7 -c 23 -s -p DADOS ENVIADOS --------- | SYN | ACK | RST | PSH | URG | FIN | TTL | Window | 1 | 0 | 0 | 1 | 0 | 0 | 64 | 65535 seq=846930886 - ack_seq=0 - doff=5 - check=52097 - ID=27032 DADOS RECEBIDOS ------------- | SYN | ACK | RST | PSH | URG | FIN | TTL | Window | 0 | 1 | 1 | 0 | 0 | 0 | 64 | 0 seq=0 - ack_seq=863708102 - doff=5 - check=49281 - ID=0 Porta fechada. Obtemos ACK+RST e Window=0. Enviar URG+FIN -------------- Nesse método, se nós nao obtermos reposta, a porta está aberta e com RST, a porta está fechada. Vantagens : É dificil detectar. Desvantagens: Funciona no meu Linux mas nao sei se funciona em outros sistemas. flashgordon# ./sendsocket -x 192.168.2.5 -d 192.168.2.7 -c 80 -u -f DADOS ENVIADOS --------- | SYN | ACK | RST | PSH | URG | FIN | TTL | Window | 0 | 0 | 0 | 0 | 1 | 1 | 64 | 65535 seq=846930886 - ack_seq=0 - doff=5 - check=31617 - ID=27032 Sem resposta... porta aberta. flashgordon# ./sendsocket -x 192.168.2.5 -d 192.168.2.7 -c 23 -u -f DADOS ENVIADOS --------- | SYN | ACK | RST | PSH | URG | FIN | TTL | Window | 0 | 0 | 0 | 0 | 1 | 1 | 64 | 65535 seq=846930886 - ack_seq=0 - doff=5 - check=46209 - ID=27032 DADOS RECEBIDOS ------------- | SYN | ACK | RST | PSH | URG | FIN | TTL | Window | 0 | 1 | 1 | 0 | 0 | 0 | 64 | 0 seq=0 - ack_seq=863708102 - doff=5 - check=49281 - ID=0 Porta fechada. Obtemos um ACK+RST. flashgordon# ./sendsocket -x 192.168.2.5 -d 192.168.2.7 -c 22 -u -f DADOS ENVIADOS --------- | SYN | ACK | RST | PSH | URG | FIN | TTL | Window | 0 | 0 | 0 | 0 | 1 | 1 | 64 | 65535 seq=846930886 - ack_seq=0 - doff=5 - check=46465 - ID=27032 Sem resposta... porta aberta. Enviar URG+FIN+PSH ------------------ Mais conhecida como XMAS scan, quando nós nao obtemos resposta ou nao obtemos um RST, a porta está aberta. Está fechada quando obtemos um RST. Vantagens : É dificil de detectar. Desvantagens: Funciona somente em alguns sistemas. flashgordon# ./sendsocket -x 192.168.2.5 -d 192.168.2.7 -c 80 -p -u -f DADOS ENVIADOS --------- | SYN | ACK | RST | PSH | URG | FIN | TTL | Window | 0 | 0 | 0 | 1 | 1 | 1 | 64 | 65535 seq=846930886 - ack_seq=0 - doff=5 - check=23938 - ID=27032 DADOS RECEBIDOS ------------- | SYN | ACK | RST | PSH | URG | FIN | TTL | Window | 1 | 1 | 0 | 0 | 0 | 0 | 47 | 10262 seq=128745367 - ack_seq=338264191 - doff=10 - check=49414 - ID=0 Porta aberta. Nós obtemos um SYN+ACK. flashgordon# ./sendsocket -x 192.168.2.5 -d 192.168.2.7 -c 22 -p -u -f DADOS ENVIADOS --------- | SYN | ACK | RST | PSH | URG | FIN | TTL | Window | 0 | 0 | 0 | 1 | 1 | 1 | 64 | 65535 seq=846930886 - ack_seq=0 - doff=5 - check=38786 - ID=27032 Sem resposta ... porta fechada. flashgordon# ./sendsocket -x 192.168.2.5 -d 192.168.2.7 -c 22 -u -f DADOS ENVIADOS --------- | SYN | ACK | RST | PSH | URG | FIN | TTL | Window | 0 | 0 | 0 | 1 | 1 | 1 | 64 | 65535 seq=846930886 - ack_seq=0 - doff=5 - check=38530 - ID=27032 DADOS RECEBIDOS ------------- | SYN | ACK | RST | PSH | URG | FIN | TTL | Window | 0 | 1 | 1 | 0 | 0 | 0 | 56 | 0 seq=0 - ack_seq=863708102 - doff=5 - check=43650 - ID=56815 Porta fechada. Nós obtemos um ACK+RST. Enviar nulo ----------- Conhecido como "null scan", ésta técnica consiste em enviar flags com zero. Nesse caso, quando a resposta é um ACK ou se nao obtemos resposta, a porta está aberta e com um RST, a porta está fechada. Vantagens : É dificil de detectar. Desvantagens: Funciona somente em alguns sistemas. flashgordon# ./sendsocket -x 192.168.2.5 -d 192.168.2.7 -c 80 DADOS ENVIADOS --------- | SYN | ACK | RST | PSH | URG | FIN | TTL | Window | 0 | 0 | 0 | 0 | 0 | 0 | 64 | 65535 seq=846930886 - ack_seq=0 - doff=5 - check=40065 - ID=27032 DADOS RECEBIDOS ------------- | SYN | ACK | RST | PSH | URG | FIN | TTL | Window | 0 | 1 | 0 | 0 | 0 | 0 | 53 | 22560 seq=-515721121 - ack_seq=1706581918 - doff=5 - check=55050 - ID=61453 Porta aberta. Nós obtemos ACK. flashgordon# ./sendsocket -x 192.168.2.5 -d 192.168.2.7 -c 23 DADOS ENVIADOS --------- | SYN | ACK | RST | PSH | URG | FIN | TTL | Window | 0 | 0 | 0 | 0 | 0 | 0 | 64 | 65535 seq=846930886 - ack_seq=0 - doff=5 - check=54657 - ID=27032 DADOS RECEBIDOS ------------- | SYN | ACK | RST | PSH | URG | FIN | TTL | Window | 0 | 1 | 1 | 0 | 0 | 0 | 64 | 0 seq=0 - ack_seq=846930886 - doff=5 - check=49537 - ID=0 Porta fechada. Obtemos ACK+RST. flashgordon# ./sendsocket -x 192.168.2.5 -d 192.168.2.7 -c 22 DADOS ENVIADOS --------- | SYN | ACK | RST | PSH | URG | FIN | TTL | Window | 0 | 0 | 0 | 0 | 0 | 0 | 64 | 65535 seq=846930886 - ack_seq=0 - doff=5 - check=54913 - ID=27032 Sem resposta... porta aberta. Enviar SYN+ACK+RST+PSH+URG+FIN ------------------------------ Consiste em enviar todas as flags ativadas (com valores 1). Nesse caso, quando a resposta é um ACK ou se nao obtemos resposta, a porta está aberta. Com um RST a porta está fechada. Vantagens : É dificil de detectar. Desvantagens: Funciona somente em UNIX. flashgordon# ./sendsocket -x 192.168.2.5 -d 192.168.2.7 -c 80 -s -a -r -p -u -f DADOS ENVIADOS --------- | SYN | ACK | RST | PSH | URG | FIN | TTL | Window | 1 | 1 | 1 | 1 | 1 | 1 | 64 | 65535 seq=846930886 - ack_seq=0 - doff=5 - check=23937 - ID=27032 DADOS RECEBIDOS ------------- | SYN | ACK | RST | PSH | URG | FIN | TTL | Window | 0 | 1 | 0 | 1 | 0 | 1 | 53 | 37920 seq=2033962504 - ack_seq=-1093275500 - doff=5 - check=8061 - ID=35468 Porta aberta. Obtemos um ACK+PSH. flashgordon# ./sendsocket -x 192.168.2.5 -d 192.168.2.7 -c 23 -s -a -r -p -u -f DADOS ENVIADOS --------- | SYN | ACK | RST | PSH | URG | FIN | TTL | Window | 1 | 1 | 1 | 1 | 1 | 1 | 64 | 65535 seq=846930886 - ack_seq=0 - doff=5 - check=38529 - ID=27032 Sem resposta... resultado nao valido porque a porta está realmente fechada. flashgordon# ./sendsocket -x 192.168.2.5 -d 192.168.2.7 -c 22 -s -a -r -p -u -f DADOS ENVIADOS --------- | SYN | ACK | RST | PSH | URG | FIN | TTL | Window | 1 | 1 | 1 | 1 | 1 | 1 | 64 | 65535 seq=846930886 - ack_seq=0 - doff=5 - check=38785 - ID=27032 Sem resposta... resultado nao valido porque a porta está aberta. Nesse caso a tecnica nao é valida para nos porque as resposta nao batem. Como eu disse, somente funciona em UNIXs. ------[ 2.2.- UDP / ICMP Sockets ] Para verificar se portas UDP estão aberta nós iremos brincar um pouco mais enviando pacotes. O protocolo UDP nao é orientado em uma conexão como o protocolo TCP, e nós nao podemos esperar por uma resposta depois de enviar um pacote. Por isso, usando somente UDP nós nao podemos saber se uma porta está ou não aberta. Para escanear nós teremos o auxilio de pacotes ICMP. Por que? porque quando voce tenta fazer uma conexão com uma porta UDP o servidor irá responder com um pacote ICMP em caso de erro; justamente um pacote tipo 3 / codigo 3 , que se você ver nos documentos RFC você entenderá: tipo 3 = destino da mensagem inacessível / codigo 3 = porta inacessível. Como nós amamos fazer estas coisas manualmente, nós usaremos 2 programas: - checkicmp.c -> Escuta por conexões ICMP no nosso computador. - sendudp.c -> Envia um pacote UDP em um host/porta. flashgordon# ./checkicmp Waiting data ... Enquanto o programa aguarda, nós abriremos outro terminal e enviaremos em uma porta fechada: flashgordon# ./sendudp 192.168.2.7 100 Sending UDP packet to 192.168.2.7:100 No primeiro terminal nós podemos ver: flashgordon# ./checkicmp Waiting data ... Received: type 3 code 3 Isto nos diz que a porta está fechada... verifique em uma porta aberta para ver o que acontece: flashgordon# ./sendudp 192.168.2.7 111 Sending UDP packet to 192.168.2.7:111 E no outro terminal: flashgordon# ./checkicmp Waiting data ... Received: type 3 code 3 Nada muda (a mensagem) ... como nós nao recebemos qualquer pacote nós podemos dizer que a porta está aberta. Teste outra vez com uma porta fechada: flashgordon# ./sendudp 192.168.2.7 101 Sending UDP packet to 192.168.2.7:101 E no outro terminal: flashgordon# ./checkicmp Waiting data ... Received: type 3 code 3 Received: type 3 code 3 Nós obtemos outro pacote ICMP nos dizendo que a porta 101 está fechada também. ------[ 3.- Detecção de sistemas operacionais ] Nós podemos saber o sistema operacional rodando em um servidor sem fazer uma conexão TCP. Como ocorre com os metodos de scan mostrados anteriormente, servidores diferentes podem responder pacotes diferentes (nós vimos que conexões TCP envia um SYN e espera por um SYN+ACK ou um RST) porque cada sistema pode responder de forma diferente. O mesmo acontece enviando pacotes UDP ou ICMP que nao são típicos para fazer fazer uma conexão padrão. Eu nao mostrarei todas as diferenças entre sistemas operacionais pois isso requer uma grande quantidade de testes, de sistemas operacionais e de tempo. :) .... Além do mais, o scanner nmap tem um ótimo banco de dados de sistemas operacionais detectáveis, baseado no famoso QueSO de Savage. Mas nós iremos brincar um pouco mais com os sockets para ver e entender tudo isso. Nos testes eu usei 4 computadores: Cliente : 192.168.2.5 (Linux Debian - kernel 2.6.18.1) Servidor1: 192.168.2.7 (Linux Debian) Servidor2: 192.168.2.6 (Windows 2000 Server) Servidor3: 192.168.2.8 (Solaris) Porta aberta nos 3 servidores: 80(TCP) Antes de fazer qualquer coisa para entender a detecção de sistemas operacionais você pode brincar com mais headers de cada pacote, mas para nao alterar os programas e como isso é somente um teste, eu usarei a mesma flag como antes. ------[ 3.1.- Alguns testes ] Para fazer os testes eu enviarei um pacote TCP à porta 80 (aberta) com flags SYN+URG+FIN ativadas. Primeiro servidor, sob Linux: flashgordon# ./sendsocket -x 192.168.2.5 -d 192.168.2.7 -c 80 -s -u -f DADOS ENVIADOS --------- | SYN | ACK | RST | PSH | URG | FIN | TTL | Window | 1 | 0 | 0 | 0 | 1 | 1 | 64 | 65535 seq=846930886 - ack_seq=0 - doff=5 - check=31617 - ID=27032 DADOS RECEBIDOS | SYN | ACK | RST | PSH | URG | FIN | TTL | Window | 1 | 1 | 0 | 0 | 0 | 0 | 64 | 6272 seq=24764339 - ack_seq=863708102 - doff=6 - check=32130 - ID=0 Segundo servidor, sob Windows: flashgordon# ./sendsocket -x 192.168.2.5 -d 192.168.2.6 -c 80 -s -u -f DADOS ENVIADOS --------- | SYN | ACK | RST | PSH | URG | FIN | TTL | Window | 1 | 0 | 0 | 0 | 1 | 1 | 64 | 65535 seq=846930886 - ack_seq=0 - doff=5 - check=17292 - ID=27032 DADOS RECEBIDOS | SYN | ACK | RST | PSH | URG | FIN | TTL | Window | 0 | 1 | 0 | 1 | 0 | 0 | 48 | 33820 seq=2043592525 - ack_seq=772288166 - doff=5 - check=30285 - ID=15717 Terceiro servidor, sob Solaris: flashgordon# ./sendsocket -x 192.168.2.5 -d 192.168.2.8 -c 80 -s -u -f DADOS ENVIADOS --------- | SYN | ACK | RST | PSH | URG | FIN | TTL | Window | 1 | 0 | 0 | 0 | 1 | 1 | 64 | 65535 seq=846930886 - ack_seq=0 - doff=5 - check=50815 - ID=27032 DADOS RECEBIDOS | SYN | ACK | RST | PSH | URG | FIN | TTL | Window | 1 | 1 | 0 | 0 | 0 | 0 | 41 | 65535 seq=1684159920 - ack_seq=1278904408 - doff=6 - check=34271 - ID=62243 Se nós analisarmos as diferenças: - Linux : SYN+ACK - TTL=64 - Window= 6272 - Windows: ACK+PSH - TTL=48 - Window=33820 - Solaris: SYN+ACK - TTL=41 - Window=65535 Como eu disse anteriormente, isto é somente um teste para entender como a detecção de sistemas operacionais funciona. Realmente nós temos que alterar mais campos e combinar com os pacotes UDP e ICMP. As diferenças entre Linux, Windows e Solaris é evidente, mas usando mais testes nós podemos ver as diferenças entre diferentes versões de sistemas operacionais, por exemplo entre um Windows 98 e Windows 2000, ou ver diferenças entre cada Distribuição Linux ou Kernels. ------[ 4.- Arquivos ] Programas usados para fazer os testes: --- sendsocket.c --------------------------8<--------------------------------- // sendsocket.c // By Pepelux // Change DEFAULT_HOST writing your private IP if you want to use always the // same IP address #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define Error(msg) { perror(msg); exit -1; } #define DEFAULT_HOST "PUT HERE YOUR PRIVATE IP" #define PBUFFER 10000 #define BUFFER_LONG 65536 #define DEFAULT_LEN (sizeof(struct tcphdr)+sizeof(struct iphdr)) void usage(char *nom); unsigned short cksum(unsigned short *, int); void SendPacket(struct sockaddr_in saddr, struct sockaddr_in daddr, int dport, int syn, int ack, int psh, int rst, int urg, int fin); int main(int argc, char *argv[]) { int dport; char *host_dest, *host_source; struct sockaddr_in saddr, daddr; struct hostent *hostentry; int i, c; int h = 0; // destination flag int x = 0; // Source flag int syn = 0; // SYN flag int ack = 0; // ACK flag int rst = 0; // RST flag int urg = 0; // URG flag int fin = 0; // FIN flag int psh = 0; // PSH flag if (geteuid() != 0) { printf("You must be root to use RAW Sockets\n"); exit(0); } // Check params while((c = getopt(argc, argv, "saprufd:x:c:")) != -1) { switch(c) { case 'd': // destination host if(strlen(optarg) == 0) usage(argv[0]); host_dest = optarg; h++; break; case 's': // SYN syn = 1; break; case 'a': // ACK ack = 1; break; case 'r': // RST rst = 1; break; case 'p': // PUSH psh = 1; break; case 'u': // URG urg = 1; break; case 'f': // FIN fin = 1; break; case 'x': // source host if(strlen(optarg) == 0) usage(argv[0]); host_source = optarg; x++; break; case 'c': // destination port if(strlen(optarg) == 0) usage(argv[0]); dport = atoi(optarg); break; default: usage(argv[0]); break; } } if (x == 0) host_source = DEFAULT_HOST; if (h == 0) usage(argv[0]); // you must write destination host. Error // IP source if((hostentry = gethostbyname(host_source)) == NULL) Error("Error solving source address"); bzero(&saddr, sizeof(struct sockaddr)); saddr.sin_family = AF_INET; saddr.sin_addr = *((struct in_addr *)hostentry->h_addr); // IP destination if((hostentry = gethostbyname(host_dest)) == NULL) Error("Error solving destination address"); bzero(&daddr, sizeof(struct sockaddr)); daddr.sin_family = AF_INET; daddr.sin_addr = *((struct in_addr *)hostentry->h_addr); // Send data SendPacket(saddr, daddr, dport, syn, ack, psh, rst, urg, fin); } void SendPacket(struct sockaddr_in saddr, struct sockaddr_in daddr, int dport, int syn, int ack, int psh, int rst, int urg, int fin) { int destination_port, source_port, on, s, rs, pid; int status, i; char buffer[BUFFER_LONG], rbuffer[BUFFER_LONG]; char string[BUFFER_LONG]; struct iphdr *iphdr, *riphdr; struct tcphdr *tcphdr, *rtcphdr; struct sockaddr from; int fromlen, ethlen; struct pseudohdr { struct in_addr saddr; struct in_addr daddr; unsigned char zero; unsigned char protocol; unsigned short length; } *pseudoheader; ethlen = sizeof(struct ethhdr); on = 1; source_port = htons(random()); destination_port = htons(dport); setvbuf(stdout, NULL, _IONBF, 0); fflush(stdout); if((pid=fork()) == -1) Error("fork"); if(pid) { if((s = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0) Error("socket"); if(setsockopt(s, IPPROTO_IP, IP_HDRINCL,(char *)&on, sizeof(on)) < 0) Error("setsockopt"); bzero(buffer, BUFFER_LONG); // TCP header tcphdr = (struct tcphdr *)(buffer+ sizeof(struct iphdr)); tcphdr->source = htons(source_port); // puerto origen tcphdr->dest = destination_port; // puerto destino tcphdr->window = htons(65535); // ventana tcphdr->seq = random(); // numero de secuencia aleatorio tcphdr->syn = syn; // flag SYN tcphdr->ack = ack; // flag ACK tcphdr->rst = rst; // flag RST tcphdr->psh = psh; // flag PSH tcphdr->urg = urg; // flag URG tcphdr->fin = fin; // flag FIN tcphdr->doff = sizeof(struct tcphdr) / 4; // TCP pseudoheader pseudoheader = (struct pseudohdr *) ((unsigned char *)tcphdr-sizeof(struct pseudohdr)); pseudoheader->saddr = saddr.sin_addr; // direccion origen pseudoheader->daddr = daddr.sin_addr; // direccion destino pseudoheader->protocol = IPPROTO_TCP; // protocolo pseudoheader->length = htons(sizeof(struct tcphdr)); tcphdr->check = cksum((unsigned short *) pseudoheader, sizeof(struct pseudohdr)+sizeof(struct tcphdr)); // IP header bzero(buffer, sizeof(struct iphdr)); iphdr = (struct iphdr *)buffer; iphdr->ihl = 5; // IHL (longitud de cabecera) iphdr->version = 4; // version iphdr->tot_len = htons(DEFAULT_LEN); // longitud del datagrama iphdr->id = htons(random()); // numero de identifiacion (aleatorio) iphdr->ttl = IPDEFTTL; // tiempo de vida iphdr->protocol = IPPROTO_TCP; // protocolo iphdr->daddr = daddr.sin_addr.s_addr; // direccion origen iphdr->saddr = saddr.sin_addr.s_addr; // direccion destino printf(" SENT DATA\n"); printf(" ---------\n"); printf("| SYN | ACK | RST | PSH | URG | FIN | TTL | Window\n"); printf("| %d | %d | %d | %d | %d | %d | %d | %d\n", syn, ack, rst, psh, urg, fin, iphdr->ttl, tcphdr->window); printf("seq=%d - ack_seq=%d - doff=%d - check=%d - ID=%d\n\n", tcphdr->seq, tcphdr->ack_seq, tcphdr->doff, tcphdr->check, iphdr->id); if(sendto(s, buffer, DEFAULT_LEN, 0x0, (struct sockaddr *) &daddr, sizeof(struct sockaddr) ) != DEFAULT_LEN) Error("sendto"); wait(&status); close(s); exit(0); } else { if((rs = socket(AF_INET, SOCK_PACKET, htons(ETH_P_IP))) < 0) Error("socket input"); if((s = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0) Error("socket"); if(setsockopt(s, IPPROTO_IP, IP_HDRINCL, (char *)&on, sizeof(on)) < 0) Error("setsockopt"); while(1) { if(recvfrom(rs, rbuffer, BUFFER_LONG, 0x0, (struct sockaddr *)&from, &fromlen) <= 0) Error("recvfrom"); riphdr = (struct iphdr *)(rbuffer+ethlen); if(riphdr->protocol == IPPROTO_TCP) { rtcphdr = (struct tcphdr *)(rbuffer+ethlen+ sizeof(struct iphdr)); if(rtcphdr->source == destination_port) { bzero(buffer, BUFFER_LONG); printf(" RECEIVED DATA\n"); printf(" -------------\n"); printf("| SYN | ACK | RST | PSH | URG | FIN | TTL | Window\n"); printf("| %d | %d | %d | %d | %d | %d | %d | %d\n", rtcphdr->syn, rtcphdr->ack, rtcphdr->rst, rtcphdr->psh, rtcphdr->urg, rtcphdr->fin, riphdr->ttl, rtcphdr->window); printf("seq=%d - ack_seq=%d - doff=%d - check=%d - ID=%d\n\n", rtcphdr->seq, rtcphdr->ack_seq, rtcphdr->doff, rtcphdr->check, riphdr->id); return; } } } close(rs); close(s); } return; } unsigned short cksum(unsigned short *ptr,int nbytes) { register long sum; unsigned short oddbyte; register unsigned short anwser; sum = 0; while(nbytes>1) { sum += *ptr++; nbytes -= 2; } if(nbytes==1) { oddbyte = 0; *((unsigned char *) & oddbyte) = *(unsigned char *)ptr; sum += oddbyte; } sum = (sum >> 16) + (sum & 0xffff); sum += (sum >> 16); anwser = ~sum; return(anwser); } void usage(char *nom) { printf("Usage: %s [s|a|r|p|u|f] [-x host_source] -d host_destination -c port\n", nom); printf("\t-s SYN flag enabled\n"); printf("\t-a ACK flag enabled\n"); printf("\t-r RST flag enabled\n"); printf("\t-p PSH flag enabled\n"); printf("\t-u URG flag enabled\n"); printf("\t-f FIN flag enabled\n"); exit(-1); } -------------------------------------------8<--------------------------------- --- checkicmp.c ---------------------------8<--------------------------------- // checkicmp.c // By Pepelux #include #include #include #include #include #define Error(msg) { perror(msg); exit -1; } int main(void) { int s; struct sockaddr_in dir = {AF_INET, 0, 0 }; char buff[1024]; int len = sizeof(dir); struct icmphdr *rec = (struct icmphdr*) (buff + sizeof(struct iphdr)); if (geteuid() != 0) { printf("You must be root\n"); exit(0); } if ((s = socket(AF_INET, SOCK_RAW, 1)) < 0) Error("socket"); printf("Waiting data ...\n"); while (1) { bzero(buff, 1024); while (recvfrom(s, buff, 1024, 0, (struct sockaddr_in*) &dir, &len) > 0) printf("Received:\ttype %d\tcode %d\n", rec->type, rec->code); } } -------------------------------------------8<--------------------------------- --- sendudp.c -----------------------------8<--------------------------------- // sendudp.c // By Pepelux #include #include #include #include #include #include #include #include #include #define Error(msg) { perror(msg); exit -1; } main (int argc, char *argv[]) { int s; int dport; struct sockaddr_in addr_dest; if (argc!=3) { printf ("Usage: %s ip port\n", argv[0]); exit(0); } dport = atoi(argv[2]); printf ("Sending UDP packet to %s:%d\n",argv[1], dport); if ((s=socket(AF_INET,SOCK_DGRAM,0)) < 0) Error("socket"); addr_dest.sin_addr.s_addr=inet_addr(argv[1]); addr_dest.sin_family=AF_INET; addr_dest.sin_port=htons(dport); if (sendto(s,"\n",1,0,(struct sockaddr*)&addr_dest,sizeof(struct sockaddr_in)) < 0) Error("sendto"); close (s); } -------------------------------------------8<--------------------------------- ------[ 5.- Referências ] Aqui estão links de páginas interessantes que eu usei para escrever este documento: Standards (RFCs): Protocolo TCP : http://www.rfc-editor.org/rfc/rfc793.txt Protocolo IP : http://www.rfc-editor.org/rfc/rfc791.txt Protocolo UDP : http://www.rfc-editor.org/rfc/rfc768.txt Protocolo ICMP : http://www.rfc-editor.org/rfc/rfc792.txt Documentação do Nmap: http://insecure.org/nmap/man/ ------[ 6.-Finalizando ] Bem, sim, tudo o que é feito pelo nmap, é mais bonito, mais rápido e melhor ... mas, isso é engraçado, não? :p Nmap tem muitos métodos de scan mas algumas vezes a resposta é imprevisível, dependendo do sistema operacional rodando. Por isso é bom escanear manualmente! Além do mais você vai deixar menos logs e irá aprender mais. Abraços!!! Pepelux http://www.enye-sec.org =-|================================================================ EOF =====| # milw0rm.com [2008-09-28]