NOTA PRELIMINAR: Para comprender correctamente esta entrada se asume que el lector tiene conocimientos sobre programación en Python y Java, además se recomienda utilizar plataformas basadas en UNIX con el programa dig instalado. El conocimiento sobre Python es requerido para comprender los scripts que se indican más adelante, así como también es necesario tener conocimientos sobre Java para comprender las rutinas que se utilizan para llevar a cabo el ataque contra el anonimato de aplicaciones y servidores web en I2P.

Ahora que se ha comprendido correctamente el funcionamiento de I2P y el significado de ficheros y directorios almacenados en una instancia, es posible utilizar este conocimiento para intentar descifrar la ubicación real de un servicio EEPSITE determinado.

En primer lugar es necesario tener acceso a la instancia de I2P, en este caso concreto interesa utilizar el proxy de I2PTunnel para tener acceso a la red, como resultará evidente para el lector, esto es un requisito fundamental, dado que de otro modo no seria posible acceder a ningún EEPSITE. Por otro lado, la información que se almacena en el fichero hosts.txt y en el directorio netDb serán el insumo principal para realizar el escaneo, ya que por un lado, el fichero hosts.txt almacena varias rutas en I2P con formato *.i2p que posteriormente pueden ser empleadas para verificar si en cada una de dichas rutas existe un EEPSITE valido en ejecución. Evidentemente para conseguir todo esto, es necesario realizar rutinas automatizadas que permitan hacerlo, en este caso concreto se utilizarán clases sencillas programadas en lenguaje Java que permitirán hacer estas tareas y almacenar resultados en ficheros de texto plano.

NOTA: En la conferencia DEFCON que se celebro en Las Vegas en el año 2011, el investigador Adrian Crenshaw hablo sobre este mecanismo de ataque, sin embargo el objetivo de esta publicación, no intenta reproducir este mismo escenario ni el mismo enfoque, dado que aquí se utiliza la API de I2P y lenguaje Java en lugar de utilizar Python. Además se intenta recolectar aun más información sobre los sitios escaneados y realizar consultas DNS “reversas” con el fin de encontrar nombres de dominio a partir de direcciones IP. Por otro lado, no se intenta consultar la base de datos interna de I2P de un modo “crudo” utilizando expresiones regulares, en lugar de ello, se emplea la clase RouterInfo de I2P y obtener información útil sobre cada uno de los Routers empleados en los túneles creados en la instancia. Para ver la presentación original y la investigación llevada a cabo por Adrian se debe acceder a su sitio web oficial aquí: http://www.irongeek.com/i.php?page=security/darknets-i2p-identifying-hidden-servers

