Exploring the roots and future of Internet Relay Chat

El protocolo CTCP explicado: VERSION, TIME, PING, DCC y seguridad

CTCP es uno de esos protocolos que llevan décadas funcionando en silencio y que la mayoría de usuarios de IRC usa sin saber que existe. Cada vez que escribes /me en un canal, estás enviando un mensaje CTCP ACTION. Cada vez que alguien te hace un VERSION y tu cliente responde automáticamente, eso es CTCP. Y cada transferencia DCC que hayas hecho en tu vida pasó por una negociación CTCP.

Llevo operando servidores IRC desde 1997 y CTCP ha sido tanto una herramienta útil como un vector de problemas. Este artículo va a explicar el protocolo desde el nivel de bytes hasta las implicaciones prácticas de seguridad.

¿Qué es CTCP y por qué existe?

CTCP significa Client-To-Client Protocol. El nombre es un poco engañoso porque los mensajes pasan por el servidor IRC — no son realmente "cliente a cliente" en el sentido de una conexión directa (eso sería DCC). Lo que CTCP hace es incrustar comandos especiales dentro de mensajes PRIVMSG y NOTICE normales, usando un carácter delimitador especial.

¿Por qué se diseñó así? Porque el protocolo IRC original (RFC 1459) no tenía un mecanismo para comunicación estructurada entre clientes. Los mensajes eran texto plano, punto. CTCP fue un hack elegante: usar un carácter que no aparece en texto normal como delimitador, y meter comandos ahí dentro. El servidor no necesita saber nada — simplemente reenvía el PRIVMSG como haría con cualquier otro.

El carácter delimitador es \x01 (SOH, Start of Heading, ASCII 1). Cualquier texto entre dos \x01 dentro de un PRIVMSG se interpreta como CTCP.

Anatomía de un mensaje CTCP

Un mensaje CTCP tiene esta estructura a nivel de protocolo IRC:

# Petición CTCP (se envía como PRIVMSG):
:nick!user@host PRIVMSG target :\x01COMANDO parámetros\x01

# Respuesta CTCP (se envía como NOTICE):
:nick!user@host NOTICE target :\x01COMANDO respuesta\x01

# Ejemplo real - petición VERSION:
:marco!marco@irc6.net PRIVMSG juan :\x01VERSION\x01

# Respuesta VERSION:
:juan!juan@libera.chat NOTICE marco :\x01VERSION HexChat 2.16.1 / Linux 6.1.0\x01

# Ejemplo ACTION (lo que envía /me):
:marco!marco@irc6.net PRIVMSG #canal :\x01ACTION está compilando UnrealIRCd por enésima vez\x01

Fíjate en la diferencia: las peticiones van como PRIVMSG, las respuestas como NOTICE. Esto es una regla que todo cliente debería respetar. Si un cliente responde con PRIVMSG en vez de NOTICE, puede generar bucles infinitos entre dos clientes que se responden mutuamente.

Advertencia: Un error frecuente en bots mal programados es responder a CTCP con PRIVMSG en vez de NOTICE. Esto puede provocar un ping-pong infinito entre dos bots, generando flood que acabará con ambos baneados. Siempre usa NOTICE para respuestas CTCP.

Los comandos CTCP estándar

VERSION

El más conocido. El cliente que recibe la petición responde con el nombre y versión de su software. Es útil para estadísticas de red y para detectar clientes problemáticos o vulnerables.

# Enviar petición VERSION a un usuario:
/ctcp nick VERSION

# Respuestas típicas:
VERSION WeeChat 4.1.2 (Linux; x86_64)
VERSION HexChat 2.16.1 / Linux 6.1.0-amd64
VERSION irssi v1.4.4
VERSION KVIrc 5.0.0 Aria

Como operador, he usado VERSION scans masivos para detectar botnets. Cuando ves 200 conexiones nuevas que responden todas con la misma cadena VERSION sospechosa, tienes un patrón claro para un kline.

