miércoles, 29 de febrero de 2012

Valgrind: debugeando aplicaciones en C

Valgrind es un debuger de memoria, es capas de detectar fugas donde nuestro programa se come la memora o (memory leaks), violaciones de segmento y varias otras pestes que los programadores solemos meter en el código.
Es compatible con varias arquitecturas: X86/Linux, AMD64/Linux, ARM/Linux, PPC32/Linux, PPC64/Linux, S390X/Linux, ARM/Android (2.3.x), X86/Darwin and AMD64/Darwin (Mac OS X 10.6 and 10.7).
La idea principal de este post es mostrar dos casos de bugs muy comunes en el código de los tantos que hay y como se pueden debuguear con esta herramienta.

Primero vamos a crear un sencillo programa que imprimirá dos lineas en la consola:

#include <stdio.h>

int main()
{
int edad = 27;
char nombre[]="Dario Clavijo";

printf("Hola, soy %s.\n",nombre);
printf("Y tengo %d anios.\n", edad);

return 0;
}
Lo compilamos:

daedalus@neuromancer:~/code$ gcc prueba.c -o prueba.c -g -Wall -o prueba

No vemos ni errores ni warnings
-Wall indica que se tienen que imprimir todos los warnings
-g indica que se tienen la información de depuración durante la compilación

Ejecutamos:
daedalus@neuromancer:~/code$ ./prueba
Hola, soy Dario Clavijo.
y tengo 27 anios.
Ejecutamos con Valgrind:
daedalus@neuromancer:~/code$ valgrind ./prueba
==2992== Memcheck, a memory error detector
==2992== Copyright (C) 2002-2011, and GNU GPL'd, by Julian Seward et al.
==2992== Using Valgrind-3.7.0 and LibVEX; rerun with -h for copyright info
==2992== Command: ./prueba
==2992==
Hola, soy Dario Clavijo.
y tengo 27 anios.
==2992==
==2992== HEAP SUMMARY:
==2992== in use at exit: 0 bytes in 0 blocks
==2992== total heap usage: 0 allocs, 0 frees, 0 bytes allocated
==2992==
==2992== All heap blocks were freed -- no leaks are possible
==2992==
==2992== For counts of detected and suppressed errors, rerun with: -v
==2992== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 5 from 3)
Ahora vamos a romper el programa haciendo un printf sin pasarle la variable:
#include <stdio.h>

int main()
{
int edad = 27;
char nombre[]="Dario Clavijo";

printf("Hola, soy %s.\n",nombre);
printf("Y tengo %d anios.\n", edad);

return 0;
}
Cuando compilamos el compilador ya se queja avisándonos de una variable no usada y de una función que no se le paso un parámetro:
daedalus@neuromancer:~/code/LCTHWC$ gcc prueba.c -o prueba.c -g -Wall -o prueba
prueba.c: In function ‘main’:
prueba.c:8:7: warning: format ‘%s’ expects a matching ‘char *’ argument [-Wformat]
prueba.c:6:12: warning: unused variable ‘nombre’ [-Wunused-variable]
Ejecutamos y obtenemos basura(esto sucede al estar leyendo en una posición de memoria que se considera sucia con valores anteriores por no haber sido inicializada):
daedalus@neuromancer:~/code$ ./prueba
Hola, soy ����.
Y tengo 27 anios.
Ejecutamos con valgrind:
daedalus@neuromancer:~/code/LCTHWC$ valgrind ./prueba
==3050== Memcheck, a memory error detector
==3050== Copyright (C) 2002-2011, and GNU GPL'd, by Julian Seward et al.
==3050== Using Valgrind-3.7.0 and LibVEX; rerun with -h for copyright info
==3050== Command: ./prueba
==3050==
Hola, soy ����.
Y tengo 27 anios.
==3050==
==3050== HEAP SUMMARY:
==3050== in use at exit: 0 bytes in 0 blocks
==3050== total heap usage: 0 allocs, 0 frees, 0 bytes allocated
==3050==
==3050== All heap blocks were freed -- no leaks are possible
==3050==
==3050== For counts of detected and suppressed errors, rerun with: -v
==3050== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 5 from 3)
En este caso valgrind no detecta nada fuera de lo común:

