miércoles, 13 de marzo de 2013

Segurinfo 2013

Otra vez reportando desde el jet set.

Ha ocurrido algo horrible.

Pude haber ido a la charla de Bruce Schneier (si, el de la cerveza), pero como me estaba escapando del trabajo y pensé que no iba a haber lugar, no lo hice.

Nadie puede imaginar la vergüenza e infelicidad que me dió cuando me enteré de que la sala estaba por la mitad. A la organización de segurinfo sólo le puedo cuestionar que habían pocas servilletas, a los (in)asistentes, todo. Mucho traje, poco cerebro. Punto.

La charla de Julio Ardita y Marcelo Stock acerca de desarrollo seguro estuvo bien, nada especial.

La charla de Claudio Caracciolo, el aire es libre, fué fenomenal. El engaño que expuso consiste en que uno entra en http://loquesea.com/login en lugar de https://loquesea.com/login. Los detalles se los dejo a él, no soy periodista. Si no fuera por el rant anterior y por el script que pego abajo, esta entrada no tendría razón de ser.

Para quienes le tienen miedo a sslstrip, que es parte del stack de su ataque, acá comparto un userscript para firefox. Al cargarlo por primera vez quedan los include en la configuración. No tiene sentido usar listas interminables de urls de login mantenidas por la comunidad por el overhead administrativo, en mi humilde opinión. Si uno sabe lo que navega normalmente, en cinco minutos prepara los includes y listo. Tengo la impresión de que hay un add-on que hace lo mismo con una bruta lista, pero para scriptish es una linda manera de evitar la polución del browser.

La idea es que estos scripts se activan para las urls que uno le dice. Si uno entra en una página por http en la que quería entrar por https, avisa.


// ==UserScript==
// @id             004
// @name           no sslstrip
// @version        1.0
// @namespace      
// @author         dev4sec
// @description    

// @include        http://accounts.google.com/*
// @include        http://login.live.com/*
// @include        http://www.facebook.com/login.php*
// @include        http://login.yahoo.com/*
// @run-at         document-start
// ==/UserScript==
 
alert("Posible sslstrip");
 
Y ahora, el infaltable glosario:

rant: se deduce del contexto