PING

Envía un timestamp y espera que el otro cliente lo devuelva. La diferencia de tiempo te da la latencia aproximada entre ambos clientes (a través del servidor).

# Enviar CTCP PING:
/ctcp nick PING

# A nivel de protocolo:
PRIVMSG nick :\x01PING 1686230400\x01

# Respuesta:
NOTICE marco :\x01PING 1686230400\x01

# Tu cliente calcula: tiempo_actual - 1686230400 = latencia

TIME

Pide la hora local del otro usuario. Parece inofensivo, pero revela la zona horaria del usuario, lo cual puede ser información más sensible de lo que parece.

# Petición:
/ctcp nick TIME

# Respuesta típica:
TIME Thu Jun 08 15:32:10 2023 CEST

ACTION

El comando detrás de /me. A diferencia de los otros, ACTION no espera respuesta. Es unidireccional.

# Cuando escribes:
/me compila código

# Se envía:
PRIVMSG #canal :\x01ACTION compila código\x01

# El cliente receptor lo muestra como:
* marco compila código

SOURCE, USERINFO, CLIENTINFO

Comandos menos usados. SOURCE indica dónde obtener el código fuente del cliente. USERINFO devuelve una cadena configurable por el usuario. CLIENTINFO lista qué comandos CTCP soporta el cliente. En la práctica, pocos clientes modernos implementan los tres.

DCC: conexiones directas vía CTCP

DCC (Direct Client-to-Client) usa CTCP para la negociación inicial, pero después establece una conexión TCP directa entre los dos clientes, sin pasar por el servidor IRC. Esto se diseñó para transferencia de archivos y para chat privado que no pudiera ser interceptado por el servidor.

# Negociación DCC SEND (transferencia de archivo):
# El emisor envía:
PRIVMSG nick :\x01DCC SEND archivo.tar.gz 3232235777 5000 1048576\x01
#                                         ^IP decimal  ^puerto ^tamaño

# La IP 3232235777 = 192.168.1.1 en formato decimal
# El receptor abre una conexión TCP a esa IP:puerto

# DCC CHAT (chat directo):
PRIVMSG nick :\x01DCC CHAT chat 3232235777 5001\x01

El problema gordo con DCC: expone la IP real del emisor. No hay forma de evitarlo en el protocolo original — es el diseño mismo. Si estás detrás de un cloak o usas Tor, un DCC SEND revela tu IP real al receptor. Esto ha sido explotado innumerables veces.

Algunos servidores modernos implementan DCC passthrough o DCC relay para mitigar esto, pero no es universal.

Consejo: Si no necesitas DCC, desactívalo completamente en tu cliente. En HexChat: Configuración → Preferencias → File Transfers → desmarcar todo. En WeeChat: /set irc.ctcp.dcc "". Reduces tu superficie de ataque sin perder nada que no puedas hacer con un servicio de transferencia externo.

CTCP y seguridad: los riesgos reales

CTCP tiene varios vectores de ataque que he visto explotar en producción:

1. CTCP flood

El ataque más básico. Un atacante envía cientos de peticiones CTCP VERSION, PING, etc. al objetivo. Si el cliente responde a todas, genera flood de salida y el servidor lo desconecta por excess flood.

# Protección en el lado cliente (WeeChat):
/set irc.ctcp.version ""
/set irc.ctcp.ping ""
/set irc.ctcp.time ""
# Esto silencia las respuestas CTCP completamente

# Protección parcial (responder pero limitar):
# La mayoría de clientes modernos tienen rate-limiting CTCP integrado
# HexChat: 5 respuestas cada 30 segundos por defecto

2. Fingerprinting

La respuesta VERSION revela tu sistema operativo, versión del cliente y a veces la arquitectura. Un atacante puede usar esto para seleccionar exploits específicos.

# Personalizar la respuesta VERSION en WeeChat:
/set irc.ctcp.version "un cliente IRC"