Ahora vamos a romper mas todavía nuestro código utilizando una variable no inicializada:
#include <stdio.h>

int main()
{
int edad;
char nombre[]="Dario Clavijo";

printf("Hola, soy %s.\n");
printf("Y tengo %d anios.\n", edad);

return 0;
}
Compilamos y el compilador ya se nos va a quejar indicando que en la linea 9 hay una variable no inicializada
daedalus@neuromancer:~/code/LCTHWC$ gcc prueba.c -o prueba.c -g -Wall -o prueba
prueba.c: In function ‘main’:
prueba.c:8:7: warning: format ‘%s’ expects a matching ‘char *’ argument [-Wformat]
prueba.c:6:12: warning: unused variable ‘nombre’ [-Wunused-variable]
prueba.c:9:13: warning: ‘edad’ is used uninitialized in this function [-Wuninitialized]
Ejecutamos y obtenemos nuevamente basura:
daedalus@neuromancer:~/code$ ./prueba
Hola, soy ����.
Y tengo 268353524 anios.
Ahora vamos a ejecutar con valgrind:
daedalus@neuromancer:~/code$ valgrind ./prueba
==3062== Memcheck, a memory error detector
==3062== Copyright (C) 2002-2011, and GNU GPL'd, by Julian Seward et al.
==3062== Using Valgrind-3.7.0 and LibVEX; rerun with -h for copyright info
==3062== Command: ./prueba
==3062==
Hola, soy �� �.
==3062== Conditional jump or move depends on uninitialised value(s)
==3062== at 0xFE72688: vfprintf@@GLIBC_2.4 (vfprintf.c:1619)
==3062== by 0xFE78843: printf@@GLIBC_2.4 (printf.c:35)
==3062== by 0x100004F3: main (prueba.c:9)
==3062==
==3062== Use of uninitialised value of size 4
==3062== at 0xFE6CA50: _itoa_word (_itoa.c:195)
==3062== by 0xFE70577: vfprintf@@GLIBC_2.4 (vfprintf.c:1619)
==3062== by 0xFE78843: printf@@GLIBC_2.4 (printf.c:35)
==3062== by 0x100004F3: main (prueba.c:9)
==3062==
==3062== Conditional jump or move depends on uninitialised value(s)
==3062== at 0xFE6CA58: _itoa_word (_itoa.c:195)
==3062== by 0xFE70577: vfprintf@@GLIBC_2.4 (vfprintf.c:1619)
==3062== by 0xFE78843: printf@@GLIBC_2.4 (printf.c:35)
==3062== by 0x100004F3: main (prueba.c:9)
==3062==
==3062== Conditional jump or move depends on uninitialised value(s)
==3062== at 0xFE6F6A4: vfprintf@@GLIBC_2.4 (vfprintf.c:1619)
==3062== by 0xFE78843: printf@@GLIBC_2.4 (printf.c:35)
==3062== by 0x100004F3: main (prueba.c:9)
==3062==
==3062== Conditional jump or move depends on uninitialised value(s)
==3062== at 0xFE6F710: vfprintf@@GLIBC_2.4 (vfprintf.c:1619)
==3062== by 0xFE78843: printf@@GLIBC_2.4 (printf.c:35)
==3062== by 0x100004F3: main (prueba.c:9)
==3062==
==3062== Conditional jump or move depends on uninitialised value(s)
==3062== at 0xFE6F9A8: vfprintf@@GLIBC_2.4 (vfprintf.c:1619)
==3062== by 0xFE78843: printf@@GLIBC_2.4 (printf.c:35)
==3062== by 0x100004F3: main (prueba.c:9)
==3062==
==3062== Conditional jump or move depends on uninitialised value(s)
==3062== at 0xFE6F754: vfprintf@@GLIBC_2.4 (vfprintf.c:1619)
==3062== by 0xFE78843: printf@@GLIBC_2.4 (printf.c:35)
==3062== by 0x100004F3: main (prueba.c:9)
==3062==
==3062== Conditional jump or move depends on uninitialised value(s)
==3062== at 0xFE6F788: vfprintf@@GLIBC_2.4 (vfprintf.c:1619)
==3062== by 0xFE78843: printf@@GLIBC_2.4 (printf.c:35)
==3062== by 0x100004F3: main (prueba.c:9)
==3062==
Y tengo 268042228 anios.
==3062==
==3062== HEAP SUMMARY:
==3062== in use at exit: 0 bytes in 0 blocks
==3062== total heap usage: 0 allocs, 0 frees, 0 bytes allocated
==3062==
==3062== All heap blocks were freed -- no leaks are possible
==3062==
==3062== For counts of detected and suppressed errors, rerun with: -v
==3062== Use --track-origins=yes to see where uninitialised values come from
==3062== ERROR SUMMARY: 24 errors from 8 contexts (suppressed: 5 from 3)
Ahora podemos ver que valgrind odio nuestro código y nos vomito con todas sus quejas indicándonos en que lineas de código están nuestros errores