sslstrip (http://www.thoughtcrime.org/software/sslstrip/): un programita que puesto en el lugar apropiado abre una conexión https por nosotros, dándonos gentilmente los mismos contenidos, pero en http. Ah, en retribución se queda con nuestro tráfico en texto plano.

userscript (http://scriptish.org/): firefox tiene un add-on llamado scriptish, que llama nuestros scripts sobre la página actual. Se puede modificar la página, hacer algunas pruebas de seguridad, cosas así.


 

jueves, 7 de marzo de 2013

Ganzuas descartables para la oficina

No es que no tenga nada que hacer en el trabajo, pero hay momentos en que la mente se distrae y ve cosas, asocia y construye.

Con elementos que se hallan en cualquier oficina, se pueden hacer unas simpáticas ganzúas, no muy resistentes, pero rápido y barato.


Primero hay que hallar la perforadora, opcionalmente la tijera y fundamentalmente los ganchos, que no sé como se llaman (nepacos me han colaborado).



Los que encontré están recubiertos, así que tuve que pelarlos. Los más finos con la tijera, yo con las uñas.



 A continuación hay que hacer muescas con la perforadora. Paciencia, echando a perder se aprende.



Hay que fijarse que el pistoncito que corta tiene un filo, debe alinearse como en la imagen.


Luego, un cortecito, para la terminación de la punta.


Por último, hay que limar las asperezas, para que no se trabe en la cerradura, para eso necesitamos otro gancho parcialmente pelado, el mismo que luego usaremos para girar.



Orgullosos, ya podemos mostrar nuestras ganzúas, que funcionan muy bien con las típicas cerraduras que hay en los cajones de las oficinas.


Estos dos modelos posteriores no fueron recortados con la tijera, la práctica hace al maestro.



Tener muchas esta bueno para poder limarlas unas contra las otras, es tranquilizante como mover las cuentas de un rosario.

En la primera etapa, exploratoria, cuando estaba perforando, un compañerito que no sabía de que se trataba me dijo "¿cuantos años es que tenés?", supongo que insinuando una cierta tendencia infantil de mi parte. Con la más recia cara le dije, "los suficientes, ¿sabés que en algunos paises es ilegal tener de estas cosas y podés ir preso?"

Las tres abrieron todas las cuatro cerraduras que probé. Aun no sé como van con los candados...

Por favor, si alguien hace alguna, quiero ver fotos.

English version

viernes, 1 de marzo de 2013

Experiencia de refactorización aplicación de red c/c++ con shunit

El otro día me vi en la necesidad de modificar una aplicación de stress test de un protocolo de red y tuve una excelente experiencia de refactorización que paso a relatar.

Ten en cuenta que el objetivo de este relato es mostrar el espíritu del proceso de refactorización, no el programa en cuestión, que tratándose de una herramienta de stress test bien prodría utilizarse para denegación de servicio. Aunque el protocolo es marginal y carece de importancia, ¿quién sabe que puede ocurrir mañana?

Aplicación original


El código original consiste en un archivo con cinco funciones minúsculas y un main gigante.


Tiene una sección para procesar las opciones, una para crear el payload, una para armar el paquete ip y un loop donde modifica el paquete sin necesidad de rehacerlo y lo envía, tantas veces como se le pida.

La elección de que poner en funciones y que no, parece ser muy laxa. No es conceptual, ya que un par de operaciones inversas se han implementado con una función y con código suelto.

La característica más interesante es que para que los paquetes sean distintos y no sea trivial detectarlos como provenientes de la herramienta, hay unos punteritos al payload que permiten modificarlo y reenviar sin hacer la system call para construir el paquete, con una importante ventaja en la performance, como luego se apreciará.

Este es el pseudo código del programa original:

procesar opciones

crear payload


crear ip packet


tantas veces como se pida

   actualizar payload sin modificar el tamaño del packet
   enviar

limpieza



Pros y contras de la implementación original

+ Muy veloz

+ Simple si estás familiarizado con el protocolo (no era mi caso al comenzar)

- Código difícil de modificar

- Fácilmente detectable

Objetivos

Poder generar tráfico más creible.

Mantener la performance en la medida de lo posible.

Mejorar el código para modificaciones posteriores

Decisiones 


Pude haber optado por intentar comprender bien el protocolo y rescribir en algún lenguaje más sencillo que C o C++. En ese momento no sabía cual era el cuello de botella de la aplicación, asi que decidí no incorporar un elemento extra contra la performance.

Decidí convertir el código a C++ para poder utilizar los contenedores con los que ya estoy familiarizado, que al final no usé. En última instancia la elección fue muy influida por no invertir trabajo previo en comprender y el deseo de usar C++.

Decidí utilizar shunit2 y testear funcionalmente en lugar de utilizar testeo unitario, ya que pese ha haber decidido usar c++, la verdad es que no sabía si iba a terminar haciéndolo en perl. De hecho, de continuar el proyecto, será embebiendo un intérprete de algún lenguaje a determinar.

Primer paso: el test


Lo primero que hice fue un test con shunit2:

#! /bin/bash

testFullDump() {

    ./prg param1 param2 param3 | \

     sed -e "s/\(some headers=\)......../\1xxxxxxxx/" \
        -e "s/\(other header=\)......../\1xxxxxxxx/" \
        > /tmp/shunit.txt
    cmp output/fullDump.txt /tmp/shunit.txt
    result=$?
    assertEquals 0 $result || return
    rm /tmp/shunit.txt
}

. shunit2


O sea:

./prg param1 param2 param3

Ejecutá el programa con tales parámetros

     sed -e "s/\(some headers=\)......../\1xxxxxxxx/" \
         -e "s/\(other header=\)......../\1xxxxxxxx/" \


Que sed reemplace lo modificado por la actualización del payload por "xxxxxxxx" de modo tal que sea igual a mi archivo de referencia, output/fullDump.txt


> /tmp/shunit.txt

Poné en /tmp/shunit.txt la salida del programa

cmp output/fullDump.txt /tmp/shunit.txt

Compará utilizando cmp mi archivo de referencia con la salida del programa

result=$?

Costumbre mía, no confiar en que $? tenga el exit code más alla de la ejecución inmediata.

assertEquals 0 $result

Comprobá que la salida de cmp sea 0, o sea que sean iguales

|| return

Si no eran iguales, interrumpí el test

rm /tmp/shunit.txt

Limpiá

testFullDump() {
   ...
}

. shunit2


Este es el modo de hacer un test con shunit2, primero definir funciones de la forma testXXX y al final ejecutar en este mismo proceso shunit2.

Segundo paso: refactorización


Teniendo esta red de protección procedí a mover algunas secciones de código a funciones, cambiar de gcc a g++, creé clases y finalmente tuve la misma funcionalidad con la que había comenzado, pero en código modularizado.

También incorporé el uso de valgrind, para evitar perder memoria, ya que este programa bien podría ser ejecutado durante mucho tiempo.

Medí la performance antes y después y me dió parecido, así que por ese lado, no había problema.

Tercer paso: crear ip packet dentro del loop


Ahora, la llamada al sistema, que supongo debe ser cara, se hace dentro del loop:

procesar opciones


crear payload


tantas veces como se pida

   actualizar payload
   crear ip packet
   enviar

limpieza


Esto no agrega nada, es sólo un paso intermedio, desde el punto de vista del resultado sigue siendo refactorización. Estoy respetando "baby steps".

La performance se vió seriamente afectada, pero dentro de un rango aceptable.

Cuarto paso: crear payload packet dentro del loop


Ahora el payload es modificado dentro del loop, esto implica un payload de tamaño variable, cosa que en la implementación original no podiamos hacer, ya que hay que recrear el packet:


procesar opciones


tantas veces como se pida

   crear payload

   crear ip packet
   enviar

limpieza


Para crear payload usé unos pocos casos creados a mano, por ahora sólo me interesa que funcione y poder medir la performance. En teoría ya no puede empeorar.

Fin



Todo esto sólo fue el preparativo para poder cumplir el objetivo: crear payloads distintos pero coherentes.

Como me sigo resistiendo a terminar de entender el protocolo, en lugar de hacer generadores, me gusta la idea de generar las payloads a partir de la captura de tráfico legítimo, reduciendo la comprensión a que no se filtre información rastreable al tráfico original y que respeten el protocolo.

De un modo u otro, jamás lo haría en c++. Usaría una arquitectura de plugins con un intérprete de lua, python, perl, lo que sea. Si eso afectara la performance, prototiparía interpretando y al final volvería a c++.

Pero esas ya son otras historias...

Aprendizaje


Con un solo test, avanzando paso a paso y versionando con git, sin necesidad de una inversión inicial de investigación, pude tomar un programa funcionalmente correcto pero apestoso y convertirlo un programa limpio y manejable sin pérdida de performance.

Cuando empecé no sabía nada del protocolo, ni siquiera que existía; cuando terminé con esta etapa me vi obligado a leer algunas RFCs y no me costó nada pues ya estaba bastante familiarizado por haberlo usado.

Algunas métricas

Para medir las lineas usé:

for version in 0 1 2; do
   git checkout PerformanceV${version}
   echo "Version ${version}"
   for type in c h cpp hpp; do
       echo "  Type ${type}"
       echo -n "    Files "
       ls -1 *.$type 2>/dev/null | wc -l
       echo -n "    Lines "
       grep *.$type -ve "^ *$" -ve "^ */" 2>/dev/null | wc-l

   done
done


siendo $EXT hpp, cpp, h o c según el caso. Con el grep quité las lineas en blanco y casi todos los comentarios.


Para medir la performance utilicé un script para tirar ejecuciones simultáneas. Me da un poquito de vergüenza, no he sido muy estricto, ya sé que esta no es la manera de hacer profiling, es sólo una medida aproximada pero al menos tomé la precaución de dejar la velocidad del micro fija.

PROCESSES=$1
REQUESTS=$2

while [ $PROCESSES != 0 ] ; do
    ./run  $REQUESTS &
    PROCESSES=$(( $PROCESSES - 1 ))
done
wait



El  script se invoca:

time ./stresstest.sh $PROCESSES $REQUESTS

Cambiando el branch, recompiliando y con REQUESTS = 1000000

Original
c
Refactorizado
c++
Rediseñado
libnet_build_* en loop
Archivos headers 1 5 7
Archivos código 1 5 7
Lineas headers 71 98 117
Lineas código 349 473 473
1 proceso 1.2 1.4 7.5
2 procesos 2.3 2.5 8.5
3 procesos 1.8 1.9 8.1
4 procesos 3.9 3.7 8.8
5 procesos 4.9 4.9 12.5

Muy llamativa la anomalía con tres procesos, ¿sistema operativo? ¿hardware? ¡quién sabe! Mirá que lo probé varias veces y el comportamiento fué siempre el mismo.

Glosario


Dado que he utilizado algunos términos de modo un tanto "libre", paso a definirlos:

packet: me refiero al datagrama, lo que va a ser enviado a la red, se le ajustan las direcciones y puertos de origen y destino, el protocolo de red y los datos y se hace una llamada al sistema para que lo entienda y pueda enviarlo.

payload: es la carga específica del protocolo en cuestión, lo único en que afecta al packet es en su tamaño.