# En HexChat (via plugin Python):
import hexchat
def version_hook(word, word_eol, userdata):
    hexchat.command("NCTCP {} VERSION {}".format(word[0], "IRC client"))
    return hexchat.EAT_ALL
hexchat.hook_server("PRIVMSG", version_hook)

3. IP leak vía DCC

Ya lo mencioné, pero vale la pena repetirlo: DCC expone tu IP real. Un atacante ni siquiera necesita que aceptes la transferencia — la IP ya está en el mensaje de negociación CTCP.

4. Exploits en parsing CTCP

Históricamente ha habido vulnerabilidades en cómo los clientes parsean mensajes CTCP. Cadenas malformadas con caracteres de control anidados han causado crashes, buffer overflows y en casos extremos ejecución de código. Los clientes modernos son más resistentes, pero el riesgo existe en implementaciones antiguas.

Configuración del lado servidor

Como operador de red, tienes herramientas para gestionar el abuso CTCP a nivel de servidor:

UnrealIRCd

# unrealircd.conf - Limitar CTCP flood
set {
    anti-flood {
        target-flood {
            # Máximo 4 CTCPs por 30 segundos a un usuario
            private-ctcp 4:30;
            # Máximo 10 CTCPs por 15 segundos en un canal
            channel-ctcp 10:15;
        }
    }
}

# User mode +T bloquea CTCPs entrantes
# Channel mode +C bloquea CTCPs en el canal

InspIRCd

# inspircd.conf

# Permite channel mode +C para bloquear CTCP
# User mode +T para bloquear CTCP privados

# Limitar CTCP con el módulo de anti-flood:

Políticas de red

En redes que he administrado, la política estándar es:

  • CTCP ACTION permitido siempre (es funcionalidad básica de chat)
  • CTCP VERSION, PING, TIME permitidos con rate-limiting estricto
  • CTCP DCC bloqueado por defecto en canales, permitido en privado
  • CTCP desconocidos (custom) bloqueados completamente

Esta configuración cubre el 99% de los casos de uso legítimos y bloquea la mayoría de abusos.

Preguntas frecuentes

¿CTCP funciona a través de bouncers como ZNC?

Sí, pero con matices. ZNC reenvía peticiones CTCP al cliente conectado. Si no hay cliente conectado, ZNC puede responder por ti con una cadena configurable. El problema es la latencia: si tu bouncer está en Frankfurt y tú en Buenos Aires, un CTCP PING va a mostrar la latencia bouncer-peticionario, no la tuya real. Puedes configurar ZNC para bloquear CTCP con el módulo ctcpflood.

¿Es posible enviar CTCP a un canal entero?

Técnicamente sí — envías un PRIVMSG con delimitadores CTCP al canal en vez de a un nick. En la práctica, la mayoría de servidores modernos bloquean CTCP a canales (excepto ACTION) o lo limitan severamente. Un CTCP VERSION a un canal de 500 personas generaría 500 respuestas NOTICE simultáneas, lo cual es básicamente un ataque DDoS autoinfligido.

¿CTCP tiene futuro con IRCv3?

IRCv3 no elimina CTCP pero reduce la necesidad de algunos comandos. Por ejemplo, message-tags permite adjuntar metadatos a mensajes sin recurrir a CTCP. server-time proporciona timestamps del servidor, haciendo TIME menos relevante. ACTION probablemente seguirá indefinidamente porque es demasiado ubicuo como para cambiarlo. El draft de CTCP en IRCv3 básicamente estandariza lo que ya existe y desaconseja los comandos más oscuros.

¿Puedo crear mis propios comandos CTCP?

Sí, es trivial. El protocolo no define un conjunto cerrado de comandos. Si tu cliente envía \x01MICOMANDO datos\x01 dentro de un PRIVMSG, es un CTCP válido. El cliente receptor necesita saber qué hacer con él — si no lo reconoce, simplemente lo ignora. Algunos bots usan CTCP custom para comunicación entre nodos.