Nivel 13 del Hack It 2008 (Euskal Encounter 16)
Tal y como hice con el post anterior relativo a la resolución del nivel 7 del Hack It 2008, paso a comentar mi propuesta de “solución oficial” para otro de los retos que remití al Call for Levelz y que Txipi tuvo a bien incluir como nivel 13 del concurso.
Cuando el jugador accede a este nivel, lo único que se le dice es que las instrucciones para pasar al siguiente nivel se encuentran en este fichero.
Bajado el fichero y descomprimido, se obtiene un fichero de texto LEEME, en donde se encuentra el siguiente texto:
Consigue conectar con el servicio alojado en A.B.C.D:31337 para obtener la contraseña cifrada del siguiente nivel. Una vez obtenida, podré¡s descifrarla fácilmente usando preguntas ;-)
Llegados a este punto no hay mucho que pensar, nos dicen que nos conectemos al puerto 31337 de una máquina concreta, y que se nos dará la contraseña… demasiado fácil parece…
Si vamos a lo directo, se prueba a hacer un telnet a ver qué pasa…
# telnet A.B.C.D 31337 Trying to A.B.C.D...
Y como era de esperar, no pasa absolutamente nada ;)
Tiene pinta de que quizás se esté filtrando el acceso de alguna forma. Una método para obtener algo más de información es empezar a mirar al tráfico de red, quizás se pueda descubrir algo interesante… cualquier sniffer es válido para esto, por ejemplo el snoop, el tcpdump o el wireshark.
Si se vuelve a hacer el telnet, esta vez mirando el tráfico generado, se obtendría algo similar a lo siguiente:
W.X.Y.Z.1756 > A.B.C.D.31337: S 3600327019:3600327019(0) win 5840 A.B.C.D.31337 > W.X.Y.Z.1756: S 3603973084:3603973084(0) ack 2708264600 win 5792 W.X.Y.Z.1756 > A.B.C.D.31337: R 2708264600:2708264600(0) win 0
Analizando el establecimiento de conexión TCP, se puede observar como ésta no se llega a establecer, finaliza de forma abrupta mediante un RST… pero no lo envía el servidor, lo enviamos nosotros, el cliente.
Curioso… somos nosotros como cliente los que cortamos la conexión. Analicemos la situación un poco más en detalle, ¿cómo funciona el three-way-handshake de TCP?
Cuando un cliente TCP quiere conectar con un servidor en un puerto determinado, envía un segmento TCP con el flag SYN activado y un número de secuencia NS1.
El servidor, si acepta la conexión, envia un segmento TCP al cliente con los flags SYN y ACK activados, su propio número de secuencia NS2 y en el número de secuencia ACK, el servidor confirma el segmento recibido del cliente, estableciendo un valor de NS1+1
El cliente, da el último paso en el establecimiento de la conexión, enviando un segmento TCP con el flag ACK activado, y el número de secuencia ACK enviado por el cliente será el número de secuencia enviado por el servidor + 1, es decir, NS2+1.
En nuestro caso concreto, el cliente no está enviando un ACK, sino un RST. Así que no se está cumpliendo lo que he dicho respecto a los flags. Y el tema de los números de secuencia, ¿se está cumpliendo?. Analicemos los números de secuencia de este establecimiento de conexión:
- El cliente envía el número de secuencia 3600327019
- El servidor envía el número de secuencia 3603973084 y confirma el número de secuencia del cliente 2708264600
- El cliente manda un RST indicando el número de secuencia que ha confirmado el servidor, para indicarle que no es correcto, ya que el cliente hubiese esperado recibir el número de secuencia de confirmación 3600327020 y no el 2708264600
El problema está sin lugar a dudas en los números de secuencia que nos envía el servidor, puede que esté siendo manipulado en tránsito, una vez ha salido del servidor y antes de llegar al cliente.
Parece que este puede ser un camino interesante de explorar… ¿cuál es la manipulación que se puede estar realizando en los números de secuencia?. Una forma de intentar sacar alguna regla es probar con valores sencillos, como 0, 1, 2… y ver qué transformación sufren.
Usando alguna herramienta de generación de tráfico, se puede hacer una batería rápida de pruebas para analizar los resultados. Por ejemplo, usando hping obtenemos lo siguiente:
# hping2 -p 31337 -M 0 -S A.B.C.D -c 1 W.X.Y.Z.1061 > A.B.C.D.31337: S 0:0(0) win 512 A.B.C.D.31337 > W.X.Y.Z.1061: S 630531730:630531730(0) ack 65536 win 5840 W.X.Y.Z.1061 > A.B.C.D.31337: R 65536:65536(0) win 0 # hping2 -p 31337 -M 1 -S A.B.C.D -c 1 W.X.Y.Z.2284 > A.B.C.D.31337: S 1:1(0) win 512 A.B.C.D.31337 > W.X.Y.Z.2284: S 633754919:633754919(0) ack 131072 win 5840 W.X.Y.Z.2284 > A.B.C.D.31337: R 131072:131072(0) win 0 # hping2 -p 31337 -M 2 -S A.B.C.D -c 1 W.X.Y.Z.2157 > A.B.C.D.31337: S 2:2(0) win 512 A.B.C.D.31337 > W.X.Y.Z.2157: S 629760691:629760691(0) ack 196608 win 5840 W.X.Y.Z.2157 > A.B.C.D.31337: R 196608:196608(0) win 0
¿Qué está pasando?
- Enviamos 0×00000000 y recibimos 0×00010000
- Enviamos 0×00000001 y recibimos 0×00020000
- Enviamos 0×00000002 y recibimos 0×00030000
Parece claro que el servidor nos está cambiando los 16 bits de menor peso, por los de mayor peso:
- Envía 0×00010000 en vez de 0×00000001
- Envía 0×00020000 en vez de 0×00000002
- Envía 0×00030000 en vez de 0×00000003
Llegado a este punto ya sabemos cómo hay que modificar localmente lo que envía el servidor para que todo cuadre y que nuestra pila TCP/IP no rechace la conexión.
A partir de aquí hay muchas formas de hacerlo. Podemos hacernos un programa que manejando raw sockets haga el trabajo, otra opción es usar las bibliotecas libpcap y libnet para hacer un programilla en C y dejar el trabajo sucio de manejar los sockets a la libreria, quizás otra opción sería usar scapy con python para hacerlo…
La solución que propongo para el cliente está basada en una regla iptables que coge todo el tráfico entrante, lo manipula y lo reinyecta, haciendo la manipulación del tráfico a nivel de proceso en espacio de usuario.
Las reglas iptables necesarias son las siguientes:
iptables -A OUTPUT -p tcp --dport 31337 -d A.B.C.D --tcp-flags RST RST -j DROP iptables -A INPUT -p tcp --sport 31337 -j QUEUE
El programa que realiza la manipulación del tráfico es el siguiente:
#include <linux/netfilter.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <libipq/libipq.h>
#include <stdio.h>
#include <stdlib.h>
#define BUFFERSIZE 4096
void exit_error(struct ipq_handle *ipq_h) {
ipq_perror("Queue Error:");
ipq_destroy_handle(ipq_h);
exit(2);
}
int main(int argc, char **argv) {
unsigned char buffer[BUFFERSIZE];
struct ipq_handle *ipq_h;
ipq_packet_msg_t *ipq_p;
struct iphdr *iph;
struct tcphdr *tcph;
unsigned long seq1, seq2;
if (!(ipq_h = ipq_create_handle(0, PF_INET))) {
exit_error(ipq_h);
}
if (ipq_set_mode(ipq_h, IPQ_COPY_PACKET, BUFFERSIZE) < 0) {
exit_error(ipq_h);
}
do {
if (ipq_read(ipq_h, buffer, BUFFERSIZE, 0) < 0) {
exit_error(ipq_h);
}
if (ipq_message_type(buffer) == IPQM_PACKET) {
ipq_p = ipq_get_packet(buffer);
iph = (struct iphdr *)ipq_p->payload;
tcph = (struct tcphdr *)(ipq_p->payload + iph->ihl*4);
seq1 = (ntohl(tcph->ack_seq) & 0x0000FFFF) << 16;
seq2 = (ntohl(tcph->ack_seq) & 0xFFFF0000) >> 16;
tcph->ack_seq = htonl(seq1 | seq2);
if (ipq_set_verdict(ipq_h, ipq_p->packet_id, NF_ACCEPT,
ipq_p->data_len, ipq_p->payload) < 0) {
exit_error(ipq_h);
}
}
} while(1);
ipq_destroy_handle(ipq_h);
return(0);
}
Para compilarlo en un Linux hace falta tener instalado el paquete iptables-dev. El comando de compilación sería el siguiente:
cc -o hackit hackit.c -lipq
Habiendo esto hecho y tras ejecutar el programilla, con un simple telnet podremos conectarnos al servidor y obtener los datos:
# telnet A.B.C.D 31337 Trying to A.B.C.D... Connected to A.B.C.D. Escape character is ‘^]’. NETWORKMXNGLXNG Connection closed by foreign host.
Prueba superada, a por el siguiente infierno... ;)
Comments
One Response to “Nivel 13 del Hack It 2008 (Euskal Encounter 16)”
Leave a Reply
En la prueba de la Euskal puse una pequeña trampa extra (una XOR del mensaje que devuelve el echo-server), para asegurarme de que la gente conseguía conectarse después de haber llegado al nivel que correspondía (en ese nivel se daba la pista para entender lo que el servidor respondía).
De esta manera, me evitaba (más o menos :-D), que la gente viera el servidor escaneando IPs y tratara de pegarse contra él sin ton ni son.