Si nos fijamos en esta porción del dump de valgrind:
==3062== Conditional jump or move depends on uninitialised value(s)
==3062== at 0xFE72688: vfprintf@@GLIBC_2.4 (vfprintf.c:1619)
==3062== by 0xFE78843: printf@@GLIBC_2.4 (printf.c:35)
==3062== by 0x100004F3: main (prueba.c:9)
Esta es la primera utilización de la variable no inicializada o sucia,
En la primera linea nos indica cual es el problema y en la cuarta linea que se trata de el archivo prueba.c y que linea es donde se encuentra el error es la 9.

Este error lo vamos a ver repetidas veces como se utilice la variable no inicializada o sucia por eso vemos los mensajes de "Conditional jump or move depends on uninitialised value(s)" se repiten varias veces."

Ahora vamos a probar con otro error mas severo, partiendo del siguiente codigo:
#include <stdio.h>

int main()
{
int edad=27;
char nombre[]="Dario";
char apellido[]="Clavijo";
char letra = 'G';

printf("Hola, soy %s.\n",nombre);
printf("Y tengo %d anios.\n", edad);
printf("Mi nombre completo es %s %c %s.\n",nombre,letra,apellido);
return 0;
}
Si lo compilamos podemos ver que el compilador no se queja:

daedalus@neuromancer:~/code$ gcc prueba.c -o prueba.c -g -Wall -o prueba

Incluso al ejecutarlo podemos ver que todo esta correcto:
daedalus@neuromancer:~/code$ ./prueba
Hola, soy Dario.
Y tengo 27 anios.
Mi nombre completo es Dario G Clavijo.
Ahora vamos a romper el código:
#include <stdio.h>

int main()
{
int edad=27;
char nombre[]="Dario";
char apellido[]="Clavijo";
char letra = 'G';

printf("Hola, soy %s.\n",nombre);
printf("Y tengo %d anios.\n", edad);
printf("Mi nombre completo es %s %c %s.\n",letra,letra,apellido);
return 0;
}
El compilador ya se nos queja:
daedalus@neuromancer:~/code$ gcc prueba.c -o prueba.c -g -Wall -o prueba
prueba.c: In function ‘main’:
prueba.c:12:7: warning: format ‘%s’ expects argument of type ‘char *’, but argument 2 has type ‘int’ [-Wformat]
Ejecutamos y plaf!:
daedalus@neuromancer:~/code$ ./prueba
Hola, soy Dario.
Y tengo 27 anios.
Violación de segmento

Obtenemos un error feo (una violación de segmento)