Todo lo dicho anteriormente se puede resumir en los siguientes pasos.

  1. Es necesario identificar cuales de los Destinations que se encuentran registrados en el fichero hosts.txt tienen un servicio EEPSITE en ejecución, para ello basta con realizar una petición HTTP y posteriormente analizar su respuesta. Como se ha indicado anteriormente, es necesario utilizar el proxy de I2P que se encuentra instalado en I2PTunnel para que las peticiones que se envíen pasen por la red de I2P, además de esto, también resulta conveniente utilizar alguna de las herramientas disponibles para interceptar y analizar las peticiones que se realizan a una aplicación web, para ello es posible utilizar soluciones tales como Burp Suite, OWASP ZAP, Paros Proxy entre otras. En cualquiera de los casos, en dichas herramientas es necesario establecer el proxy de salida, que en este caso será el que inicia I2PTunnel para navegar. Este por defecto se encuentra en ejecución en la máquina local en el puerto 4444, de esta forma, el mecanismo será el siguiente:
    1. Desde un programa Java se leerá el fichero hosts.txt y posteriormente se establecerá que el proxy para la realizar la conexión se encuentra ubicado en 127.0.0.1:8080 (que es donde el proxy de OWAS ZAP estará en ejecución)
    2. En OWASP ZAP se establece como proxy local en el puerto 8080 para recibir cualquier petición que llegue por dicho puerto.
    3. Cada petición recibida en OWASP ZAP por el puerto 8080 será enrutada a I2PTunnel en el puerto 4444, además de esto se almacenan las peticiones y respuestas recibidas por parte de los servidores consultados, lo que permitirá posteriormente realizar ataques más elaborados. Las siguientes imágenes enseñan esta explicación


    En la imagen anterior se puede apreciar que el proxy de salida es el que se ha iniciado en I2P. Por otro lado, se recomienda establecer un valor alto al tiempo de TimeOut con el fin de dar suficiente tiempo al proxy de enviar y recibir las peticiones (normalmente esto puede llevar un tiempo en la red I2P) de esta forma se evitan “falsos negativos” con mensajes “504 Gateway Timeout” el valor de 500 será suficiente para una prueba corta.
    NOTA: El valor de “TimeOut” dependerá de que tan extenso se decida hacer el escaneo, dado que una vez se cumpla el tiempo establecido en dicha propiedad, automáticamente todas las peticiones que se realicen dará como resultado un “504 Gateway Timeout” por lo tanto si se desea realizar un ataque realmente completo, es posible que se desee establecer un valor más alto.

    En esta imagen se puede apreciar, como se establece el proxy local para recibir peticiones en el puerto 8080 que será utilizado por el programa escrito en Java del que más adelante se hablará con detenimiento.

    1. Como punto final del proceso de escaneo de EEPSITES, es necesario iniciar un sniffer como wireshark para capturar todos los paquetes con sus correspondientes direcciones IP, esto será útil para comparar las direcciones obtenidas con las que se encuentran registradas en la instancia local de NetDB. Para ello es necesario iniciar wiresharky establecer como filtro el valor
      tcp.port == 13834 or udp.port == 13834

      Si se desea hacer desde consola es posible utilizar el comando dumpcap el cual viene incluido dentro de las librerías de Wireshark, en dicho caso el comando que se debería ejecutar es el siguiente:

      >dumpcap -i eth0 -s 0 – f “port 13834” -w /dir/saved_capture.pcap -a duration:3600

      En cualquiera de los casos anteriores el valor 13834 corresponde al puerto que abre I2P para la comunicación entre los túneles, dado que este puerto es utilizado para cualquier tipo de petición o respuesta en I2P, es conveniente almacenar los paquetes capturados en un fichero PCAP para que posteriormente sea posible realizar un análisis. Para verificar el valor de este puerto en un instancia de I2P en ejecución se debe acceder a la página de configuración localizada en: http://127.0.0.1:7657/config

  1. Una vez ha terminado el escaneo de servicios, se generan reportes útiles que serán tomados de insumo para determinar cuales de los dominios *.i2p tienen un EEPSITE en la red. Ahora es el momento de relacionar las direcciones IP capturadas con las que se almacenan en los ficheros “router-info” de NetDB, en este punto es importante el uso de la API Java de I2P para leer los ficheros “router-info” y acceder a su información, además de que también es importante comparar estas direcciones IP con las que se han capturado en el paso anterior. En primer termino se debe utilizar wireshark y abrir el PCAP generado en el paso anterior, la herramienta en este caso cargará toda la información que se encuentra contenida en el fichero de captura. Para exportar esta información en un formato simple y fácil de manipular, se debe seleccionar la opción “Statistics → EndPoints” se abrirá una ventana con la información del fichero capturado separado por pestañas que indican el protocolo de los paquetes capturados, en este caso, se puede seleccionar la opción “IPv4” que contendrá todos los paquetes capturados que resultan interesantes, posteriormente presionar el botón “Copiar” tal como se puede ver en la imagen

  2. De esta forma se tendrá en memoria toda la información que se puede visualizar desde wireshark en formato CSV (separado por comas). Esta información se debe almacenar en un fichero de texto vacío, la cual será utilizada a continuación.
  3. Ahora se toman los ficheros generados en los pasos anteriores, en primer lugar, se debe comparar las direcciones IP contenidas en el fichero PCAP que se ha exportado en formato CSV contra la base de datos interna que contiene cada una de las direcciones IP de los routers (NetDB) para ello se utiliza la clase Java NetDBFilterIP la cual utiliza la API Java de I2P para consultar y acceder a los servicios internos de esta red privada. En los siguientes párrafos se explicará en detalle su funcionamiento.
    Una vez finaliza su ejecución la clase NetDBFilterIP genera un fichero consolidado con las direcciones IP que coinciden entre los ficheros almacenados en I2P (router-info de NetDB) y las direcciones capturadas en el proceso de escaneo.
  4. Tomando como insumo el fichero filtrado generado en el paso anterior, se procede a buscar resultados con dichas direcciones en internet, el mecanismo es muy similar al que se ha empleado en la clase SearchEEPSitesI2P sin embargo en esta ocasión el uso de un proxy es opcional, se debe realizar la conexión de forma directa en Internet y se almacenarán los resultados generados para su posterior análisis. Una de las ventajas de esta clase es que permite realizar la resolución de nombres de dominio utilizando cada una de las direcciones IP del fichero de texto.
    NOTA: En el caso de que se tengan inconvenientes con el ISP por realizar “escaneos” a múltiples sitios en internet contra el puerto 80, se puede utilizar otro proxy, como por ejemplo un SOCKS proxy creado en I2P desde I2PTunnel o utilizando Polipo/TOR tal como se ha indicado en entradas anteriores para enviar cada petición utilizando TOR. Por otro lado, también es posible Y ALTAMENTE RECOMENDADO, utilizar Burp Suite, Paros Proxy o OWASP ZAP para realizar las peticiones contra estos sitios web en internet, del mismo modo que se ha hecho anteriormente contra los sitios web internos en I2P (EEPSITES).
  5. Finalmente, tras la ejecución de las clases anteriores y llevar a cabo cada uno de los pasos mencionados, se tendrá un directorio con información útil sobre los routers, hosts virtuales, nombres de dominio y direcciones IP, lo que reduce de forma considerable el anonimato de ciertos servicios que se encuentran en ejecución en la red I2P y a su vez, tienen un dominio de acceso público en internet.

