sábado, 23 de febrero de 2008

Publicidad web: Automatizando clics

Aunque la automatización del movimiento del mouse y la realización de clics tiene cientos de aplicaciones lícitas, a nadie se le escapa la posibilidad de usarlo para realizar clics fraudulentos en la los anuncios contextuales de la web.

La primera pregunta que se plantea uno ...

... es qué medidas de seguridad adoptarán las empresas que, como por ejemplo Google (Adsense), ofrecen este servicio.

Es lógico pensar que si todos los clics vienen desde la misma IP no se tendrán en cuenta. Pero esta barrera es fácil saltársela con sistemas de navegación anónima como Tor.

Pero las empresas que se dedican a estas cosas, que no son tontas, no dan esos clics por válidos.

Hasta aqui todo bien, pues sigue la lógica que cabría esperar.

El problema se produce en el momento que estas empresas deciden considerar al propietario de la web que recibe los clics como culpable de los mismos, eliminándole la cuenta.

Este hecho produce una consecuencia terrible: la aparición de un ataque de denegación de servicio.


La segunda pregunta que se plantea uno ...

... es cuanto se puede tardar en desarrollar un programa que mueva el mouse a una posición (x,y) y realice un clic sobre ella.

Después de googlear un poquito y realizar algunas pruebas la conclusión es extremecedora ... En unos 20 minutos el programa esta listo.


Cómo mover el mouse con XLib:

A continuación pego una función que mueve el puntero del mouse a la posición X,Y pasada como parámetro.

void mouse_move(int x, int y)
{
Display *display = XOpenDisplay(0);
Window root = DefaultRootWindow(display);
XWarpPointer(display, None, root, 0, 0, 0, 0, x, y);
XCloseDisplay(display);
}


Cómo hacer clic con XLib:

A continuación pego una función que hace clic sobre la posición actual del mouse. Para realizar clic con el botón derecho se usa button=0.

void mouse_click(int button)
{
Display *display = XOpenDisplay(NULL);
XEvent event;
memset(&event, 0x00, sizeof(event));
event.type = ButtonPress;
event.xbutton.button = button;
event.xbutton.same_screen = True;
XQueryPointer(display, RootWindow(display, DefaultScreen(display)), 
&event.xbutton.root, &event.xbutton.window, &event.xbutton.x_root,
&event.xbutton.y_root, &event.xbutton.x, &event.xbutton.y,
&event.xbutton.state);
event.xbutton.subwindow = event.xbutton.window;

while(event.xbutton.subwindow)
{
event.xbutton.window = event.xbutton.subwindow;
XQueryPointer(display, event.xbutton.window, &event.xbutton.root,
&event.xbutton.subwindow, &event.xbutton.x_root,
&event.xbutton.y_root, &event.xbutton.x, &event.xbutton.y,
&event.xbutton.state);
}

if(XSendEvent(display, PointerWindow, True, 0xfff, &event)==0)
fprintf(stderr, "XSendEvent()\n");

XFlush(display);
usleep(100000);

event.type = ButtonRelease;
event.xbutton.state = 0x100;

if(XSendEvent(display, PointerWindow, True, 0xfff, &event)==0)
fprintf(stderr, "XSendEvent()\n");

XFlush(display);
XCloseDisplay(display);
}




El programa:

Vistas las funciones anteriores resulta trivial desarrollar un programa:

int main(int argc, char* argv[])
{
if(argc!=3)
{
printf("Usage: %s [x coord] [y coord]\n\n", argv[0]);
return 0;
}

int x = atoi(argv[1]);
int y = atoi(argv[2]);
mouse_move(x, y);
mouse_click(0);
return 0;
}


Solo queda decir que las librerias necesarias de XLib son X11/Xlib.h y X11/Xutil.h
Y que el programa se compila con:

$ gcc click.c -lX11


Conclusiones:

La publicidad web genera mucho dinero, lo que la hace atractiva para todo intento de uso fraudulento. Sin embargo las empresas que se dedican a esto saben lo que hacen y no resulta sencillo tomarles el pelo.
Lo que quiero decir con esto es que el intento de ganar dinero haciendo clics en publicidad propia dificilmente llegará a buen término, por lo que no se lo recomiendo a nadie.

Para finalizar un comentario para las empresas que se dedican a cancelar la cuenta del usuario que recibe clics fraudulentos: mala idea.


domingo, 17 de febrero de 2008

Cómo funciona el exploit vmsplice


Despues del revuelo ocasinado por el bug vmsplice() en el kernel Linux vamos a realizar un pequeño análisis de su funcionamiento. Para el que no lo sepa, la explotación del bug vmsplice permite a un usuario del sistema convertirse en root. Esto ocurre en los kernels que van desde la versión 2.6.17 a la 2.6.24.1.