Ahora vamos a debuguear nuestro error feo y tan odiado por valgrind:
daedalus@neuromancer:~/code$ valgrind ./prueba
==4012== Memcheck, a memory error detector
==4012== Copyright (C) 2002-2011, and GNU GPL'd, by Julian Seward et al.
==4012== Using Valgrind-3.7.0 and LibVEX; rerun with -h for copyright info
==4012== Command: ./prueba
==4012==
Hola, soy Dario.
Y tengo 27 anios.
==4012== Invalid read of size 1
==4012== at 0xFFB9A34: strlen (in /usr/lib/valgrind/vgpreload_memcheck-ppc32-linux.so)
==4012== by 0xFE700FF: vfprintf@@GLIBC_2.4 (vfprintf.c:1620)
==4012== by 0xFE78843: printf@@GLIBC_2.4 (printf.c:35)
==4012== by 0x10000543: main (prueba.c:12)
==4012== Address 0x47 is not stack'd, malloc'd or (recently) free'd
==4012==
==4012==
==4012== Process terminating with default action of signal 11 (SIGSEGV)
==4012== Access not within mapped region at address 0x47
==4012== at 0xFFB9A34: strlen (in /usr/lib/valgrind/vgpreload_memcheck-ppc32-linux.so)
==4012== by 0xFE700FF: vfprintf@@GLIBC_2.4 (vfprintf.c:1620)
==4012== by 0xFE78843: printf@@GLIBC_2.4 (printf.c:35)
==4012== by 0x10000543: main (prueba.c:12)
==4012== If you believe this happened as a result of a stack
==4012== overflow in your program's main thread (unlikely but
==4012== possible), you can try to increase the size of the
==4012== main thread stack using the --main-stacksize= flag.
==4012== The main thread stack size used in this run was 8388608.
Mi nombre completo es ==4012==
==4012== HEAP SUMMARY:
==4012== in use at exit: 0 bytes in 0 blocks
==4012== total heap usage: 0 allocs, 0 frees, 0 bytes allocated
==4012==
==4012== All heap blocks were freed -- no leaks are possible
==4012==
==4012== For counts of detected and suppressed errors, rerun with: -v
==4012== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 5 from 3)
Violación de segmento
La siguiente porción de código nos indica el mensaje "invalid read of size 1" esto es porque tratamos de asignar una variable tipo char de largo 1 byte.
El mensaje "Address 0x47 is not stack'd, malloc'd or (recently) free'd" nos indica que la dirección donde intento acceder no estaba ni reservada ni liberada.
==4012== Invalid read of size 1
==4012== at 0xFFB9A34: strlen (in /usr/lib/valgrind/vgpreload_memcheck-ppc32-linux.so)
==4012== by 0xFE700FF: vfprintf@@GLIBC_2.4 (vfprintf.c:1620)
==4012== by 0xFE78843: printf@@GLIBC_2.4 (printf.c:35)
==4012== by 0x10000543: main (prueba.c:12)
==4012== Address 0x47 is not stack'd, malloc'd or (recently) free'd
En la  siguiente porcion de codigo podemos ver que el programa fue terminado por una senial 11 "Process terminating with default action of signal 11 (SIGSEGV)" enviada por el kernel para matar el proceso.
En la segunda linea nos indica que el programa trato de acceder a una región de memoria no mapeada y por eso se dio un stack overflow.
==4012== Process terminating with default action of signal 11 (SIGSEGV)
==4012== Access not within mapped region at address 0x47
==4012== at 0xFFB9A34: strlen (in /usr/lib/valgrind/vgpreload_memcheck-ppc32-linux.so)
==4012== by 0xFE700FF: vfprintf@@GLIBC_2.4 (vfprintf.c:1620)
==4012== by 0xFE78843: printf@@GLIBC_2.4 (printf.c:35)
==4012== by 0x10000543: main (prueba.c:12)
==4012== If you believe this happened as a result of a stack
==4012== overflow in your program's main thread (unlikely but
==4012== possible), you can try to increase the size of the
==4012== main thread stack using the --main-stacksize= flag.
==4012== The main thread stack size used in this run was 8388608.
Mi nombre completo es ==4012==
Conlusion: Utilizando las opciones -g -Wall cuando compilamos y valgrind para debuguear nuestro programa durante la ejecución podemos escribir código mas seguro y limpio.

Bookmark and Share

domingo, 26 de febrero de 2012

fwknop: Single Packet Authorization and Port Knocking

Fwknop es una herramienta que permite agregar un nivel mas de seguridad en nuestra red.
Si lo que queremos es ser invisibles hacia fuera y asegurarnos que nadie pueda entrar en nuestros servicios por lo general configuramos el netfilter para que descarte cualquier paquete entrante a nuestra WAN.
Pero en este caso además de poder hacer eso vamos a incluso poder tener servicios accesibles como ssh que estarán escuchando en un puerto cerrado por netfilter y para eso esta herramienta nos vendrá a la mano.

