Evadiendo por la via rápida el challenge de CloudFlare for-fun-and-profit

Para hoy tenemos una entrada de Vicente, un antiguo compañero de S2 Grupo, administrador de sistemas y fanático de la naturaleza. Se le puede encontrar en su twitter @vicendominguez y en su blog.

¡Buenos días amigos! Vuestro sysadmin favorito al teclado hoy. Vamos a la materia.

En el mercado existen multitud de herramientas para pentesting. Todas ellas incorporan sistemas de evasión tanto de IDS, como WAF etc.; nmap incorpora fragmentación, cloak, decoys, spoof, custom data packet; sqlmap lleva check-waf y un montón de tampers, y así casi-todas. Echadle un vistazo porque la lista de aplicaciones que usáis todos los días para vuestros pentest favoritos que incorporan estos extras es considerable. Además tienen cierto éxito con los habituales sistemas de protección/detección. Ya sabéis de lo que hablo. En detección: snort, suricata…; en protección-waf: modsecurity, naxsi… y todos estos requieren algunos esfuerzos “extras” para detectar todas las posibilidades existentes. Bien. Fantástico.

Y luego de todo esto, tenemos CloudFlare.

CloudFlare es una compañía de protección de ataques que ofrece varios productos de protección al mercado. Desde la filtración de todo el tráfico de vuestro AS (BGP) hasta una “simple” capa de proxys-inversos-mejorados pasando por otros filtrados intermedios usando túneles GRE. Mas allá, los señores de CloudFlare han diseñado su core a través de filtros BPF sobre versiones adaptadas de iptables. Por cierto, lo de los filtros BPF miradlo porque es todo una ciencia de trabajo sobre código binario en los paquetes de red. Eso sí, la heurística que acompaña a estas tecnologías es el know-how puro y duro, que nuestros compañeros de CloudFlare esconden más que la fórmula de la conocida bebida carbonatada.

Resulta que CloudFlare, además, gestiona un sistema de reputación de IPs y, además de las mismas virtudes de otros WAF e IDS y de las suyas propias (BPF Filters), ellos aportan su “maravilloso challenge” construido sobre su sistema de reputación. De esta forma, un buen número de visitas a una página con CloudFlare desde la red Tor os pedirá un captcha para continuar. En algunos nodos os aseguro que lo hace casi siempre y que es muuuuuuuy pesado cuando se “surfea” un rato. No son todos.

CloudFlare realmente tiene dos sistemas de “challenge”: uno basado en javascript y otro basado en captchas.


(CF captcha)

El challenge de captchas, que ya hemos comentado que nos comemos sí-o-sí cuando venimos por Tor, no tiene demasiada vuelta de tuerca. Hay que manualmente gestionar ese challenge. Y dado que es un sistema visual, no hay narices de soltar una sola auditoria inicial automática contra esos sites de primeras.

El segundo desafío está basado en javascript. Desconozco si el cliente de CF puede forzar un desafío u otro. Después de mucho probar puedo decir que este desafío aparece generalmente cuando nuestras IPs de acceso a la web con CloudFlare no tienen buena reputación o la web esta siendo atacada en ese momento. He conseguido también que en una web donde no se me pedía al principio después de soltar un nikto aparezca el javascript de turno a mitad del proceso.


(CF Javascript)

El resultado de este challenge que introduce CF es que cualquier aplicación se vuelve loca. No funciona sqlmap, no funciona nikto, no funcionan los scripts de nmap, falsos positivos a mansalva… basura. Así que si te toca hacerle un pentest perimetral y te encuentras con la protección de CloudFlare, lo vas a pasar mal y te vas a encontrar con un “todo falso positivo”. Es decir: Bye-bye fácil, hola todo-manual. Sin embargo, eso no quita que la aplicación real no tenga alguna cosa. Lo que sí significa es que no se puede mirar con las herramientas de la forma habitual. Comprad donuts que va para largo.

Si analizamos un poco mas el desafío javascript de CloudFlare es una fórmula aritmética que se envía en un GET a una URL concreta y finalmente nos da una nueva cookie para esa sesión. Así que si quisiéramos pasarlo, el objetivo es obtener esa cookie. Evidentemente, eso se puede hacer manualmente. Pero si queremos automatizarlo, porque son varios hosts o varias tools las que vamos a lanzar a la vez y además de continuo porque nos dedicamos a esto (cosa que no es mi caso porque yo soy del sector del streaming de vídeo), entonces podemos darle una mini vuelta.

Es aquí donde entra cualquier sistema que pueda interactuar con javascript. Nos vale node con casper.js o nos vale pyV8 (python) como cloudflare-scrape. Cualquier cosa que hable con la V8 de Chrome desde terminal nos sirve. Tengo algún test hecho en casper.js para otros menesteres, pero meter en el sistema node y demás cosillas no me apetecía. He hecho las pruebas con cloudflare-scrape en python.

Como os decía, el challenge es (pego de nuevo): “una fórmula aritmética que se envía en un GET a una URL concreta y finalmente nos da una nueva cookie para esa sesión”.

Si miramos el código del scrape, los operandos de la fórmula aritmética se obtienen aquí:

challenge = re.search(r'name="jschl_vc" value="(\w+)"', page).group(1)
builder = re.search(r"setTimeout.+?\r?\n([\s\S]+?a\.value =.+?)\r?\n", page).group(1)
builder = re.sub(r"a\.value =(.+?) \+ .+?;", r"\1", builder)
builder = re.sub(r"\s{3,}[a-z](?: = |\.).+", "", builder)

Con builder hay que ejecutarlo (eval) y darle una transformación más:

answer = str(int(ctxt.eval(builder)) + len(domain))