Algunos exploits disponibles son:

http://www.milw0rm.com/exploits/5092
http://www.milw0rm.com/exploits/5093


Veamos un ejemplo con el segundo exploit:

$ uname -r
2.6.23

$ gcc exploit.c
$ ./a.out
-----------------------------------
Linux vmsplice Local Root Exploit
By qaaz
-----------------------------------
[+] addr: 0xc011481b
[+] root
$ id
uid=0(root) gid=0(root) grupos=501(user)



La llamada al sistema vmsplice:

vmsplice es una llamada al sistema que puede ser accedida desde espacio de usuario mediante syscall().

syscall(__NR_vmsplice, fd, iov, nr_segs, flags);

Encontramos su implementación en fs/splice.c, en las fuentes del kernel Linux:





asmlinkage long sys_vmsplice(
int fd, const struct iovec __user *iov,unsigned long nr_segs, unsigned int flags){...}



Nos interesa prestar atención a los parámetros recibidos por la llamada al sistema, pues son los que el usuario puede controlar al llamar a la función.

Cuando un usuario llama a sys_vmsplice() se produce la siguiente secuencia de llamadas:
- sys_vmsplice() - vmsplice_to_pipe() - get_iovec_page_array()

Dado que el bug se encuentra en get_iovec_page_array() nos interesará saber qué parámetros de la misma puede controlar el usuario. Es decir, qué parámetros de sys_vmsplice() llegan hasta get_iovec_page_array() y si estos parámetros pasan por algun filtro durante el proceso.

Si leemos con atención las funciones implicadas vemos que desde la llamada a sys_vmsplice() hasta la ejecución de la función vulnerable get_iovec_page_array() se mantienen los parámetros: iov y nr_segs.



La función get_iovec_page_array():

La función get_iovec_page_array() es la función vulnerable que el exploit aprovecha para obtener privilegios de root. Como se ha comentado en el apartado anterior, el usuario puede controlar los parámetros iov y nr_segs de esta función.

Observemos el siguiente código de la función vulnerable:






struct iovec entry;
...

if(copy_from_user_mmap_sem(&entry, iov, sizeof(entry)))
break;

...

len = entry.iov_len;

...

npages = (off + len + PAGE_SIZE - 1) >> PAGE_SHIFT;

if (npages > PIPE_BUFFERS - buffers)
npages = PIPE_BUFFERS - buffers;

error = get_user_pages(current, current->mm,(unsigned long) base, npages, 0, 0,&pages[buffers], NULL);






"iov" es copiada en "entry" y el valor de "iov_len" asignado a la variable "len". Por lo tanto, el usuario tiene el control de la variable "len".
A continuación se calcula "npages" mediante off + len + PAGE_SIZE - 1.

En la función get_user_pages() se asume que npages es como mínimo 1. Dado que "len" no debe ser 0, off + len + PAGE_SIZE - 1 siempre será mayor o igual que PAGE_SIZE. Sin embargo, si "len" tiene un valor cercano a UINT32_MAX, entonces npages podría valer 0 (integer overflow).

Como consecuencia get_user_pages() podría retornar más de PIPE_BUFFERS entradas, lo que como veremos a continuación, produce un buffer overflow.


A continuación, en get_iovec_page_array(), viene el siguiente código:





for (i = 0; i < error; i++)
{
const int plen = min_t(size_t, len, PAGE_SIZE - off);
partial[buffers].offset = off;partial[buffers].len = plen;
off = 0;
len -= plen;buffers++;
}







El tamaño del array "partial" es PIPE_BUFFERS pero como hemos comentado, el valor de "error" será superior a este. Por lo tanto, las estructuras de datos que se encuentren a continuación de "partial" serán sobreescritas con "off" y "plen".

Normalmente la víctima de esta sobreescritura es el array "pages" que contiene punteros. En este caso, por ejemplo, se podria sobreescribir "pages" con valores NULL.



Cómo ejecutar código arbitrario:

Normalmente cuando el Kernel intenta acceder a un puntero NULL se obtiene una excepción que finaliza el proceso. Pero existe una técnica que puede ser usada por el atacanta para que en lugar de obtener un error se ejecute código arbitrario. Esta técnica consiste en mapear la dirección 0 y guardar allí datos que permitiran el control del usuario y la ejecución de código en el contexto del kernel.



Posibles soluciones:

Sin duda la mejor solución al problema consiste en actualizar el kernel o aplicar el parche correspondiente. Pero en casos de que eso no sea posible, por ejemplo, por la imposibilidad de reiniciar la máquina, puede usarse el siguiente módulo:

http://home.powertech.no/oystein/ptpatch2008/ptpatch2008.c



Referencias:
- Informe de McAfee Labs
- http://www.coseinc.com/coseinc_linux_advisory_3.pdf