Esta herramienta se encargara de ejecutar una vieja técnica llamada portknoking enviando un solo paquete autenticado (SPA) con AES (cifrado de bloques Rijndael de clave simétrica de hasta 128 bits) o mediante GPG y si la autenticación es exitosa el puerto nos quedara abierto por un intervalo de 10 segundos.

La diferencia básica entre AES y GPG es que AES al ser un algoritmo de cifrado simétrico con claves mas chicas permite el mismo nivel de seguridad que GPG(Asimétrico).

Ahora vamos a describir como se compone un paquete SPA

- 16 bytes de datos aleatorios
- usuario local
- timestamp local
- fwknop versión
- modo (acceso o comando)
- acceso deseado o comando a ejecutar
- MD5 sum

Los primeros 16 bytes de datos al azar se emplean para asegurarse de que cada paquete sea único permitiendo al servidor mantener un cache de mensajes SPA recibidos para frustrar los ataques de replay.
Luego se incluye el usuario local por razones de privilegios del lado del servidor.
El modo le indicara al servidor si lo que debe hacer es ejecutar un comando o permitir el acceso a un puerto.
Luego cada campo se separa por : y el mensaje entero se codifica con base64.
Por ultimo se emplea uno de los dos algoritmos de cifrado o AES o GPG.
Ahora vamos a probar la herramienta:

Primero instalamos fwknop

Servidor(como root):

apt-get install fwknop-server

Cliente(como root):

apt-get install fwknop-client

Configuramos el servidor de fwknop archivo: /etc/fwknop/access.conf:

SOURCE: ANY;
OPEN_PORTS: tcp/22;
KEY: <encrypt_key>
FW_ACCESS_TIMEOUT: 10;
REQUIRE_USERNAME: dclavijo;
DATA_COLLECT_MODE: ULOG_PCAP;

En modo servidor fwknop puede adquirir los paquetes de una interfaz en modo promiscuo usando libpcap.

Ahora en el cliente verificamos que no podemos establecer la conexión

[cliente]$ nc -v 192.168.1.1 22

Hasta ahora bien parece que netcat se cuelga porque no puede completar la conexión simplemente porque el netfilter del servidor descarto el paquete SYN antes de que pudiese acceder al stack tcpip.

Ahora para poder acceder a nuestro puerto mandamos el paquete SPA

daedalus@neuromancer:~/code/fwknop-2.0/client$ ./fwknop -A tcp/22 -s -D 192.168.1.1 -v
Enter encryption password:

FKO Field Values:
=================

Random Value: 9854906951929078
Username: daedalus
Timestamp: 1330307367
FKO Version: 1.9.12
Message Type: 1
Message String: 0.0.0.0,tcp/22
Nat Access:
Server Auth:
Client Timeout: 0
Digest Type: 3

Encoded Data: 9854906951929078:ZGFlZGFsdXM:1330307367:1.9.12:1:MC4wLjAuMCx0Y3AvMjI

SPA Data Digest: Oo2OlFRdw3SjO2xKlsFuSxy62zVPuMYRNg41xvQAy+Y

Final Packed/Encrypted/Encoded Data:

9xZYQdwWlSOaUPue68lN0hn+7T8zSzKU9b/PtGbdVhmci4e4jXELn/EzoyFvtXMWKBihMFEkE+SiUQ+mS6zcwLod+r7n/DbEGWQbBx/Q51f9GszQ+KHlH49iy/KupGC9BGvBG+1J4C3JtzJBd1aAS+mcWX4Y8rCyQ

Generating SPA packet:
protocol: udp
port: 62201
IP/host: 192.168.1.1
send_spa_packet: bytes sent: 161

Ahora tranquilamente podemos conectarnos al servidor:

[cliente]$ nc -v 192.168.1.1 22
192.168.1.1 22 (ssh) open
SSH-2.0-OpenSSH_5.5p1 Debian-6

Ahora la instancia del proceso de Fwknop que esta corriendo en el servidor configuro el netfilter para que desde nuestra ip los paquetes hacia el puerto 22 no sean descartados.
Incluso después de 10 segundos fwknop expirara la sesión volviendo al estado por defecto pero esto no implica que perdamos la conexión ya establecida sino que futuras conexiones serán descartadas.