Ahora que se ha explicado de forma breve el vector de ataque con unos pocos pasos, se procede a explicar en detalle cada una de las clases que se han utilizado para obtener información sobre los EEPSITES escaneados.

CÓDIGO FUENTE EMPLEADO PARA EL ATAQUE

En primer lugar se encuentra la clase SearchEEPSitesI2P la cual se encarga de leer el fichero hosts.txt para obtener cada uno de los Destinations registrados en dicho fichero, posteriormente se recorren cada una de las lineas del fichero y se crea un hilo para realizar una conexión HTTP a cada uno de los Destinations leídos, estas conexiones pasan primero por el proxy creado por OWASP ZAP el cual se encarga de enviar peticiones y recibir respuestas al proxy de I2PTunnel. Una de las ventajas de este enfoque es la capacidad de almacenar los resultados y posteriormente analizar posibles vulnerabilidades en los servicios que retornen un estado positivo (código HTTP 200), de hecho, esta es una de las principales potencialidades de OWASP ZAP, aunque en esta entrada no se hablará sobre su uso en profundidad, en futuras publicaciones se hará una extensiva serie que hablará sobre la seguridad en servidores y aplicaciones web. Una vez que el escaneo termina, se generan una serie de ficheros que contienen el código HTML de cada uno de los sitios que ha retornado una respuesta positiva, así como un fichero CVS separado por comas con información útil que ha retornado el servidor web en su respuesta.

Cuando se ejecuta esta clase, se disparan bastantes hilos (uno por linea encontrada en el fichero de hosts.txt) por lo tanto, desde OWASP ZAP se tendrá la sensación de que existen múltiples peticiones a varios sitios al mismo tiempo, tal como se enseña en la siguiente imagen.

Explicación del funcionamiento de la clase SearchEEPSitesI2P:

Como puede verse esta clase contiene un método “main” que es la rutina principal del programa, el cual se encarga de instanciar un objeto SearchEEPSitesI2P para invocar al método readHostsI2P el cual espera que el usuario ingrese manualmente la ubicación de dicho fichero, que como se ha mencionado anteriormente, depende del sistema operativo que se utilice, en cualquier caso es necesario indicar la ruta completa. Si la ruta indicada es valida, el programa automáticamente comienza con el proceso de escaneo, para ello, crea un directorio que es solicitado al usuario en donde se almacenarán todos los ficheros que serán el resultado de las respuestas de cada uno de los EEPSITES consultados, en el caso de que el directorio no exista, se crea. Posteriormente, se procede a leer cada una de las lineas contenidas en el fichero hosts.txt» utilizando un ciclo «WHILE». Dentro de dicho ciclo, se crea un hilo de ejecución, con el fin de agilizar el procesamiento del fichero, ya que el rendimiento del programa puede verse seriamente afectado si se utiliza un único proceso para realizar todas las peticiones, se desea que este mecanismo se realice de forma paralela y que se puedan realizar múltiples peticiones al mismo tiempo. El nombre de cada hilo, contendrá la linea que se ha leído del fichero “hosts.txt” y de forma inmediata ejecutará el método “run()” definido en la interfaz “Runnable”. Dentro de el método «>run solamente se realiza una llamada al método «connectEepsite » el cual se encargará de realizar todo el procesamiento de las peticiones. En el método connectEepsite es donde realmente se realiza toda la lógica interesante del escaneo, en primer lugar se establecen las propiedades correspondientes al host y al puerto donde se encuentra ejecutándose el proxy, posteriormente se procede a realizar una conexión HTTP contra el sitio EEPSITE ingresado por parámetro, si la respuesta de esta petición es positiva (código HTTP 200) se procede a almacenar el contenido HTML devuelto en un fichero cuyo nombre será el nombre del EEPSITE (este fichero se almacena en el directorio de resultados definido anteriormente), además de esto, también se consultan algunas propiedades de la respuesta y se almacenan en un fichero CSV con cada uno de sus valores, el contenido de este fichero sigue la siguiente estructura:

EEPSITE_HTTP , Código HTTP Respuesta, Mensaje de Respuesta, Header Field Server, Host, Last_Modified

Por ejemplo

http://freshcoffee.i2p,200,OK,Apache,XXXXXX,FECHA

En el caso de que un EEPSITE retorne una respuesta negativa, se almacena este resultado en un fichero que contienen cada una de las rutas de los EEPSITE que han fallado. Finalmente se cierra correctamente cada una de las conexiones que se han abierto, con la finalidad de no perjudicar la máquina local con conexiones a sitios remotos sin cerrar.

Consideraciones antes de Ejecutar la clase SearchEEPSitesI2P

Luego de la explicación dada sobre esta clase para escanear EEPSITES, es necesario tener en cuenta las siguientes consideraciones antes de ejecutar esta clase:

  • Es necesario tener en el CLASSPATH la dependencia commons-lang de Apache. Se puede obtener esta librería desde aquí: http://commons.apache.org/lang/download_lang.cgi se recomienda utilizar las ultimas versiones.
  • En el caso de que el proxy utilizado se encuentre en un puerto y/o máquina distinta a la que se encuentra definida en la clase (127.0.0.1:8080) es necesario editar estas propiedades antes de compilar y ejecutar el programa, en concreto, estas propiedades son:
    sysProps.put( "http.proxyHost", "127.0.0.1");
    sysProps.put( "http.proxyPort", "8080" );
    

Explicación del funcionamiento de la clase NetDBFilterIP:

Esta clase intenta consultar la información de las direcciones IP almacenadas en el directorio de routers local (NETDB) con el fin de comparar estas direcciones con el fichero PCAP producido a partir del escaneo ejecutado contra los Destinations del fichero hosts.txt

El método main de esta clase es el punto de anclaje y el que se encarga de invocar el procesamiento principal, En primera instancia intenta excluir aquellas direcciones IP que no resultan interesantes dado que son direcciones del segmento de red local. Posteriormente se ejecuta el método readNetDBDirectory el cual se encarga de leer la ruta donde se encuentra el servicio de NetDB en la instancia local de I2P, en máquinas Linux el valor por defecto es /home/<usuario>/.i2p/netDb/