challenge y ahora answer son mandados al famoso GET de la URL de turno:

params = {"jschl_vc": challenge, "jschl_answer": answer}
submit_url = "%s://%s/cdn-cgi/l/chk_jschl" % (parsed.scheme, domain)
headers["Referer"] = url
return requests.get(submit_url, params=params, headers=headers, **kwargs)

Este es el kit y es mérito de Anorov. Simplemente comentar que la libreria de python funciona de perlas. Nosotros solo nos la adaptamos a nuestro objetivo: conseguir la cookie. La cookie realmente son dos. Matizo más: para conseguir la integridad de todo, CloudFlare utiliza dos cookies más tu IP más el dominio de destino más tu user-agent. Ojo con esto último que nos afectará a las tools.

Para obtener las cookies, creamos un script (sysadmin aficionado, el “código-molón” os lo dejo a vosotros):

import sys
import requests
import cfscrape

sess = requests.session()
sess.mount("http://", cfscrape.CloudflareAdapter())
sess.get (sys.argv[1])

print "cf_clearance=%s;__cfduid=%s" % (sess.cookies["cf_clearance"],
       sess.cookies["__cfduid"])

cfcookie.py de nombre, amigos. Y con ansias, empezamos las pruebas. Modo normal:

SITE=www.sitewithcloudflarechallenge.com; curl -s -s $SITE -A 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/34.0.1847.116 Chrome/34.0.1847.116 Safari/537.36' |grep title

<title>Just a moment...</title>

Ese title es el del desafío de CloudFlare, no el de la web que buscamos, así que allá vamos de nuevo ;)

SITE=www.sitewithcloudflarechallenge.com; curl --cookie `./cfcookie.py $SITE` -s $SITE -A 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/34.0.1847.116 Chrome/34.0.1847.116 Safari/537.36' |grep title

<title>sitewithcloudflarechallenge</title>

¡TADA! Ojo que el user-agent es el mismo en cfscrape.py. Maravilloso. Ya os vienen las ideas, ¿verdad? Efectivamente un:

sqlmap —cookie `./cfcookie.py $SITE` -...

También funciona. Acordaos del user-agent. Ahora tu mundo vuelve a ser infinito. ¡Power!

Nikto es diferente porque no permite una cookie por línea de comandos. Para que veáis la prueba, sólo para la prueba, hacen falta dos terminales: la de ejecución del nikto y una con un tcpdump para ver las respuestas. Para el tcpdump he usado la línea:

tcpdump -A -s0 port 80 |grep title

Se puede usar un tshark o un ngrep. Sí, me encantan todas. ¿Os he dicho que tshark lleva un sistema de reporting de paquetes que se puede usar para evaluar ataques DDOS?

En la otra ventana lanzamos nikto normal contra un sistema que nos pida el challenge de javascript. Parece ir en principio, pero mientras vemos en la primera ventana que todo da 200 OK en la segunda, en el tcpdump vemos lo siguiente:

  <title>Just a moment...</title>
  <title>Just a moment...</title>
  <title>Just a moment...</title>
  <title>Just a moment...</title>
  <title>Just a moment...</title>
  <title>Just a moment...</title>
  <title>Just a moment...</title>
  <title>Just a moment...</title>
  <title>Just a moment...</title>
  <title>Just a moment...</title>
  <title>Just a moment...</title>
  <title>Just a moment...</title>
  <title>Just a moment...</title>
  <title>Just a moment...</title>
  <title>Just a moment...</title>
  <title>Just a moment...</title>
  <title>Just a moment...</title>
  <title>Just a moment...</title>

Así que no va. Todo lo que veis es falso-positivo-vergonzoso-de-la-muerte.

Nikto utiliza una STATIC-COOKIE pero en el fichero de configuración que trae. Dicha variable usa comillas así que tendríamos que adaptar el script. Eso ya os lo dejo a vosotros. Pero, una vez adaptado es ejecutarlo y pegarlo en el configfile:

python myscriptadaptadocf.py http://www.xxxxxxxxxxx.com/
STATIC-COOKIE=“cf_clearance"="4960843aaaaaaaaaaaaaaaaa42c4bdb23aaaaaaaaaaa-aaaaaaaaaaa3-1205";"__cfduid"="df59aaaaaaaaaaaaaaaaaaaa53"

Pegamos en la config, lanzamos de nuevo y el tcpdump dice que:

<title>HOLAHOLAWEBSITE</title>
<title>HOLAHOLAWEBSITE</title>
<title>404 Not Found</title>
<title>404 Not Found</title>
<title>404 Not Found</title>
<title>404 Not Found</title>
<title>404 Not Found</title>
<title>404 Not Found</title>
<title>404 Not Found</title>
<title>404 Not Found</title>
<title>404 Not Found</title>

Ryu, Yooouuu win!

Bueno, pues esto es todo. Me he alargado en favor de la comprensión y la solución del problema pero esto es más simple que el mecanismo de un botijo: se trata de usar un pequeño trick que ya estaba hecho.

El “azuquita-del-bueno” para terminar por mi parte es que me he programado un proxy.

Prácticamente todas las utilidades soportan un http proxy. Y es maravilloso: la idea es que cuando paso por el proxy me hace el challenge solo. El tema es que lo de programar… pues ejem… se me cae y a veces da resultados incongruentes… y a veces hace cosas raras y… en fin… os presto la idea para que la podáis trabajar los que os dedicáis a esto.

NOTA: la instalación de pyV8 sobre Centos 6 la tengo descrita en el blog (fácil-fácil oigan).

Un saludo y hasta la próxima.

Comments

  1. woo interesante, realmente lo necesito para node. A ver si lo consigo. Cualquier ayuda me viene genial.

    Un saludo.