Incluso fwknop es invulnerable a los ataques de replay, supongamos que un atacante fue capaz de escuchar el mensaje SPA que se envió desde el cliente al servidor al puerto UDP 62201.

[atacante]# tcpdump -i wlan0 -c 1 -s 0 -l -nn -X udp port 62201
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on wlan0, link-type EN10MB (Ethernet), capture size 65535 bytes
23:49:29.049157 IP 192.168.2.179.51065 > 192.168.1.1.62201: UDP, length 161
0x0000: 4500 00bd efc6 4000 4011 c564 c0a8 02b3 E.....@.@..d....
0x0010: c0a8 0101 c779 f2f9 00a9 cb79 3978 5a59 .....y.....y9xZY
0x0020: 5164 7757 6c53 4f61 5550 7565 3638 6c4e QdwWlSOaUPue68lN
0x0030: 3068 6e2b 3754 387a 537a 4b55 3962 2f50 0hn+7T8zSzKU9b/P
0x0040: 7447 6264 5668 6d63 6934 6534 6a58 454c tGbdVhmci4e4jXEL
0x0050: 6e2f 457a 6f79 4676 7458 4d57 4b42 6968 n/EzoyFvtXMWKBih
0x0060: 4d46 456b 452b 5369 5551 2b6d 5336 7a63 MFEkE+SiUQ+mS6zc
0x0070: 774c 6f64 2b72 376e 2f44 6245 4757 5162 wLod+r7n/DbEGWQb
0x0080: 4278 2f51 3531 6639 4773 7a51 2b4b 486c Bx/Q51f9GszQ+KHl
0x0090: 4834 3969 792f 4b75 7047 4339 4247 7642 H49iy/KupGC9BGvB
0x00a0: 472b 314a 3443 334a 747a 4a42 6431 6141 G+1J4C3JtzJBd1aA
0x00b0: 532b 6d63 5758 3459 3872 4379 51 S+mcWX4Y8rCyQ
1 packets captured
1 packets received by filter
0 packets dropped by kernel

Ahora el atacante mediante la herramienta netcat puede reenviarnos el paquete SPA

[attacker]$ echo "9xZYQdwWlSOaUPue68lN0hn+7T8zSzKU9b/PtGbdVhmci4e4jXELn/EzoyFvtXMWKBihMFEkE+SiUQ+mS6zcwLod+r7n/DbEGWQbBx/Q51f9GszQ+KHlH49iy/KupGC9BGvBG+1J4C3JtzJBd1aAS+mcWX4Y8rCyQ" |nc -u 192.168.1.1 62201

Y simplemente en el syslog podemos ver que se recibió un mensaje pero no se realizo ninguna acción.

Feb 26 23:30:11 server fwknop: attempted message replay from: 192.168.1.179

Bookmark and Share

Passwords faciles y muy fuertes

El cómic de XKCD lo dice todo: en vez de utilizar un password ultra complicado y corto deberíamos utilizar passwords largos y fáciles de acordar.

Bookmark and Share

viernes, 17 de febrero de 2012

SEAndroid: el Android de la NSA

Cerca de el año 2000 la NSA creo una serie de patches para el kernel linux llamados SELinux, los cuales brindaban una serie de medidas extras de seguridad.
Ahora la NSA libero el código de una serie de patches sobre el ecosistema Android con el fin de hacerlo mas "seguro".
En un principio y a groso modo se porto el parche de SELinux al kernel de Android.

A continuación cito las principales características de SEAndroid:

*Per-file security labeling support for yaffs2,
*Filesystem images (yaffs2 and ext4) labeled at build time,
*Kernel permission checks controlling Binder IPC,
*Labeling of service sockets and socket files created by init,
*Labeling of device nodes created by ueventd,
*Flexible, configurable labeling of apps and app data directories,
*Userspace permission checks controlling use of the Zygote socket commands,
*Minimal port of SELinux userspace,
*SELinux support for the Android toolbox,
*Small TE policy written from scratch for Android,
*Confined domains for system services and apps,
*Use of MLS categories to isolate apps.

Fuente [http://www.kriptopolis.org/node/8988]

Bookmark and Share