NOTA: Los ficheros que se encuentran ubicados en este directorio son ficheros en formato hexadecimal y no se pueden leer correctamente con un editor de textos ordinario, si se desea abrir este tipo de ficheros, se recomienda utilizar un programa como Okteta el cual se puede instalar fácilmente en máquinas bajo plataforma Linux (Debian y derivados) con el siguiente comando:

>apt-get install okteta

Este directorio debe ser ingresado correctamente cuando se solicite por el programa, de otro modo su ejecución finalizará de forma instantánea.

Una vez se ha leído dicho directorio, el programa invoca el método searchIPAddresses el cual antes de comenzar a realizar todo el procesamiento de comparación, intenta invocar el método readScanDir el cual se encarga de leer el directorio donde se almacenarán los ficheros resultantes del proceso de búsqueda y comparación, el valor que debería ingresarse cuando sea solicitado es el mismo que se ha ingresado con anterioridad por la clase SearchEEPSitesI2P para almacenar los resultados del escaneo de EEPSITES.

Una vez se han ingresado estos valores, el programa comienza por listar todos los ficheros que se encuentran incluidos en el directorio netDb de I2P, el listado de ficheros leídos de dicho directorio es consultado por medio del uso de la clase net.i2p.data.RouterInfo que se encuentra incluida en las librerías de la API Java de I2P. Con todos estos objetos en memoria, se solicita a continuación la ruta completa donde se encuentra el fichero CSV exportado anteriormente desde wireshark. Una vez se ha cargado dicho fichero, el programa comienza por crear unas estructuras de datos básicas para almacenar los resultados encontrados. Para hacer el proceso de comparación, se busca en las opciones de cada objeto RouterInfo almacenado, en concreto se debe buscar las opciones “host” o “ihost” (esta ultima en el caso de que se proporcione varias direcciones IP validas para el router, caso típico de servidores en cluster) el valor de esta propiedad se consulta fácilmente de esta forma:


<span style="font-family: Monospace;"><span style="font-size: x-small;"><span style="color: #000000;">routerAddress.getOptions().get(</span><span style="color: #2a00ff;">"host"</span><span style="color: #000000;">)</span></span></span>

<span style="color: #000000;"><span style="font-family: Monospace;"><span style="font-size: x-small;">

Para hacer estas comparaciones entre el fichero CSV y los objetos RouterInfo cargados en memoria, se realizan dos ciclos for anidados para ir comparando cada uno de los valores de ambas estructuras de datos. En cada una de las iteraciones de dichos ciclos se determina si la dirección almacenada en el fichero CSV existe dentro de alguno de los objetos RouterInfo y en el caso de que sea así, se almacena en una estructura de direcciones IP encontradas, en el caso contrario, se almacena en una estructura de direcciones IP no encontradas.

Una vez termina la ejecución de los ciclos de comparación anteriormente descritos, se procede a volcar en dos ficheros de texto independientes, los datos almacenados en las estructuras de datos de direcciones IP encontradas y las direcciones IP no encontradas, estos ficheros son almacenados en el directorio de resultados que se ha solicitado al principio del programa, por defecto el valor de dichos ficheros es addressesInsideNetDb.txt y addressesNotFound.txt

Consideraciones antes de Ejecutar la clase NetDBFilterIP:

  • Antes de ejecutar esta rutina, se deben cambiar las direcciones IP que se han indicado en el código fuente para excluir del proceso de comparación, en los valores por defecto se han establecido las direcciones 192.168.1.33 y 192.168.1.1 que corresponden a la dirección de la máquina local y el router respectivamente. Quien ejecute esta clase, debe cambiar estos valores y establecer los suyos propios. La finalidad de establecer estas direcciones, es la de excluir aquellas direcciones del segmento de red local que se incluyen en el fichero de captura PCAP.
  • Para conseguir compilar y ejecutar correctamente esta clase es necesario importar en el ClassPath las librerías necesarias, en este caso es necesario incluir en la ruta de construcción los ficheros JAR desktopgui.jar, i2p.jar, router.jar y routerconsole.jar estas librerías se encuentran localizadas en <RUTA_INSTALACION_I2P>/lib donde RUTA_INSTALACION_I2P suele ser /usr/local/i2p

Explicación del funcionamiento de la clase OutProxyTester:

Esta clase permite la ejecución del escaneo de sitios web partiendo de las direcciones IP filtradas por la clase NetDBFilterIP por lo tanto, para que pueda ejecutarse correctamente, espera recibir el fichero de direcciones IP generado anteriormente, el fichero cuyo nombre por defecto es addressesInsideNetDb.txt El funcionamiento de esta clase es muy similar al de la clase SearchEEPSitesI2P en el sentido de que también realiza un escaneo de sitios web, la diferencia radica en que la primera lo hace sobre la red de I2P y la segunda lo hace sobre Internet con el fin de localizar información relacionada con las direcciones IP encontradas.

Cuando se ejecuta esta clase, el método main es la rutina principal y se encarga de invocar el método readAddressesInside el cual se encarga de solicitar la ruta completa del fichero generado por la clase NetDBFilterIP, en el caso de que la ruta sea correcta se solicita la ruta donde se deben almacenar los resultados las pruebas realizadas por este programa, esta ruta puede ser el directorio que se ha ingresado en la ejecución de los programas anteriores. Posteriormente se procede a ejecutar el método crawlOutsideServers el cual se encarga de leer el fichero addressesInsideNetDb.txt ingresado anteriormente, por cada una de las lineas leídas en dicho fichero (cada linea contiene una dirección IP) el programa automáticamente comienza la ejecución de un nuevo hilo, cada uno de estos hilos invoca al inicio de su ejecución al método connectOutSiteWebServer enviando como parámetro la dirección IP. Posteriormente dicho método se encarga de realizar una petición HTTP contra la dirección IP dada, en el caso de que la respuesta sea positiva, se intenta almacenar este contenido en un fichero HTML y posteriormente se ejecuta el comando dig para realizar una consulta DNS reversa (partiendo de la dirección IP) la finalidad de esto, es conocer el nombre de dominio del servidor web que se acaba de consultar.

NOTA: Tal como se ha dicho anteriormente, la utilidad dig se debe encontrar instalada en la máquina donde se están ejecutando estos programas (preferiblemente una máquina con alguna distribución de Linux instalada)

Finalmente, este programa se encarga de registrar esta información en un fichero de texto que se almacenará en el directorio que se ha especificado anteriormente para almacenar los resultados del programa. En el caso de que la respuesta del servidor no sea positiva, se asume que no existe un servidor web en dicha dirección IP, por ende no se realiza ningún tipo de consulta DNS, solamente se registran estas direcciones en un fichero de texto independiente que contendrá todas las direcciones IP que han fallado

Por otro lado, aunque no es un requisito indispensable, se recomienda utilizar un proxy intermedio como OWASP ZAP o Burp Suite para procesar cada petición/respuesta y posteriormente realizar escaneos en busca de vulnerabilidades e incluso realizar ataques activos contra el servidor web. En el caso de Burp Suite, estas imágenes demuestran como las peticiones a cada dirección IP, son capturadas y procesadas.

Consideraciones antes de Ejecutar la clase OutProxyTester:

  • En primer lugar, es necesario definir si se desea utilizar un proxy como Burp Suite o OWASP ZAP para realizar las peticiones, por defecto el programa tiene definido un proxy que se encuentra en ejecución en la máquina local por el puerto “81”, por lo tanto si se desea cambiar este valor o no utilizar un proxy en lo absoluto, es necesario modificar estas lineas del código fuente de la clase
     			Properties sysProps = System.getProperties();
    			sysProps.put( "http.proxyHost", "127.0.0.1");
    			sysProps.put( "http.proxyPort", "81" );
    

Ahora bien, todo el código fuente explicado en esta publicación puede encontrarse aquí:

http://www.fileserve.com/file/zgzC5dt/I2PScript.tar

Cada una de las clases indicadas se puede encontrar desde PasteBin aquí:

SearchEEPSitesI2P: http://pastebin.com/7kp3DVhs

NetDBFilterIP: http://pastebin.com/MnSGaZnB

OutProxyTester: http://pastebin.com/eJaLBbae

SystemCommandExecutor: http://pastebin.com/bRmjqdrS

ThreadedStreamHandler: http://pastebin.com/EXXLJfsD